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 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 **63 MCP tools**.
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.** 63 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`.
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
- Then install the hooks (one command sets up auto-save, pre-compact backup, and the resume-hint that warns when you're about to redo work):
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 # registers all five events, in every Claude profile
223
- chat-recall install-hooks --no-resume-hint # skip the resume warning
224
- chat-recall install-hooks --no-wakeup # skip the session-start wake-up bundle
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 install-hooks` registers it in all five tools. Each one keeps its
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/hooks.json`, every profile | `PreToolUse` |
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 (62, including 4 code-intelligence tools that register when the companion binary is installed)
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
 
@@ -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 hooks.json entry is one of ours.
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 hooks.json. */
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
- * Merge the board hooks into one already-parsed hooks.json object.
34
- *
35
- * Every prior entry of ours is dropped first, so a reinstall after an upgrade
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 function mergeBoardHooks(config: {
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
- * Register the board hooks in every given hooks.json.
49
- *
50
- * Returns the files it wrote. A file it cannot parse is skipped rather than
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 function registerBoardHooks(files: string[], execPath: string, cliEntry: string): string[];
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":"AAqBA,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,mDAAmD;AACnD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC,CAAC,MAAM,EAAE;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,CAAC,CAKvF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CAAE,EAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI,CASN;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CAAE,GAAG,MAAM,CAWtF;AAYD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAehG;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD"}
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"}
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Registration for the two board hooks, on every platform.
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, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
20
- import { dirname, join } from 'node:path';
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 hooks.json entry is one of ours.
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?.hooks?.[0]?.command;
47
- if (typeof cmd !== 'string')
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 hooks.json. */
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
- * Merge the board hooks into one already-parsed hooks.json object.
62
- *
63
- * Every prior entry of ours is dropped first, so a reinstall after an upgrade
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 function mergeBoardHooks(config, execPath, cliEntry) {
69
- if (!config.hooks || typeof config.hooks !== 'object')
70
- config.hooks = {};
71
- const entries = boardHookEntries(execPath, cliEntry);
72
- for (const { event } of BOARD_HOOKS) {
73
- const current = Array.isArray(config.hooks[event]) ? config.hooks[event] : [];
74
- const kept = current.filter((h) => !isBoardHookEntry(h));
75
- for (const [ev, entry] of entries)
76
- if (ev === event)
77
- kept.push(entry);
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
- /** Drop the board hooks from one already-parsed hooks.json object. */
82
- export function removeBoardHooks(config) {
83
- if (!config.hooks)
84
- return 0;
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
- /** Read a hooks.json, tolerating an absent or unreadable file. */
98
- function readConfig(file) {
99
- if (!existsSync(file))
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
- * Register the board hooks in every given hooks.json.
113
- *
114
- * Returns the files it wrote. A file it cannot parse is skipped rather than
115
- * overwritten: another tool's config is not ours to replace.
116
- */
117
- export function registerBoardHooks(files, execPath, cliEntry) {
118
- const written = [];
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
@@ -1 +1 @@
1
- {"version":3,"file":"board-hooks.js","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"board-hooks.js","sourceRoot":"","sources":["../src/board-hooks.ts"],"names":[],"mappings":"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"}