@cmodjs/core 1.0.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/commands.md +22 -11
- package/docs/dependencies.md +6 -4
- package/docs/hooks.md +26 -3
- package/docs/index.md +1 -1
- package/docs/install-steps.md +5 -3
- package/docs/mod.md +29 -3
- package/docs/permissions.md +4 -4
- package/docs/session.md +126 -63
- package/docs/testing.md +7 -5
- package/docs/ui.md +9 -1
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +7 -2
- package/mod.d.ts +30 -6
- package/mod.d.ts.map +1 -1
- package/mod.js +34 -1
- package/package.json +1 -1
- package/records.d.ts +4 -1
- package/records.d.ts.map +1 -1
- package/records.js +6 -1
- package/register.d.ts.map +1 -1
- package/register.js +17 -0
- package/runtime/announce.d.ts.map +1 -1
- package/runtime/announce.js +2 -0
- package/runtime/before.d.ts +32 -0
- package/runtime/before.d.ts.map +1 -0
- package/runtime/before.js +214 -0
- package/runtime/claude.d.ts +2 -1
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +1 -0
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +333 -61
- package/runtime/dependencies.js +1 -1
- package/runtime/engine.d.ts +2 -6
- package/runtime/engine.d.ts.map +1 -1
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +8 -8
- package/runtime/hooks.d.ts +1 -1
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +19 -4
- package/runtime/installer.d.ts +1 -1
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +6 -5
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +129 -58
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +5 -4
- package/runtime/sessions.d.ts +26 -4
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +315 -117
- package/runtime/toaster.d.ts +5 -12
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +133 -23
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +26 -6
- package/session/context/context.d.ts +3 -1
- package/session/context/context.d.ts.map +1 -1
- package/session/context/turn/message/message.d.ts +1 -0
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +42 -1
- package/session/context/turn/turn.d.ts +2 -1
- package/session/context/turn/turn.d.ts.map +1 -1
- package/session/conversation.d.ts +20 -11
- package/session/conversation.d.ts.map +1 -1
- package/session/conversation.js +50 -27
- package/session/goal/goal.d.ts +3 -2
- package/session/goal/goal.d.ts.map +1 -1
- package/session/goal/task.d.ts +4 -4
- package/session/goal/task.d.ts.map +1 -1
- package/session/session.d.ts +80 -21
- package/session/session.d.ts.map +1 -1
- package/session/session.js +1 -0
- package/src/jobs/program.ts +7 -2
- package/src/mod.ts +48 -5
- package/src/records.ts +6 -1
- package/src/register.ts +17 -0
- package/src/runtime/announce.ts +2 -0
- package/src/runtime/before.ts +200 -0
- package/src/runtime/claude.ts +3 -0
- package/src/runtime/conversations.ts +337 -60
- package/src/runtime/dependencies.ts +1 -1
- package/src/runtime/engine.ts +2 -1
- package/src/runtime/grants.ts +8 -10
- package/src/runtime/hooks.ts +20 -4
- package/src/runtime/installer.ts +6 -5
- package/src/runtime/lifecycle.ts +135 -55
- package/src/runtime/metadata.ts +5 -4
- package/src/runtime/sessions.ts +316 -111
- package/src/runtime/toaster.ts +160 -44
- package/src/runtime/ui.ts +23 -4
- package/src/session/context/context.ts +3 -1
- package/src/session/context/turn/message/message.ts +39 -1
- package/src/session/context/turn/turn.ts +2 -1
- package/src/session/conversation.ts +56 -31
- package/src/session/goal/goal.ts +3 -2
- package/src/session/goal/task.ts +4 -4
- package/src/session/session.ts +62 -15
- package/src/testing/fake-claude.ts +4 -2
- package/src/testing/fake-sessions.ts +94 -20
- package/src/testing.ts +27 -3
- package/src/utils/call-effects.ts +2 -2
- package/src/utils/disposable.ts +3 -0
- package/src/utils/matcher.ts +1 -1
- package/src/utils/paths.ts +6 -3
- package/testing/fake-claude.d.ts +1 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +3 -1
- package/testing/fake-sessions.d.ts +8 -1
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +97 -18
- package/testing.d.ts +1 -0
- package/testing.d.ts.map +1 -1
- package/testing.js +23 -2
- package/utils/call-effects.d.ts +1 -0
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +1 -1
- package/utils/disposable.d.ts +4 -0
- package/utils/disposable.d.ts.map +1 -0
- package/utils/disposable.js +1 -0
- package/utils/matcher.js +1 -1
- package/utils/paths.d.ts +2 -1
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +6 -3
package/docs/commands.md
CHANGED
|
@@ -98,7 +98,7 @@ Releases the mod at `path` (default: the current folder) at the version in its `
|
|
|
98
98
|
4. Commits the release as the `release` branch, and builds the release archive from that commit.
|
|
99
99
|
5. Builds the program `cli/` declares, and writes `SHA256SUMS` for every file of the release.
|
|
100
100
|
6. Writes `.claude-plugin/marketplace.json`, listing the archive and the cmod plugin. The cmod entry points at the `release` branch of `heyJordanParker/cmod`, so the mod's `"cmod"` dependency resolves in this marketplace and follows each cmod release with no new publish of the mod.
|
|
101
|
-
7. Commits that file, tags `v<version>`, pushes the tag and the `release` branch,
|
|
101
|
+
7. Commits that file, tags `v<version>`, pushes the tag and the `release` branch, creates the GitHub release with every file, then pushes the branch that holds `marketplace.json`. So Claude Code never updates anyone to a release whose files are not up yet. Its notes are the version's section of the committed `CHANGELOG.md`, above GitHub's compare link ([mod.md](mod.md#release-notes)). `--dry-run` prints them.
|
|
102
102
|
8. Prints the link to paste as the Repository when you submit the mod at [claude.ai/directory/manage](https://claude.ai/directory/manage), such as `https://github.com/owner/greeter/tree/release`. The portal reads the branch from the link, so the directory follows `release`.
|
|
103
103
|
|
|
104
104
|
The release keeps `package.json`, so Claude Code still installs the mod's packages for its install and uninstall steps. It keeps `bun.lock` too, unless a `"files"` list leaves it out. `main` keeps the source only.
|
|
@@ -179,30 +179,33 @@ Lists every plugin Claude Code has installed or linked, with its version. A mod
|
|
|
179
179
|
|
|
180
180
|
```text
|
|
181
181
|
cmod history sessions [--all] [--since 2d] [--running] [--project <folder>] [--json]
|
|
182
|
-
cmod history search <text> [--all] [--since 2d] [--running] [--project <folder>] [--limit <n>] [--before <session>:<id>] [--json]
|
|
182
|
+
cmod history search <text> [--all] [--since 2d] [--running] [--project <folder>] [--session <id> [--task <id>] [--turn <id>] [--context <n>] [--goal]] [--limit <n>] [--before <session>:<id>] [--json]
|
|
183
183
|
cmod history messages [<session>] [--task <id>] [--turn <id>] [--after <id>] [--last <n>] [--json]
|
|
184
|
-
cmod history turns [<session>] [--context <n>] [--last <n>] [--json]
|
|
185
|
-
cmod history contexts [<session>] [--json]
|
|
186
|
-
cmod history tasks [<session>] [--task <id>] [--json]
|
|
187
|
-
cmod history goal [<session>] [--json]
|
|
184
|
+
cmod history turns [<session>] [--task <id>] [--context <n>] [--last <n>] [--json]
|
|
185
|
+
cmod history contexts [<session>] [--task <id>] [--json]
|
|
186
|
+
cmod history tasks [<session>] [--task <id> | --nested] [--json]
|
|
187
|
+
cmod history goal [<session>] [--task <id>] [--json]
|
|
188
|
+
cmod history usage [<session>] [--task <id>] [--json]
|
|
188
189
|
cmod history state [<session>] [--json]
|
|
189
|
-
cmod history follow <session>... [--json]
|
|
190
|
+
cmod history follow [<session>...] [--running] [--json]
|
|
190
191
|
cmod history index
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
Reads every conversation Claude Code saved, as the sessions, contexts, turns, tasks, and messages `sessions` answers a mod ([session.md](session.md)), so a person or an agent reads them in a terminal without parsing a transcript.
|
|
194
195
|
|
|
195
196
|
- `sessions` lists the conversations of the current folder's project, or `--project`'s, newest first, each with its time, id, and title. `--all` lists every project's, `--since` keeps those active in the last `30m`, `6h`, `2d`, or `1w`, or since a date such as `2026-10-01`, and `--running` keeps those open in Claude Code now.
|
|
196
|
-
- `search` prints the newest 50 messages whose text or tool input contains the text, at least 3 characters, any case, each with its time and session. Each tool input value is searched in its first 1,024 characters, and tool results are not searched. `--all`, `--since`, and `--running` scope it as they scope `sessions`, `--limit` changes the 50, and `--before <session>:<id>` prints the page after the message it names, the last one of the page before.
|
|
197
|
+
- `search` prints the newest 50 messages whose text or tool input contains the text, at least 3 characters, any case, each with its time and session. Each tool input value is searched in its first 1,024 characters, and tool results are not searched. `--all`, `--since`, and `--running` scope it as they scope `sessions`, `--limit` changes the 50, and `--before <session>:<id>` prints the page after the message it names, the last one of the page before. `--session` searches one conversation, and inside it `--task` searches a task and the tasks it started, `--turn` one turn, `--context` one context, and `--goal` the tasks of the conversation or of `--task`.
|
|
197
198
|
- `messages` prints one conversation's messages in order: the session named, or the one the command runs in when Claude Code runs it. `--task` reads a task's messages instead, `--turn` keeps one turn's, `--after` starts after a message id, and `--last` keeps that many from the end.
|
|
198
199
|
- `turns` prints the conversation's turns, each opened by a prompt and closed when Claude finishes. `--context` keeps one context's, and `--last` keeps that many from the end.
|
|
199
200
|
- `contexts` prints the conversation's contexts, a new one after each compaction.
|
|
200
|
-
- `tasks` prints the tasks the conversation started,
|
|
201
|
+
- `tasks` prints the tasks the conversation started, with `--task` the tasks that task started, and with `--nested` every task at any depth.
|
|
201
202
|
- `goal` prints what the conversation is for: the goal a mod set, or its first prompt.
|
|
203
|
+
- `usage` prints the model and the context's tokens as of the last reply.
|
|
204
|
+
- `--task` reads `turns`, `contexts`, `goal`, and `usage` of the session a task's owner runs, the way they read the conversation's.
|
|
202
205
|
- `state` prints what a running conversation resumes from.
|
|
203
|
-
- `follow` prints each message, turn, context, task, and goal change of the sessions named as Claude Code saves it, until stopped with Ctrl+C. The cmod plugin runs it to hear other running sessions.
|
|
206
|
+
- `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.
|
|
204
207
|
- `--json` prints JSON instead of one line each.
|
|
205
|
-
- `index` reads every saved conversation into the index at `~/.local/share/cmod/history-
|
|
208
|
+
- `index` reads every saved conversation into the index at `~/.local/share/cmod/history-5.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.
|
|
206
209
|
|
|
207
210
|
## Run in an install script
|
|
208
211
|
|
|
@@ -250,6 +253,14 @@ cmod settings <mod> <user|project|local> <json> [--project <folder>]
|
|
|
250
253
|
|
|
251
254
|
Changes Claude Code settings for a set-up mod, as its `settings.user.update`, `settings.project.update`, and `settings.local.update` do ([mod.md](mod.md#settings-claude-code-s-settings-files)), such as `cmod settings keeper user '{"cleanupPeriodDays":365}'`. It writes each top-level key of the JSON object into the user, project, or local settings, and `null` removes the key. The project and local settings are those of `--project`, the current folder when it is left out, and `mcpServers` in the project settings goes to `.mcp.json`. Each key needs the mod's `config` permission for it. cmod keeps every other key and the file's formatting, and `cmod teardown` takes out each value the mod set that is still the same.
|
|
252
255
|
|
|
256
|
+
### cmod rules
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
cmod rules [--project <folder>] [--json]
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Links the `rules/` folder of each mod that is on and has the `prompt` permission into Claude Code's rules folder, and removes the link of each that is off, lacks the permission, or is removed ([mod.md](mod.md#rules)). A mod the person installed links at `<config>/rules/<mod>`, and one turned on only for `--project`, the current folder when it is left out, links at `<project>/.claude/rules/<mod>`. It prints the rules it linked. With `--json` it prints `{ linked, placed, removed }`, where `placed` and `removed` name the mods whose link this run made or took away. `cmod install`, `update`, `remove`, `enable`, `disable`, `permission`, `setup`, `teardown`, `link`, and `unlink` run it after they succeed, and the cmod plugin runs it at each session's start and each prompt.
|
|
263
|
+
|
|
253
264
|
### cmod option
|
|
254
265
|
|
|
255
266
|
```text
|
package/docs/dependencies.md
CHANGED
|
@@ -88,13 +88,15 @@ export const outline = defineMod({
|
|
|
88
88
|
A call that cannot answer rejects with one of these reasons:
|
|
89
89
|
|
|
90
90
|
```text
|
|
91
|
-
tracer is not installed.
|
|
91
|
+
tracer is not installed. Add it with /plugin.
|
|
92
|
+
tracer is not set up. Its toast says why.
|
|
92
93
|
tracer is installing. Try again when it's ready.
|
|
93
94
|
tracer has no method signatures.
|
|
94
95
|
tracer: <the message of the error the method threw>
|
|
95
96
|
```
|
|
96
97
|
|
|
97
|
-
- `tracer is not installed` covers a provider whose code did not load
|
|
98
|
+
- `tracer is not installed` covers a provider whose code did not load.
|
|
99
|
+
- `tracer is not set up` covers a provider the person declined to install and one whose install failed. Its own toast shows why, with `Retry` or `Install`.
|
|
98
100
|
- `tracer is installing` covers a provider that is still running its install step. A provider that is only starting makes the call wait for it, up to 9 seconds.
|
|
99
101
|
- When tracer is disabled, Claude Code unloads the mod that lists it, with its own error: `Dependency "tracer" is disabled — enable it or remove the dependency`.
|
|
100
102
|
- A call fails with `mod.dependencies.tracer.signatures passed its 30 s deadline` when the other mod takes longer than 30 seconds. A job bounds the whole function that makes the call by its own deadline ([jobs.md](jobs.md)), so a slow call inside `slashCommand` can fail the command first, with `/outline passed the 30 s deadline of slashCommand`.
|
|
@@ -103,11 +105,11 @@ tracer: <the message of the error the method threw>
|
|
|
103
105
|
notInstalled(name: string): string
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
`notInstalled`, exported from `mod.js`, returns the first reason for a mod name: `<name> is not installed.
|
|
108
|
+
`notInstalled`, exported from `mod.js`, returns the first reason for a mod name: `<name> is not installed. Add it with /plugin.` A test uses it to expect that message.
|
|
107
109
|
|
|
108
110
|
## Test the calls
|
|
109
111
|
|
|
110
|
-
`testMod(outline, { dependencies: { tracer: { signatures: async () => [] } } })` answers tracer's methods with the functions given. A call to a mod the test does not fake fails with `tracer is not installed.
|
|
112
|
+
`testMod(outline, { dependencies: { tracer: { signatures: async () => [] } } })` answers tracer's methods with the functions given. A call to a mod the test does not fake fails with `tracer is not installed. Add it with /plugin.`, as in Claude Code. [testing.md](testing.md) covers it.
|
|
111
113
|
|
|
112
114
|
A test of the provider's own `api` fires `cmod.call`, as a caller's `mod.dependencies` does. The answer is `{ value }` with what the method returned, or `{ deny }` with the reason it failed:
|
|
113
115
|
|
package/docs/hooks.md
CHANGED
|
@@ -67,28 +67,45 @@ export const safeDelete = defineMod({
|
|
|
67
67
|
|
|
68
68
|
## Every hook
|
|
69
69
|
|
|
70
|
-
`Hook` names the
|
|
70
|
+
`Hook` names the 33 hooks a mod listens to, every hook Claude Code has. The input of each is the input a `settings.json` hook reads, typed as `ClassicHookInputs['<hook>']` in the `claude-code` module, with the changes this table names.
|
|
71
71
|
|
|
72
72
|
| Hook | Input changes |
|
|
73
73
|
| --- | --- |
|
|
74
|
+
| `Setup` | none |
|
|
74
75
|
| `SessionStart` | none |
|
|
75
76
|
| `SessionEnd` | none |
|
|
76
77
|
| `UserPromptSubmit` | none |
|
|
78
|
+
| `UserPromptExpansion` | none |
|
|
77
79
|
| `InstructionsLoaded` | none |
|
|
80
|
+
| `ConfigChange` | none |
|
|
81
|
+
| `DirectoryAdded` | none |
|
|
82
|
+
| `PreModelSwitch` | none |
|
|
83
|
+
| `PostModelSwitch` | none |
|
|
78
84
|
| `PreToolUse` | Claude Mod Manager (cmod) builds the input: `session_id`, `cwd`, `hook_event_name`, `tool_name`, `tool_input`, `tool_use_id`, `agent_id` inside a subagent, `agent_type` inside a subagent or when the session started as an agent, and `files`. It has no `transcript_path` and no `permission_mode`. |
|
|
79
85
|
| `PermissionRequest` | none |
|
|
80
86
|
| `PermissionDenied` | none |
|
|
81
87
|
| `PostToolUse` | adds `files` |
|
|
82
88
|
| `PostToolUseFailure` | adds `files` |
|
|
83
89
|
| `PostToolBatch` | none |
|
|
90
|
+
| `MessageDisplay` | none |
|
|
84
91
|
| `SubagentStart` | none |
|
|
85
92
|
| `SubagentStop` | none |
|
|
93
|
+
| `TodoCreated` | none |
|
|
94
|
+
| `TodoCompleted` | none |
|
|
95
|
+
| `TeammateIdle` | none |
|
|
96
|
+
| `Elicitation` | none |
|
|
97
|
+
| `ElicitationResult` | none |
|
|
86
98
|
| `Notification` | none |
|
|
87
99
|
| `PreCompact` | none |
|
|
100
|
+
| `PostCompact` | none |
|
|
88
101
|
| `Stop` | none |
|
|
89
102
|
| `StopFailure` | none |
|
|
90
103
|
| `CwdChanged` | no `transcript_path` |
|
|
91
104
|
| `FileChanged` | none |
|
|
105
|
+
| `WorktreeCreate` | none |
|
|
106
|
+
| `WorktreeRemove` | none |
|
|
107
|
+
|
|
108
|
+
`TodoCreated` and `TodoCompleted` are Claude Code's `TaskCreated` and `TaskCompleted` hooks, under names that say what they hear: an item added to or checked off Claude Code's todo checklist, which its `TaskCreate`, `TaskUpdate`, and `TaskList` tools keep. A cmod `Task` is work handed to another session ([session.md](session.md#turn-context-goal-and-task)), and these hooks never hear one.
|
|
92
109
|
|
|
93
110
|
cmod raises `CwdChanged` itself, never Claude Code. It raises it when the working folder changes: after `/cd`, after a Bash or PowerShell call, and at a prompt. `sessions.current.cwd` and `sessions.current.project` already hold the new folder when the listener runs.
|
|
94
111
|
|
|
@@ -170,6 +187,8 @@ type HookAnswer = {
|
|
|
170
187
|
updatedToolOutput?: unknown
|
|
171
188
|
updatedMCPToolOutput?: unknown
|
|
172
189
|
retry?: boolean
|
|
190
|
+
displayContent?: string
|
|
191
|
+
worktreePath?: string
|
|
173
192
|
}
|
|
174
193
|
}
|
|
175
194
|
```
|
|
@@ -186,12 +205,16 @@ Each event reads only some `hookSpecificOutput` fields:
|
|
|
186
205
|
| --- | --- |
|
|
187
206
|
| `SessionStart` | `additionalContext`, `initialUserMessage`, `sessionTitle`, `watchPaths`, `reloadSkills` |
|
|
188
207
|
| `UserPromptSubmit` | `additionalContext`, `sessionTitle`, `suppressOriginalPrompt` |
|
|
208
|
+
| `UserPromptExpansion` | `additionalContext`, `suppressOriginalPrompt` |
|
|
209
|
+
| `PreModelSwitch` | `permissionDecision`, `permissionDecisionReason` |
|
|
189
210
|
| `PreToolUse` | `additionalContext`, `permissionDecision` (`'allow'` and `'ask'` need `registerPermissionCheck`), `permissionDecisionReason`, `updatedInput` |
|
|
190
211
|
| `PermissionRequest` | `decision` |
|
|
191
212
|
| `PermissionDenied` | `retry` |
|
|
192
213
|
| `PostToolUse` | `additionalContext`, `updatedToolOutput`, `updatedMCPToolOutput` |
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
214
|
+
| `MessageDisplay` | `displayContent`: the text the person sees in place of the message |
|
|
215
|
+
| `WorktreeCreate` | `worktreePath`: the worktree the hook made, in place of Claude Code's own |
|
|
216
|
+
| `Setup`, `PostModelSwitch`, `PostToolUseFailure`, `PostToolBatch`, `SubagentStart`, `SubagentStop`, `Stop` | `additionalContext` |
|
|
217
|
+
| `SessionEnd`, `InstructionsLoaded`, `ConfigChange`, `DirectoryAdded`, `TodoCreated`, `TodoCompleted`, `TeammateIdle`, `Elicitation`, `ElicitationResult`, `Notification`, `PreCompact`, `PostCompact`, `StopFailure`, `CwdChanged`, `FileChanged`, `WorktreeRemove` | none |
|
|
195
218
|
|
|
196
219
|
A field the event does not read is an error: `it answered hookSpecificOutput.<field>, which <event> does not read. Remove it from the answer.` A flag set to `false`, such as `retry: false`, counts as unset.
|
|
197
220
|
|
package/docs/index.md
CHANGED
|
@@ -85,7 +85,7 @@ A mod imports from the files below, and every other file in this package is inte
|
|
|
85
85
|
| `mod.js` | `sessions`, `Sessions`, `Session`, `SessionScope`, `SessionSend`, `SessionEvent`, `SessionPayload`, `TaskStartArgs`, `Usage`, `Matcher` | [session.md](session.md) |
|
|
86
86
|
| `mod.js` | `Message`, `MessageType`, `Prompt`, `Note`, `Reply`, `ToolCall`, `ToolResult`, `Skill`, `Document`, `Notice`, `Sender`, `SenderType`, `Person`, `Schedule`, `Channel`, `Sdk` | [session.md](session.md#message) |
|
|
87
87
|
| `mod.js` | `Context`, `ContextStart`, `ContextEnd`, `Turn`, `TurnStart`, `TurnEnd`, `Goal`, `GoalChange`, `Task`, `TaskStart`, `TaskEnd` | [session.md](session.md#turn-context-goal-and-task) |
|
|
88
|
-
| `mod.js` | `Hook`, `HookInput`, `HookListener`, `HookOptions`, `HookAnswer`, and one constant
|
|
88
|
+
| `mod.js` | `Hook`, `HookInput`, `HookListener`, `HookOptions`, `HookAnswer`, and one constant for each of Claude Code's 33 hooks | [hooks.md](hooks.md) |
|
|
89
89
|
| `mod.js` | `Toast`, `ToastId`, `ToastType`, `ToastOptions` | [ui.md](ui.md#toasts) |
|
|
90
90
|
| `mod.js` | `Place`, `PlaceProps`, `ToolGroup`, `ToolProgress`, `CommandOutput`, `AskUserQuestion`, `Spinner`, `TurnDuration`, `SessionMode`, `PromptHint`, `AbovePrompt` | [ui.md](ui.md#places-change-a-row-claude-code-draws) |
|
|
91
91
|
| `mod.js` | `notInstalled` | [dependencies.md](dependencies.md) |
|
package/docs/install-steps.md
CHANGED
|
@@ -37,9 +37,9 @@ cmod refuses a `cmod` key that breaks these rules, with the fix in the message:
|
|
|
37
37
|
Before a step runs for the first time, cmod shows the person the commands, the keys, and the permissions, and asks to run them. cmod hashes the commands, the keys, 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)).
|
|
38
38
|
|
|
39
39
|
- In Claude Code, the mod opens the installer pane, `Install <mod>`, focused. It 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 /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 Escape decline.
|
|
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.`
|
|
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.
|
|
41
41
|
- In a terminal, `cmod install`, `cmod link`, `cmod try`, and `cmod update` print the commands, the program, the keys, and the permissions, and ask `Set up <mod>? [y/N]`. `--yes` approves without asking.
|
|
42
|
-
- `Not now` leaves the mod off
|
|
42
|
+
- `Not now` leaves the mod off with the toast `Not installed` and an `Install` button that asks again.
|
|
43
43
|
|
|
44
44
|
The mod starts only once its install step has run ([mod.md](mod.md)).
|
|
45
45
|
|
|
@@ -117,7 +117,9 @@ The install step runs again on every new version of the mod and whenever its scr
|
|
|
117
117
|
|
|
118
118
|
- When the first install of a mod fails or is stopped, cmod runs the uninstall step to undo it. So the uninstall step must work on a partial install.
|
|
119
119
|
- When the install step of an upgrade fails or is stopped, cmod keeps the version it set up before.
|
|
120
|
-
- The failure shows as an error toast with `
|
|
120
|
+
- The failure shows at once as an error toast with `Retry`, `Report`, and `Hide` ([ui.md](ui.md#toasts)).
|
|
121
|
+
- When waiting can fix the failure, cmod also retries in the background after 2, 4, 8, 16, 32, 64, and 128 seconds, then every 128 seconds, and the toast says `Trying again in <n> s.` A try that works replaces the toast with `Installed`. Waiting can fix two failures: a linked checkout whose build or install step failed, which cmod tries again once the checkout changed, and a release download that got 404 or no answer, as when a release was published seconds ago. Any other failure waits for `Retry`.
|
|
122
|
+
- Before each try, cmod reads the mod's install record, so a mod another session installed starts without running its install again.
|
|
121
123
|
|
|
122
124
|
## The uninstall step
|
|
123
125
|
|
package/docs/mod.md
CHANGED
|
@@ -49,7 +49,7 @@ Claude Code loads the mod at the start of each session. cmod then checks that th
|
|
|
49
49
|
|
|
50
50
|
The first time a mod starts on a machine, cmod shows the toast `Installed` under its name. Opened, it lists the mod's `plugin.json` `description`, what setup added, such as `Adds the my-mod pane, a render of AbovePrompt and a hook on UserPromptSubmit.`, what its install step set up, and each permission the person allowed, with a Settings button for `/mods <name>`. The first time a new version starts, the toast is `Updated to <version>`: the first three changes of that version's `CHANGELOG.md` section, the version the machine ran before, and a Changelog button ([Release notes](#release-notes)). A mod with no changelog lists what it adds now and no longer adds instead. A new session, `/reload-plugins`, and a version the machine already ran show no toast.
|
|
51
51
|
|
|
52
|
-
When a step fails, or the person declines the install, the mod stays stopped. Once
|
|
52
|
+
When a step fails, or the person declines the install, the mod stays stopped. Once another session sets it up, the mod starts at the next prompt of every session it stopped in, with no `/reload-plugins`.
|
|
53
53
|
|
|
54
54
|
When a step fails, the mod does not start. An error toast above the prompt names the failure, with the fix under it:
|
|
55
55
|
|
|
@@ -61,7 +61,24 @@ When a step fails, the mod does not start. An error toast above the prompt names
|
|
|
61
61
|
- `the details of its session did not load: <error>` when Claude Code cannot name the session's id, model, or start time.
|
|
62
62
|
- `it decides permissions on <events>, so hooks/register.ts must call registerPermissionCheck(addHook) after registerMod` when `setup` adds a hook on `PermissionRequest`, or a job that decides permissions, and `register.ts` does not register the permission check.
|
|
63
63
|
|
|
64
|
-
Each of these
|
|
64
|
+
Each of these has `Retry`, which starts the mod again, and `Report` and `Hide` ([ui.md](ui.md#toasts)).
|
|
65
|
+
|
|
66
|
+
## Rules
|
|
67
|
+
|
|
68
|
+
A mod's `rules/` folder holds rules in Claude Code's own rule format: one Markdown file per rule, with an optional `paths` list in its front matter. A rule without `paths` applies to every conversation, and a rule with `paths` applies when Claude reads a matching file.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
my-mod/
|
|
72
|
+
└── rules/
|
|
73
|
+
├── tests.md <- applies to every conversation
|
|
74
|
+
└── sql.md <- front matter paths: ["**/*.sql"]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
While the mod is on and has the `prompt` permission, cmod links the folder into Claude Code's rules folder: a mod the person installed links at `<config>/rules/<mod>`, and a mod turned on for one project at `<project>/.claude/rules/<mod>`. A mod turned off for one project keeps its rules out of that project through `claudeMdExcludes` in the project's `.claude/settings.local.json`. A rule the person excluded with `claudeMdExcludes` stays out of the notes too. Turning the mod off, turning `prompt` off, and removing the mod remove the link, and an update links the new version's folder. cmod changes only the links and exclusions it made.
|
|
78
|
+
|
|
79
|
+
Claude Code reads rules without `paths` only as a session starts. So when cmod links a mod's rules into a running session, it adds them to the conversation as one note, and when it removes the link, one note says the mod's rules no longer apply. A rule taken out of the folder is withdrawn by a note too. When an update changes a rule without `paths`, one note gives its new text. Claude Code loads rules with `paths` itself, at the read that matches.
|
|
80
|
+
|
|
81
|
+
The `Installed` toast lists the rules, as in `Adds 2 rules: sql and tests.` `cmod check` fails a mod with a `rules/` folder whose `package.json` `"cmod"` does not ask for `"prompt": true`.
|
|
65
82
|
|
|
66
83
|
## Release notes
|
|
67
84
|
|
|
@@ -235,6 +252,7 @@ mod.use(slashCommand({ name: 'ci-runs', description: 'Show the CI runs', reply:
|
|
|
235
252
|
## settings: Claude Code's settings files
|
|
236
253
|
|
|
237
254
|
```ts
|
|
255
|
+
settings.list(): Promise<Setting[]>
|
|
238
256
|
settings.user.read(): Promise<Settings>
|
|
239
257
|
settings.user.update(values: Readonly<Record<string, unknown>>): Promise<void>
|
|
240
258
|
settings.project.read(): Promise<Settings>
|
|
@@ -243,6 +261,14 @@ settings.local.read(): Promise<Settings>
|
|
|
243
261
|
settings.local.update(values: Readonly<Record<string, unknown>>): Promise<void>
|
|
244
262
|
```
|
|
245
263
|
|
|
264
|
+
`settings.list` answers every row of Claude Code's `/config` menu, its own and each plugin's, with the value each holds now:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
type Setting = { key: string; label: string; description?: string; kind: 'boolean' | 'choice' | 'text' | 'number'; value: boolean | string | number | readonly string[]; options?: readonly string[]; plugin?: string; isLocked: boolean }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`plugin` names the plugin a row belongs to, and Claude Code's own rows have none. `isLocked` is true when a managed setting or the organization's policy holds the value.
|
|
271
|
+
|
|
246
272
|
`settings.user` is the person's own `~/.claude/settings.json`, `settings.project` is the project's `.claude/settings.json`, and `settings.local` is the project's `.claude/settings.local.json`.
|
|
247
273
|
|
|
248
274
|
- `read` answers the values in that one file, such as `(await settings.user.read())['enabledPlugins']`.
|
|
@@ -326,7 +352,7 @@ type Entry = { name: string; isFile: boolean; isFolder: boolean; isLink: boolean
|
|
|
326
352
|
type Stat = { isFile: boolean; isFolder: boolean; isLink: boolean; size: number; modifiedAt: number; realPath?: string }
|
|
327
353
|
```
|
|
328
354
|
|
|
329
|
-
- A relative path is under the session's working folder. An absolute path is used as given. `~/.claude`
|
|
355
|
+
- A relative path is under the session's working folder. An absolute path is used as given. `~/.claude` and `$CLAUDE_CONFIG_DIR` are Claude Code's config folder, and `~` and `$HOME` are the home folder. Text is UTF-8.
|
|
330
356
|
- `read` rejects a missing file and a file over 4 MiB.
|
|
331
357
|
- `write` writes the whole file and creates its folders. Writing outside the project and the mod's data folder needs the `files` permission.
|
|
332
358
|
- `list` lists a folder, the working folder without a path. A symbolic link is neither a file nor a folder, with `isLink` true. `modifiedAt` is the time it last changed, in milliseconds since 1970.
|
package/docs/permissions.md
CHANGED
|
@@ -19,11 +19,11 @@ They sit in the `package.json` `cmod` key, beside `install` and `keys`, because
|
|
|
19
19
|
| `"network": ["api.github.com"]` | Connect to api.github.com | `http.fetch` to that host. A path starting with `/` or `~/` names a socket, such as `"~/.docker/run/docker.sock"`. The address of the mod's own running `program` job needs none |
|
|
20
20
|
| `"run": ["gh"]`, or `"run": "*"` | Run gh on your computer | `process.run`, `process.spawn`, and the `program` and `check` jobs. A program matches by its file name, so `"mermaid-ascii"` covers `${mod.dataFolder}/bin/mermaid-ascii` and the copy in cmod's programs folder |
|
|
21
21
|
| `"files": ["~/.zshrc"]` | Change ~/.zshrc | `fs.write` and `metadata.update` outside the project and the mod's data folder. A folder covers every file below it |
|
|
22
|
-
| `"conversation": true` | Read your Claude Code conversations | Reading `sessions`: their messages, turns, contexts, goals, and tasks, `sessions.list`, `
|
|
23
|
-
| `"prompt": true` | Add text Claude reads and start turns | A session's `send`, the `prompt` and `schedule` jobs, and the hook answers listed below |
|
|
22
|
+
| `"conversation": true` | Read your Claude Code conversations | Reading `sessions`: their messages, turns, contexts, goals, and tasks, `sessions.list`, `search`, `on`, `before`, `usage`, and `clients`. Also reading Claude Code's transcript files |
|
|
23
|
+
| `"prompt": true` | Add text Claude reads and start turns | A session's `send`, a `before` answer that changes or refuses text, the mod's `rules/`, the `prompt` and `schedule` jobs, and the hook answers listed below |
|
|
24
24
|
| `"model": true` | Ask a model, which uses your plan | `model.complete` |
|
|
25
|
-
| `"tasks": true` | Start tasks that work beside Claude | `
|
|
26
|
-
| `"tools": true` | Use and change Claude's tool calls | A listener or a job that changes a tool call's input |
|
|
25
|
+
| `"tasks": true` | Start tasks that work beside Claude | A session's `startTask`, and a `before(TaskStart)` or `before(TaskEnd)` answer |
|
|
26
|
+
| `"tools": true` | Use and change Claude's tool calls | A session's `callTool`, a `before(ToolCall)` answer, and a listener or a job that changes a tool call's input |
|
|
27
27
|
| `"config": ["cleanupPeriodDays"]` | Change cleanupPeriodDays in your Claude Code settings | `settings.user.update`, `settings.project.update`, and `settings.local.update` of that setting. `mcpServers` covers the project's `.mcp.json` |
|
|
28
28
|
| `"approve": true` | Approve Claude's tool calls for you | an allow or an ask from the mod's permission rules or hooks, and a `retry`. A deny needs no permission |
|
|
29
29
|
|