@cmodjs/core 4.1.0 → 4.2.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.
Files changed (62) hide show
  1. package/docs/commands.md +3 -3
  2. package/docs/install-steps.md +3 -3
  3. package/docs/jobs.md +2 -0
  4. package/docs/session.md +2 -0
  5. package/docs/testing.md +1 -1
  6. package/docs/ui.md +1 -1
  7. package/jobs/tool.d.ts +1 -0
  8. package/jobs/tool.d.ts.map +1 -1
  9. package/jobs/tool.js +2 -2
  10. package/package.json +1 -1
  11. package/records.d.ts +1 -1
  12. package/records.js +1 -1
  13. package/register.js +4 -4
  14. package/runtime/conversations.d.ts +3 -0
  15. package/runtime/conversations.d.ts.map +1 -1
  16. package/runtime/conversations.js +30 -2
  17. package/runtime/hotkeys.js +1 -1
  18. package/runtime/installer.d.ts +1 -0
  19. package/runtime/installer.d.ts.map +1 -1
  20. package/runtime/installer.js +4 -3
  21. package/runtime/lifecycle.d.ts +2 -2
  22. package/runtime/lifecycle.d.ts.map +1 -1
  23. package/runtime/lifecycle.js +25 -4
  24. package/runtime/pages.d.ts.map +1 -1
  25. package/runtime/pages.js +15 -5
  26. package/runtime/sessions.d.ts +8 -1
  27. package/runtime/sessions.d.ts.map +1 -1
  28. package/runtime/sessions.js +16 -1
  29. package/runtime/ui.d.ts +1 -0
  30. package/runtime/ui.d.ts.map +1 -1
  31. package/runtime/ui.js +5 -3
  32. package/runtime/widgets.js +2 -1
  33. package/session/conversation.d.ts +1 -0
  34. package/session/conversation.d.ts.map +1 -1
  35. package/session/session.d.ts +1 -0
  36. package/session/session.d.ts.map +1 -1
  37. package/src/jobs/tool.ts +3 -2
  38. package/src/records.ts +1 -1
  39. package/src/register.ts +4 -4
  40. package/src/runtime/conversations.ts +32 -2
  41. package/src/runtime/hotkeys.ts +1 -1
  42. package/src/runtime/installer.ts +5 -3
  43. package/src/runtime/lifecycle.ts +23 -7
  44. package/src/runtime/pages.ts +12 -5
  45. package/src/runtime/sessions.ts +23 -2
  46. package/src/runtime/ui.ts +5 -3
  47. package/src/runtime/widgets.ts +1 -1
  48. package/src/session/conversation.ts +1 -0
  49. package/src/session/session.ts +1 -0
  50. package/src/testing/fake-claude.ts +1 -0
  51. package/src/testing/fake-elements.ts +6 -0
  52. package/src/testing/fake-sessions.ts +12 -1
  53. package/src/testing.ts +28 -5
  54. package/testing/fake-claude.d.ts.map +1 -1
  55. package/testing/fake-claude.js +1 -0
  56. package/testing/fake-elements.d.ts.map +1 -1
  57. package/testing/fake-elements.js +6 -0
  58. package/testing/fake-sessions.d.ts +3 -0
  59. package/testing/fake-sessions.d.ts.map +1 -1
  60. package/testing/fake-sessions.js +8 -0
  61. package/testing.d.ts.map +1 -1
  62. package/testing.js +31 -6
package/docs/commands.md CHANGED
@@ -134,7 +134,7 @@ It needs, and refuses to start without:
134
134
 
135
135
  ## Install mods
136
136
 
137
- `cmod install`, `cmod update`, `cmod remove`, `cmod enable`, `cmod disable`, `cmod link`, `cmod unlink`, and `cmod try` write the time to `plugins-changed` in the cmod store, then write `store-changed` beside it, when they finish, also when they fail partway, such as an update that updated some mods. The cmod plugin of every running session watches `store-changed` and Claude Code's `installed_plugins.json`, reads the `enabledPlugins` of the user's `settings.json` each time Claude Code reports that file changed and at each prompt, and runs `/reload-plugins` when `plugins-changed`, `installed_plugins.json`, or `enabledPlugins` changed. So each session runs the plugins the person has now, whether a `cmod` command, `/plugin`, `claude plugin`, or Claude Code's automatic update changed them. A reload keeps the prompt cache: the next request reads the whole conversation from the cache and pays only for the plugins' new content. A reload clears each mod's `memory` state. A machine where cmod set up no mod has no store, and these commands write nothing there. `cmod permission` needs no reload, because a mod sees its grants change before its next call.
137
+ `cmod install`, `cmod update`, `cmod remove`, `cmod enable`, `cmod disable`, `cmod link`, `cmod unlink`, and `cmod try` write the time to `plugins-changed` in the cmod store, then write `store-changed` beside it, when they finish, also when they fail partway, such as an update that updated some mods. The cmod plugin of every running session watches `store-changed` and Claude Code's `installed_plugins.json`, reads the `enabledPlugins` of the user's `settings.json` each time Claude Code reports that file changed and at each prompt, and shows a `Plugins changed` toast when `plugins-changed`, `installed_plugins.json`, or `enabledPlugins` changed, whether a `cmod` command, `/plugin`, `claude plugin`, or Claude Code's automatic update changed them. Its `Reload` button runs `/reload-plugins` in that session, and cmod runs it in no session on its own. A reload keeps the prompt cache: the next request reads the whole conversation from the cache and pays only for the plugins' new content. A reload clears each mod's `memory` state. A machine where cmod set up no mod has no store, and these commands write nothing there. `cmod permission` needs no reload, because a mod sees its grants change before its next call.
138
138
 
139
139
  ### cmod install
140
140
 
@@ -223,7 +223,7 @@ Reads every conversation Claude Code saved, as the sessions, contexts, turns, ta
223
223
  - `state` prints what a running conversation resumes from.
224
224
  - `follow` prints each message, turn, context, task, and goal change of the sessions named as Claude Code saves it, until stopped with Ctrl+C. `--running` follows every running session of the project too, and each one that starts later. The cmod plugin runs it to hear other running sessions.
225
225
  - `--json` prints JSON instead of one line each.
226
- - `index` reads every saved conversation into the index at `~/.local/share/cmod/history-8.sqlite`, which holds where each message is in its transcript, not a copy of it, and each conversation's turns, contexts, and tasks. While a mod other than cmod holds the `conversation` permission, the cmod plugin runs it in the background at each session's start, and a second run while one is running exits at once. The index forgets a conversation whose transcript is deleted.
226
+ - `index` reads every saved conversation into the index at `~/.local/share/cmod/history-11.sqlite`, which holds where each message is in its transcript, not a copy of it, and each conversation's turns, contexts, and tasks. While a mod other than cmod holds the `conversation` permission, the cmod plugin runs it in the background at each session's start, and a second run while one is running exits at once. The index forgets a conversation whose transcript is deleted.
227
227
 
228
228
  ## Run in an install script
229
229
 
@@ -233,7 +233,7 @@ Reads every conversation Claude Code saved, as the sessions, contexts, turns, ta
233
233
  cmod watch
234
234
  ```
235
235
 
236
- Reloads every running session once a checkout `cmod link` linked builds after a change. It watches each linked folder, outside `node_modules` and `.git`. Once a folder has had no change for 2 seconds, it bundles the mod's hooks module as `cmod publish` does. A bundle that builds marks `plugins-changed`, so every session runs `/reload-plugins` once. A bundle that still fails 10 seconds after the last change shows `<mod> does not build` with the error as one toast in every session, and the toast goes once the mod builds. So an agent's half-written file reloads nothing and shows nothing. The cmod plugin starts one only in a session that an app shows, once a mod is linked, at the session's start or later, so a machine with no linked mod runs none. The first holds `watch.lock` in the cmod store, watches, and touches its file there every 5 seconds. Another one exits at once while it runs, and the cmod plugin of each such session starts one again once that file is 15 seconds old, so a machine runs one watcher however many sessions it has open.
236
+ Tells every running session once a checkout `cmod link` linked builds after a change. It watches each linked folder, outside `node_modules` and `.git`, and skips a change to a file git ignores, such as the `.claude-plugin/types/` Claude Code writes each time it loads the mod. Once a folder has had no change for 2 seconds, it bundles the mod's hooks module as `cmod publish` does. A bundle that builds marks `plugins-changed`, so every session shows the `Plugins changed` toast. A bundle that still fails 10 seconds after the last change shows `<mod> does not build` with the error as one toast in every session, and the toast goes once the mod builds. So an agent's half-written file reloads nothing and shows nothing. The cmod plugin starts one only in a session that an app shows, once a mod is linked, at the session's start or later, so a machine with no linked mod runs none. The first holds `watch.lock` in the cmod store, watches, and touches its file there every 5 seconds. Another one exits at once while it runs, and the cmod plugin of each such session starts one again once that file is 15 seconds old, so a machine runs one watcher however many sessions it has open.
237
237
 
238
238
  A cmod before 4.1.0 linked a checkout by writing it into `CLAUDE_CODE_PLUGIN_DIRS` in the `env` block of Claude Code's `settings.json`. `cmod watch` at its start, and `cmod setup`, link each folder still in that list as `cmod link` does now and take it out of the list. A folder that fails to link stays in the list, and its error prints on stderr.
239
239
 
@@ -34,9 +34,9 @@ cmod refuses a `cmod` key that breaks these rules, with the fix in the message:
34
34
 
35
35
  ## Consent
36
36
 
37
- Before a step runs for the first time, cmod shows the person the commands, the hotkeys, and the permissions, and asks to run them. cmod hashes the commands, the hotkeys, the permissions, and every file in each script folder, and remembers the hash the person approved with each permission. A change to any of those asks again, and an added permission is the only one it asks about ([permissions.md](permissions.md)).
37
+ Before a step runs for the first time, cmod shows the person the commands, the hotkeys, and the permissions, and asks to run them. cmod remembers what the person approved in three parts: each permission, each hotkey with its default combo, and one hash of the commands, the program, and every file in each script folder. An update asks again only for what it adds: a changed command or script file, a new hotkey or a new default combo, or a new permission, and it lists only the new hotkeys and permissions ([permissions.md](permissions.md)). An update that only drops a hotkey or a permission sets the mod up without asking.
38
38
 
39
- - In Claude Code, the mod opens its install page, the `Install <mod>` tab of the Sidebar. Claude Code gives the Sidebar the keys when the prompt is empty and nothing else holds them. Otherwise a click on the page does. A Sidebar opened this way shows only on a terminal 144 columns wide, so below that the toast `Installing`, with `Waiting for your answer` and an `Answer` button, opens it. The page asks `Install <mod>?` and lists `<mod> wants to:`, each permission as one sentence, then `and change your computer:` with `Run <install> now, and <uninstall> when you remove it` and `Bind shift+tab to Cycle the mode`, naming only what the mod has. `Accept` (a) runs the setup, and a loading toast above the prompt shows the install with a progress bar. `Not now` (n) and the `×` on the tab decline. Escape hides the Sidebar, and the question waits in its tab and its toast.
39
+ - In Claude Code, the mod opens its install page, the `Install <mod>` tab of the Sidebar. Claude Code gives the Sidebar the keys when the prompt is empty and nothing else holds them. Otherwise a click on the page does. A Sidebar opened this way shows only on a terminal 144 columns wide, so below that the toast `Installing`, with `Waiting for your answer` and an `Answer` button, opens it. The page asks `Install <mod>?` and lists `<mod> wants to:`, each permission as one sentence, then `and change your computer:` with `Run <install> now, and <uninstall> when you remove it`, `Install <program>, which Claude runs by its name`, and `Bind shift+tab to Cycle the mode`, naming only what the mod has. `Accept` (a) runs the setup, and a loading toast above the prompt shows the install with a progress bar. `Not now` (n) and the `×` on the tab decline. Escape hides the Sidebar, and the question waits in its tab and its toast.
40
40
  - In a session with no screen, such as `claude -p`, the mod waits and logs `<mod> waits for consent to run <install>. Run cmod install <mod> in a terminal.` That person is already in a terminal, and no screen exists to draw a button on.
41
41
  - In a terminal, `cmod install`, `cmod link`, `cmod try`, and `cmod update` print the commands, the program, the hotkeys, and the permissions, and ask `Set up <mod>? [y/N]`. `--yes` approves without asking.
42
42
  - `Not now` leaves the mod off with the toast `Not installed` and an `Install` button that asks again.
@@ -47,7 +47,7 @@ The mod starts only once its install step has run ([mod.md](mod.md)).
47
47
 
48
48
  Claude Code updates a plugin by itself only when its marketplace has automatic updates on, and they are off for every marketplace that is not Anthropic's. So the setup of a mod installed from a marketplace turns them on, through `"autoUpdate": true` on that marketplace's `extraKnownMarketplaces` entry in `settings.json`. The consent lists it as `Update <mod> automatically from the <marketplace> marketplace`, and a mod with nothing to consent to logs it. The setup never changes a choice the person made, in `settings.json` or with the toggle under `/plugin` Marketplaces, and leaves a linked mod alone. The cmod plugin's own `cmod` marketplace gets the same entry whenever a `cmod` command installs the cmod plugin or `cmod update` updates it.
49
49
 
50
- Claude Code downloads an update in the background and loads it at the next launch or `/reload-plugins`. An update whose scripts, hotkeys, or permissions changed waits for consent again, so an update never runs a new install step unasked.
50
+ Claude Code downloads an update in the background and loads it at the next launch or `/reload-plugins`. An update that changes a command or a script file, adds a hotkey or changes its default combo, or adds a permission waits for consent again, so an update never runs a new install step unasked.
51
51
 
52
52
  In Claude Code, the mod sets itself up through the `cmod` program the cmod plugin installed, never one elsewhere on PATH, and only once that program is at least the version of the `@cmodjs/core` the mod bundles. Until then its toast says `Waiting for cmod`. After a minute it says `<mod> needs cmod <version> or later, and this computer has cmod <version>, so cmod is updating itself.` and runs `cmod update cmod` once. The mod sets up as soon as a new enough cmod is installed. When the update leaves cmod older, the next wait shows `<mod> needs cmod <version> or later, and the newest cmod is <version>.` with a Retry button instead of updating again.
53
53
 
package/docs/jobs.md CHANGED
@@ -98,6 +98,7 @@ tool<Schema, State>(options: {
98
98
  readonly name: string
99
99
  readonly description: string
100
100
  readonly inputSchema?: Schema
101
+ readonly isDeferred?: boolean
101
102
  readonly execute: (input: FromSchema<Schema>, mod: Mod<State>) => unknown
102
103
  }): Job<Tool, State>
103
104
 
@@ -107,6 +108,7 @@ type Tool = { readonly name: string; remove(): void }
107
108
  - `name` is 1 to 64 letters, digits, `_`, or `-`. Claude sees the tool as `mcp__<plugin name>__<name>`, and the handle's `name` is that full name. `remove()` on the handle stops the mod answering the tool.
108
109
  - `description` tells Claude what the tool does.
109
110
  - `inputSchema` is a JSON Schema with `type: 'object'`. It types `execute`'s `input`, so write it inline. cmod checks each call's input against it, draft 2020-12, and denies an input that does not fit with the list of its errors. Its properties cannot be named `tool`, `tool_use_id`, `consent`, or `agentId`, which Claude Code keeps for itself.
111
+ - `isDeferred: false` puts the tool's schema in Claude's tool list from the first turn, for a tool Claude calls in most turns. `true`, or leaving it out, keeps the tool behind Claude Code's ToolSearch, so Claude searches for it before its first call. Each tool kept in the list adds its schema to every turn's context.
110
112
  - `execute` returns the result Claude reads. A `string` and an array of content blocks go to Claude as they are. Any other value goes as JSON. `undefined` returns nothing.
111
113
  - An input that does not fit the schema is denied with one line per error under a heading, such as `The open_ticket tool input is not valid:` followed by `- #: Instance does not have required property "title".` The text after `- ` is the location in the input, `#` for its root, and the validator's message.
112
114
  - An `execute` that throws denies the call with `The <name> tool failed: <error>`.
package/docs/session.md CHANGED
@@ -61,6 +61,7 @@ While a mod holds the `conversation` permission, the cmod plugin indexes every c
61
61
  type Session = {
62
62
  readonly id: string
63
63
  readonly title: string
64
+ readonly color?: string
64
65
  readonly project: string
65
66
  readonly agentType?: string
66
67
  readonly model: string
@@ -89,6 +90,7 @@ type Session = {
89
90
  ```
90
91
 
91
92
  - `id` is Claude Code's session id, and a task owner's `id` is its task's id. `title` is the name the person gave it with `/rename`, or the first line of its first prompt. A resumed or forked session with no name, and a session the mod started in after its first prompt, has an empty `sessions.current.title` until `sessions.list()` reads it.
93
+ - `color` is the color the person last picked with `/color`, such as `cyan`, and is absent when they never picked one or reset it with `/color default`. A task's owner has none. `sessions.current.color` follows each `/color` the person runs, and a resumed session's color holds from the mod's start, with or without the `conversation` permission, as `title` does.
92
94
  - `project` is the project the session works in, and `cwd` is its working folder. For the current session both follow `/cd`, and `cwd` follows a `cd` in a Bash or PowerShell call.
93
95
  - `agentType` is the agent definition the session runs, such as `Explore` for a task's owner, or the agent a session started with `claude --agent` runs. A plain session has none.
94
96
  - `model` is the model the session runs on. `startedAt` and `lastAt` are ISO times.
package/docs/testing.md CHANGED
@@ -187,7 +187,7 @@ The current session, `test-session`, starts empty. A test builds the conversatio
187
187
 
188
188
  Each one runs the mod's session listeners, as a new message does in Claude Code. A `MessageDraft` is a message without its place: `{ type: Prompt, text, from? }`, `{ type: Note, text, from? }`, `{ type: Reply, text }`, `{ type: ToolCall, name, input?, callId? }`, `{ type: ToolResult, callId, output, isError? }`, `{ type: Skill, name, text }`, `{ type: InstructionFile, path, text }`, `{ type: AttachedFile, path, attachmentType, text }`, or `{ type: Notice, text }`.
189
189
 
190
- The `sessions` option gives the earlier conversations, each a `FakeSession`: an `id`, its `messages` as drafts, its `goal`, and any `Session` field, such as `title`, `project`, or `isRunning`. A session that names no `project` is in the test's project.
190
+ The `sessions` option gives the earlier conversations, each a `FakeSession`: an `id`, its `messages` as drafts, its `goal`, and any `Session` field, such as `title`, `color`, `project`, or `isRunning`. A session that names no `project` is in the test's project. `{ id: 'test-session', color: 'cyan' }` gives the current session its `/color` from the mod's start, and `tested.type('/color cyan')` and `tested.type('/color default')` run Claude Code's `/color`, which changes `sessions.current.color`. `tested.type('/color')` throws, because Claude Code picks a random color for it.
191
191
 
192
192
  ```ts
193
193
  import { expect, test } from 'bun:test'
package/docs/ui.md CHANGED
@@ -31,7 +31,7 @@ A mod never asks the person to type a command. When a toast, a page, or a pane n
31
31
  - `Link`, with `LinkProps`, draws a link.
32
32
  - `Code`, with `CodeProps`, draws highlighted code.
33
33
  - `Markdown`, with `MarkdownProps`, draws markdown text, as Claude Code draws a reply.
34
- - `Input`, with `InputProps`, draws a text input.
34
+ - `Input`, with `InputProps`, draws a text input. It is one line tall unless `rows` says more: `rows: 4` draws four lines that wrap, as a `<textarea>`, for a note or a comment.
35
35
  - `Select`, with `SelectProps`, draws a choice of options.
36
36
  - `Image`, with `ImageProps`, draws an image, with `alt` text. Only the terminal draws it.
37
37
  - `Raster`, with `RasterProps`, draws a grid of terminal cells, each a glyph with its colors. Only the terminal draws it.
package/jobs/tool.d.ts CHANGED
@@ -10,6 +10,7 @@ export declare function tool<const Schema extends object = {
10
10
  readonly name: string;
11
11
  readonly description: string;
12
12
  readonly inputSchema?: Schema;
13
+ readonly isDeferred?: boolean;
13
14
  readonly execute: (input: Schema extends JSONSchema ? FromSchema<Schema> : never, mod: Mod<State>) => unknown;
14
15
  }): Job<Tool, State>;
15
16
  //# sourceMappingURL=tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/jobs/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAUzC,MAAM,MAAM,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,IAAI,IAAI,CAAA;CAAE,CAAA;AAE5D,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC3H,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,SAAS,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,OAAO,CAAA;CAC9G,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA0CnB"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/jobs/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAUzC,MAAM,MAAM,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,IAAI,IAAI,CAAA;CAAE,CAAA;AAE5D,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC3H,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,SAAS,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,OAAO,CAAA;CAC9G,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA0CnB"}
package/jobs/tool.js CHANGED
@@ -6,7 +6,7 @@ import { messageOf } from '../utils/text.js';
6
6
  import { withinDeadline } from './context.js';
7
7
  const toolName = /^[A-Za-z0-9_-]{1,64}$/;
8
8
  export function tool(options) {
9
- const { name, description, inputSchema, execute } = options;
9
+ const { name, description, inputSchema, isDeferred, execute } = options;
10
10
  return (job) => {
11
11
  const { mod, announce, reserveName } = job;
12
12
  const { claude, router, report } = engineOf(mod);
@@ -19,7 +19,7 @@ export function tool(options) {
19
19
  const named = reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`);
20
20
  const validator = new Validator(inputSchema ?? { type: 'object' }, '2020-12', false);
21
21
  let registered;
22
- claude.tool.register({ name, description, ...(inputSchema === undefined ? {} : { inputSchema: inputSchema }) }).then((answer) => {
22
+ claude.tool.register({ name, description, ...(inputSchema === undefined ? {} : { inputSchema: inputSchema }), ...(isDeferred === undefined ? {} : { isDeferred }) }).then((answer) => {
23
23
  registered = answer.tool;
24
24
  }, (error) => {
25
25
  report({ id: `tool:${name}`, message: `The ${name} tool is not added`, error });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmodjs/core",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Build Claude Code mods: hooks, panes, permissions, checks, and an install that just works.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/records.d.ts CHANGED
@@ -91,7 +91,7 @@ export declare function linkedMarketplacesOf(knownMarketplaces: unknown, links:
91
91
  export declare function hasLinkedMods(knownMarketplaces: unknown, links: Links): boolean;
92
92
  export declare function widgetsPath(store: string): string;
93
93
  export declare function parseConsent(value: unknown, path: string): Record<string, string[]>;
94
- export declare const coreVersion = "4.1.0";
94
+ export declare const coreVersion = "4.2.0";
95
95
  export declare function hotkeyCommand(mod: string, hotkey: string): string;
96
96
  export declare function hotkeyBindings(mod: string, hotkeys: Hotkeys | undefined): Record<string, string>;
97
97
  export declare function boundCombos(keybindings: unknown): Map<string, string>;
package/records.js CHANGED
@@ -266,7 +266,7 @@ function readHotkeys(hotkeys) {
266
266
  throw new Error(`package.json "cmod.hotkeys" is not a map of hotkeys to their title and default combo. ${fix}`);
267
267
  return { hotkeys: checkHotkeys('package.json "cmod"', hotkeys) };
268
268
  }
269
- export const coreVersion = '4.1.0';
269
+ export const coreVersion = '4.2.0';
270
270
  export function hotkeyCommand(mod, hotkey) {
271
271
  return `${mod}--${hotkey}`;
272
272
  }
package/register.js CHANGED
@@ -82,7 +82,7 @@ function routeToMod(_$, eventInput, passOn) {
82
82
  return lifecycle.route(passOn.event, eventInput, (passed) => passOn(passed));
83
83
  }
84
84
  function routeFailure(_$, eventInput, passOn) {
85
- return lifecycle.caught(passOn.event, eventInput, (passed) => passOn(passed), passOn.error);
85
+ return lifecycle.caught(passOn.event, eventInput, (passed) => passOn(passed), passOn);
86
86
  }
87
87
  const withCaller = (eventInput, passOn) => ({ ...eventInput, caller: passOn.origin.plugin });
88
88
  const withoutCaller = (passOn) => (passed) => passOn(Object.fromEntries(Object.entries(passed).filter(([key]) => key !== 'caller')));
@@ -90,7 +90,7 @@ function routeCallToMod(_$, eventInput, passOn) {
90
90
  return lifecycle.route('cmod.call', withCaller(eventInput, passOn), withoutCaller(passOn));
91
91
  }
92
92
  function routeCallFailure(_$, eventInput, passOn) {
93
- return lifecycle.caught('cmod.call', withCaller(eventInput, passOn), withoutCaller(passOn), passOn.error);
93
+ return lifecycle.caught('cmod.call', withCaller(eventInput, passOn), withoutCaller(passOn), passOn);
94
94
  }
95
95
  export function registerMod(addHook, definition, options) {
96
96
  permissionCheck = undefined;
@@ -139,11 +139,11 @@ export function registerMod(addHook, definition, options) {
139
139
  addHook('ui.press', routeToMod).catch(routeFailure);
140
140
  addHook('ui.scroll', routeToMod).catch(routeFailure);
141
141
  addHook('ui.close', routeToMod).catch(routeFailure);
142
- addHook('cmod.call', routeCallToMod).catch(routeCallFailure);
142
+ addHook('cmod.call', { to: definition.name }, routeCallToMod).catch(routeCallFailure);
143
143
  addHook('session.compact', routeToMod).catch(routeFailure);
144
144
  if (definition.name !== cmodPluginName)
145
145
  return;
146
- addHook('command.describe', routeToMod).catch(routeFailure);
146
+ addHook('command.describe', { command: /--/ }, routeToMod).catch(routeFailure);
147
147
  addHook('session.append', routeToMod).catch(routeFailure);
148
148
  addHook('session.receive', routeToMod).catch(routeFailure);
149
149
  addHook('agent.spawn', routeToMod).catch(routeFailure);
@@ -2,12 +2,15 @@ import type { Args, EventResult, Frozen } from 'claude-code';
2
2
  import { type TurnData } from '../session/conversation.js';
3
3
  import type { Claude } from './claude.js';
4
4
  import type { Router } from './router.js';
5
+ import { type SessionColor } from './sessions.js';
5
6
  export type Conversations = {
6
7
  answer(e: Frozen<Args<'cmod.call'>> & {
7
8
  readonly caller?: string;
8
9
  }): Promise<EventResult<'cmod.call'> | undefined>;
9
10
  moved(): void;
10
11
  turnClosedBy(rowId: string): TurnData | undefined;
12
+ color(): SessionColor;
13
+ onColor(listener: (told: SessionColor) => void): void;
11
14
  };
12
15
  export declare function serveConversations({ claude, router, store, project, report, clear, }: {
13
16
  readonly claude: Claude;
@@ -1 +1 @@
1
- {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../src/runtime/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAK5D,OAAO,EAA0L,KAAK,QAAQ,EAAmB,MAAM,4BAA4B,CAAA;AAUnQ,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,OAAO,KAAK,EAAc,MAAM,EAAE,MAAM,aAAa,CAAA;AAyCrD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,CAAA;IAClH,KAAK,IAAI,IAAI,CAAA;IACb,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAClD,CAAA;AAMD,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,MAAM,EACN,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC,GAAG,aAAa,CA4rBhB"}
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../src/runtime/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAK5D,OAAO,EAA0L,KAAK,QAAQ,EAAmB,MAAM,4BAA4B,CAAA;AAUnQ,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,OAAO,KAAK,EAAc,MAAM,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EAAqJ,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAuCpM,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,CAAA;IAClH,KAAK,IAAI,IAAI,CAAA;IACb,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACjD,KAAK,IAAI,YAAY,CAAA;IACrB,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI,CAAA;CACtD,CAAA;AAMD,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,MAAM,EACN,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC,GAAG,aAAa,CAwtBhB"}
@@ -15,7 +15,7 @@ import { beforeAnswerKeys, foldBefore, installBefore, relayOf } from './before.j
15
15
  import { cmodPluginName } from './dependencies.js';
16
16
  import { readLines, runCmod, spawnCmod } from './programs.js';
17
17
  import { PassedTimeout } from './deadline.js';
18
- import { actPermission, conversationMethods, everySession, isAct, isHeardBy, isInScope, resultOf, spawnTask, usageOf } from './sessions.js';
18
+ import { actPermission, conversationMethods, everySession, isAct, isHeardBy, isInScope, pickedColor, resultOf, spawnTask, usageOf } from './sessions.js';
19
19
  const relayKinds = new Set(['act', 'before', 'answer', 'watch', 'goal', 'taskEnd']);
20
20
  const taskVariable = 'CMOD_TASK';
21
21
  const isRelay = (value) => isObject(value) && relayKinds.has(value['kind']);
@@ -82,6 +82,27 @@ export function serveConversations({ claude, router, store, project, report, cle
82
82
  };
83
83
  const ended = (id) => new Error(`session ${id} has ended`);
84
84
  const isRunning = async (id) => (await json(['history', 'sessions', '--running', '--all'])).some((each) => each.id === id);
85
+ let color = { session: '' };
86
+ let onColor = () => undefined;
87
+ const recolor = (id, picked) => {
88
+ if (session !== id)
89
+ return;
90
+ color = { session: id, ...(picked === undefined ? {} : { color: picked }) };
91
+ onColor(color);
92
+ };
93
+ const readColor = (id) => void json(['history', 'sessions', '--running', '--all'])
94
+ .then((running) => recolor(id, running.find((each) => each.id === id)?.color))
95
+ .catch((error) => report('color', `Could not read the color of session ${id}`, error));
96
+ router.add('command.run', async (e, next) => {
97
+ const answer = await next(e);
98
+ if (e.command !== 'color' || session === '')
99
+ return answer;
100
+ if (e.args.trim() === '')
101
+ readColor(session);
102
+ else
103
+ recolor(session, pickedColor(e.args));
104
+ return answer;
105
+ });
85
106
  let lastTurn;
86
107
  const closings = new Map();
87
108
  const dispatch = (to, changes, isInProject = true) => {
@@ -324,8 +345,11 @@ export function serveConversations({ claude, router, store, project, report, cle
324
345
  liveIds.clear();
325
346
  waitingRows.clear();
326
347
  conversation = createConversation(emptyConversation(id));
348
+ const isNew = source === 'startup' || source === 'clear';
349
+ if (!isNew)
350
+ readColor(id);
327
351
  starting =
328
- source === 'startup' || source === 'clear'
352
+ isNew
329
353
  ? Promise.resolve(dispatch(id, conversation.startContext(new Date().toISOString())))
330
354
  : json(['history', 'state', id]).then((state) => {
331
355
  if (session === id)
@@ -736,6 +760,10 @@ export function serveConversations({ claude, router, store, project, report, cle
736
760
  };
737
761
  return {
738
762
  turnClosedBy: (rowId) => closings.get(rowId),
763
+ color: () => color,
764
+ onColor(listener) {
765
+ onColor = listener;
766
+ },
739
767
  moved() {
740
768
  for (const [key, watch] of ownWatches)
741
769
  ownWatches.set(key, { ...watch, project: project() });
@@ -18,7 +18,7 @@ export async function createHotkeys({ name, declared, configRoot, claude, router
18
18
  if (name === cmodPluginName) {
19
19
  router.add('command.describe', async (e, next) => {
20
20
  const described = await next(e);
21
- return e.command.startsWith(hotkeyCommand(e.provider.plugin, '')) ? { ...described, isHidden: true } : described;
21
+ return e.command.startsWith(hotkeyCommand(e.provider.plugin.replace(/@[^@]*$/, ''), '')) ? { ...described, isHidden: true } : described;
22
22
  });
23
23
  }
24
24
  const entries = Object.entries(declared);
@@ -7,6 +7,7 @@ export declare const installPage = "cmod:install";
7
7
  export type Consent = {
8
8
  readonly install: string;
9
9
  readonly uninstall: string;
10
+ readonly program: string;
10
11
  readonly hotkeys: string;
11
12
  readonly permissions: readonly string[];
12
13
  readonly updates?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/runtime/installer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAM3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAuD,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhH,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AAOD,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,SAAS,CAAA;IACxF,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7I,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAC3D,CAAA;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAS,CA2MvK"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/runtime/installer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAM3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAmE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAE5H,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AAOD,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,SAAS,CAAA;IACxF,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7I,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAC3D,CAAA;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAS,CA4MvK"}
@@ -3,7 +3,7 @@ import { Box, elementData, Input, Text } from '../ui/elements.js';
3
3
  import { filledButton } from './buttons.js';
4
4
  import { messageOf } from '../utils/text.js';
5
5
  import { cmodPluginName, withBareRefusals } from './dependencies.js';
6
- import { merged, partMethods } from './ui.js';
6
+ import { merged, partMethods, stalePress } from './ui.js';
7
7
  export const installPage = 'cmod:install';
8
8
  const keptDraws = 8;
9
9
  export function createInstaller(name, claude, waiting, nextDraw) {
@@ -82,7 +82,7 @@ export function createInstaller(name, claude, waiting, nextDraw) {
82
82
  const press = async ({ draw: drawn, handler, args }) => {
83
83
  const run = draws.get(drawn)?.[handler];
84
84
  if (run === undefined)
85
- throw new Error(`${name}: that button drew before the last redraw. Press the one it shows now.`);
85
+ throw new Error(`${name}: ${stalePress}`);
86
86
  return collect(() => run(...args));
87
87
  };
88
88
  return {
@@ -104,10 +104,11 @@ export function createInstaller(name, claude, waiting, nextDraw) {
104
104
  isSeen = false;
105
105
  question?.cancel();
106
106
  },
107
- consent({ install, uninstall, hotkeys, permissions, updates }) {
107
+ consent({ install, uninstall, program, hotkeys, permissions, updates }) {
108
108
  const changes = [
109
109
  ...(install === '' ? [] : [`Run ${install} now${uninstall === '' ? '' : `, and ${uninstall} when you remove it`}`]),
110
110
  ...(install === '' && uninstall !== '' ? [`Run ${uninstall} when you remove it`] : []),
111
+ ...(program === '' ? [] : [`Install ${program}, which Claude runs by its name`]),
111
112
  ...(hotkeys === '' ? [] : [`Bind ${hotkeys}`]),
112
113
  ...(updates === undefined ? [] : [`Update ${name} automatically from the ${updates} marketplace`]),
113
114
  ];
@@ -1,4 +1,4 @@
1
- import type { Args, EventResult, Frozen, HookFailure, PluginOptions } from 'claude-code';
1
+ import type { Args, Caught, EventResult, Frozen, PluginOptions } from 'claude-code';
2
2
  import type { Mod, ModDefinition } from '../mod.js';
3
3
  import type { Options } from '../options.js';
4
4
  import { type Steps } from '../records.js';
@@ -25,7 +25,7 @@ export type Lifecycle<State extends object> = {
25
25
  readonly toasts: readonly ShownToast[];
26
26
  start(claude: Claude, read: (claude: Claude) => Promise<Plugin>): Promise<void>;
27
27
  route<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>): Promise<EventResult<N>>;
28
- caught<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>, failure: HookFailure): Promise<EventResult<N>>;
28
+ caught<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>, failure: Caught): Promise<EventResult<N>>;
29
29
  raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>;
30
30
  };
31
31
  export declare function readPlugin(claude: Claude): Promise<Plugin>;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -302,6 +302,8 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
302
302
  phase = 'active';
303
303
  stopWatchingRecord();
304
304
  runtime.claude.ui.invalidate('ui.render');
305
+ if (definition.name === cmodPluginName)
306
+ runtime.claude.ui.invalidate('command.describe');
305
307
  void runSteps(active.mod).catch((error) => toastFailure({ id: 'cmod:steps', message: 'Its installer stopped', error }));
306
308
  const missed = missedSessionStart;
307
309
  missedSessionStart = undefined;
@@ -357,7 +359,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
357
359
  });
358
360
  const installedElsewhere = new Promise((resolve) => (settleAsked = () => resolve('installed')));
359
361
  watchRecord();
360
- const answer = await Promise.race([installer.consent({ ...event, updates }), installedElsewhere]).finally(() => {
362
+ const answer = await Promise.race([installer.consent({ ...event, program: plugin?.steps.program ?? '', updates }), installedElsewhere]).finally(() => {
361
363
  settleAsked = undefined;
362
364
  });
363
365
  await installer.close();
@@ -695,7 +697,7 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
695
697
  return answer;
696
698
  return runtime.toaster.draw((await answer), runtime.claude.ui.resolve(render), render.props, render.requestId);
697
699
  },
698
- async caught(event, e, next, { kind, message }) {
700
+ async caught(event, e, next, { error: { kind, message }, called }) {
699
701
  const call = e;
700
702
  const isAsk = event === 'cmod.call' && call.to === definition.name && beforeAskOf(call) !== undefined;
701
703
  const reasons = { timeout: `it ran past the ${hookMs / 1000} s Claude Code gives a hook`, throw: message ?? 'it threw' };
@@ -703,7 +705,9 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
703
705
  if (reason === undefined)
704
706
  return isAsk ? { value: null } : next(e);
705
707
  const error = new Error(reason);
706
- toastFailure({ id: `stopped:${event}`, message: `Claude Code stopped its ${event.replace(/^classic\./, '')} hook`, error });
708
+ const failedBeneath = called && kind === 'throw' ? await next(e).then(() => undefined, messageOf) : undefined;
709
+ if (failedBeneath === undefined || failedBeneath !== message)
710
+ toastFailure({ id: `stopped:${event}`, message: `Claude Code stopped its ${event.replace(/^classic\./, '')} hook`, error });
707
711
  return router.dispatchFailure(event, e, error, next);
708
712
  },
709
713
  };
@@ -1039,6 +1043,16 @@ async function createMod(definition, runtime) {
1039
1043
  parts = drawnParts({ claude, report: reportPart, press: (to, input) => (pages === undefined ? pressElsewhere(to, input) : pages.press(to, input)) });
1040
1044
  const widgets = conversations === undefined ? undefined : serveWidgets({ claude, router, store: runtime.store, report: reportPart, turnClosedBy: (rowId) => conversations.turnClosedBy(rowId), parts, markdownIn: area.markdownIn });
1041
1045
  pages = widgets === undefined ? undefined : servePages({ claude, router, report: reportPart, widgets, parts, tellStylers: area.restyled });
1046
+ const tellColor = (to, told) => {
1047
+ if (told.session === '' || to === cmodPluginName)
1048
+ return;
1049
+ claude.clock.after(0, () => void claude.cmod.call({ to, method: conversationMethods.sessionColor, input: told }).catch((error) => claude.ui.log(`cmod: ${to} did not hear the session's color: ${messageOf(error)}`, { to: 'debug' })));
1050
+ };
1051
+ conversations?.onColor((told) => {
1052
+ client.recolor(told);
1053
+ for (const { mod: to } of pages?.declared() ?? [])
1054
+ tellColor(to, told);
1055
+ });
1042
1056
  router.add('cmod.call', async (e, next) => {
1043
1057
  if (e.to !== definition.name)
1044
1058
  return next(e);
@@ -1050,11 +1064,18 @@ async function createMod(definition, runtime) {
1050
1064
  client.changed(changedSession, change, isInProject !== false);
1051
1065
  return { value: null };
1052
1066
  }
1067
+ if (e.method === conversationMethods.sessionColor && call.caller === cmodPluginName) {
1068
+ client.recolor(e.input);
1069
+ return { value: null };
1070
+ }
1053
1071
  const ask = beforeAskOf(call);
1054
1072
  if (ask !== undefined)
1055
1073
  return { value: (await client.answerBefore(ask.session, ask.event, ask.change, ask.isInProject)) ?? null };
1056
1074
  if (e.method === partMethods.changed && call.caller !== undefined && pages !== undefined) {
1057
- return { value: { isHeld: pages.update(call.caller, e.input), stylers: pages.stylers() } };
1075
+ const update = e.input;
1076
+ if (update.parts !== undefined && conversations !== undefined)
1077
+ tellColor(call.caller, conversations.color());
1078
+ return { value: { isHeld: pages.update(call.caller, update), stylers: pages.stylers() } };
1058
1079
  }
1059
1080
  if (call.caller !== undefined) {
1060
1081
  const turn = e.method === partMethods.draw ? e.input.turn : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../src/runtime/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAe,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAChI,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAI3C,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAA;AAE3F,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAExF,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,IAAI,SAAS,QAAQ,EAAE,CAAA;IAC/B,OAAO,IAAI,SAAS,MAAM,EAAE,CAAA;IAC5B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAA;IACnH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAA;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC1C,KAAK,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,EAAE,CAGzD;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,MAAM,EACN,OAAO,EACP,KAAK,EACL,WAAW,GACZ,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAA;CAC3D,GAAG,KAAK,CAqHR"}
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../src/runtime/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA2B,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAC5I,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAI3C,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAA;AAE3F,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAExF,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,IAAI,SAAS,QAAQ,EAAE,CAAA;IAC/B,OAAO,IAAI,SAAS,MAAM,EAAE,CAAA;IAC5B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAA;IACnH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAA;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC1C,KAAK,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,EAAE,CAGzD;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,MAAM,EACN,OAAO,EACP,KAAK,EACL,WAAW,GACZ,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAA;CAC3D,GAAG,KAAK,CA4HR"}
package/runtime/pages.js CHANGED
@@ -2,7 +2,7 @@ import { isObject } from '../records.js';
2
2
  import { Text } from '../ui/elements.js';
3
3
  import { messageOf } from '../utils/text.js';
4
4
  import { cmodPluginName } from './dependencies.js';
5
- import { partMethods } from './ui.js';
5
+ import { partMethods, stalePress } from './ui.js';
6
6
  const askedWaitMs = 1000;
7
7
  export function pagesOf(input) {
8
8
  const listed = isObject(input) && Array.isArray(input['pages']) ? input['pages'] : [];
@@ -89,14 +89,17 @@ export function servePages({ claude, router, report, widgets, parts, tellStylers
89
89
  .then((answer) => void update(mod, (isObject(answer) ? answer : {})), (error) => {
90
90
  parts.forget(mod);
91
91
  claude.ui.invalidate('ui.render');
92
- report(`press:${mod}`, `${mod} did not take the press`, error);
92
+ if (messageOf(error).includes(stalePress))
93
+ claude.ui.log(`cmod: ${mod} draws its page again, because the button pressed was from an older drawing:${messageOf(error)}`, { to: 'debug' });
94
+ else
95
+ report(`press:${mod}`, `${mod} did not take the press`, error);
93
96
  })
94
97
  .finally(() => pressing.delete(pending));
95
98
  pressing.add(pending);
96
99
  if (inputDepth === 0)
97
100
  void pending.then(takeWaiting);
98
101
  };
99
- const cacheKey = (page, params, columns, isFocused) => `${page}\u0000${JSON.stringify(params ?? null)}\u0000${columns}\u0000${isFocused}`;
102
+ const lastDrawn = new Map();
100
103
  const plain = (data) => JSON.parse(JSON.stringify(data));
101
104
  return {
102
105
  declared: () => [...declared].map(([mod, pages]) => ({ mod, pages })),
@@ -104,11 +107,18 @@ export function servePages({ claude, router, report, widgets, parts, tellStylers
104
107
  element(mod, page, params, columns, isFocused) {
105
108
  if (!declared.has(mod))
106
109
  void ask(mod);
107
- const cached = cacheKey(page, params, columns, isFocused);
110
+ const given = JSON.stringify(params ?? null);
111
+ const cached = `${page}\u0000${given}\u0000${columns}\u0000${isFocused}`;
108
112
  const name = `The ${page} page of ${mod}`;
113
+ const keyPrefix = `${mod}/${page}:`;
109
114
  void parts.fill(mod, cached, { page, params: plain(params ?? null), columns, isFocused }, name)?.then(() => claude.ui.invalidate('ui.render'));
110
115
  const failure = parts.failure(mod, cached);
111
- return parts.element(mod, cached, `${mod}/${page}:`, name, `page:${mod}/${page}`) ?? (failure === undefined ? undefined : Text({ color: 'error', children: `${name} could not draw: ${failure}` }));
116
+ const drawnAt = (key) => parts.element(mod, key, keyPrefix, name, `page:${mod}/${page}`);
117
+ const last = lastDrawn.get(keyPrefix);
118
+ const drawn = drawnAt(cached);
119
+ if (drawn !== undefined)
120
+ lastDrawn.set(keyPrefix, { params: given, cached });
121
+ return drawn ?? (last?.params === given ? drawnAt(last.cached) : undefined) ?? (failure === undefined ? undefined : Text({ color: 'error', children: `${name} could not draw: ${failure}` }));
112
122
  },
113
123
  press,
114
124
  update,
@@ -30,7 +30,13 @@ export declare const conversationMethods: {
30
30
  readonly act: 'cmod:act';
31
31
  readonly changed: 'cmod:changed';
32
32
  readonly before: 'cmod:before';
33
+ readonly sessionColor: 'cmod:sessionColor';
33
34
  };
35
+ export type SessionColor = {
36
+ readonly session: string;
37
+ readonly color?: string;
38
+ };
39
+ export declare function pickedColor(args: string): string | undefined;
34
40
  export type Act = 'send' | 'startTask' | 'fork' | 'clone' | 'startBackgroundTask' | 'callTool' | 'usage' | 'surfaces' | 'abort';
35
41
  export declare const beforePermission: (event: BeforeEvent) => string;
36
42
  export declare const isAct: (action: string) => action is Act;
@@ -55,7 +61,8 @@ export type SessionsClient = {
55
61
  listen(method: string, args: readonly unknown[]): Subscription;
56
62
  turn(data: TurnData): Turn;
57
63
  /** An empty title keeps the title the session already has. */
58
- follow(current: Omit<SessionData, 'isRunning' | 'lastAt'>): void;
64
+ follow(current: Omit<SessionData, 'isRunning' | 'lastAt' | 'color'>): void;
65
+ recolor(told: SessionColor): void;
59
66
  };
60
67
  export declare function usageOf(claude: Pick<Claude, 'session'>): Promise<Usage>;
61
68
  export declare function payloadOf(change: SessionChange): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}