@basou/core 0.49.0 → 0.50.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/dist/index.d.ts +89 -4
- package/dist/index.js +329 -143
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,13 +23,142 @@ function summarizeAdapterOutput(_stream, _raw) {
|
|
|
23
23
|
throw new Error("adapter_output summary is not implemented in this release");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// src/adapters/codex/hooks-json.ts
|
|
27
|
+
var SESSION_START_HOOK_TIMEOUT_SECONDS = 30;
|
|
28
|
+
var SESSION_START_HOOK_MATCHER = "startup|resume|clear";
|
|
29
|
+
var SESSION_START_HOOK_CONTEXT_LIMIT = 0;
|
|
30
|
+
var SESSION_START_HOOK_STATUS_MESSAGE = "basou orient";
|
|
31
|
+
var BASOU_SESSION_START_HOOK = /(?:\bbasou|(?:@basou|packages)\/cli\/dist\/index\.js['"]?)\s+hook\s+session-start\b/;
|
|
32
|
+
function isBasouSessionStartHookCommand(command) {
|
|
33
|
+
return BASOU_SESSION_START_HOOK.test(command);
|
|
34
|
+
}
|
|
35
|
+
function shellQuote(value) {
|
|
36
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
37
|
+
}
|
|
38
|
+
function buildSessionStartHookCommand(options) {
|
|
39
|
+
return `node ${shellQuote(options.cliEntry)} hook session-start 2>/dev/null || true`;
|
|
40
|
+
}
|
|
41
|
+
function isRecord(value) {
|
|
42
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function cloneHooksFile(hooksFile) {
|
|
45
|
+
if (hooksFile === void 0 || hooksFile === null) return {};
|
|
46
|
+
if (!isRecord(hooksFile)) {
|
|
47
|
+
throw new Error("The Codex hooks.json is not a JSON object.");
|
|
48
|
+
}
|
|
49
|
+
return structuredClone(hooksFile);
|
|
50
|
+
}
|
|
51
|
+
function canonicalHandler(command) {
|
|
52
|
+
return {
|
|
53
|
+
type: "command",
|
|
54
|
+
command,
|
|
55
|
+
timeout: SESSION_START_HOOK_TIMEOUT_SECONDS,
|
|
56
|
+
statusMessage: SESSION_START_HOOK_STATUS_MESSAGE,
|
|
57
|
+
additionalContextLimit: SESSION_START_HOOK_CONTEXT_LIMIT
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function handlerIsCanonical(entry, command) {
|
|
61
|
+
const want = canonicalHandler(command);
|
|
62
|
+
return Object.keys(want).every((k) => entry[k] === want[k]);
|
|
63
|
+
}
|
|
64
|
+
function upsertSessionStartHook(hooksFile, command) {
|
|
65
|
+
const root = cloneHooksFile(hooksFile);
|
|
66
|
+
if (root.hooks === void 0) {
|
|
67
|
+
root.hooks = {};
|
|
68
|
+
} else if (!isRecord(root.hooks)) {
|
|
69
|
+
throw new Error("The 'hooks' key in the Codex hooks.json is not an object.");
|
|
70
|
+
}
|
|
71
|
+
const hooks = root.hooks;
|
|
72
|
+
if (hooks.SessionStart === void 0) {
|
|
73
|
+
hooks.SessionStart = [];
|
|
74
|
+
} else if (!Array.isArray(hooks.SessionStart)) {
|
|
75
|
+
throw new Error("The 'hooks.SessionStart' key in the Codex hooks.json is not an array.");
|
|
76
|
+
}
|
|
77
|
+
const groups = hooks.SessionStart;
|
|
78
|
+
for (const group of groups) {
|
|
79
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) continue;
|
|
80
|
+
for (const entry of group.hooks) {
|
|
81
|
+
if (!isRecord(entry)) continue;
|
|
82
|
+
if (typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
83
|
+
const unchanged = handlerIsCanonical(entry, command);
|
|
84
|
+
Object.assign(entry, canonicalHandler(command));
|
|
85
|
+
return { hooksFile: root, action: unchanged ? "unchanged" : "updated" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
groups.push({ matcher: SESSION_START_HOOK_MATCHER, hooks: [canonicalHandler(command)] });
|
|
90
|
+
return { hooksFile: root, action: "installed" };
|
|
91
|
+
}
|
|
92
|
+
function removeSessionStartHook(hooksFile) {
|
|
93
|
+
const root = cloneHooksFile(hooksFile);
|
|
94
|
+
if (!isRecord(root.hooks) || !Array.isArray(root.hooks.SessionStart)) {
|
|
95
|
+
return { hooksFile: root, action: "absent" };
|
|
96
|
+
}
|
|
97
|
+
const hooks = root.hooks;
|
|
98
|
+
const groups = hooks.SessionStart;
|
|
99
|
+
let removed = false;
|
|
100
|
+
const kept = [];
|
|
101
|
+
for (const group of groups) {
|
|
102
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) {
|
|
103
|
+
kept.push(group);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const keptHandlers = group.hooks.filter((entry) => {
|
|
107
|
+
if (isRecord(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
108
|
+
removed = true;
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
if (keptHandlers.length === group.hooks.length) {
|
|
114
|
+
kept.push(group);
|
|
115
|
+
} else if (keptHandlers.length > 0) {
|
|
116
|
+
group.hooks = keptHandlers;
|
|
117
|
+
kept.push(group);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!removed) return { hooksFile: root, action: "absent" };
|
|
121
|
+
if (kept.length === 0) {
|
|
122
|
+
delete hooks.SessionStart;
|
|
123
|
+
} else {
|
|
124
|
+
hooks.SessionStart = kept;
|
|
125
|
+
}
|
|
126
|
+
if (Object.keys(hooks).length === 0) {
|
|
127
|
+
delete root.hooks;
|
|
128
|
+
}
|
|
129
|
+
return { hooksFile: root, action: "removed" };
|
|
130
|
+
}
|
|
131
|
+
function findBasouSessionStartHook(hooksFile) {
|
|
132
|
+
if (!isRecord(hooksFile) || !isRecord(hooksFile.hooks) || !Array.isArray(hooksFile.hooks.SessionStart)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const groups = hooksFile.hooks.SessionStart;
|
|
136
|
+
for (let g = 0; g < groups.length; g++) {
|
|
137
|
+
const group = groups[g];
|
|
138
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) continue;
|
|
139
|
+
for (let h = 0; h < group.hooks.length; h++) {
|
|
140
|
+
const entry = group.hooks[h];
|
|
141
|
+
if (isRecord(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
142
|
+
return {
|
|
143
|
+
command: entry.command,
|
|
144
|
+
groupIndex: g,
|
|
145
|
+
handlerIndex: h,
|
|
146
|
+
matcher: typeof group.matcher === "string" ? group.matcher : void 0,
|
|
147
|
+
handler: structuredClone(entry)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
26
155
|
// src/adapters/claude-code/settings-hook.ts
|
|
27
156
|
var STOP_HOOK_TIMEOUT_SECONDS = 20;
|
|
28
157
|
var BASOU_STOP_HOOK = /(?:\bbasou|(?:@basou|packages)\/cli\/dist\/index\.js['"]?)\s+hook\s+stop\b/;
|
|
29
158
|
function isBasouStopHookCommand(command) {
|
|
30
159
|
return BASOU_STOP_HOOK.test(command);
|
|
31
160
|
}
|
|
32
|
-
function
|
|
161
|
+
function shellQuote2(value) {
|
|
33
162
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
34
163
|
}
|
|
35
164
|
function buildStopHookCommand(options) {
|
|
@@ -38,14 +167,14 @@ function buildStopHookCommand(options) {
|
|
|
38
167
|
if (options.requireReview === true) flags.push("--require-review");
|
|
39
168
|
if (options.minEdits !== void 0) flags.push(`--min-edits ${options.minEdits}`);
|
|
40
169
|
const suffix = flags.length > 0 ? ` ${flags.join(" ")}` : "";
|
|
41
|
-
return `node ${
|
|
170
|
+
return `node ${shellQuote2(options.cliEntry)} hook stop${suffix} 2>/dev/null || true`;
|
|
42
171
|
}
|
|
43
|
-
function
|
|
172
|
+
function isRecord2(value) {
|
|
44
173
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
174
|
}
|
|
46
175
|
function cloneSettings(settings) {
|
|
47
176
|
if (settings === void 0 || settings === null) return {};
|
|
48
|
-
if (!
|
|
177
|
+
if (!isRecord2(settings)) {
|
|
49
178
|
throw new Error("Claude settings is not a JSON object.");
|
|
50
179
|
}
|
|
51
180
|
return structuredClone(settings);
|
|
@@ -54,7 +183,7 @@ function upsertStopHook(settings, command) {
|
|
|
54
183
|
const root = cloneSettings(settings);
|
|
55
184
|
if (root.hooks === void 0) {
|
|
56
185
|
root.hooks = {};
|
|
57
|
-
} else if (!
|
|
186
|
+
} else if (!isRecord2(root.hooks)) {
|
|
58
187
|
throw new Error("The 'hooks' key in Claude settings is not an object.");
|
|
59
188
|
}
|
|
60
189
|
const hooks = root.hooks;
|
|
@@ -65,9 +194,9 @@ function upsertStopHook(settings, command) {
|
|
|
65
194
|
}
|
|
66
195
|
const stop = hooks.Stop;
|
|
67
196
|
for (const group of stop) {
|
|
68
|
-
if (!
|
|
197
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
69
198
|
for (const entry of group.hooks) {
|
|
70
|
-
if (!
|
|
199
|
+
if (!isRecord2(entry)) continue;
|
|
71
200
|
if (typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
|
|
72
201
|
const unchanged = entry.type === "command" && entry.command === command && entry.timeout === STOP_HOOK_TIMEOUT_SECONDS;
|
|
73
202
|
entry.type = "command";
|
|
@@ -82,7 +211,7 @@ function upsertStopHook(settings, command) {
|
|
|
82
211
|
}
|
|
83
212
|
function removeStopHook(settings) {
|
|
84
213
|
const root = cloneSettings(settings);
|
|
85
|
-
if (!
|
|
214
|
+
if (!isRecord2(root.hooks) || !Array.isArray(root.hooks.Stop)) {
|
|
86
215
|
return { settings: root, action: "absent" };
|
|
87
216
|
}
|
|
88
217
|
const hooks = root.hooks;
|
|
@@ -90,12 +219,12 @@ function removeStopHook(settings) {
|
|
|
90
219
|
let removed = false;
|
|
91
220
|
const newStop = [];
|
|
92
221
|
for (const group of stop) {
|
|
93
|
-
if (!
|
|
222
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) {
|
|
94
223
|
newStop.push(group);
|
|
95
224
|
continue;
|
|
96
225
|
}
|
|
97
226
|
const keptHooks = group.hooks.filter((entry) => {
|
|
98
|
-
if (
|
|
227
|
+
if (isRecord2(entry) && typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
|
|
99
228
|
removed = true;
|
|
100
229
|
return false;
|
|
101
230
|
}
|
|
@@ -122,19 +251,198 @@ function removeStopHook(settings) {
|
|
|
122
251
|
return { settings: root, action: "removed" };
|
|
123
252
|
}
|
|
124
253
|
function findBasouStopHookCommand(settings) {
|
|
125
|
-
if (!
|
|
254
|
+
if (!isRecord2(settings) || !isRecord2(settings.hooks) || !Array.isArray(settings.hooks.Stop)) {
|
|
126
255
|
return null;
|
|
127
256
|
}
|
|
128
257
|
for (const group of settings.hooks.Stop) {
|
|
129
|
-
if (!
|
|
258
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
130
259
|
for (const entry of group.hooks) {
|
|
131
|
-
if (
|
|
260
|
+
if (isRecord2(entry) && typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
|
|
132
261
|
return entry.command;
|
|
133
262
|
}
|
|
134
263
|
}
|
|
135
264
|
}
|
|
136
265
|
return null;
|
|
137
266
|
}
|
|
267
|
+
var ENTRY = String.raw`(?:[^\s'"]*/)?(?:@basou|packages)/cli/dist/index\.js`;
|
|
268
|
+
var INVOCATION = String.raw`(?:basou|node[ \t]+(?:'(?:[^']*/)?(?:@basou|packages)/cli/dist/index\.js'|"(?:[^"]*/)?(?:@basou|packages)/cli/dist/index\.js"|${ENTRY}))`;
|
|
269
|
+
var WRAPPER = String.raw`(?:[ \t]+2>[ \t]*/dev/null)?(?:[ \t]*\|\|[ \t]*true)?`;
|
|
270
|
+
var CLAUDE_SESSION_START = new RegExp(
|
|
271
|
+
String.raw`^[ \t]*${INVOCATION}[ \t]+hook[ \t]+session-start${WRAPPER}[ \t]*$`
|
|
272
|
+
);
|
|
273
|
+
var CLAUDE_ORIENT_SESSION_START = new RegExp(
|
|
274
|
+
String.raw`^[ \t]*${INVOCATION}[ \t]+orient${WRAPPER}[ \t]*$`
|
|
275
|
+
);
|
|
276
|
+
function isClaudeSessionStartHookCommand(command) {
|
|
277
|
+
return CLAUDE_SESSION_START.test(command);
|
|
278
|
+
}
|
|
279
|
+
function isBasouOrientSessionStartCommand(command) {
|
|
280
|
+
return CLAUDE_ORIENT_SESSION_START.test(command);
|
|
281
|
+
}
|
|
282
|
+
function basouSessionStartKind(entry) {
|
|
283
|
+
if (!isRecord2(entry) || typeof entry.command !== "string") return null;
|
|
284
|
+
if (isClaudeSessionStartHookCommand(entry.command)) return "session-start";
|
|
285
|
+
if (isBasouOrientSessionStartCommand(entry.command)) return "orient";
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
function matcherKey(group) {
|
|
289
|
+
const m = group.matcher;
|
|
290
|
+
return m === void 0 || m === "" || m === "*" ? "*" : String(m);
|
|
291
|
+
}
|
|
292
|
+
function upsertClaudeSessionStartHook(settings, command) {
|
|
293
|
+
const root = cloneSettings(settings);
|
|
294
|
+
if (root.hooks === void 0) {
|
|
295
|
+
root.hooks = {};
|
|
296
|
+
} else if (!isRecord2(root.hooks)) {
|
|
297
|
+
throw new Error("The 'hooks' key in Claude settings is not an object.");
|
|
298
|
+
}
|
|
299
|
+
const hooks = root.hooks;
|
|
300
|
+
if (hooks.SessionStart === void 0) {
|
|
301
|
+
hooks.SessionStart = [];
|
|
302
|
+
} else if (!Array.isArray(hooks.SessionStart)) {
|
|
303
|
+
throw new Error("The 'hooks.SessionStart' key in Claude settings is not an array.");
|
|
304
|
+
}
|
|
305
|
+
const groups = hooks.SessionStart;
|
|
306
|
+
let found = false;
|
|
307
|
+
let changed = false;
|
|
308
|
+
let replacedOrient = false;
|
|
309
|
+
const keptUnder = /* @__PURE__ */ new Set();
|
|
310
|
+
const nextGroups = [];
|
|
311
|
+
for (const group of groups) {
|
|
312
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) {
|
|
313
|
+
nextGroups.push(group);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const key = matcherKey(group);
|
|
317
|
+
const nextHooks = [];
|
|
318
|
+
let removedHere = false;
|
|
319
|
+
for (const entry of group.hooks) {
|
|
320
|
+
const kind = basouSessionStartKind(entry);
|
|
321
|
+
if (kind === null || !isRecord2(entry)) {
|
|
322
|
+
nextHooks.push(entry);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
found = true;
|
|
326
|
+
if (kind === "orient") replacedOrient = true;
|
|
327
|
+
if (keptUnder.has(key)) {
|
|
328
|
+
changed = true;
|
|
329
|
+
removedHere = true;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (entry.type !== "command" || entry.command !== command || entry.timeout !== SESSION_START_HOOK_TIMEOUT_SECONDS) {
|
|
333
|
+
changed = true;
|
|
334
|
+
}
|
|
335
|
+
entry.type = "command";
|
|
336
|
+
entry.command = command;
|
|
337
|
+
entry.timeout = SESSION_START_HOOK_TIMEOUT_SECONDS;
|
|
338
|
+
keptUnder.add(key);
|
|
339
|
+
nextHooks.push(entry);
|
|
340
|
+
}
|
|
341
|
+
if (removedHere && nextHooks.length === 0) continue;
|
|
342
|
+
group.hooks = nextHooks;
|
|
343
|
+
nextGroups.push(group);
|
|
344
|
+
}
|
|
345
|
+
hooks.SessionStart = nextGroups;
|
|
346
|
+
if (!found) {
|
|
347
|
+
nextGroups.push({
|
|
348
|
+
matcher: SESSION_START_HOOK_MATCHER,
|
|
349
|
+
hooks: [{ type: "command", command, timeout: SESSION_START_HOOK_TIMEOUT_SECONDS }]
|
|
350
|
+
});
|
|
351
|
+
return { settings: root, action: "installed" };
|
|
352
|
+
}
|
|
353
|
+
if (replacedOrient) return { settings: root, action: "replaced" };
|
|
354
|
+
return { settings: root, action: changed ? "updated" : "unchanged" };
|
|
355
|
+
}
|
|
356
|
+
function removeClaudeSessionStartHook(settings) {
|
|
357
|
+
const root = cloneSettings(settings);
|
|
358
|
+
if (!isRecord2(root.hooks) || !Array.isArray(root.hooks.SessionStart)) {
|
|
359
|
+
return { settings: root, action: "absent" };
|
|
360
|
+
}
|
|
361
|
+
const hooks = root.hooks;
|
|
362
|
+
let removed = false;
|
|
363
|
+
const nextGroups = [];
|
|
364
|
+
for (const group of hooks.SessionStart) {
|
|
365
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) {
|
|
366
|
+
nextGroups.push(group);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const keptHooks = group.hooks.filter((entry) => basouSessionStartKind(entry) === null);
|
|
370
|
+
if (keptHooks.length === group.hooks.length) {
|
|
371
|
+
nextGroups.push(group);
|
|
372
|
+
} else {
|
|
373
|
+
removed = true;
|
|
374
|
+
if (keptHooks.length > 0) {
|
|
375
|
+
group.hooks = keptHooks;
|
|
376
|
+
nextGroups.push(group);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (!removed) return { settings: root, action: "absent" };
|
|
381
|
+
if (nextGroups.length === 0) {
|
|
382
|
+
delete hooks.SessionStart;
|
|
383
|
+
} else {
|
|
384
|
+
hooks.SessionStart = nextGroups;
|
|
385
|
+
}
|
|
386
|
+
if (Object.keys(hooks).length === 0) delete root.hooks;
|
|
387
|
+
return { settings: root, action: "removed" };
|
|
388
|
+
}
|
|
389
|
+
function findClaudeSessionStartHooks(settings) {
|
|
390
|
+
const found = [];
|
|
391
|
+
if (!isRecord2(settings) || !isRecord2(settings.hooks)) return found;
|
|
392
|
+
const groups = settings.hooks.SessionStart;
|
|
393
|
+
if (!Array.isArray(groups)) return found;
|
|
394
|
+
for (const group of groups) {
|
|
395
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
396
|
+
for (const entry of group.hooks) {
|
|
397
|
+
const kind = basouSessionStartKind(entry);
|
|
398
|
+
if (kind === null || !isRecord2(entry) || typeof entry.command !== "string") continue;
|
|
399
|
+
found.push({
|
|
400
|
+
command: entry.command,
|
|
401
|
+
kind,
|
|
402
|
+
matcher: typeof group.matcher === "string" ? group.matcher : void 0
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return found;
|
|
407
|
+
}
|
|
408
|
+
function isClaudeSessionStartMalformed(settings) {
|
|
409
|
+
return isRecord2(settings) && isRecord2(settings.hooks) && settings.hooks.SessionStart !== void 0 && !Array.isArray(settings.hooks.SessionStart);
|
|
410
|
+
}
|
|
411
|
+
var RUNS_BASOU = new RegExp(
|
|
412
|
+
String.raw`(?:^|[\s;&|(])(?:basou|node(?:[ \t]+-[^\s]+)*[ \t]+(?:'[^']*(?:@basou|packages)/cli/dist/index\.js'|"[^"]*(?:@basou|packages)/cli/dist/index\.js"|${ENTRY})|npx(?:[ \t]+-y)?[ \t]+@basou/cli)[ \t]+(orient|hook[ \t]+session-start)(?=$|[\s;&|)])`,
|
|
413
|
+
"g"
|
|
414
|
+
);
|
|
415
|
+
function isInsideQuotes(text, index) {
|
|
416
|
+
let single = false;
|
|
417
|
+
let double = false;
|
|
418
|
+
for (let i = 0; i < index; i++) {
|
|
419
|
+
const c = text[i];
|
|
420
|
+
if (c === "'" && !double) single = !single;
|
|
421
|
+
else if (c === '"' && !single) double = !double;
|
|
422
|
+
}
|
|
423
|
+
return single || double;
|
|
424
|
+
}
|
|
425
|
+
function findUnrecognizedSessionStart(settings) {
|
|
426
|
+
const found = [];
|
|
427
|
+
if (!isRecord2(settings) || !isRecord2(settings.hooks)) return found;
|
|
428
|
+
const groups = settings.hooks.SessionStart;
|
|
429
|
+
if (!Array.isArray(groups)) return found;
|
|
430
|
+
for (const group of groups) {
|
|
431
|
+
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
432
|
+
for (const entry of group.hooks) {
|
|
433
|
+
if (!isRecord2(entry) || typeof entry.command !== "string") continue;
|
|
434
|
+
if (basouSessionStartKind(entry) !== null) continue;
|
|
435
|
+
const command = entry.command;
|
|
436
|
+
for (const m of command.matchAll(RUNS_BASOU)) {
|
|
437
|
+
const at = (m.index ?? 0) + (m[0].length - m[0].trimStart().length);
|
|
438
|
+
if (isInsideQuotes(command, at)) continue;
|
|
439
|
+
found.push({ command, runs: m[1] === "orient" ? "orient" : "session-start" });
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return found;
|
|
445
|
+
}
|
|
138
446
|
|
|
139
447
|
// src/adapters/claude-code/ask-user-question.ts
|
|
140
448
|
function readString(value) {
|
|
@@ -1065,135 +1373,6 @@ async function resolveCodexCommand(lookup = isOnPath) {
|
|
|
1065
1373
|
throw new Error("Codex CLI not found in PATH. Install codex first.");
|
|
1066
1374
|
}
|
|
1067
1375
|
|
|
1068
|
-
// src/adapters/codex/hooks-json.ts
|
|
1069
|
-
var SESSION_START_HOOK_TIMEOUT_SECONDS = 30;
|
|
1070
|
-
var SESSION_START_HOOK_MATCHER = "startup|resume|clear";
|
|
1071
|
-
var SESSION_START_HOOK_CONTEXT_LIMIT = 0;
|
|
1072
|
-
var SESSION_START_HOOK_STATUS_MESSAGE = "basou orient";
|
|
1073
|
-
var BASOU_SESSION_START_HOOK = /(?:\bbasou|(?:@basou|packages)\/cli\/dist\/index\.js['"]?)\s+hook\s+session-start\b/;
|
|
1074
|
-
function isBasouSessionStartHookCommand(command) {
|
|
1075
|
-
return BASOU_SESSION_START_HOOK.test(command);
|
|
1076
|
-
}
|
|
1077
|
-
function shellQuote2(value) {
|
|
1078
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
1079
|
-
}
|
|
1080
|
-
function buildSessionStartHookCommand(options) {
|
|
1081
|
-
return `node ${shellQuote2(options.cliEntry)} hook session-start 2>/dev/null || true`;
|
|
1082
|
-
}
|
|
1083
|
-
function isRecord2(value) {
|
|
1084
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1085
|
-
}
|
|
1086
|
-
function cloneHooksFile(hooksFile) {
|
|
1087
|
-
if (hooksFile === void 0 || hooksFile === null) return {};
|
|
1088
|
-
if (!isRecord2(hooksFile)) {
|
|
1089
|
-
throw new Error("The Codex hooks.json is not a JSON object.");
|
|
1090
|
-
}
|
|
1091
|
-
return structuredClone(hooksFile);
|
|
1092
|
-
}
|
|
1093
|
-
function canonicalHandler(command) {
|
|
1094
|
-
return {
|
|
1095
|
-
type: "command",
|
|
1096
|
-
command,
|
|
1097
|
-
timeout: SESSION_START_HOOK_TIMEOUT_SECONDS,
|
|
1098
|
-
statusMessage: SESSION_START_HOOK_STATUS_MESSAGE,
|
|
1099
|
-
additionalContextLimit: SESSION_START_HOOK_CONTEXT_LIMIT
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
function handlerIsCanonical(entry, command) {
|
|
1103
|
-
const want = canonicalHandler(command);
|
|
1104
|
-
return Object.keys(want).every((k) => entry[k] === want[k]);
|
|
1105
|
-
}
|
|
1106
|
-
function upsertSessionStartHook(hooksFile, command) {
|
|
1107
|
-
const root = cloneHooksFile(hooksFile);
|
|
1108
|
-
if (root.hooks === void 0) {
|
|
1109
|
-
root.hooks = {};
|
|
1110
|
-
} else if (!isRecord2(root.hooks)) {
|
|
1111
|
-
throw new Error("The 'hooks' key in the Codex hooks.json is not an object.");
|
|
1112
|
-
}
|
|
1113
|
-
const hooks = root.hooks;
|
|
1114
|
-
if (hooks.SessionStart === void 0) {
|
|
1115
|
-
hooks.SessionStart = [];
|
|
1116
|
-
} else if (!Array.isArray(hooks.SessionStart)) {
|
|
1117
|
-
throw new Error("The 'hooks.SessionStart' key in the Codex hooks.json is not an array.");
|
|
1118
|
-
}
|
|
1119
|
-
const groups = hooks.SessionStart;
|
|
1120
|
-
for (const group of groups) {
|
|
1121
|
-
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
1122
|
-
for (const entry of group.hooks) {
|
|
1123
|
-
if (!isRecord2(entry)) continue;
|
|
1124
|
-
if (typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
1125
|
-
const unchanged = handlerIsCanonical(entry, command);
|
|
1126
|
-
Object.assign(entry, canonicalHandler(command));
|
|
1127
|
-
return { hooksFile: root, action: unchanged ? "unchanged" : "updated" };
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
groups.push({ matcher: SESSION_START_HOOK_MATCHER, hooks: [canonicalHandler(command)] });
|
|
1132
|
-
return { hooksFile: root, action: "installed" };
|
|
1133
|
-
}
|
|
1134
|
-
function removeSessionStartHook(hooksFile) {
|
|
1135
|
-
const root = cloneHooksFile(hooksFile);
|
|
1136
|
-
if (!isRecord2(root.hooks) || !Array.isArray(root.hooks.SessionStart)) {
|
|
1137
|
-
return { hooksFile: root, action: "absent" };
|
|
1138
|
-
}
|
|
1139
|
-
const hooks = root.hooks;
|
|
1140
|
-
const groups = hooks.SessionStart;
|
|
1141
|
-
let removed = false;
|
|
1142
|
-
const kept = [];
|
|
1143
|
-
for (const group of groups) {
|
|
1144
|
-
if (!isRecord2(group) || !Array.isArray(group.hooks)) {
|
|
1145
|
-
kept.push(group);
|
|
1146
|
-
continue;
|
|
1147
|
-
}
|
|
1148
|
-
const keptHandlers = group.hooks.filter((entry) => {
|
|
1149
|
-
if (isRecord2(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
1150
|
-
removed = true;
|
|
1151
|
-
return false;
|
|
1152
|
-
}
|
|
1153
|
-
return true;
|
|
1154
|
-
});
|
|
1155
|
-
if (keptHandlers.length === group.hooks.length) {
|
|
1156
|
-
kept.push(group);
|
|
1157
|
-
} else if (keptHandlers.length > 0) {
|
|
1158
|
-
group.hooks = keptHandlers;
|
|
1159
|
-
kept.push(group);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
if (!removed) return { hooksFile: root, action: "absent" };
|
|
1163
|
-
if (kept.length === 0) {
|
|
1164
|
-
delete hooks.SessionStart;
|
|
1165
|
-
} else {
|
|
1166
|
-
hooks.SessionStart = kept;
|
|
1167
|
-
}
|
|
1168
|
-
if (Object.keys(hooks).length === 0) {
|
|
1169
|
-
delete root.hooks;
|
|
1170
|
-
}
|
|
1171
|
-
return { hooksFile: root, action: "removed" };
|
|
1172
|
-
}
|
|
1173
|
-
function findBasouSessionStartHook(hooksFile) {
|
|
1174
|
-
if (!isRecord2(hooksFile) || !isRecord2(hooksFile.hooks) || !Array.isArray(hooksFile.hooks.SessionStart)) {
|
|
1175
|
-
return null;
|
|
1176
|
-
}
|
|
1177
|
-
const groups = hooksFile.hooks.SessionStart;
|
|
1178
|
-
for (let g = 0; g < groups.length; g++) {
|
|
1179
|
-
const group = groups[g];
|
|
1180
|
-
if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
|
|
1181
|
-
for (let h = 0; h < group.hooks.length; h++) {
|
|
1182
|
-
const entry = group.hooks[h];
|
|
1183
|
-
if (isRecord2(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
|
|
1184
|
-
return {
|
|
1185
|
-
command: entry.command,
|
|
1186
|
-
groupIndex: g,
|
|
1187
|
-
handlerIndex: h,
|
|
1188
|
-
matcher: typeof group.matcher === "string" ? group.matcher : void 0,
|
|
1189
|
-
handler: structuredClone(entry)
|
|
1190
|
-
};
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
1376
|
// src/schemas/observed-duration.ts
|
|
1198
1377
|
function readObservedDuration(ev) {
|
|
1199
1378
|
const stored = ev.duration_ms;
|
|
@@ -6251,7 +6430,7 @@ function parseBuildStamp(raw) {
|
|
|
6251
6430
|
}
|
|
6252
6431
|
}
|
|
6253
6432
|
var BASOU_CORE_BUILD = parseBuildStamp(
|
|
6254
|
-
true ? '{"version":"0.
|
|
6433
|
+
true ? '{"version":"0.50.0","commit":"5b4c5dc","committedAt":"2026-09-23T19:12:06+09:00"}' : void 0
|
|
6255
6434
|
);
|
|
6256
6435
|
|
|
6257
6436
|
// src/lib/duration.ts
|
|
@@ -10578,10 +10757,12 @@ export {
|
|
|
10578
10757
|
finalizeSessionYaml,
|
|
10579
10758
|
findBasouSessionStartHook,
|
|
10580
10759
|
findBasouStopHookCommand,
|
|
10760
|
+
findClaudeSessionStartHooks,
|
|
10581
10761
|
findDecisionGaps,
|
|
10582
10762
|
findErrorCode,
|
|
10583
10763
|
findReviewGaps,
|
|
10584
10764
|
findUnbindableRepos,
|
|
10765
|
+
findUnrecognizedSessionStart,
|
|
10585
10766
|
formatDurationMs,
|
|
10586
10767
|
genesisHash,
|
|
10587
10768
|
getChangesSince,
|
|
@@ -10592,8 +10773,11 @@ export {
|
|
|
10592
10773
|
importSessionFromJson,
|
|
10593
10774
|
inspectChainTail,
|
|
10594
10775
|
instructionMode,
|
|
10776
|
+
isBasouOrientSessionStartCommand,
|
|
10595
10777
|
isBasouSessionStartHookCommand,
|
|
10596
10778
|
isBasouStopHookCommand,
|
|
10779
|
+
isClaudeSessionStartHookCommand,
|
|
10780
|
+
isClaudeSessionStartMalformed,
|
|
10597
10781
|
isGitNotFound,
|
|
10598
10782
|
isImportDerivedSource,
|
|
10599
10783
|
isLazyExpired,
|
|
@@ -10646,6 +10830,7 @@ export {
|
|
|
10646
10830
|
recordSessionBaseline,
|
|
10647
10831
|
refreshTaskLinkedSessions,
|
|
10648
10832
|
reimportPreservingId,
|
|
10833
|
+
removeClaudeSessionStartHook,
|
|
10649
10834
|
removeMarkerSection,
|
|
10650
10835
|
removeSessionStartHook,
|
|
10651
10836
|
removeStopHook,
|
|
@@ -10693,6 +10878,7 @@ export {
|
|
|
10693
10878
|
unknownManifestKeys,
|
|
10694
10879
|
unstampedProtocolSectionsFrom,
|
|
10695
10880
|
updateTaskStatusWithEvent,
|
|
10881
|
+
upsertClaudeSessionStartHook,
|
|
10696
10882
|
upsertSessionStartHook,
|
|
10697
10883
|
upsertStopHook,
|
|
10698
10884
|
verifyEventsChain,
|