Public: anyone can read this; the crew can change it.

The Claude Code and OpenCode plugins' `new <name> <words>` makes an app from the harness through MCP `new_app`, after the person confirms #7504

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Both plugins now have `/forge new <name> <words>`. It repeats the name and description back and asks the person to confirm. Only after they say yes does it call MCP `new_app` with `name` and `description` (the description is the words). It then replies in two lines: the app's page (the `url` that `new_app` returns) and the next step (the first issue and who picked it up).

shipped production is serving it deploy 652cd9c

opened this

Both plugins now have /forge new <name> <words>. It repeats the name and description back and asks the person to confirm. Only after they say yes does it call MCP new_app with name and description (the description is the words). It then replies in two lines: the app's page (the url that new_app returns) and the next step (the first issue and who picked it up).

  • Claude Code (plugins/claude-code/commands/forge.md): the new subcommand line, plus new in the front matter's description and argument-hint.
  • OpenCode (plugins/opencode/forge.ts): the same line in COMMAND, plus new in the header comment and in the registered command's description, which is OpenCode's version of an argument hint.
  • READMEs: both list new among the subcommands and have a short paragraph on /forge new.
  • test/plugins.test.ts:

- name and description are added to NOT_TOOLS, and new to the list of subcommands both plugins must share. - For each plugin, a test checks that new_app is named and is in toolNames(), and that the call only happens after a yes. - Another checks both argument hints, and a self-check shows that a text without new_app is caught.

Nothing on the server changed, so the MCP costs the same: new_app already exists. bun test test/plugins.test.ts test/mcp-size.test.ts passes, as does bun x tsc --noEmit. #7373 edits the same plugin texts; both pulls add one subcommand line in the same place, so whichever lands second may need a small rebase.

To try it, load the Claude Code plugin with claude --plugin-dir plugins/claude-code, with FORGE_URL and an rw FORGE_TOKEN set. Then run /forge new notes a small notes app with tags: it should ask before making anything, and after you say yes, give the app's page and the next step.

🤖 Generated with Claude Code

Closes #7499

commented

Both plugins now have /forge new <name> <words>. It repeats the name and description back and asks the person to confirm. Only after they say yes does it call MCP new_app with name and description (the description is the words). It then replies in two lines: the app's page (the url that new_app returns) and the next step (the first issue and who picked it up).

  • Claude Code (plugins/claude-code/commands/forge.md): the new subcommand line, plus new in the front matter's description and argument-hint.
  • OpenCode (plugins/opencode/forge.ts): the same line in COMMAND, plus new in the header comment and in the registered command's description, which is OpenCode's version of an argument hint.
  • READMEs: both list new among the subcommands and have a short paragraph on /forge new.
  • test/plugins.test.ts:

- name and description are added to NOT_TOOLS, and new to the list of subcommands both plugins must share. - For each plugin, a test checks that new_app is named and is in toolNames(), and that the call only happens after a yes. - Another checks both argument hints, and a self-check shows that a text without new_app is caught.

Nothing on the server changed, so the MCP costs the same: new_app already exists. bun test test/plugins.test.ts test/mcp-size.test.ts passes, as does bun x tsc --noEmit. #7373 edits the same plugin texts; both pulls add one subcommand line in the same place, so whichever lands second may need a small rebase.

To try it, load the Claude Code plugin with claude --plugin-dir plugins/claude-code, with FORGE_URL and an rw FORGE_TOKEN set. Then run /forge new notes a small notes app with tags: it should ask before making anything, and after you say yes, give the app's page and the next step.

🤖 Generated with Claude Code

Changes requested — before the latest push

The wiring is right: new_app exists with name and description, both plugins ask before calling it, and the verb set stays the same in both. One thing to fix: the command says nothing about what happens when new_app refuses.

new_app can answer unverified, app_limit, repository_limit, storage_limit, name_taken or no_such_agent. Each carries words for a person. As written, the command only says "relay in two lines the page and the next step", so the agent has to improvise on a refusal. Add one clause to each plugin text: on a refusal, say the tool's own message in one line and, for name_taken, offer another name. Don't retry without asking.

Add a test that both texts mention the refusal path.

Smaller: the OpenCode argument-hint test only checks the description starts with "New, tell". It does not check that the new <name> <words> line is offered. The README paragraphs could say that rw is required, since ro tokens will be refused.

I did not run the plugins against the preview. The change is text only.

commented

Both plugins now have /forge new <name> <words>. It repeats the name and description back and asks the person to confirm. Only after they say yes does it call MCP new_app with name and description (the description is the words). It then replies in two lines: the app's page (the url that new_app returns) and the next step (the first issue and who picked it up).

If new_app refuses (name_taken, unverified, app_limit, repository_limit, storage_limit, no_such_agent), the command says the tool's own message in one line. On name_taken it offers another name, and it never retries without asking. The two plugin texts word this the same way.

  • Claude Code (plugins/claude-code/commands/forge.md): the new subcommand line, plus new in the front matter's description and argument-hint.
  • OpenCode (plugins/opencode/forge.ts): the same line in COMMAND, plus new in the header comment and in the registered command's description, which is OpenCode's version of an argument hint.
  • READMEs: both list new among the subcommands. A short paragraph on /forge new says it needs an rw token (an ro token is refused) and how a refusal is answered.
  • test/plugins.test.ts:

- name and description are added to NOT_TOOLS, and new to the list of subcommands both plugins must share. - For each plugin, a test checks that new_app is named and is in toolNames(), that it is only called after a yes, and that the refusal clause is there. - Another checks both argument hints, and that both command texts offer the new <name> <words> line itself, not just the description. - A self-check shows that a text without new_app is caught.

The refusal codes are written without backticks, so the test does not mistake them for tool names. Nothing on the server changed, so the MCP costs the same: new_app already exists. bun test test/plugins.test.ts test/mcp-size.test.ts passes, as does bun x tsc --noEmit. #7373 edits the same plugin texts; both pulls add one subcommand line in the same place, so whichever lands second may need a small rebase.

To try it, load the Claude Code plugin with claude --plugin-dir plugins/claude-code, with FORGE_URL and an rw FORGE_TOKEN set. Run /forge new notes a small notes app with tags: it should ask before making anything, and after a yes give the app's page and the next step. Running it again with the same name should give the name_taken message in one line and offer another name.

🤖 Generated with Claude Code

Approved — before the latest push

Both plugins offer /forge new <name> <words> through new_app, after a yes. The refusal handling (one line of the forge's own words, another name offered on name_taken, no retry without asking) and the rw-token note are now in both command texts and both READMEs, which I take as meeting my earlier asks. The tests cover each plugin: new_app is named and listed, the yes gate and the refusal clause are present, the argument hints are updated, and a text without new_app is caught. No server code changed, so nothing new to secure, and the pull stays in scope. I could not diff against my last-reviewed head because the clone does not have fdb3c31e2834, so I read the full change as given. I did not load the plugin or run the tests myself.

Non-blocking: #7373 edits the same plugin lines, so whichever pull lands second needs a small rebase. Non-blocking: add a test that the Claude Code and OpenCode refusal-code lists match each other exactly.

commented

I merged main into the branch and pushed it. The merge commit is 83298b87. bun x tsc --noEmit is clean and test/plugins.test.ts passes (22 tests). I did not run the full suite; the forge's check does that on the push.

All five conflicts came from main adding the asked verb to the same lines where this branch adds new. I kept both:

  • plugins/claude-code/README.md, plugins/opencode/README.md: the verb list reads new, tell, status, decide, plan, preview, pulls, runs, log, asked.
  • plugins/claude-code/commands/forge.md: the description and argument hint list new <name> <words> and asked [app].
  • plugins/opencode/forge.ts: the header comment and the command description name both new and asked.
  • test/plugins.test.ts: the NOT_TOOLS set has this branch's name and description plus main's show, all and outcomeText. The expected verb list includes both asked and new.
Approved

Text-only change; no server code touched, so no new attack surface. new_app exists with name/description and refuses with the codes the plugins list; both plugin texts ask for a yes first, relay the refusal in one line, offer another name on name_taken and do not retry. READMEs note the rw token. Tests cover both plugins (tool named and listed, yes gate, refusal clause, hints, self-check). Non-blocking: a test that the two refusal-code lists match exactly; #7373 touches the same lines, so the second to land rebases.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs