chat-recall 0.7.20 → 0.7.23
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/README.md +53 -11
- package/dist/board-hooks.d.ts +37 -27
- package/dist/board-hooks.d.ts.map +1 -1
- package/dist/board-hooks.js +50 -80
- package/dist/board-hooks.js.map +1 -1
- package/dist/cli.js +9526 -9086
- package/dist/cli.js.map +1 -1
- package/dist/install-hooks.d.ts +105 -0
- package/dist/install-hooks.d.ts.map +1 -0
- package/dist/install-hooks.js +424 -0
- package/dist/install-hooks.js.map +1 -0
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +2609 -1163
- package/dist/mcp.js.map +1 -1
- package/dist/message-hook-core.d.ts +93 -0
- package/dist/message-hook-core.d.ts.map +1 -0
- package/dist/message-hook-core.js +173 -0
- package/dist/message-hook-core.js.map +1 -0
- package/dist/message-hook.d.ts +2 -0
- package/dist/message-hook.d.ts.map +1 -0
- package/dist/message-hook.js +508 -0
- package/dist/message-hook.js.map +1 -0
- package/dist/scan-worker.js +36 -24
- package/dist/service-installer.d.ts.map +1 -1
- package/dist/service-installer.js +7 -0
- package/dist/service-installer.js.map +1 -1
- package/dist/watch.js +504 -483
- package/package.json +2 -2
- package/skills/chat-recall/SKILL.md +6 -1
- package/skills/chat-recall-sessions/SKILL.md +161 -0
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
Every coding agent you run keeps a full transcript of its work: the decisions, the
|
|
20
20
|
reasoning, the keys somebody pasted, the problems it found and never filed. Each one
|
|
21
21
|
writes to its own directory in its own format, none can read another's, and nothing
|
|
22
|
-
reads any of them. `chat-recall` does, through **
|
|
22
|
+
reads any of them. `chat-recall` does, through **68 MCP tools**.
|
|
23
23
|
|
|
24
24
|
| | What it does | Tools |
|
|
25
25
|
|---|---|---|
|
|
@@ -131,7 +131,7 @@ Exclusions live on the machine that holds the data, so the CLI owns them; they a
|
|
|
131
131
|
## Four things it actually does
|
|
132
132
|
|
|
133
133
|
1. **Cross-tool unified memory.** One index, one search, one UI over Claude Code (`~/.claude/projects/`), Codex (`~/.codex/`), OpenCode (`~/.local/share/opencode/`), Antigravity (`~/.gemini/antigravity-cli/`), Cursor (`~/.cursor/` for the CLI, `~/.config/Cursor/` for the IDE), Hermes Agent (`~/.hermes/`) and Pi (`~/.pi/agent/`). Sessions, plans, tasks, CLAUDE.md files, paste cache, shell history and agent diaries all share one pluggable `MemorySource` interface.
|
|
134
|
-
2. **The agent recalls itself.**
|
|
134
|
+
2. **The agent recalls itself.** 68 MCP tools, so Claude Code can `recall_smart_resume`, `recall_search` (with `like_session` to find similar work), `recall_edits_timeline`, `recall_subagent_search` and `recall_redundant_files` rather than asking you what happened last time. It writes back too, via `recall_decision_record`, `recall_kg_add` and `recall_set`.
|
|
135
135
|
3. **Warns before you redo work.** A `UserPromptSubmit` hook searches for similar past sessions on every prompt and injects a short "you have done this before, in session X" note into the agent's context.
|
|
136
136
|
4. **Temporal knowledge graph.** Decisions and tool mentions become entity-relationship triples with `valid_from`/`valid_to` windows, so you can ask what was decided in March and whether it still holds.
|
|
137
137
|
|
|
@@ -216,16 +216,20 @@ CHAT_RECALL_DAEMON_IDLE_SECS=0 # keep the daemon resident (default 900)
|
|
|
216
216
|
If the daemon cannot be reached for any reason, the session loads the server
|
|
217
217
|
itself and works exactly as it did before — slower, never broken.
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
`chat-recall init` installs the Claude Code hooks in every profile's
|
|
220
|
+
`settings.json`. If you added only the MCP server, from a marketplace button or
|
|
221
|
+
by hand, the MCP server installs them when it starts. You run nothing else.
|
|
222
|
+
|
|
223
|
+
To choose which hooks run, or to remove them:
|
|
220
224
|
|
|
221
225
|
```bash
|
|
222
|
-
chat-recall install-hooks
|
|
223
|
-
chat-recall install-hooks --no-
|
|
224
|
-
chat-recall install-hooks --
|
|
225
|
-
chat-recall install-hooks --no-escalate # skip the session-end escalation
|
|
226
|
-
chat-recall install-hooks --uninstall # remove all of ours, leave third-party hooks alone
|
|
226
|
+
chat-recall install-hooks --no-resume-hint # without the resume warning
|
|
227
|
+
chat-recall install-hooks --no-messages # without messages between sessions
|
|
228
|
+
chat-recall install-hooks --uninstall # remove ours; third-party hooks stay
|
|
227
229
|
```
|
|
228
230
|
|
|
231
|
+
After `--uninstall` the MCP server does not put them back.
|
|
232
|
+
|
|
229
233
|
| Hook | When it fires | What it does |
|
|
230
234
|
|---|---|---|
|
|
231
235
|
| `SessionStart` | New session (`startup` / `clear`) | Injects the project-scoped wake-up bundle |
|
|
@@ -233,6 +237,44 @@ chat-recall install-hooks --uninstall # remove all of ours, leave third-part
|
|
|
233
237
|
| `Stop` | After every assistant turn | Auto-saves topics, decisions, and tools to `~/.chat-recall/memory/` |
|
|
234
238
|
| `PreCompact` | Before Claude Code compacts context | Emergency save so nothing is lost to compaction |
|
|
235
239
|
| `SessionEnd` | When the session closes | Escalates the session's learnings in the background, so nothing is delayed |
|
|
240
|
+
| `SessionStart`, `UserPromptSubmit`, `Stop`, `SessionEnd` | Each turn | Messages between sessions: tells the session its id, delivers what waits for it, reports its state |
|
|
241
|
+
| `SessionStart`, `UserPromptSubmit`, `Stop` (background) | Until a message arrives | Waits for a message from another session and wakes this one, also when it is idle |
|
|
242
|
+
|
|
243
|
+
The other AI tools get the message hook too, so their sessions can send and
|
|
244
|
+
receive messages. The installer writes it only for a tool that is installed:
|
|
245
|
+
|
|
246
|
+
| Tool | Where | When a message arrives in the session |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| Codex | `~/.codex/hooks.json` | At the next prompt. Approve the hook once with `/hooks` in Codex. |
|
|
249
|
+
| Antigravity | `~/.gemini/config/hooks.json` | At the next prompt |
|
|
250
|
+
| Cursor | `~/.cursor/hooks.json` | When the agent stops, as a follow-up prompt |
|
|
251
|
+
| opencode | `~/.config/opencode/plugins/chat-recall-messages.js` | At the next prompt |
|
|
252
|
+
| pi | `~/.pi/agent/extensions/chat-recall-messages.js` | At the next prompt |
|
|
253
|
+
|
|
254
|
+
Only Claude Code wakes an idle session. In the other tools, an idle session
|
|
255
|
+
reads its messages when its user types next. `chat-recall doctor` shows the
|
|
256
|
+
hook of each tool.
|
|
257
|
+
|
|
258
|
+
## Sessions that work together
|
|
259
|
+
|
|
260
|
+
One session can start others and direct them. In Claude Code, ask for it in
|
|
261
|
+
plain words: "start two workers, one on the API and one on the web form, and
|
|
262
|
+
tell me when both are done".
|
|
263
|
+
|
|
264
|
+
- `recall_session_spawn` starts a child as a Claude Code background session,
|
|
265
|
+
linked to its parent. `claude attach <id>` opens it.
|
|
266
|
+
- `recall_message_send` sends to a session by id or name, or to `parent`,
|
|
267
|
+
`children` or `group`. The message wakes the recipient within about a second,
|
|
268
|
+
also when it waits for input.
|
|
269
|
+
- `recall_session_status` lists the sessions: what each works on, its board
|
|
270
|
+
cards, and whether it is working, idle, waiting for a permission prompt, or
|
|
271
|
+
ended.
|
|
272
|
+
- Decisions, facts and shared state go in the memory every session reads:
|
|
273
|
+
`recall_decision_record`, `recall_kg_add`, `recall_set`.
|
|
274
|
+
|
|
275
|
+
A message from another session is not a message from you. A child acts on its
|
|
276
|
+
parent's messages because its parent started it with that instruction. Any
|
|
277
|
+
other session treats a message as information and checks with you first.
|
|
236
278
|
|
|
237
279
|
## The decision guard
|
|
238
280
|
|
|
@@ -256,13 +298,13 @@ guard uninstalled; a wrong warning costs a line of context. Set
|
|
|
256
298
|
`CHAT_RECALL_GUARD_ENFORCE=1` to block instead, per machine. `CHAT_RECALL_GUARD=0`
|
|
257
299
|
turns it off.
|
|
258
300
|
|
|
259
|
-
`chat-recall
|
|
301
|
+
`chat-recall init` registers it in all five tools. Each one keeps its
|
|
260
302
|
pre-execution hook in its own file, so the installer writes five shapes and
|
|
261
303
|
skips any tool that is not on the machine (`--no-guard` to skip all of them):
|
|
262
304
|
|
|
263
305
|
| Tool | Where | Event |
|
|
264
306
|
|---|---|---|
|
|
265
|
-
| Claude Code | `~/.claude/
|
|
307
|
+
| Claude Code | `~/.claude/settings.json`, every profile | `PreToolUse` |
|
|
266
308
|
| Codex | `~/.codex/hooks.json` | `PreToolUse` |
|
|
267
309
|
| Antigravity | `~/.gemini/antigravity-cli/hooks.json` | `PreToolUse` — the one tool with a real `ask` verdict, so it can prompt |
|
|
268
310
|
| Cursor | `~/.cursor/hooks.json` | `beforeShellExecution` + `afterFileEdit` |
|
|
@@ -315,7 +357,7 @@ codeindex is open source (MIT) at [github.com/munhq/codeindex](https://github.co
|
|
|
315
357
|
| **Paste** | `~/.claude/paste-cache/*.txt` | Large pasted blobs |
|
|
316
358
|
| **Diary** | `~/.chat-recall/index/diary/<agent>/*.json` | What the agent told its future self via `recall_diary_write` |
|
|
317
359
|
|
|
318
|
-
## MCP tools (
|
|
360
|
+
## MCP tools (68, including 4 code-intelligence tools that register when the companion binary is installed)
|
|
319
361
|
|
|
320
362
|
**Search & retrieve** — `recall_search`, `recall_memory_search`, `recall_recent`, `recall_show`, `recall_context`, `recall_summary`, `recall_smart_resume`, `recall_project_context`, `recall_weekly_digest`, `recall_analytics_summary`, `recall_wake_up`.
|
|
321
363
|
|
package/dist/board-hooks.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
type HookEntry = {
|
|
2
|
+
matcher: string;
|
|
3
|
+
hooks: Array<{
|
|
4
|
+
type: string;
|
|
5
|
+
command: string;
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
1
8
|
/** The two events the board loop needs, and the flag each one passes. */
|
|
2
9
|
export declare const BOARD_HOOKS: ReadonlyArray<{
|
|
3
10
|
event: string;
|
|
@@ -12,7 +19,7 @@ export declare const BOARD_HOOKS: ReadonlyArray<{
|
|
|
12
19
|
*/
|
|
13
20
|
export declare function boardHookCommand(execPath: string, cliEntry: string, mode: 'claim' | 'close'): string;
|
|
14
21
|
/**
|
|
15
|
-
* True when this
|
|
22
|
+
* True when this hook entry is one of ours.
|
|
16
23
|
*
|
|
17
24
|
* It answers for two shapes. The first is the CLI command above. The second is
|
|
18
25
|
* `chat_recall_task_hook.sh`, a wrapper that shipped briefly and did nothing
|
|
@@ -21,36 +28,39 @@ export declare function boardHookCommand(execPath: string, cliEntry: string, mod
|
|
|
21
28
|
* cleaned up.
|
|
22
29
|
*/
|
|
23
30
|
export declare function isBoardHookEntry(entry: unknown): boolean;
|
|
24
|
-
/** The `[event, entry]` pairs for a
|
|
25
|
-
export declare function boardHookEntries(execPath: string, cliEntry: string): Array<[string,
|
|
26
|
-
matcher: string;
|
|
27
|
-
hooks: Array<{
|
|
28
|
-
type: string;
|
|
29
|
-
command: string;
|
|
30
|
-
}>;
|
|
31
|
-
}]>;
|
|
31
|
+
/** The `[event, entry]` pairs for a settings.json. */
|
|
32
|
+
export declare function boardHookEntries(execPath: string, cliEntry: string): Array<[string, HookEntry]>;
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* replaces a stale bundle path instead of stacking a second registration.
|
|
37
|
-
* Entries that are not ours are untouched: a profile carries the resume hint on
|
|
38
|
-
* the same event.
|
|
34
|
+
* The events of the message hook's turn runs. SessionStart tells the session
|
|
35
|
+
* its own id; the prompt and the turn end deliver what already waits;
|
|
36
|
+
* SessionEnd marks the session ended so a parent stops waiting for it.
|
|
39
37
|
*/
|
|
40
|
-
export declare
|
|
41
|
-
hooks?: Record<string, unknown[]>;
|
|
42
|
-
}, execPath: string, cliEntry: string): void;
|
|
43
|
-
/** Drop the board hooks from one already-parsed hooks.json object. */
|
|
44
|
-
export declare function removeBoardHooks(config: {
|
|
45
|
-
hooks?: Record<string, unknown[]>;
|
|
46
|
-
}): number;
|
|
38
|
+
export declare const MESSAGE_HOOK_EVENTS: readonly string[];
|
|
47
39
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* overwritten: another tool's config is not ours to replace.
|
|
40
|
+
* The events that start the waiter. It runs in the background (asyncRewake)
|
|
41
|
+
* until a message arrives, then exits 2 and Claude Code wakes the session; the
|
|
42
|
+
* turn that follows ends in a Stop, which starts the next waiter.
|
|
52
43
|
*/
|
|
53
|
-
export declare
|
|
44
|
+
export declare const MESSAGE_WAITER_EVENTS: readonly string[];
|
|
45
|
+
/** The command Claude Code runs for the message hook. Both paths quoted, as above. */
|
|
46
|
+
export declare function messageHookCommand(execPath: string, hookEntry: string, waiter?: boolean): string;
|
|
47
|
+
/** True when this hook entry is the message hook, its turn run or its waiter. */
|
|
48
|
+
export declare function isMessageHookEntry(entry: unknown): boolean;
|
|
49
|
+
/** True when this hook entry is the waiter. */
|
|
50
|
+
export declare function isMessageWaiterEntry(entry: unknown): boolean;
|
|
51
|
+
type HookCommand = {
|
|
52
|
+
type: string;
|
|
53
|
+
command: string;
|
|
54
|
+
asyncRewake?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** The `[event, entry]` pairs for the message hook: the turn runs, then the waiters. */
|
|
57
|
+
export declare function messageHookEntries(execPath: string, hookEntry: string): Array<[string, {
|
|
58
|
+
matcher: string;
|
|
59
|
+
hooks: HookCommand[];
|
|
60
|
+
}]>;
|
|
54
61
|
/** Resolve the CLI entry that a hook should run. */
|
|
55
62
|
export declare function resolveCliEntry(distDir: string): string;
|
|
63
|
+
/** Resolve the message hook bundle that sits beside the CLI. */
|
|
64
|
+
export declare function resolveMessageHookEntry(distDir: string): string;
|
|
65
|
+
export {};
|
|
56
66
|
//# sourceMappingURL=board-hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board-hooks.d.ts","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"board-hooks.d.ts","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"AA0BA,KAAK,SAAS,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC;AAQtF,yEAAyE;AACzE,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,CAGjF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAEpG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKxD;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAK/F;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAA+D,CAAC;AAEjH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAAiD,CAAC;AAErG,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,CAE9F;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG1D;AAED,+CAA+C;AAC/C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CAAC,CAOlI;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG/D"}
|
package/dist/board-hooks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The commands for the two board hooks and the message hook, on every
|
|
3
|
+
* platform. install-hooks.ts writes them into each profile's settings.json.
|
|
3
4
|
*
|
|
4
5
|
* The other five hooks are POSIX shell scripts, and `install-hooks` refuses to
|
|
5
6
|
* register any of them on Windows: Claude Code runs a hook command through
|
|
@@ -15,9 +16,18 @@
|
|
|
15
16
|
* Registration is also the only reason the loop reaches anyone: `init` never
|
|
16
17
|
* installed hooks, so until now the board moved for whoever had separately run
|
|
17
18
|
* `chat-recall install-hooks`.
|
|
19
|
+
*
|
|
20
|
+
* The message hook is registered the same way, for the same reason. It is its
|
|
21
|
+
* own small bundle (`dist/message-hook.js`) because it runs on every tool call,
|
|
22
|
+
* and the full CLI takes about 0.4 s to start.
|
|
18
23
|
*/
|
|
19
|
-
import { existsSync
|
|
20
|
-
import {
|
|
24
|
+
import { existsSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
/** The command string of a hook entry, or null for a malformed one. */
|
|
27
|
+
function entryCommand(entry) {
|
|
28
|
+
const cmd = entry?.hooks?.[0]?.command;
|
|
29
|
+
return typeof cmd === 'string' ? cmd : null;
|
|
30
|
+
}
|
|
21
31
|
/** The two events the board loop needs, and the flag each one passes. */
|
|
22
32
|
export const BOARD_HOOKS = [
|
|
23
33
|
{ event: 'UserPromptSubmit', mode: 'claim' },
|
|
@@ -34,7 +44,7 @@ export function boardHookCommand(execPath, cliEntry, mode) {
|
|
|
34
44
|
return `"${execPath}" "${cliEntry}" task-hook --${mode}`;
|
|
35
45
|
}
|
|
36
46
|
/**
|
|
37
|
-
* True when this
|
|
47
|
+
* True when this hook entry is one of ours.
|
|
38
48
|
*
|
|
39
49
|
* It answers for two shapes. The first is the CLI command above. The second is
|
|
40
50
|
* `chat_recall_task_hook.sh`, a wrapper that shipped briefly and did nothing
|
|
@@ -43,14 +53,14 @@ export function boardHookCommand(execPath, cliEntry, mode) {
|
|
|
43
53
|
* cleaned up.
|
|
44
54
|
*/
|
|
45
55
|
export function isBoardHookEntry(entry) {
|
|
46
|
-
const cmd = entry
|
|
47
|
-
if (
|
|
56
|
+
const cmd = entryCommand(entry);
|
|
57
|
+
if (cmd === null)
|
|
48
58
|
return false;
|
|
49
59
|
if (cmd.includes('chat_recall_task_hook.sh'))
|
|
50
60
|
return true;
|
|
51
61
|
return cmd.includes('task-hook') && (cmd.includes('--claim') || cmd.includes('--close'));
|
|
52
62
|
}
|
|
53
|
-
/** The `[event, entry]` pairs for a
|
|
63
|
+
/** The `[event, entry]` pairs for a settings.json. */
|
|
54
64
|
export function boardHookEntries(execPath, cliEntry) {
|
|
55
65
|
return BOARD_HOOKS.map(({ event, mode }) => [
|
|
56
66
|
event,
|
|
@@ -58,87 +68,47 @@ export function boardHookEntries(execPath, cliEntry) {
|
|
|
58
68
|
]);
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* replaces a stale bundle path instead of stacking a second registration.
|
|
65
|
-
* Entries that are not ours are untouched: a profile carries the resume hint on
|
|
66
|
-
* the same event.
|
|
71
|
+
* The events of the message hook's turn runs. SessionStart tells the session
|
|
72
|
+
* its own id; the prompt and the turn end deliver what already waits;
|
|
73
|
+
* SessionEnd marks the session ended so a parent stops waiting for it.
|
|
67
74
|
*/
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
config.hooks[event] = kept;
|
|
79
|
-
}
|
|
75
|
+
export const MESSAGE_HOOK_EVENTS = ['SessionStart', 'UserPromptSubmit', 'Stop', 'SessionEnd'];
|
|
76
|
+
/**
|
|
77
|
+
* The events that start the waiter. It runs in the background (asyncRewake)
|
|
78
|
+
* until a message arrives, then exits 2 and Claude Code wakes the session; the
|
|
79
|
+
* turn that follows ends in a Stop, which starts the next waiter.
|
|
80
|
+
*/
|
|
81
|
+
export const MESSAGE_WAITER_EVENTS = ['SessionStart', 'UserPromptSubmit', 'Stop'];
|
|
82
|
+
/** The command Claude Code runs for the message hook. Both paths quoted, as above. */
|
|
83
|
+
export function messageHookCommand(execPath, hookEntry, waiter = false) {
|
|
84
|
+
return `"${execPath}" "${hookEntry}"${waiter ? ' --wait' : ''}`;
|
|
80
85
|
}
|
|
81
|
-
/**
|
|
82
|
-
export function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let removed = 0;
|
|
86
|
-
for (const { event } of BOARD_HOOKS) {
|
|
87
|
-
const current = Array.isArray(config.hooks[event]) ? config.hooks[event] : [];
|
|
88
|
-
const kept = current.filter((h) => !isBoardHookEntry(h));
|
|
89
|
-
removed += current.length - kept.length;
|
|
90
|
-
if (kept.length)
|
|
91
|
-
config.hooks[event] = kept;
|
|
92
|
-
else
|
|
93
|
-
delete config.hooks[event];
|
|
94
|
-
}
|
|
95
|
-
return removed;
|
|
86
|
+
/** True when this hook entry is the message hook, its turn run or its waiter. */
|
|
87
|
+
export function isMessageHookEntry(entry) {
|
|
88
|
+
const cmd = entryCommand(entry);
|
|
89
|
+
return cmd !== null && /message-hook\.(js|ts)"?( --wait)?$/.test(cmd.trim());
|
|
96
90
|
}
|
|
97
|
-
/**
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
return { hooks: {} };
|
|
101
|
-
try {
|
|
102
|
-
const parsed = JSON.parse(readFileSync(file, 'utf-8'));
|
|
103
|
-
if (!parsed.hooks || typeof parsed.hooks !== 'object')
|
|
104
|
-
parsed.hooks = {};
|
|
105
|
-
return parsed;
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
return { hooks: {} };
|
|
109
|
-
}
|
|
91
|
+
/** True when this hook entry is the waiter. */
|
|
92
|
+
export function isMessageWaiterEntry(entry) {
|
|
93
|
+
return isMessageHookEntry(entry) && /--wait$/.test(entryCommand(entry).trim());
|
|
110
94
|
}
|
|
111
|
-
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (const file of files) {
|
|
120
|
-
if (existsSync(file)) {
|
|
121
|
-
try {
|
|
122
|
-
JSON.parse(readFileSync(file, 'utf-8'));
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const config = readConfig(file);
|
|
129
|
-
mergeBoardHooks(config, execPath, cliEntry);
|
|
130
|
-
try {
|
|
131
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
132
|
-
writeFileSync(file, JSON.stringify(config, null, 2) + '\n');
|
|
133
|
-
written.push(file);
|
|
134
|
-
}
|
|
135
|
-
catch { /* a read-only profile directory is not a reason to fail setup */ }
|
|
136
|
-
}
|
|
137
|
-
return written;
|
|
95
|
+
/** The `[event, entry]` pairs for the message hook: the turn runs, then the waiters. */
|
|
96
|
+
export function messageHookEntries(execPath, hookEntry) {
|
|
97
|
+
const turn = messageHookCommand(execPath, hookEntry);
|
|
98
|
+
const waiter = messageHookCommand(execPath, hookEntry, true);
|
|
99
|
+
return [
|
|
100
|
+
...MESSAGE_HOOK_EVENTS.map((event) => [event, { matcher: '', hooks: [{ type: 'command', command: turn }] }]),
|
|
101
|
+
...MESSAGE_WAITER_EVENTS.map((event) => [event, { matcher: '', hooks: [{ type: 'command', command: waiter, asyncRewake: true }] }]),
|
|
102
|
+
];
|
|
138
103
|
}
|
|
139
104
|
/** Resolve the CLI entry that a hook should run. */
|
|
140
105
|
export function resolveCliEntry(distDir) {
|
|
141
106
|
const bundled = join(distDir, 'cli.js');
|
|
142
107
|
return existsSync(bundled) ? bundled : join(distDir, 'cli.ts');
|
|
143
108
|
}
|
|
109
|
+
/** Resolve the message hook bundle that sits beside the CLI. */
|
|
110
|
+
export function resolveMessageHookEntry(distDir) {
|
|
111
|
+
const bundled = join(distDir, 'message-hook.js');
|
|
112
|
+
return existsSync(bundled) ? bundled : join(distDir, 'message-hook.ts');
|
|
113
|
+
}
|
|
144
114
|
//# sourceMappingURL=board-hooks.js.map
|
package/dist/board-hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board-hooks.js","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"board-hooks.js","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"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"}
|