Files
windmill/system_prompts
hugocasaandClaude Opus 5.5 813c080d46 feat: sandbox new low-code and raw apps by default (#11760)
* feat: sandbox new low-code and raw apps by default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: navigate the top page from plain links in a sandboxed low-code app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: allow clipboard and fullscreen in sandboxed raw-app frames

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: leave non-http links to the browser in the sandboxed app frame

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let a link's own click handlers run before relaying it out of the frame

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: read link attributes so SVG anchors relay correctly

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: tidy the app deploy drawer to the brand guidelines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: show enabled frontend api scopes as badges, rename runs-as section

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep frontend api access collapsed by default, badges next to its title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: lead the run-on-behalf-of section with the user picker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: explain publisher execution and WM_END_USER_EMAIL in the run-on-behalf-of tooltip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: shorten the run-on-behalf-of tooltip, mention viewer data access for raw apps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: tell the AI what breaks in a sandboxed raw app and that turning it off is a security risk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: warn in the deploy drawer when an app is not isolated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: drop the sandbox toggle help line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: say when to turn sandbox isolation off in the toggle tooltip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: show who a non-deployer's app runs as, read-only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show a new app runs as the deployer, gray read-only user line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: read-only on-behalf-of display as an opt-in selector prop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show a folder's default run-as user on a new app instead of the deployer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: deploy an app under a group folder default without a mismatched email

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* revert: leave a new app's run-as user unselected, as on main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: warn in the app deploy drawer when a deploy changes the run-as user

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: snapshot the deployed policy so later edits don't change the saved app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:54:00 +02:00
..

System Prompts

The single source of the AI guidance Windmill ships: what an agent needs to know about Windmill itself to write flows, scripts, apps, pipelines, resources, triggers and schedules. Two consumers are built from it:

  • the chat (the frontend AI chat: flow, script, app and global modes), through the $system_prompts alias to auto-generated/;
  • the CLI (wmill init / wmill refresh prompts), as the skills embedded in cli/src/guidance/skills.gen.ts, next to the AGENTS.wmill.md template in cli/src/guidance/core.ts.

Guidance written in one consumer only is guidance the other never gets. Write it here.

Structure

system_prompts/
├── base/              # Hand-written guidance, one file per topic (flow-base.md, raw-app.md, …)
│                      # plus CLI-only intros (flow-cli.md, script-cli.md, raw-app-cli.md, …)
├── languages/         # Hand-written per-language guidance (bun.md, python3.md, …)
├── generate.py        # Builds everything under auto-generated/ and cli/src/guidance/skills.gen.ts
├── utils.py           # Shared helpers, including the fence renderer
└── auto-generated/    # Generated — never edit
    ├── prompts.ts     # One export per base/ and languages/ file (chat render), SDK docs, schema
    ├── index.ts       # Chat helpers (getFlowPrompt, getScriptPrompt, …)
    ├── skills/        # One SKILL.md per CLI skill (cli render)
    ├── sdks/          # SDK reference extracted from the TypeScript and Python clients
    └── cli/           # CLI command reference extracted from cli/src/commands/

How a topic is assembled

TOPICS in generate.py lists, for each topic, the parts both consumers concatenate in order: a file under base/ or languages/, or a token filled per consumer ({lang}, {sdk}, {wac_sdk}, {openflow_schema}, {cli_commands}). The same entry produces the topic's chat helper in index.ts and its CLI skill, so a part added there reaches both. A part tagged ('chat', …) or ('cli', …) goes to that consumer only, and cli_intro heads the CLI skill with a CLI-only workflow file (flow-cli.md, script-cli.md, raw-app-cli.md).

Topic Chat helper CLI skill Shared parts
script getScriptPrompt(lang) write-script-<lang> script-base.md, the language file, its SDK
flow getFlowPrompt() write-flow flow-base.md, the OpenFlow schema
raw app getRawAppPrompt(lang) raw-app raw-app.md (chat adds the SDK)
resources getResourcePrompt() resources resources.md
workflow-as-code getWorkflowAsCodePrompt(lang) write-workflow-as-code workflow-as-code.md, the WAC SDK
pipeline getPipelinePrompt() write-pipeline pipeline-base.md
data tables getDatatablePrompt() datatables datatable.md
workspace settings getWorkspaceSettingsPrompt() workspace-settings workspace-settings.md
triggers, schedules, preview, CLI commands — same names CLI only

The app chat embeds RAW_APP_BASE directly (chat/app/core.ts).

Consumer fences

A sentence that only one consumer should see stays in the shared file, fenced:

A tool name outside that character set fails every run of the flow.
<!-- chat-only -->
The flow write tools refuse such a name.
<!-- /chat-only -->
<!-- cli-only -->
`wmill lint <flow folder>` reports it before anything runs.
<!-- /cli-only -->

render_for (utils.py) renders every file once per consumer: prompts.ts gets the chat render, the skills get the cli render, and the fence lines reach neither. Fences sit on their own lines and cannot nest; an unbalanced or misspelled fence fails generation (an HTML comment opening with cli or chat, or ending in only, is read as a fence attempt).

Use a fence for a sentence or a section. When most of a topic differs per consumer, a CLI-only file used as cli_intro reads better than a file that is mostly fences.

What goes where

  • Here: anything true of Windmill regardless of who is asking — module shapes, data flow rules, what the editor accepts, how data tables, secrets or app access work.
  • In the chat's TypeScript prompt builders (frontend/src/lib/components/copilot/chat/*/core.ts): only tool plumbing (which tool to call, its arguments) and values known at run time (the user's name, this app's data-table policy, session capabilities).
  • In cli/src/guidance/core.ts (AGENTS.wmill.md): project-level CLI workflow — which skill to use, deploying, debugging jobs.

A file in TOPICS names no chat tool, not even inside a chat-only fence: it reaches every chat mode (flow mode's system prompt, global mode's get_instructions), each with its own tool names, and global/sessionToolset.test.ts fails a prompt that names a tool its session lacks. Describe the action instead ("the flow write tools refuse such a name") and name the tool in TypeScript. The one exception is flow-chat-special-modules.md, which only flow mode reads.

Regenerating

After editing anything here, or when SDK methods, the OpenFlow schema or CLI commands change:

python system_prompts/generate.py

CI (.github/workflows/check-system-prompts.yml) runs system_prompts/check-freshness.sh, which fails when the committed output is stale.

To also refresh the standalone skills in a Claude plugin checkout:

python system_prompts/generate.py --plugin-dir ~/windmill-claude-plugin

--plugin-dir accepts:

  • the windmill-claude-plugin repo root
  • a plugin root such as plugins/windmill
  • a direct skills/ directory

To regenerate the public docs repo (consumed by context7):

python system_prompts/generate.py --context7-dir ~/windmill-cli-docs

--context7-dir writes a fully-rendered snapshot (AGENTS.md, cli-commands.md, skills/<name>/SKILL.md, README.md, manifest.json with the Windmill version) with all template placeholders resolved — suitable for ingestion by docs aggregators. In CI this runs from .github/workflows/publish-cli-docs.yml on every release tag. The generator refuses to wipe the target directory unless it's empty or has a context7 marker (context7.json, manifest.json, or a windmill-cli-docs git remote), so a typo can't delete unrelated files.