From bc51b04c31c990737033b82596dd013fc404f2d5 Mon Sep 17 00:00:00 2001 From: bernatsampera Date: Tue, 4 Aug 2026 19:20:51 +0200 Subject: [PATCH] Add framework prompts to Commands dashboard and clarify setup guidance - Expose built-in framework prompts alongside project commands/ files in the /api/commands endpoint and Commands.jsx UI, grouped by owner. - Tighten setup.md so approvals aren't re-asked for explicitly requested items and agent.md is written without confirmation. - Document commands/ folders and module portability rules in framework-instructions.md; trim duplicated conventions from docs/modules.md in favor of that single source. --- docs/design.md | 2 +- docs/modules.md | 10 +---- gcontext/commands.py | 11 +++++ gcontext/dashboard.py | 49 ++++++++++++---------- gcontext/prompts/framework-instructions.md | 19 +++++++++ gcontext/prompts/setup.md | 31 ++++++++++---- web/src/Commands.jsx | 21 ++++++++-- 7 files changed, 97 insertions(+), 46 deletions(-) diff --git a/docs/design.md b/docs/design.md index f78f02b..8892473 100644 --- a/docs/design.md +++ b/docs/design.md @@ -14,7 +14,7 @@ This principle removed two features in sequence. An early version shipped a ~230 There is no database, no manifest system, no type registry. An agent is a directory: markdown for context, YAML for config, an env file for secret values. If you can create a folder and put a markdown file in it, you can extend the agent. The whole thing versions with git, which means agent state gets diffs, history, review, and rollback for free. -The predecessor of this design had typed modules (integration / task / workflow) with manifest files. It was rejected: the classification created decision paralysis ("is this a task or a workflow?") and the manifests had too many fields. A folder is a folder. Progressive complexity instead: start with markdown (knowledge), add a `connection.yaml` when you need secrets (integration), add a `flow.yaml` when work has steps (process). +The predecessor of this design had typed modules (integration / task / workflow) with manifest files. It was rejected: the classification created decision paralysis ("is this a task or a workflow?") and the manifests had too many fields. A folder is a folder. Progressive complexity instead: start with markdown (knowledge), add a `connection.yaml` when you need secrets (integration), add scripts and commands when procedures prove themselves. ## Config is YAML, content is markdown, behavior is scripts diff --git a/docs/modules.md b/docs/modules.md index afafdd9..1285991 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -48,15 +48,7 @@ There is no enforced schema beyond `index.md`. Different modules have different ## How a module grows -Nothing is enforced in code; these are the conventions the framework instructions push to every connected agent. Retrieval in gcontext is `list_dir` and `grep`, no index and no search, so the tree itself is the index. The rules keep it navigable: - -- One topic per module. When a second topic appears, it is a second module, not a subfolder. -- A folder's `index.md` is its map: what the folder holds, plus one line per child file or subfolder. A reader (human or agent) should know where to go after reading only the `index.md`. -- Stay flat until several files share a clear sub-topic. Then make one subfolder per sub-topic (`playbooks/`, `logs/`, `scripts/`), and give it its own `index.md` if it holds more than a handful of files. Never create folders for dates or counts; `logs/2026/08/` hides content that one append-only file with a stated format holds better. -- Soft limits, not caps: keep a single listing under a couple dozen entries, and nesting within about three levels below `modules/`. Passing them is a signal to reorganize, not an error. -- Split a file when it stops being readable in one pass, not before. Many small fragments cost more round-trips than one coherent file. - -The soft limits deliberately replace harder rules from an earlier design (a fixed maximum of files per level): agents follow numeric caps literally and produce premature subfolders. Judgment plus a self-describing `index.md` scales further. +Nothing is enforced in code. The conventions (one topic per module, index.md as the folder's map, stay flat until sub-topics emerge, split files only when they stop being readable in one pass) are pushed to every connected agent by the framework instructions (`gcontext/prompts/framework-instructions.md` in the package); that file is the single source for them. ## How someone uses a module diff --git a/gcontext/commands.py b/gcontext/commands.py index 7731959..65e4879 100644 --- a/gcontext/commands.py +++ b/gcontext/commands.py @@ -123,6 +123,17 @@ def discover(root: Path) -> list[Path]: ) +_FRAMEWORK_SKIP = {"framework-instructions", "resources", "README"} + + +def discover_framework_prompts() -> list[Path]: + """Framework-shipped prompt files (same filter as register_framework_prompts).""" + prompts_dir = Path(__file__).parent / "prompts" + return sorted( + p for p in prompts_dir.glob("*.md") if p.stem not in _FRAMEWORK_SKIP + ) + + def register_framework_prompts(mcp) -> int: """Register the framework's own prompts, shipped in the package. diff --git a/gcontext/dashboard.py b/gcontext/dashboard.py index a7b207c..0117cfb 100644 --- a/gcontext/dashboard.py +++ b/gcontext/dashboard.py @@ -93,6 +93,28 @@ async def api_modules(request: Request) -> JSONResponse: return JSONResponse(result) +def _command_entry(path: Path, owner: str, rel: str, kind: str) -> dict: + entry = {"owner": owner, "name": f"{owner}__{path.stem}", "kind": kind, "path": rel} + try: + text = path.read_text(encoding="utf-8") + if path.suffix == ".md": + meta, _ = commands_mod.parse_command(text) + else: + meta = commands_mod.parse_script_command(text) + entry["description"] = meta.get("description", "") + entry["args"] = [ + { + "name": p.get("name", "?"), + "description": p.get("description", ""), + "required": bool(p.get("required", False)), + } + for p in (meta.get("parameters") or []) + ] + except (ValueError, KeyError, yaml.YAMLError) as e: + entry["error"] = str(e) + return entry + + @mcp.custom_route("/api/commands", methods=["GET"]) async def api_commands(request: Request) -> JSONResponse: root = _root() @@ -100,29 +122,10 @@ async def api_commands(request: Request) -> JSONResponse: for path in commands_mod.discover(root): rel = str(path.relative_to(root)) owner = path.parent.parent.name - entry = { - "owner": owner, - "name": f"{owner}__{path.stem}", - "kind": path.suffix.lstrip("."), - "path": rel, - } - try: - text = path.read_text(encoding="utf-8") - if path.suffix == ".md": - meta, _ = commands_mod.parse_command(text) - else: - meta = commands_mod.parse_script_command(text) - entry["description"] = meta.get("description", "") - entry["args"] = [ - { - "name": p.get("name", "?"), - "description": p.get("description", ""), - "required": bool(p.get("required", False)), - } - for p in (meta.get("parameters") or []) - ] - except (ValueError, KeyError, yaml.YAMLError) as e: - entry["error"] = str(e) + result.append(_command_entry(path, owner, rel, path.suffix.lstrip("."))) + for path in commands_mod.discover_framework_prompts(): + entry = _command_entry(path, "framework", f"gcontext/prompts/{path.name}", "md") + entry["name"] = path.stem result.append(entry) return JSONResponse(result) diff --git a/gcontext/prompts/framework-instructions.md b/gcontext/prompts/framework-instructions.md index 1ee8385..b60a83d 100644 --- a/gcontext/prompts/framework-instructions.md +++ b/gcontext/prompts/framework-instructions.md @@ -17,9 +17,28 @@ How the folder is organized: practice. Read the index.md before writing a script against a service, and update it when you learn something worth keeping. - modules//: accumulated knowledge on a topic, entry point index.md. + Modules are portable: another agent can use one by copying the folder. So + keep a module connection-agnostic in its process files (say "the payment + provider", not "Stripe"); the agent finds the concrete service in + connections/ at run time. Company-specific facts learned while working + (playbooks, logs) are fine; hard-wired service names in the steps are not. - scripts/ folders (inside connections and modules): proven procedures. Run them by path with run_script instead of rewriting them, and save a script there once it has proven itself. +- commands/ folders (inside connections and modules): user-invokable entry + points, exposed as MCP prompts (slash commands in Claude Code, named + /mcp______). Two file types: + - .md: a prompt command. Starts with a `---` YAML frontmatter block + holding `description` and optional `parameters` (list of `name`, + `description`, `required`); the body is injected into the conversation + with `$name` placeholders filled from the arguments. + - .py: a script command. Starts with the same frontmatter as a + `# ---` comment block; invoking it tells the agent to run the file via + run_script with the arguments as params. + When the user asks for a reusable command or workflow entry point, this + is where it goes: write the file with write_file under the connection or + module it belongs to. New commands appear after a server restart, which + the user must do; tell them. - archive/: retired state. Not scanned or listed, still readable by path. How state grows: one topic per module. A folder's index.md is its map and diff --git a/gcontext/prompts/setup.md b/gcontext/prompts/setup.md index de2af56..9f09d3f 100644 --- a/gcontext/prompts/setup.md +++ b/gcontext/prompts/setup.md @@ -17,6 +17,14 @@ The user's request, possibly empty: "$request" - The user does not need to know gcontext's concepts. Never ask "do you want a connection or a module?". They describe goals; YOU decide what each goal needs and explain your plan in one plain line per item. +- Setup is not a one-shot. Say early that nothing has to be decided now: + the user can start with one or two items and add or change anything later + by running setup again or simply asking the agent. +- Do not re-confirm what the user already said. When the user explicitly + named a service or a topic, that item is approved; asking "should I add + the X connection?" again is noise. The plan confirmation (Step 3) also + counts as write approval for the files that build those items: announce + each write in one line, but do not ask again per file. - Inspect before you ask. Never ask the user something the state folder can answer. Start with list_dir(".") and read what you need from there. - When your runtime has an interactive question tool (AskUserQuestion in @@ -64,22 +72,27 @@ Translate the description into a plan. The mapping is yours to make: - Knowledge the agent must hold (how the company works, a product, a process, a team's rules): a module each. Prefer a few broad modules over many thin ones; a module can grow files later. -- If agent.md is still the placeholder, writing it from the user's - description is always part of the plan. +- If agent.md is still the placeholder, write it yourself from the user's + description. It is not a plan item and never a question: do not ask what + it should say or confirm its content, just write it at the start of the + build and mention in one line that you did. Show the plan as a short list, one plain line per item ("stripe: so the agent -can look up payments and refunds"), and confirm it as a choice question: -build all of it, or let the user deselect items (multi-select when the tool -supports it). First-time setups with many items are the normal case; do not -talk the user out of a big plan, but order it so something useful exists -early. +can look up payments and refunds"). Items the user explicitly asked for are +already approved; confirm only the items you inferred yourself, as a choice +question (multi-select when the tool supports it). If every item was +explicitly requested, skip the confirmation and start building. First-time +setups with many items are the normal case; do not talk the user out of a +big plan, but order it so something useful exists early, and remind them +the rest can always be added later. ## Step 4: Build, one item at a time Work through the confirmed plan. Finish each item before starting the next, and say briefly where you are ("2 of 5"). Suggested order: agent.md -first, then modules (they only need conversation), then connections (each -needs the user to place secrets). +first (written directly from the description, no questions), then modules +(they only need conversation), then connections (each needs the user to +place secrets). **Add a connection:** diff --git a/web/src/Commands.jsx b/web/src/Commands.jsx index a7e4494..bed78ff 100644 --- a/web/src/Commands.jsx +++ b/web/src/Commands.jsx @@ -57,7 +57,7 @@ export default function Commands() {

Commands

- Files under commands/ folders, served as MCP prompts. New files appear after a server restart. + All MCP prompts: project commands from commands/ folders and built-in framework prompts, grouped by owner.

{cmds.length === 0 ? ( @@ -69,9 +69,22 @@ export default function Commands() { ) : ( <>
Commands ({cmds.length})
-
- {cmds.map((c) => )} -
+ {(() => { + const groups = {}; + cmds.forEach((c) => { (groups[c.owner] = groups[c.owner] || []).push(c); }); + const owners = Object.keys(groups).sort((a, b) => a === "framework" ? 1 : b === "framework" ? -1 : a.localeCompare(b)); + return owners.map((owner) => ( +
+
+ {owner} + ({groups[owner].length}) +
+
+ {groups[owner].map((c) => )} +
+
+ )); + })()} )}