command-code 1.79.2 → 1.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # command-code
2
2
 
3
+ ## 1.81.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat: add `codemode` tool that lets the model write scripts to orchestrate tool calls, MCP only by default, change via /config
8
+
9
+ ## 1.80.0
10
+
11
+ ### Minor Changes
12
+
13
+ - feat: Mods Hub + ModApi
14
+ - feat: add muse wonder
15
+ - fix: show mod user follow-ups in the feed when consumed and reserve prompt newline shortcuts.
16
+
3
17
  ## 1.79.2
4
18
 
5
19
  ### Patch Changes
@@ -116,6 +116,7 @@ The model catalog — every id `/model`, `--model`, `model:effort` shorthand, an
116
116
  | `meta/muse-spark-1.2-contributor` | Muse Spark 1.2 Contributor | 1.05M | low, medium, high, xhigh | $0.1/$0.2 · cache $0.002 | Go and above | Muse Spark 1.2 at ~95% off |
117
117
  | `meta/muse-spark-1.3` | Muse Spark 1.3 | 1.05M | low, medium, high, xhigh, max | $1.25/$4.25 · cache $0.15 | GOAT and above | multimodal reasoning for long-horizon agentic and coding workflows |
118
118
  | `meta/muse-spark-1.3-contributor` | Muse Spark 1.3 Contributor | 1.05M | low, medium, high, xhigh | $0.1/$0.2 · cache $0.002 | Go and above | Muse Spark 1.3 at up to 95% off |
119
+ | `meta/muse-wonder-1.0` | Muse Wonder | 1.05M | low, medium, high, xhigh | $4/$20 · cache $0.2 | Max | frontier coding, long-running agents and advanced computer use |
119
120
  ## xAI
120
121
 
121
122
  | Id (use EXACTLY this) | Name | Context | Efforts | $/1M in/out · cache read | Min plan | Best for |
@@ -337,6 +337,7 @@ Valid model ids (the /model catalog — use these EXACT ids):
337
337
  - meta/muse-spark-1.2-contributor
338
338
  - meta/muse-spark-1.3
339
339
  - meta/muse-spark-1.3-contributor
340
+ - meta/muse-wonder-1.0
340
341
  - nvidia/nemotron-3-ultra-550b-a55b
341
342
  - poolside/laguna-s-2.1-free
342
343
  - inclusionai/ling-3.0-flash-sante:free
@@ -439,6 +439,58 @@ MCP tools are hidden entirely in plan mode - Command Code can't verify a remote
439
439
 
440
440
  ---
441
441
 
442
+ ## Codemode
443
+
444
+ The `codemode` tool lets the model write a short JavaScript script that calls other tools. Instead of one tool call per round trip, the model writes a script that does several steps and runs it in one go: it loops, fans out with `Promise.all`, and filters, and only what it prints or returns reaches the context. A chain of MCP calls with a filter at the end becomes one call:
445
+
446
+ ```js
447
+ const issues = await tools.mcp__linear__list_issues({team: "Core"});
448
+ return issues.filter(issue => issue.open).map(issue => issue.id);
449
+ ```
450
+
451
+ | Parameter | Type | Required | Description |
452
+ | --- | --- | --- | --- |
453
+ | `code` | string | yes | The body of an async function. Top-level `await` and `return` work. |
454
+ | `timeout_ms` | number | no | Deadline for the whole script. Default 120,000, between 1,000 and 600,000. |
455
+
456
+ ### Configure codemode
457
+
458
+ Codemode has three modes, set in `/config`. The mode decides when the tool is offered to the model and which tools a script may call.
459
+
460
+ - **MCP tools.** The default. The tool is offered whenever an MCP server is connected, and scripts can call the MCP tools. Everything else works as before: MCP tools can still be loaded and called directly.
461
+ - **Read-only and MCP tools.** The tool is offered in every session. Scripts can call the MCP tools and the read-only built-in tools: reading files and directories, glob, grep, web search, and the todo and task tools. These tools are made script-first and are removed from context, so that the model uses them through codemode.
462
+ - **Off.** Disables codemode completely.
463
+
464
+ In every mode, a script can never edit or write files, run commands, ask the user a question, review a plan, switch plan mode, load tools, or start another script.
465
+
466
+ ### Globals
467
+
468
+ | Global | What it does |
469
+ | --- | --- |
470
+ | `tools.<name>(args)` | Calls a tool. One object argument, shaped like the tool's parameters. |
471
+ | `ALL_TOOLS` | The tools this script may call, with a one-line description each. |
472
+ | `searchTools("query")` | Finds callable tools by description. |
473
+ | `describeTool("name")` | Returns a tool's full schema. |
474
+ | `CWD` | The working directory. |
475
+ | `text(...)`, `console.log(...)` | Print a line. |
476
+ | `return`, `exit()` | Send back a value; end the script early. |
477
+ | `store("key", value)`, `load("key")` | Keep a JSON value across scripts in the session. |
478
+
479
+ ### Permissions
480
+
481
+ Every nested call is a real tool call: same mode filter, permission rules, input repair, and output truncation as a direct call, with prompts one at a time. `codemode` itself is read-only and never prompts, since it can do no more than the calls it makes. It is hidden in any mode that leaves a script nothing to call, such as plan mode under the default setting.
482
+
483
+ ### Limits
484
+
485
+ Scripts run in a sandbox with no file system, network, timers, or process access; the tool bridge is the only way out.
486
+
487
+ - 128 MB of memory, one sandbox per call
488
+ - 8 nested calls in flight at once
489
+ - 1 MB of printed output, after which the script fails; 25,000 tokens reach the model
490
+ - 256 KB per stored value, 1 MB per session store
491
+
492
+ ---
493
+
442
494
  ## Permission modes & tool visibility
443
495
 
444
496
  Which tools the model even *sees* depends on the mode:
@@ -15,6 +15,7 @@ The executable registry is authoritative. Run `cmd config list --scope effective
15
15
  | `peek-recap-diagram` | user | boolean | `true`, `false` | When a task that changed files or ran commands finishes, draw one short text diagram recapping it (one extra model call per task, same as /peek). |
16
16
  | `peek-recap-delay` | user | enum | `0`, `1`, `2`, `5`, `10`, `15`, `30`, `60` | Recap a finished task only when this long passed since your last message, so a quick back-and-forth is not recapped every time (same as /peek delay). |
17
17
  | `image-vision` | user | enum | `ask`, `enabled`, `disabled` | Let a text-only model read attached images by describing them with the vision model ("Ask on first use" prompts the first time you attach an image). |
18
+ | `codemode` | user | enum | `mcp`, `tools`, `off` | The codemode tool lets the model write a short code script that can call several tools and run it in one go: fewer round trips, less raw tool output in context. Scripts never edit, write, or run commands. |
18
19
  | `branch-summary-skip-prompt` | user | boolean | `true`, `false` | Never ask about summarizing when switching branches in /tree. |
19
20
  | `default-export-format` | user | enum | `html`, `jsonl`, `md` | Format a bare /export (no format or path) writes — an explicit /export html\|jsonl\|md\|<path> always overrides. |
20
21
  | `default-share-gist-format` | user | enum | `html`, `jsonl`, `md` | Format a bare /share gist (no format argument) posts — an explicit /share gist html\|jsonl\|md always overrides. |
@@ -114,9 +114,13 @@ run `pnpm generate:knowledge` after editing the docs under `packages/docs/src/ap
114
114
  ## 6. Ship it (optional)
115
115
 
116
116
  - Personal: leave it in `~/.commandcode/mods/`.
117
- - A package: a `package.json` with `{"commandcode": {"mods": ["./index.ts"]}}`, installed via
118
- `cmd mods add npm:<name>` / `cmd mods add owner/repo` / `cmd mods add ./local-dir`.
119
- Full packaging/filtering detail: `reference/packaging.md`.
117
+ - A package: a `package.json` with `{"keywords": ["cmd-mod"], "commandcode": {"mods": ["./index.ts"]}}`,
118
+ installed via `cmd mods add npm:<name>` / `cmd mods add owner/repo` / `cmd mods add ./local-dir`.
119
+ **Always include the `cmd-mod` keyword** — it is what lists the package on the
120
+ [mods hub](https://commandcode.ai/mods) once it is published to npm; without it the mod is
121
+ invisible there. Add a kind keyword too (`cmd-mod-tool`, `cmd-mod-command`, …) and a
122
+ `repository` field so the hub row shows the right category and a star count. Full
123
+ packaging/filtering detail: `reference/packaging.md`.
120
124
 
121
125
  ## Boundaries to state up front
122
126
 
@@ -92,13 +92,32 @@ export default function (cmd: ModApi): void {
92
92
  // ── observe events (never mutate here) ──────────────────────────────────────────────
93
93
  cmd.on('tool_errored', () => cmd.ui.notify('a tool errored', 'warning'));
94
94
 
95
+ // ── a keyboard shortcut (interactive TUI only; dispatched after host bindings) ──────
96
+ cmd.addShortcut('alt+u', {
97
+ description: 'Show context usage',
98
+ handler: () => {
99
+ const usage = cmd.getContextUsage();
100
+ if (!usage || usage.percent === null)
101
+ return {message: 'context window not measured yet'};
102
+ return {
103
+ message: `${cmd.getModel()} · ${usage.percent}% of ${usage.contextWindow} tokens`,
104
+ };
105
+ },
106
+ });
107
+
108
+ // ── skills shipped beside this mod (resolved against this file) ────────────────────
109
+ // cmd.addSkillsPath('./skills');
110
+
95
111
  // ── live surfaces (usable any time after the harness binds) ─────────────────────────
96
- // cmd.ui.notify / confirm / select / input — talk to the user.
112
+ // cmd.ui.notify / confirm / select / input — talk to the user; ui.setTitle — the tab.
97
113
  // cmd.queueMessage({content, deliverAs}) — steer the running loop or queue a follow-up.
114
+ // cmd.sendUserMessage(text) — a real user turn (starts one when idle).
98
115
  // cmd.session — durable custom-entry persistence (undefined until bound).
99
116
  // cmd.sessions — compact(), tree(), leafId(), navigateTree(...), setLabel(...).
100
- // cmd.setModel / setEffort / setSessionName.
117
+ // cmd.setModel / setEffort / setSessionName, and their get* counterparts.
118
+ // cmd.getContextUsage / getCommands / getToolInfo / isIdle — live reads.
101
119
  // cmd.getAllTools / getActiveTools / setActiveTools — a mod-managed tool filter.
120
+ // cmd.shutdown() — exit once the turn settles.
102
121
  //
103
122
  // Example: on the first run, greet once via a queued steering message.
104
123
  let greeted = false;
@@ -25,6 +25,8 @@
25
25
  | `cmd.addFlag` | `(name, {type: 'boolean'\|'string'; default?; description?}) => Disposable` |
26
26
  | `cmd.addProvider` | `(module: ProviderModule) => Disposable` |
27
27
  | `cmd.addRenderer` | `(customType: string, (data) => readonly string[]) => Disposable` |
28
+ | `cmd.addShortcut` | `(keys: string, {description?; handler}) => Disposable` |
29
+ | `cmd.addSkillsPath` | `(path: string) => Disposable` |
28
30
  | `cmd.on` | `(event: AgentEventType \| 'session_start' \| 'session_shutdown', handler) => Disposable` |
29
31
 
30
32
  What each does:
@@ -36,6 +38,8 @@ What each does:
36
38
  - `cmd.on(event, handler)` - observe any `AgentEvent` plus the host lifecycle events `session_start` / `session_shutdown` (fired when a harness binds the host and when a session switch or dispose replaces it). Handlers are isolated: a throw becomes a `mod_error {hook: 'on:<event>'}` event, never a crash.
37
39
  - `cmd.addProvider(providerModule)` - register a model provider through the same `ProviderModule` seam the built-ins use (id, transport, auth hooks, model list). The host appends mod providers to its set - mods extend the provider set, never replace it.
38
40
  - `cmd.addRenderer(customType, data => lines)` - a renderer for a custom entry type; returns the lines to print (style them with ansi escapes - picocolors, `@commandcode/tui` helpers, or raw codes). First registration per type wins across mods.
41
+ - `cmd.addShortcut(keys, {description, handler})` - a keyboard shortcut in the interactive TUI. `keys` is a `modifier+key` chord that **must include Ctrl or Alt** (`alt+r`, `ctrl+r`); a modifier-less or Shift-only chord (`p`, `enter`, `shift+p`) is rejected with a warning, because it would also type into the prompt, and so is a `Ctrl+Shift` chord whose key reaches the terminal as a bare control byte — letters, digits, Enter, Tab, Backspace (`ctrl+shift+p`, `ctrl+shift+enter`) — where the Shift never arrives (arrow / navigation keys keep their modifiers, so `ctrl+shift+up` is fine). `ctrl+m`, `ctrl+i`, `ctrl+h` and `ctrl+[` are rejected too: terminals send them as Enter, Tab, Backspace and Escape. A chord a built-in already owns (`ctrl+c`, `ctrl+o`, an editor key like `ctrl+k`) is rejected the same way — the host's own bindings run first, so it would never fire — and so are the keys the prompt handles itself (`alt+w`, `ctrl+z`, `ctrl+g`, `ctrl+s`, `ctrl+v`, `alt+v`), which stay taken even when rebound. The handler gets the same `{args, ui, cwd, exec}` context a command handler does and may return the same `{message}` / `{prompt}` result. The host dispatches mod shortcuts AFTER its own bindings, so a mod can add keys but never shadow interrupt, exit, mode cycling, or the transcript toggles; `Esc` stays host-owned. First registration per chord wins across mods; `/hotkeys` lists them under **Mods**. Headless hosts never fire shortcuts.
42
+ - `cmd.addSkillsPath(path)` - an extra skills location (one skill directory, or a directory of skills) for this session's skill discovery. Relative paths resolve against the mod's own file, so a package can point at the `./skills` it ships. Collected at factory time. A package can declare the same thing without code through its manifest - see [What a package ships](./packaging.md#what-a-package-ships).
39
43
 
40
44
  ## Live methods (any time after the harness binds)
41
45
 
@@ -44,15 +48,29 @@ What each does:
44
48
  | `cmd.getFlag` | `(name) => boolean \| string \| undefined` |
45
49
  | `cmd.showEntry` | `(customType, data?) => void` |
46
50
  | `cmd.queueMessage` | `({content, deliverAs?: 'steer' \| 'follow-up'}) => void` |
51
+ | `cmd.sendUserMessage` | `(content, {deliverAs?}) => void` |
47
52
  | `cmd.exec` | `({command, args?, cwd?, signal?}) => Promise<{stdout, stderr, code}>` |
48
53
  | `cmd.setSessionName` / `setModel` / `setEffort` | `(value) => void` (buffered pre-bind) |
54
+ | `cmd.getSessionName` / `getModel` / `getEffort` | `() => value \| undefined` (undefined pre-bind) |
55
+ | `cmd.getContextUsage` | `() => {tokens, contextWindow, percent} \| undefined` |
56
+ | `cmd.getCommands` | `() => readonly {name, description?, mod}[]` |
57
+ | `cmd.isIdle` | `() => boolean` |
58
+ | `cmd.shutdown` | `() => void` |
49
59
  | `cmd.getAllTools` / `getActiveTools` | `() => readonly string[]` |
60
+ | `cmd.getToolInfo` | `() => readonly {name, description, readOnly, active}[]` |
50
61
  | `cmd.setActiveTools` | `(names: readonly string[]) => void` |
51
62
 
52
63
  Details:
53
64
 
54
- - `cmd.ui` - `notify` (a `notice` feed row), `confirm` / `select` / `input` (the Interaction question modal in the TUI; deterministic defaults headless: false/undefined), `setStatus`, `widget`, `refreshWidgets`. The full dialog/status/widget contract is in [UI surface](./ui.md#ui-surface).
55
- - `cmd.queueMessage({content, deliverAs})` - `steer` lands after the current tool batch (behind any queued user messages in the same poll), `follow-up` only when the run would stop (ahead of continuation nudges, behind queued user input).
65
+ - `cmd.ui` - `notify` (a `notice` feed row), `confirm` / `select` / `input` (the Interaction question modal in the TUI; deterministic defaults headless: false/undefined), `setStatus`, `widget`, `refreshWidgets`, `setTitle`. The full dialog/status/widget contract is in [UI surface](./ui.md#ui-surface).
66
+ - `cmd.queueMessage({content, deliverAs})` - `steer` lands after the current tool batch (behind any queued user messages in the same poll), `follow-up` only when the run would stop (ahead of continuation nudges, behind queued user input). Queued messages steer a run that is already going; they never START one.
67
+ - `cmd.sendUserMessage(content, {deliverAs})` - a REAL user message: echoed in the feed like typed input and, when the agent is idle, starting a turn right away. While a run is going, `steer` (default) queues it behind the active turn exactly as typing would, and `follow-up` holds it until the run would stop. Headless there is no submit path, so it falls back to `queueMessage` with the same `deliverAs`.
68
+ - `cmd.getSessionName()` / `getModel()` / `getEffort()` - the live values behind the setters; `undefined` before a session binds.
69
+ - `cmd.getContextUsage()` - `{tokens, contextWindow, percent}` for the bound session: `tokens` and `percent` are `null` until the first model call has measured the window (never a misleading 0%).
70
+ - `cmd.getCommands()` - every slash command mods registered on this host (`{name, description, mod}`), so a mod can discover siblings or avoid a taken name. Built-in commands are host-owned and not listed.
71
+ - `cmd.isIdle()` - `false` while an agent run is in flight on the bound harness (`run_start` … `run_end`), `true` otherwise and pre-bind.
72
+ - `cmd.shutdown()` - asks the interactive host to exit once the current turn settles (the same path as `/exit`). Headless: no-op - the print run ends on its own.
73
+ - `cmd.getToolInfo()` - the registered tools with `description`, `readOnly`, and `active` (per the mod-managed filter) - the descriptive counterpart of `getAllTools()`'s bare names.
56
74
  - `cmd.session` - the `ModSessionApi` persistence seam (appendCustomEntry / appendCustomMessageEntry / getCustomEntries), undefined until bound. See [ModContext.session - the mods' persistence surface](./hooks-and-events.md#mod-context-session-the-mods-persistence-surface).
57
75
  - `cmd.showEntry(customType, data)` - render a custom entry into the live feed through the renderer registered for that type (unrendered types pretty-print as JSON). The TUI wires the sink; headless runs drop entries. Pair with `cmd.session.appendCustomEntry` when the data should also persist.
58
76
  - `cmd.sessions` - live session controls: `compact()`, `tree()` (the session branch tree as stable `{id, label, children}` nodes), `leafId()`, `navigateTree({targetId, summarize?, customInstructions?})`, and `setLabel({targetId, label})`. These read live harness state, so unlike the buffered setters they throw with a clear message when no session is bound yet.
@@ -44,12 +44,56 @@ A package declares what it ships via `package.json` - exact paths, directories,
44
44
  ```json
45
45
  {
46
46
  "name": "@team/review-mod",
47
- "commandcode": {"mods": ["./src/review.ts", "src/checks/*.ts"]}
47
+ "description": "Blocks risky diffs and adds a /review command.",
48
+ "keywords": ["cmd-mod", "cmd-mod-tool", "cmd-mod-command"],
49
+ "repository": "https://github.com/team/review-mod",
50
+ "commandcode": {
51
+ "mods": ["./src/review.ts", "src/checks/*.ts"],
52
+ "skills": ["./skills"]
53
+ }
48
54
  }
49
55
  ```
50
56
 
57
+ The `cmd-mod` keyword is **required** — it is the only thing that lists your package on the
58
+ [mods hub](https://commandcode.ai/mods). Everything else shapes the hub row: a `description`
59
+ becomes the summary, a kind keyword (`cmd-mod-tool`, `cmd-mod-command`, `cmd-mod-hook`,
60
+ `cmd-mod-provider`, `cmd-mod-renderer`, `cmd-mod-flag`) sets the category badge, and a
61
+ `repository` pointing at GitHub adds the star count. Ship these from the start so the hub
62
+ lists the mod well the moment you publish, with nothing to submit.
63
+
51
64
  No manifest → the `mods/` convention directory → a root `index.ts`, in that order.
52
65
 
66
+ `commandcode.skills` lists skill directories the package ships beside its mods (each entry is one skill directory or a directory of skills, relative to the package root; entries escaping the root are dropped). They join skill discovery for every session the package loads in, so one `cmd mods add` brings the mods and the skills that go with them. A mod can contribute the same thing from code with `cmd.addSkillsPath`.
67
+
68
+ Publish to npm with the `cmd-mod` keyword and the package is listed on the [mods hub](https://commandcode.ai/mods) automatically - the hub reads the npm registry for that keyword, so there is nothing to submit. Monthly downloads come straight from npm; a GitHub `repository` field adds the star count.
69
+
70
+ ## Listing a git-only mod (the curated registry)
71
+
72
+ Two things feed [the hub](https://commandcode.ai/mods). Any npm package with the `cmd-mod` keyword lists automatically (above). A **curated registry** - `mods/registry.json` in the public [`CommandCodeAI/command-code`](https://github.com/CommandCodeAI/command-code) repo - adds what npm can't: mods that live only in a git repo and never hit npm, richer metadata layered over an npm entry (matched by `npm`), an `official` badge, and a `hidden` list for moderation. Open a PR against that file to get a git-only mod listed - nothing else to submit.
73
+
74
+ ```json
75
+ {
76
+ "version": 1,
77
+ "hidden": [],
78
+ "mods": [
79
+ {
80
+ "name": "review-guard",
81
+ "source": "owner/review-guard",
82
+ "description": "Blocks risky diffs and adds a /review command.",
83
+ "author": {"name": "Ada", "github": "ada"},
84
+ "repo": "https://github.com/owner/review-guard",
85
+ "kinds": ["tool", "command"],
86
+ "tags": ["safety", "review"],
87
+ "official": false
88
+ }
89
+ ]
90
+ }
91
+ ```
92
+
93
+ `name`, `source` (what `cmd mods add <source>` takes), `description` (≤240 chars), `author.name`, and at least one `kinds` value (`tool`/`command`/`hook`/`provider`/`renderer`/`flag`) are required; `repo`, `npm`, `tags`, `official`, and `addedAt` are optional. A GitHub `repo` adds a live star count, `npm` merges the entry with its npm package for the download count, `official` marks a team-maintained mod, and a `hidden` name (a mod or an npm package) keeps spam or impersonation off the hub. The schema is `mods.schema.json` beside the file.
94
+
95
+ The resolved list - npm discovery merged with the curated file, live numbers included - is served at [`commandcode.ai/mods.json`](https://commandcode.ai/mods.json), CORS-open, so the CLI or any script can read the same list the hub renders.
96
+
53
97
  ## Filtering a source's mods
54
98
 
55
99
  A `mods.sources` entry can be the object form to load only part of a package:
@@ -6,8 +6,9 @@ Everything a mod can put on screen rides `cmd.ui`, `cmd.addRenderer`, and `cmd.s
6
6
 
7
7
  ## Notifications and dialogs
8
8
 
9
- - `cmd.ui.notify(message)` - a `notice` feed row.
9
+ - `cmd.ui.notify(message, level?)` - a `notice` feed row, styled by `level`: `'info'` (default) is a dim bullet, `'warning'` an amber ⚠, `'error'` a red ⚠. Rendered during a turn (the per-turn feed projector); a notice raised at factory time flushes once the session binds.
10
10
  - `cmd.ui.confirm({title, message?})` / `cmd.ui.select({title, options})` / `cmd.ui.input({title, placeholder?})` - the Interaction question modal in the TUI. Headless, each resolves its deterministic default: confirm → `false`, select/input → `undefined` - never auto-approved.
11
+ - `cmd.ui.setTitle(text | null)` - the terminal tab title. The text replaces the session title on the tab (and survives the host's own title re-assertions) until `null` hands the tab back. Headless: no-op.
11
12
 
12
13
  ## Footer status segments and editor widgets
13
14
 
@@ -59,4 +60,8 @@ Rendering is deliberately **line-based, not component-based**: mods return style
59
60
  | `select` / `input` | modal | resolves `undefined` |
60
61
  | `setStatus` | footer segment under the input | stored, rendered nowhere |
61
62
  | `widget` | no-op (wire-up pending) | no-op |
63
+ | `setTitle` | terminal tab title | no-op |
62
64
  | `showEntry` | rendered feed row | dropped |
65
+ | `addShortcut` | dispatched after host bindings | never fires |
66
+ | `sendUserMessage` | real user turn (queued while busy) | falls back to `queueMessage` |
67
+ | `shutdown` | exits after the turn settles | no-op |