Files
windmill/system_prompts
hugocasaandClaude Opus 5.5 88c3cc0ced feat: guide the AI when no resource type fits a service (#11615)
* feat: guide the AI when no resource type fits a service

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

* fix: report a failed resource type search and model a synced instance in evals

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

* fix: keep named resource types first and list only related names on search failure

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

* fix: point the sync note at the hub sync button only

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

* refactor: list the instance's resource types when the search finds nothing

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

* docs: let the user create a resource type instead of syncing the hub

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

* docs: make the linked-variable resource the default, not the only option

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

* test: add a cli eval for a service without a hub resource type

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

* feat: keep the resource type search fresh and serve it without embeddings

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

* feat: let the global chat create a resource type after the user confirms it

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

* test: simulate create_resource_type in global evals and fix the in-house API checklists

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

* fix: reuse unchanged custom resource type vectors and match names case-insensitively

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

* fix: add the unsynced note whatever the resource type search returns

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

* fix: carry the no-type rule into flow inputs and script parameters, and ask the chat to relay the sync

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

* docs: tell script chat to hand the user the schema it cannot create

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

* fix: skip the resource type fingerprint query when no index is loaded

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

* feat: index resource types locally when the hub's embeddings cannot be fetched

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

* fix: match short service names in the resource type text fallback

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

* fix: name custom resource types with the c_ prefix the Resources page uses

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

* test: require the c_ prefix in in-house API evals and a real sync mention

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

* test: cover workspace scoping of the resource type text search

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

* docs: say the form adds the c_ prefix unless an admin turns it off

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

* fix: refresh resource type names and the resources tab after the chat creates a type

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

* perf: fingerprint resource types without sorting every schema

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

* fix: tell the global chat in each message when the hub's types are not synced

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

* fix: send users who cannot deploy to an admin for a resource type, and have the cli list types first

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YR13nJWziUSWktZwhC7dx

* refactor: move the resource type search index changes to their own pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YR13nJWziUSWktZwhC7dx

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:24:10 +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
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.