@cmodjs/core 4.1.0 → 4.1.1
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/docs/commands.md +3 -3
- package/docs/install-steps.md +3 -3
- package/docs/session.md +2 -0
- package/docs/testing.md +1 -1
- package/docs/ui.md +1 -1
- package/package.json +1 -1
- package/records.d.ts +1 -1
- package/records.js +1 -1
- package/register.js +4 -4
- package/runtime/conversations.d.ts +3 -0
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +30 -2
- package/runtime/hotkeys.js +1 -1
- package/runtime/installer.d.ts +1 -0
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +4 -3
- package/runtime/lifecycle.d.ts +2 -2
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +25 -4
- package/runtime/pages.d.ts.map +1 -1
- package/runtime/pages.js +15 -5
- package/runtime/sessions.d.ts +8 -1
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +16 -1
- package/runtime/ui.d.ts +1 -0
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +5 -3
- package/runtime/widgets.js +2 -1
- package/session/conversation.d.ts +1 -0
- package/session/conversation.d.ts.map +1 -1
- package/session/session.d.ts +1 -0
- package/session/session.d.ts.map +1 -1
- package/src/records.ts +1 -1
- package/src/register.ts +4 -4
- package/src/runtime/conversations.ts +32 -2
- package/src/runtime/hotkeys.ts +1 -1
- package/src/runtime/installer.ts +5 -3
- package/src/runtime/lifecycle.ts +23 -7
- package/src/runtime/pages.ts +12 -5
- package/src/runtime/sessions.ts +23 -2
- package/src/runtime/ui.ts +5 -3
- package/src/runtime/widgets.ts +1 -1
- package/src/session/conversation.ts +1 -0
- package/src/session/session.ts +1 -0
- package/src/testing/fake-elements.ts +6 -0
- package/src/testing/fake-sessions.ts +12 -1
- package/src/testing.ts +28 -5
- package/testing/fake-elements.d.ts.map +1 -1
- package/testing/fake-elements.js +6 -0
- package/testing/fake-sessions.d.ts +3 -0
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +8 -0
- package/testing.d.ts.map +1 -1
- 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
|
|
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-
|
|
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
|
-
|
|
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
|
|
package/docs/install-steps.md
CHANGED
|
@@ -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
|
|
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
|
|
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/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/package.json
CHANGED
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.
|
|
94
|
+
export declare const coreVersion = "4.1.1";
|
|
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.
|
|
269
|
+
export const coreVersion = '4.1.1';
|
|
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
|
|
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
|
|
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;
|
|
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"}
|
package/runtime/conversations.js
CHANGED
|
@@ -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
|
-
|
|
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() });
|
package/runtime/hotkeys.js
CHANGED
|
@@ -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);
|
package/runtime/installer.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/runtime/installer.js
CHANGED
|
@@ -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}:
|
|
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
|
];
|
package/runtime/lifecycle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Args, EventResult, Frozen,
|
|
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:
|
|
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":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAqB,WAAW,EAAE,MAAM,EAAc,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/runtime/lifecycle.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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;
|
package/runtime/pages.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
package/runtime/sessions.d.ts
CHANGED
|
@@ -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":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAM5D,OAAO,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAC3E,OAAO,EAAsB,KAAK,IAAI,EAAE,MAAM,iCAAiC,CAAA;AAC/E,OAAO,EAA+D,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAG5K,OAAO,EAAuD,KAAK,WAAW,EAA+F,KAAK,WAAW,EAA8D,KAAK,YAAY,EAAuC,KAAK,QAAQ,EAAkB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA;AACla,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC1F,OAAO,EAAkF,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AACxH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAKzC,OAAO,EAA+B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7D,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAEzJ,eAAO,MAAM,YAAY,MAAM,CAAA;AAE/B,eAAO,MAAM,mBAAmB;aAC9B,QAAQ,EAAE,eAAe;aACzB,MAAM,EAAE,aAAa;aACrB,QAAQ,EAAE,eAAe;aACzB,KAAK,EAAE,YAAY;aACnB,QAAQ,EAAE,eAAe;aACzB,KAAK,EAAE,YAAY;aACnB,IAAI,EAAE,WAAW;aACjB,UAAU,EAAE,iBAAiB;aAC7B,SAAS,EAAE,gBAAgB;aAC3B,WAAW,EAAE,kBAAkB;aAC/B,GAAG,EAAE,UAAU;aACf,OAAO,EAAE,cAAc;aACvB,MAAM,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
|
package/runtime/sessions.js
CHANGED
|
@@ -31,7 +31,13 @@ export const conversationMethods = {
|
|
|
31
31
|
act: 'cmod:act',
|
|
32
32
|
changed: 'cmod:changed',
|
|
33
33
|
before: 'cmod:before',
|
|
34
|
+
sessionColor: 'cmod:sessionColor',
|
|
34
35
|
};
|
|
36
|
+
const resetColors = ['default', 'reset', 'none', 'gray', 'grey'];
|
|
37
|
+
export function pickedColor(args) {
|
|
38
|
+
const picked = args.trim().toLowerCase();
|
|
39
|
+
return resetColors.includes(picked) ? undefined : picked;
|
|
40
|
+
}
|
|
35
41
|
const actPermissions = { send: 'prompt', startTask: 'tasks', fork: 'tasks', clone: 'tasks', startBackgroundTask: 'tasks', usage: 'conversation', surfaces: 'conversation', abort: 'prompt' };
|
|
36
42
|
const startsTurns = ['CronCreate'];
|
|
37
43
|
const ownSchedule = ['CronDelete', 'CronList'];
|
|
@@ -129,6 +135,8 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
129
135
|
const parents = new Map();
|
|
130
136
|
const known = new Map();
|
|
131
137
|
let current = { id: '', title: '', project: '', cwd: '', model: '', startedAt: '', lastAt: '', isRunning: true };
|
|
138
|
+
let told = { session: '' };
|
|
139
|
+
const colored = ({ color: _color, ...data }) => (told.session === data.id && told.color !== undefined ? { ...data, color: told.color } : data);
|
|
132
140
|
const keyOf = (scope) => scope.session ?? everySession;
|
|
133
141
|
const subscribe = (key) => {
|
|
134
142
|
if (queued.has(key))
|
|
@@ -349,6 +357,9 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
349
357
|
get title() {
|
|
350
358
|
return read().title;
|
|
351
359
|
},
|
|
360
|
+
get color() {
|
|
361
|
+
return read().color;
|
|
362
|
+
},
|
|
352
363
|
get project() {
|
|
353
364
|
return read().project;
|
|
354
365
|
},
|
|
@@ -595,7 +606,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
595
606
|
follow(next) {
|
|
596
607
|
const moved = next.id !== current.id && current.id !== '';
|
|
597
608
|
const previous = current.id;
|
|
598
|
-
current = { ...next, title: next.title || (next.id === current.id ? current.title : ''), isRunning: true, lastAt: new Date().toISOString() };
|
|
609
|
+
current = colored({ ...next, title: next.title || (next.id === current.id ? current.title : ''), isRunning: true, lastAt: new Date().toISOString() });
|
|
599
610
|
if (!moved)
|
|
600
611
|
return;
|
|
601
612
|
for (const into of [listening, deciding]) {
|
|
@@ -612,6 +623,10 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
612
623
|
subscribe(previous);
|
|
613
624
|
subscribe(current.id);
|
|
614
625
|
},
|
|
626
|
+
recolor(next) {
|
|
627
|
+
told = next;
|
|
628
|
+
current = colored(current);
|
|
629
|
+
},
|
|
615
630
|
};
|
|
616
631
|
}
|
|
617
632
|
export const recordOf = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {});
|
package/runtime/ui.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const partMethods: {
|
|
|
14
14
|
readonly changed: 'cmod:partsChanged';
|
|
15
15
|
readonly stylers: 'cmod:markdownStylers';
|
|
16
16
|
};
|
|
17
|
+
export declare const stalePress = "that button drew before the last redraw. Press the one it shows now.";
|
|
17
18
|
export type PagePlace = 'sidebar' | 'settings';
|
|
18
19
|
export type WidgetDeclaration = {
|
|
19
20
|
readonly id: string;
|