eklavya 1.24.2 → 1.25.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.
Files changed (82) hide show
  1. package/dist/assets/dashboard.html +1012 -255
  2. package/dist/assets/tutor/references/focus-and-level.md +7 -0
  3. package/dist/claude-mem.js +9 -7
  4. package/dist/claude-mem.js.map +1 -1
  5. package/dist/cli-memory.js +786 -0
  6. package/dist/cli-memory.js.map +1 -0
  7. package/dist/cli.js +95 -773
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.js +79 -13
  10. package/dist/config.js.map +1 -1
  11. package/dist/dashboard.js +289 -15
  12. package/dist/dashboard.js.map +1 -1
  13. package/dist/db.js +33 -6
  14. package/dist/db.js.map +1 -1
  15. package/dist/hooks/capture-lib.js +55 -0
  16. package/dist/hooks/capture-lib.js.map +1 -0
  17. package/dist/hooks/capture-tool.js +1 -1
  18. package/dist/hooks/capture-tool.js.map +1 -1
  19. package/dist/hooks/commit-lib.js +255 -0
  20. package/dist/hooks/commit-lib.js.map +1 -0
  21. package/dist/hooks/lib.js +37 -5
  22. package/dist/hooks/lib.js.map +1 -1
  23. package/dist/hooks/memory-lib.js +93 -78
  24. package/dist/hooks/memory-lib.js.map +1 -1
  25. package/dist/hooks/pre-tool-gate.js +5 -10
  26. package/dist/hooks/pre-tool-gate.js.map +1 -1
  27. package/dist/hooks/prompt-submit-nudge.js +26 -1
  28. package/dist/hooks/prompt-submit-nudge.js.map +1 -1
  29. package/dist/hooks/session-start.js +13 -4
  30. package/dist/hooks/session-start.js.map +1 -1
  31. package/dist/install.js +280 -67
  32. package/dist/install.js.map +1 -1
  33. package/dist/memory/capture.js +25 -5
  34. package/dist/memory/capture.js.map +1 -1
  35. package/dist/memory/privacy.js +105 -7
  36. package/dist/memory/privacy.js.map +1 -1
  37. package/dist/memory/provider.js +13 -3
  38. package/dist/memory/provider.js.map +1 -1
  39. package/dist/memory/recall.js +18 -6
  40. package/dist/memory/recall.js.map +1 -1
  41. package/dist/memory/spool.js +105 -23
  42. package/dist/memory/spool.js.map +1 -1
  43. package/dist/memory/store.js +32 -2
  44. package/dist/memory/store.js.map +1 -1
  45. package/dist/memory/summarize.js +5 -5
  46. package/dist/memory/summarize.js.map +1 -1
  47. package/dist/memory/worker.js +71 -10
  48. package/dist/memory/worker.js.map +1 -1
  49. package/dist/migrate.js +60 -14
  50. package/dist/migrate.js.map +1 -1
  51. package/dist/paths.js +49 -0
  52. package/dist/paths.js.map +1 -1
  53. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  54. package/dist/plugin/cli/CLAUDE.md +16 -4
  55. package/dist/plugin/hooks/CLAUDE.md +33 -3
  56. package/dist/plugin/hooks/run.mjs +69 -5
  57. package/dist/plugin/scripts/install-git-hook.sh +114 -24
  58. package/dist/plugin/skills/CLAUDE.md +1 -1
  59. package/dist/plugin/skills/setup/SKILL.md +9 -2
  60. package/dist/plugin/skills/tutor/references/focus-and-level.md +7 -0
  61. package/dist/safe-write.js +146 -0
  62. package/dist/safe-write.js.map +1 -0
  63. package/dist/slug.js +4 -1
  64. package/dist/slug.js.map +1 -1
  65. package/dist/srs.js +33 -1
  66. package/dist/srs.js.map +1 -1
  67. package/dist/store.js +29 -19
  68. package/dist/store.js.map +1 -1
  69. package/dist/tools/config_tools.js +23 -1
  70. package/dist/tools/config_tools.js.map +1 -1
  71. package/dist/tools/get_session_quiz_plan.js +51 -30
  72. package/dist/tools/get_session_quiz_plan.js.map +1 -1
  73. package/dist/tools/log_session_concepts.js +30 -23
  74. package/dist/tools/log_session_concepts.js.map +1 -1
  75. package/dist/tools/record_attempt.js +18 -8
  76. package/dist/tools/record_attempt.js.map +1 -1
  77. package/dist/tools/types.js +29 -0
  78. package/dist/tools/types.js.map +1 -1
  79. package/dist/tools/upsert_concepts.js +12 -10
  80. package/dist/tools/upsert_concepts.js.map +1 -1
  81. package/dist/user-skill/eklavya/SKILL.md +46 -21
  82. package/package.json +2 -1
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Does this Bash command create a commit? The PreToolUse gate's one question.
3
+ *
4
+ * It used to be a single regex anchored at the start of the line or after `;`,
5
+ * `&` or `|`. That missed a commit on its own line (`npm test\ngit commit`),
6
+ * inside `( )`, `{ }`, `$( )` or backticks, behind `env`, `command`, `time`,
7
+ * `nice` or `VAR=x`, through `/usr/bin/git` or `bash -c "…"`, and after
8
+ * `git --no-pager` or `git -C "my dir"`. Each extra case made the regex less
9
+ * readable, so this is a small shell lexer instead: split the command into
10
+ * simple commands the way the shell would, peel off the prefixes that run their
11
+ * argument as a command, and look at what git is asked to do.
12
+ *
13
+ * What it deliberately still lets through, because none of it runs a commit:
14
+ * a quoted string (`echo "git commit"`), a comment, a here-document body, and
15
+ * other subcommands that merely contain the word (`git commit-tree`,
16
+ * `git log --grep commit`).
17
+ *
18
+ * Scope matches the git pre-commit hook (`cli/eklavya-gate`), checked against
19
+ * git 2.50: `git commit` and `git merge --continue` run pre-commit; a clean
20
+ * `git merge`, `cherry-pick`, `revert`, `am` and `rebase --continue` do not. Two
21
+ * enforcement paths disagreeing about what a commit is would be a gate one of
22
+ * them waves through.
23
+ *
24
+ * Known misses, accepted: git aliases (`git ci`), a variable holding the command
25
+ * (`$GIT commit`), and scripts on disk. This is a speed bump for an agent that
26
+ * forgot the quiz, not a sandbox; the git hook is the backstop.
27
+ */
28
+ /** Every simple command in `src`, as its words with quotes resolved. */
29
+ export function simpleCommands(src) {
30
+ const out = [];
31
+ let i = 0;
32
+ const heredocs = [];
33
+ // Skip here-document bodies queued on the line just ended. `i` sits just
34
+ // past the newline.
35
+ const skipHeredocs = () => {
36
+ while (heredocs.length) {
37
+ const { delim, strip } = heredocs.shift();
38
+ while (i < src.length) {
39
+ const nl = src.indexOf('\n', i);
40
+ const end = nl === -1 ? src.length : nl;
41
+ let line = src.slice(i, end);
42
+ if (strip)
43
+ line = line.replace(/^\t+/, '');
44
+ i = end + 1;
45
+ if (line === delim)
46
+ break;
47
+ }
48
+ }
49
+ };
50
+ // One command list, up to `stop` (`)` or a backtick) or the end of input.
51
+ const list = (stop) => {
52
+ let words = [];
53
+ let word = '';
54
+ let inWord = false;
55
+ const endWord = () => {
56
+ if (inWord)
57
+ words.push(word);
58
+ word = '';
59
+ inWord = false;
60
+ };
61
+ const endCmd = () => {
62
+ endWord();
63
+ if (words.length)
64
+ out.push(words);
65
+ words = [];
66
+ };
67
+ while (i < src.length) {
68
+ const c = src[i];
69
+ if (c === stop) {
70
+ i += 1;
71
+ endCmd();
72
+ return;
73
+ }
74
+ if (c === ' ' || c === '\t') {
75
+ endWord();
76
+ i += 1;
77
+ }
78
+ else if (c === '\n') {
79
+ endCmd();
80
+ i += 1;
81
+ skipHeredocs();
82
+ }
83
+ else if (c === ';' || c === '&' || c === '|' || c === ')') {
84
+ endCmd();
85
+ i += 1;
86
+ }
87
+ else if (c === '(') {
88
+ endCmd();
89
+ i += 1;
90
+ list(')');
91
+ }
92
+ else if (c === '`') {
93
+ i += 1;
94
+ inWord = true;
95
+ list('`');
96
+ }
97
+ else if (c === '$' && src[i + 1] === '(') {
98
+ i += 2;
99
+ inWord = true;
100
+ list(')');
101
+ }
102
+ else if (c === '#' && !inWord) {
103
+ const nl = src.indexOf('\n', i);
104
+ i = nl === -1 ? src.length : nl;
105
+ }
106
+ else if (c === '\\') {
107
+ // A backslash-newline is a line continuation; anything else is literal.
108
+ if (src[i + 1] !== '\n') {
109
+ word += src[i + 1] ?? '';
110
+ inWord = true;
111
+ }
112
+ i += 2;
113
+ }
114
+ else if (c === "'") {
115
+ const close = src.indexOf("'", i + 1);
116
+ const end = close === -1 ? src.length : close;
117
+ word += src.slice(i + 1, end);
118
+ inWord = true;
119
+ i = end + 1;
120
+ }
121
+ else if (c === '"') {
122
+ i += 1;
123
+ inWord = true;
124
+ while (i < src.length && src[i] !== '"') {
125
+ if (src[i] === '\\') {
126
+ word += src[i + 1] ?? '';
127
+ i += 2;
128
+ }
129
+ else if (src[i] === '$' && src[i + 1] === '(') {
130
+ i += 2;
131
+ list(')');
132
+ }
133
+ else if (src[i] === '`') {
134
+ i += 1;
135
+ list('`');
136
+ }
137
+ else {
138
+ word += src[i];
139
+ i += 1;
140
+ }
141
+ }
142
+ i += 1;
143
+ }
144
+ else if (c === '<' && src.startsWith('<<', i) && !src.startsWith('<<<', i)) {
145
+ endWord();
146
+ i += 2;
147
+ const strip = src[i] === '-';
148
+ if (strip)
149
+ i += 1;
150
+ while (src[i] === ' ' || src[i] === '\t')
151
+ i += 1;
152
+ const m = /^[^\s;&|()<>]+/.exec(src.slice(i));
153
+ if (m) {
154
+ heredocs.push({ delim: m[0].replace(/["'\\]/g, ''), strip });
155
+ i += m[0].length;
156
+ }
157
+ }
158
+ else {
159
+ word += c;
160
+ inWord = true;
161
+ i += 1;
162
+ }
163
+ }
164
+ endCmd();
165
+ };
166
+ list(null);
167
+ return out;
168
+ }
169
+ const ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
170
+ /** Words that sit in front of a command without changing which one runs. */
171
+ const KEYWORDS = new Set(['{', '}', '!', 'if', 'then', 'else', 'elif', 'do', 'while', 'until', 'exec', 'nohup', 'builtin']);
172
+ /**
173
+ * Wrappers that run their argument as a command, and which of their options
174
+ * take a separate value. Anything else starting with `-` is a flag.
175
+ */
176
+ const WRAPPERS = {
177
+ sudo: new Set(['-u', '-g', '-C', '-D', '-h', '-p', '-r', '-t', '-U']),
178
+ doas: new Set(['-u', '-C']),
179
+ env: new Set(['-u', '-C', '-S']),
180
+ command: new Set(),
181
+ time: new Set(['-f', '-o']),
182
+ nice: new Set(['-n']),
183
+ xargs: new Set(['-I', '-n', '-L', '-P', '-d', '-E', '-s', '-a']),
184
+ timeout: new Set(['-s', '-k']),
185
+ };
186
+ /** `git` options that take a separate value, before the subcommand. */
187
+ const GIT_VALUE_OPTS = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--config-env', '--super-prefix']);
188
+ const SHELLS = new Set(['sh', 'bash', 'zsh', 'dash', 'ksh']);
189
+ const base = (word) => word.replace(/^.*[/\\]/, '').replace(/\.exe$/i, '');
190
+ function commits(words, depth) {
191
+ let k = 0;
192
+ // Peel prefixes until the word that actually runs.
193
+ for (;;) {
194
+ const w = words[k];
195
+ if (w === undefined)
196
+ return false;
197
+ if (ASSIGNMENT.test(w) || KEYWORDS.has(w)) {
198
+ k += 1;
199
+ continue;
200
+ }
201
+ const wrapper = WRAPPERS[base(w)];
202
+ if (!wrapper)
203
+ break;
204
+ k += 1;
205
+ // `timeout 10 git commit`: the duration is its first bare argument.
206
+ let needsDuration = base(w) === 'timeout';
207
+ for (let a = words[k]; a !== undefined; a = words[k]) {
208
+ if (a.startsWith('-') && a !== '-')
209
+ k += wrapper.has(a) ? 2 : 1;
210
+ else if (ASSIGNMENT.test(a))
211
+ k += 1;
212
+ else if (needsDuration) {
213
+ needsDuration = false;
214
+ k += 1;
215
+ }
216
+ else
217
+ break;
218
+ }
219
+ }
220
+ const cmd = base(words[k] ?? '');
221
+ const args = words.slice(k + 1);
222
+ if ((SHELLS.has(cmd) || cmd === 'eval') && depth < 4) {
223
+ let script;
224
+ if (cmd === 'eval')
225
+ script = args.join(' ');
226
+ else {
227
+ const flag = args.findIndex((a) => /^-[a-z]*c[a-z]*$/.test(a));
228
+ if (flag !== -1)
229
+ script = args.slice(flag + 1).find((a) => !a.startsWith('-'));
230
+ }
231
+ return script !== undefined && isCommitCommand(script, depth + 1);
232
+ }
233
+ if (cmd !== 'git')
234
+ return false;
235
+ let j = 0;
236
+ for (let a = args[j]; a?.startsWith('-'); a = args[j])
237
+ j += GIT_VALUE_OPTS.has(a) ? 2 : 1;
238
+ const sub = args[j];
239
+ if (sub === 'commit')
240
+ return true;
241
+ return sub === 'merge' && args.slice(j + 1).includes('--continue');
242
+ }
243
+ /**
244
+ * True when running `command` would create a commit. Never throws: any
245
+ * internal failure answers false, so the gate fails open.
246
+ */
247
+ export function isCommitCommand(command, depth = 0) {
248
+ try {
249
+ return simpleCommands(command).some((words) => commits(words, depth));
250
+ }
251
+ catch {
252
+ return false;
253
+ }
254
+ }
255
+ //# sourceMappingURL=commit-lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-lib.js","sourceRoot":"","sources":["../../src/hooks/commit-lib.ts"],"names":[],"mappings":"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"}
package/dist/hooks/lib.js CHANGED
@@ -62,19 +62,51 @@ export async function readInput() {
62
62
  * which is the same answer `eklavya_have_deps` gave.
63
63
  */
64
64
  export function openExisting() {
65
+ return openOrDiagnose().db;
66
+ }
67
+ /**
68
+ * `openExisting`, plus the reason when the answer is no.
69
+ *
70
+ * No file is not a problem: it is a first session, and the MCP server creates
71
+ * the database. A file that is there and cannot be opened or read is, and it
72
+ * used to be indistinguishable -- every hook returned in silence and Eklavya
73
+ * just stopped. `new Database` succeeds on a file full of garbage, so the header
74
+ * is read (`schema_version`) before calling it healthy; an empty file reads as
75
+ * version 0, which is what the server's own first open looks like mid-creation.
76
+ * A busy database is somebody else's write in progress, not a fault. Never throws.
77
+ */
78
+ export function openOrDiagnose() {
65
79
  const file = dbPath();
66
80
  if (!fs.existsSync(file))
67
- return null;
81
+ return { db: null, problem: null };
82
+ let db;
83
+ try {
84
+ db = new Database(file);
85
+ }
86
+ catch (err) {
87
+ return { db: null, problem: isNativeLoadError(err) ? 'native' : 'unreadable' };
88
+ }
68
89
  try {
69
- const db = new Database(file);
70
90
  db.pragma('busy_timeout = 2000');
71
91
  db.pragma('foreign_keys = ON');
72
- return db;
92
+ db.pragma('schema_version');
93
+ return { db, problem: null };
73
94
  }
74
- catch {
75
- return null;
95
+ catch (err) {
96
+ try {
97
+ db.close();
98
+ }
99
+ catch {
100
+ /* already unusable */
101
+ }
102
+ const code = err?.code ?? '';
103
+ return { db: null, problem: /^SQLITE_(BUSY|LOCKED)/.test(code) ? null : 'unreadable' };
76
104
  }
77
105
  }
106
+ function isNativeLoadError(err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ return /NODE_MODULE_VERSION|better_sqlite3\.node|bindings file|dlopen|ERR_DLOPEN_FAILED/i.test(message);
109
+ }
78
110
  /**
79
111
  * `meta` key holding the UserPromptSubmit nudge's per-session bookkeeping.
80
112
  * Shared here rather than exported from the hook, because `session-start`
@@ -1 +1 @@
1
- {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/hooks/lib.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/hooks/lib.ts"],"names":[],"mappings":"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"}
@@ -1,70 +1,86 @@
1
- import { projectKey } from '../store.js';
2
- import { identityFor } from '../memory/identity.js';
3
- import { capture, drainSpool } from '../memory/capture.js';
4
- import { batchSession, hasClaimableJob, pendingEventCount } from '../memory/store.js';
5
- import { processPending, writeSessionSummary } from '../memory/worker.js';
6
- import { recall, recallForPrompt } from '../memory/recall.js';
1
+ import { drainSpool } from '../memory/capture.js';
2
+ import { batchSession, hasClaimableJob, openSessions } from '../memory/store.js';
3
+ import { processPending, pruneIfDue, writeSessionSummary } from '../memory/worker.js';
4
+ import { parseStamp } from '../time.js';
5
+ import { recall } from '../memory/recall.js';
7
6
  import { notify, queuePausedAlert, sessionWrapUp } from '../memory/notify.js';
8
7
  import { countEntries } from '../memory/store.js';
9
8
  import { queueDepth } from '../memory/worker.js';
10
9
  import { isInternalObserver, launchWorker, reserveWorker } from '../memory/reservation.js';
11
- export function identityOf(input, cwd, sid) {
12
- const identity = identityFor({
13
- cwd,
14
- sessionId: sid ?? 'default',
15
- agentId: input.agent_id ?? null,
16
- host: 'claude-code',
17
- });
18
- // `identityFor` resolves the project from the cwd; `projectKey` is the
19
- // learning half's spelling of the same thing, and the two must not diverge or
20
- // a memory and a mastery row disagree about which codebase they belong to.
21
- return { ...identity, project: projectKey(identity.checkout) };
22
- }
23
- /** Records one event. Returns false on any failure, and never throws. */
24
- export function record(db, resolved, identity, event) {
25
- try {
26
- return capture(db, resolved.config, identity, event) === 'stored';
27
- }
28
- catch {
29
- return false;
30
- }
31
- }
10
+ // The per-call capture helpers live in `capture-lib.ts`, so the two hooks that
11
+ // run on every tool call and every prompt do not load this module's worker,
12
+ // provider, recall and notify graph. Re-exported so the seam hooks and tests
13
+ // keep one import.
14
+ export { batchIfFull, identityOf, record } from './capture-lib.js';
32
15
  /**
33
- * Closes a batch once enough evidence has accumulated. Batching is cheap SQL;
34
- * summarising is not, so this never summarises — the seam does.
16
+ * A Stop seam closes a session's open evidence into a batch only past one of
17
+ * these. The Stop hook fires at the end of every turn, and with an observer
18
+ * configured every closed batch is a `claude -p` call — so closing on every turn
19
+ * made a two-event "yes, go ahead" turn cost a model call. Eight events is
20
+ * roughly one real task (a prompt, a few reads, an edit or two, a test run);
21
+ * twenty minutes is long enough that a pause for thought does not split a task,
22
+ * and short enough that an abandoned session's tail is summarised the same
23
+ * afternoon. `batchIfFull` still closes at `memory.batch_max_events` per tool
24
+ * call, and a session start (`all`) closes everything whatever its size.
35
25
  */
36
- export function batchIfFull(db, resolved, identity) {
37
- try {
38
- const max = resolved.config.memory.batch_max_events;
39
- if (pendingEventCount(db, identity.project, identity.sessionId) < max)
40
- return;
26
+ export const SEAM_MIN_EVENTS = 8;
27
+ export const SEAM_MAX_AGE_MS = 20 * 60_000;
28
+ /**
29
+ * Closes the project's open evidence that is worth a summary now.
30
+ *
31
+ * Every session in the project, not only this one: a session that ended with
32
+ * two events after its last closed batch has no seam of its own left, so its
33
+ * tail is closed here — at once with `all`, otherwise once it is old enough.
34
+ * That is what keeps the thresholds from stranding anything.
35
+ */
36
+ function closeOpenBatches(db, resolved, project, all) {
37
+ const now = Date.now();
38
+ for (const open of openSessions(db, project)) {
39
+ const oldest = parseStamp(open.oldest);
40
+ const due = all ||
41
+ open.events >= SEAM_MIN_EVENTS ||
42
+ // An unreadable stamp is closed rather than kept open for ever.
43
+ oldest === null ||
44
+ now - oldest >= SEAM_MAX_AGE_MS;
45
+ if (!due)
46
+ continue;
41
47
  batchSession(db, {
42
- project: identity.project,
43
- sessionId: identity.sessionId,
44
- reason: 'size',
45
- maxEvents: max,
48
+ project,
49
+ sessionId: open.sessionId,
50
+ reason: 'session_seam',
51
+ maxEvents: resolved.config.memory.batch_max_events,
46
52
  });
47
53
  }
48
- catch {
49
- /* A batch that did not close is one that closes at the seam instead. */
50
- }
51
54
  }
52
55
  /**
53
- * The session seam: close what is open and summarise it.
56
+ * The session seam: replay the spool, close what is worth closing, summarise
57
+ * it, and run the retention sweep when it is due.
58
+ *
59
+ * `all` is for a session start, where whatever the last session left open is
60
+ * closed however small; a Stop seam leaves a short fresh turn open to join the
61
+ * next (`SEAM_MIN_EVENTS`, `SEAM_MAX_AGE_MS`).
54
62
  *
55
63
  * With a provider configured the hook never summarises itself: waiting on a
56
64
  * model inside a Stop hook would make the developer wait for it to finish a
57
65
  * turn, which PRD LRN-04 forbids. It hands the queue to a detached worker
58
66
  * instead and returns.
59
67
  */
60
- export async function flushAtSeam(db, resolved, identity) {
68
+ export async function flushAtSeam(db, resolved, identity, opts = {}) {
69
+ // Before batching, so a spooled event joins the batch it belongs to.
70
+ replaySpool(db);
71
+ try {
72
+ closeOpenBatches(db, resolved, identity.project, opts.all ?? false);
73
+ }
74
+ catch {
75
+ /* Evidence left open is closed at the next seam. */
76
+ }
77
+ try {
78
+ pruneIfDue(db, resolved.config);
79
+ }
80
+ catch {
81
+ /* Retention runs again at the next seam; nothing is lost by waiting. */
82
+ }
61
83
  try {
62
- batchSession(db, {
63
- project: identity.project,
64
- sessionId: identity.sessionId,
65
- reason: 'session_seam',
66
- maxEvents: resolved.config.memory.batch_max_events,
67
- });
68
84
  if (resolved.config.providers.observer) {
69
85
  // A paused queue is waiting on the developer (a login, a usage limit, a
70
86
  // missing `claude`). Launching per seam would relearn that once a turn;
@@ -132,27 +148,12 @@ export function recallBlock(db, resolved, identity, scope) {
132
148
  }
133
149
  }
134
150
  /**
135
- * Recall for one prompt, mid-session, or null.
136
- *
137
- * Silent far more often than not: too short a prompt, nothing relevant, or
138
- * nothing this session has not already been handed. That is the design — a
139
- * recall on every turn is a tax on every turn.
151
+ * The most the Stop hook spends on notifications, all of them together. Under
152
+ * the 4-second bound each sink already has plus one second of slack, and a
153
+ * third of the hook's 15-second timeout, which leaves the rest for batching,
154
+ * summarising and the quiz decision.
140
155
  */
141
- export function promptRecall(db, resolved, identity, prompt) {
142
- try {
143
- if (!resolved.config.memory.enabled)
144
- return null;
145
- const result = recallForPrompt(db, resolved.config, {
146
- project: identity.project,
147
- sessionId: identity.sessionId,
148
- prompt,
149
- });
150
- return result?.block ?? null;
151
- }
152
- catch {
153
- return null;
154
- }
155
- }
156
+ export const NOTIFY_BUDGET_MS = 5_000;
156
157
  /**
157
158
  * Everything that happens once the seam's work has been flushed: the session
158
159
  * summary, the wrap-up, and the one alert worth interrupting for.
@@ -177,13 +178,15 @@ export async function wrapUpAtSeam(db, resolved, identity) {
177
178
  const attempts = db
178
179
  .prepare('SELECT COUNT(*) AS n, COALESCE(SUM(grade >= 3), 0) AS passed FROM attempts WHERE session_id = ?')
179
180
  .get(identity.sessionId);
180
- await notify(db, resolved.config, sessionWrapUp({
181
- project: identity.project,
182
- sessionId: identity.sessionId,
183
- entries: countEntries(db, identity.project),
184
- questions: attempts.n,
185
- passed: attempts.passed,
186
- }));
181
+ const sends = [
182
+ notify(db, resolved.config, sessionWrapUp({
183
+ project: identity.project,
184
+ sessionId: identity.sessionId,
185
+ entries: countEntries(db, identity.project),
186
+ questions: attempts.n,
187
+ passed: attempts.passed,
188
+ })),
189
+ ];
187
190
  // A paused queue is capture that has stopped and will not restart by
188
191
  // itself. Everything else about memory degrades quietly on purpose; this
189
192
  // one is worth saying out loud, once per reason.
@@ -192,12 +195,24 @@ export async function wrapUpAtSeam(db, resolved, identity) {
192
195
  const reason = db
193
196
  .prepare("SELECT error_class FROM memory_jobs WHERE status = 'paused' ORDER BY updated_at DESC LIMIT 1")
194
197
  .get();
195
- await notify(db, resolved.config, queuePausedAlert({
198
+ sends.push(notify(db, resolved.config, queuePausedAlert({
196
199
  project: identity.project,
197
200
  errorClass: reason?.error_class ?? 'unknown',
198
201
  failed: queue.paused + queue.failed,
199
- }));
202
+ })));
200
203
  }
204
+ // Together, not one after the other, and never past the budget: each sink
205
+ // is bounded on its own, but two notifications in turn to the same dead
206
+ // sinks was twice that bound inside a hook with 15 seconds for everything.
207
+ // A send still running at the deadline is abandoned; its ledger row says an
208
+ // attempt started, so a later seam retries it.
209
+ let timer;
210
+ const deadline = new Promise((resolve) => {
211
+ timer = setTimeout(resolve, NOTIFY_BUDGET_MS);
212
+ timer.unref?.();
213
+ });
214
+ await Promise.race([Promise.allSettled(sends), deadline]);
215
+ clearTimeout(timer);
201
216
  }
202
217
  catch {
203
218
  /* A wrap-up nobody received is a wrap-up. A thrown one is a broken session. */
@@ -1 +1 @@
1
- {"version":3,"file":"memory-lib.js","sourceRoot":"","sources":["../../src/hooks/memory-lib.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-lib.js","sourceRoot":"","sources":["../../src/hooks/memory-lib.ts"],"names":[],"mappings":"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"}
@@ -6,20 +6,15 @@
6
6
  */
7
7
  import { run, openExisting, config, cwdOf, sessionId } from './lib.js';
8
8
  import { isSessionOff } from '../session.js';
9
- /**
10
- * `git commit` only counts at a command position: after the start of the line or
11
- * a shell separator. This is what keeps `echo "git commit"` from being blocked,
12
- * while `npm test && git commit -m x` is still caught. The option group allows a
13
- * value after a flag, so `git -C . commit` and `git -c user.name=x commit` are
14
- * caught alongside plain `git commit`.
15
- */
16
- const COMMIT = /(^|[;&|])\s*(sudo\s+)?git(\s+-\S+(\s+[^-\s]\S*)?)*\s+commit(\s|$)/;
9
+ import { isCommitCommand } from './commit-lib.js';
17
10
  await run(async (input) => {
18
11
  // Fast path: no string match, no work. PreToolUse fires on every Bash call.
12
+ // Every command the gate holds (`git commit`, `git merge --continue`) spells
13
+ // one of these two words; `commit-lib.ts` says what counts and why.
19
14
  const command = input.tool_input?.command;
20
- if (!command || !command.includes('commit'))
15
+ if (typeof command !== 'string' || !/commit|continue/.test(command))
21
16
  return 0;
22
- if (!COMMIT.test(command))
17
+ if (!isCommitCommand(command))
23
18
  return 0;
24
19
  const cwd = cwdOf(input);
25
20
  if (!config(cwd).config.quiz.enforced)
@@ -1 +1 @@
1
- {"version":3,"file":"pre-tool-gate.js","sourceRoot":"","sources":["../../src/hooks/pre-tool-gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,MAAM,GAAG,mEAAmE,CAAC;AAEnF,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;IACxB,4EAA4E;IAC5E,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC;IAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IAEpC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IAEhD,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IAElB,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC;IAEnB,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,mEAAmE,CAAC;SAC5E,GAAG,CAAC,GAAG,CAAuE,CAAC;IAElF,4EAA4E;IAC5E,wDAAwD;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEhC,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,2EAA2E;IAC3E,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAEvC,MAAM,MAAM,GACV,8DAA8D,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,uBAAuB;QACtH,CAAC,QAAQ;YACP,CAAC,CAAC,mKAAmK;YACrK,CAAC,CAAC,uBAAuB,CAAC;QAC5B,6FAA6F;QAC7F,iDAAiD,CAAC;IAEpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC;QAChB,kBAAkB,EAAE;YAClB,aAAa,EAAE,YAAY;YAC3B,kBAAkB,EAAE,MAAM;YAC1B,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC,IAAI,CACP,CAAC;IACF,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"pre-tool-gate.js","sourceRoot":"","sources":["../../src/hooks/pre-tool-gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;IACxB,4EAA4E;IAC5E,6EAA6E;IAC7E,oEAAoE;IACpE,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC;IAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9E,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IAExC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IAEhD,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IAElB,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC;IAEnB,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,mEAAmE,CAAC;SAC5E,GAAG,CAAC,GAAG,CAAuE,CAAC;IAElF,4EAA4E;IAC5E,wDAAwD;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEhC,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,2EAA2E;IAC3E,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAEvC,MAAM,MAAM,GACV,8DAA8D,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,uBAAuB;QACtH,CAAC,QAAQ;YACP,CAAC,CAAC,mKAAmK;YACrK,CAAC,CAAC,uBAAuB,CAAC;QAC5B,6FAA6F;QAC7F,iDAAiD,CAAC;IAEpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC;QAChB,kBAAkB,EAAE;YAClB,aAAa,EAAE,YAAY;YAC3B,kBAAkB,EAAE,MAAM;YAC1B,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC,IAAI,CACP,CAAC;IACF,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,CAAC"}