@codename_inc/spectre 3.7.0 → 5.0.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/README.md +6 -7
- package/package.json +3 -2
- package/plugins/spectre/.claude-plugin/plugin.json +1 -1
- package/plugins/spectre/bin/spectre-register +5 -0
- package/plugins/spectre/hooks/hooks.json +3 -14
- package/plugins/spectre/hooks/scripts/bootstrap.mjs +98 -0
- package/plugins/spectre/hooks/scripts/handoff-resume.mjs +404 -0
- package/plugins/spectre/hooks/scripts/lib.mjs +82 -0
- package/plugins/spectre/hooks/scripts/load-knowledge.mjs +189 -0
- package/plugins/spectre/hooks/scripts/register_learning.mjs +264 -0
- package/plugins/spectre/hooks/scripts/{test_bootstrap.cjs → test_bootstrap.mjs} +12 -7
- package/plugins/spectre/hooks/scripts/{test_handoff-resume.cjs → test_handoff-resume.mjs} +13 -11
- package/plugins/spectre/hooks/scripts/{test_load-knowledge.cjs → test_load-knowledge.mjs} +103 -22
- package/plugins/spectre/hooks/scripts/test_register-learning.mjs +335 -0
- package/plugins/spectre/skills/apply/SKILL.md +87 -0
- package/plugins/spectre/{commands/architecture_review.md → skills/architecture_review/SKILL.md} +9 -0
- package/plugins/spectre/{commands/clean.md → skills/clean/SKILL.md} +9 -0
- package/plugins/spectre/{commands/code_review.md → skills/code_review/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_plan.md → skills/create_plan/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_tasks.md → skills/create_tasks/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_test_guide.md → skills/create_test_guide/SKILL.md} +9 -0
- package/plugins/spectre/{commands/evaluate.md → skills/evaluate/SKILL.md} +11 -2
- package/plugins/spectre/{commands/execute.md → skills/execute/SKILL.md} +12 -3
- package/plugins/spectre/{commands/fix.md → skills/fix/SKILL.md} +9 -0
- package/plugins/spectre/{commands/forget.md → skills/forget/SKILL.md} +9 -0
- package/plugins/spectre/skills/{spectre-guide → guide}/SKILL.md +6 -5
- package/plugins/spectre/{commands/handoff.md → skills/handoff/SKILL.md} +9 -0
- package/plugins/spectre/{commands/kickoff.md → skills/kickoff/SKILL.md} +9 -0
- package/plugins/spectre/skills/{spectre-learn → learn}/SKILL.md +19 -59
- package/plugins/spectre/skills/learn/references/recall-template.md +34 -0
- package/plugins/spectre/{commands/plan.md → skills/plan/SKILL.md} +66 -25
- package/plugins/spectre/{commands/plan_review.md → skills/plan_review/SKILL.md} +9 -0
- package/plugins/spectre/skills/prototype/SKILL.md +314 -0
- package/plugins/spectre/{commands/quick_dev.md → skills/quick_dev/SKILL.md} +9 -0
- package/plugins/spectre/{commands/rebase.md → skills/rebase/SKILL.md} +9 -0
- package/plugins/spectre/skills/recall/SKILL.md +17 -0
- package/plugins/spectre/{commands/research.md → skills/research/SKILL.md} +9 -0
- package/plugins/spectre/skills/scope/SKILL.md +174 -0
- package/plugins/spectre/{commands/ship.md → skills/ship/SKILL.md} +9 -0
- package/plugins/spectre/{commands/sweep.md → skills/sweep/SKILL.md} +9 -0
- package/plugins/spectre/skills/tdd/SKILL.md +111 -0
- package/plugins/spectre/{commands/test.md → skills/test/SKILL.md} +9 -0
- package/plugins/spectre/skills/ux/SKILL.md +121 -0
- package/plugins/spectre/{commands/validate.md → skills/validate/SKILL.md} +9 -0
- package/plugins/spectre-codex/agents/analyst.toml +117 -0
- package/plugins/spectre-codex/agents/dev.toml +65 -0
- package/plugins/spectre-codex/agents/finder.toml +101 -0
- package/plugins/spectre-codex/agents/patterns.toml +203 -0
- package/plugins/spectre-codex/agents/reviewer.toml +123 -0
- package/plugins/spectre-codex/agents/sync.toml +146 -0
- package/plugins/spectre-codex/agents/tester.toml +205 -0
- package/plugins/spectre-codex/agents/web-research.toml +104 -0
- package/plugins/spectre-codex/hooks/hooks.json +23 -0
- package/plugins/{spectre/hooks/scripts/bootstrap.cjs → spectre-codex/hooks/scripts/bootstrap.mjs} +15 -16
- package/plugins/{spectre/hooks/scripts/handoff-resume.cjs → spectre-codex/hooks/scripts/handoff-resume.mjs} +21 -27
- package/plugins/{spectre/hooks/scripts/lib.cjs → spectre-codex/hooks/scripts/lib.mjs} +3 -4
- package/plugins/spectre-codex/hooks/scripts/load-knowledge.mjs +189 -0
- package/plugins/spectre-codex/hooks/scripts/register_learning.mjs +264 -0
- package/plugins/spectre-codex/skills/apply/SKILL.md +87 -0
- package/plugins/spectre-codex/skills/architecture_review/SKILL.md +129 -0
- package/plugins/spectre-codex/skills/clean/SKILL.md +322 -0
- package/plugins/spectre-codex/skills/code_review/SKILL.md +417 -0
- package/plugins/spectre-codex/skills/create_plan/SKILL.md +126 -0
- package/plugins/spectre-codex/skills/create_tasks/SKILL.md +383 -0
- package/plugins/spectre-codex/skills/create_test_guide/SKILL.md +129 -0
- package/plugins/spectre-codex/skills/evaluate/SKILL.md +59 -0
- package/plugins/spectre-codex/skills/execute/SKILL.md +96 -0
- package/plugins/spectre-codex/skills/fix/SKILL.md +70 -0
- package/plugins/spectre-codex/skills/forget/SKILL.md +67 -0
- package/plugins/spectre-codex/skills/guide/SKILL.md +359 -0
- package/plugins/spectre-codex/skills/handoff/SKILL.md +170 -0
- package/plugins/spectre-codex/skills/kickoff/SKILL.md +124 -0
- package/plugins/spectre-codex/skills/learn/SKILL.md +595 -0
- package/plugins/{spectre/skills/spectre-learn → spectre-codex/skills/learn}/references/recall-template.md +4 -1
- package/plugins/spectre-codex/skills/plan/SKILL.md +211 -0
- package/plugins/spectre-codex/skills/plan_review/SKILL.md +42 -0
- package/plugins/spectre-codex/skills/prototype/SKILL.md +314 -0
- package/plugins/spectre-codex/skills/quick_dev/SKILL.md +110 -0
- package/plugins/spectre-codex/skills/rebase/SKILL.md +82 -0
- package/plugins/spectre-codex/skills/recall/SKILL.md +17 -0
- package/plugins/spectre-codex/skills/research/SKILL.md +168 -0
- package/plugins/spectre-codex/skills/scope/SKILL.md +174 -0
- package/plugins/spectre-codex/skills/ship/SKILL.md +181 -0
- package/plugins/spectre-codex/skills/sweep/SKILL.md +91 -0
- package/plugins/{spectre/skills/spectre-tdd → spectre-codex/skills/tdd}/SKILL.md +1 -1
- package/plugins/spectre-codex/skills/test/SKILL.md +389 -0
- package/plugins/spectre-codex/skills/ux/SKILL.md +121 -0
- package/plugins/spectre-codex/skills/validate/SKILL.md +352 -0
- package/src/config.test.js +6 -5
- package/src/install.test.js +100 -11
- package/src/lib/config.js +107 -54
- package/src/lib/constants.js +17 -23
- package/src/lib/doctor.js +19 -22
- package/src/lib/install.js +98 -313
- package/src/lib/knowledge.js +7 -37
- package/src/lib/paths.js +0 -12
- package/src/pack.test.js +87 -0
- package/plugins/spectre/commands/learn.md +0 -15
- package/plugins/spectre/commands/recall.md +0 -5
- package/plugins/spectre/commands/scope.md +0 -119
- package/plugins/spectre/commands/ux_spec.md +0 -91
- package/plugins/spectre/hooks/scripts/load-knowledge.cjs +0 -120
- package/plugins/spectre/hooks/scripts/precompact-warning.cjs +0 -19
- package/plugins/spectre/hooks/scripts/register_learning.cjs +0 -144
- package/plugins/spectre/hooks/scripts/test_register-learning.cjs +0 -146
- package/plugins/spectre/skills/spectre-apply/SKILL.md +0 -189
package/src/lib/config.js
CHANGED
|
@@ -279,57 +279,103 @@ function writeHooksConfig(hooksPath, config) {
|
|
|
279
279
|
fs.writeFileSync(hooksPath, `${JSON.stringify(nextConfig, null, 2)}\n`);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
function
|
|
283
|
-
return `node ${shellQuote(path.join(runtimeRoot, 'hooks', 'session-start.mjs'))}`;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function isSpectreSessionStartHook(hook) {
|
|
282
|
+
function isSpectreHook(hook) {
|
|
287
283
|
return hook
|
|
288
284
|
&& typeof hook === 'object'
|
|
289
285
|
&& hook.type === 'command'
|
|
290
286
|
&& typeof hook.command === 'string'
|
|
291
|
-
&&
|
|
287
|
+
&& (
|
|
288
|
+
hook.command.includes('spectre/hooks/session-start.mjs')
|
|
289
|
+
|| hook.command.includes('spectre/hooks/scripts/')
|
|
290
|
+
);
|
|
292
291
|
}
|
|
293
292
|
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
293
|
+
function materializeHookCommand(command, runtimeRoot) {
|
|
294
|
+
return command
|
|
295
|
+
.replaceAll('${CODEX_HOME}/spectre', runtimeRoot)
|
|
296
|
+
.replaceAll('${CODEX_HOME}\\spectre', runtimeRoot)
|
|
297
|
+
.replace(/^node\s+(.+)$/, (_match, scriptPath) => `node ${shellQuote(scriptPath)}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function materializeGeneratedHooks(generatedHooks, runtimeRoot) {
|
|
301
|
+
const materialized = {};
|
|
299
302
|
|
|
300
|
-
for (const
|
|
301
|
-
if (!
|
|
302
|
-
nextGroups.push(group);
|
|
303
|
+
for (const [eventName, groups] of Object.entries(generatedHooks ?? {})) {
|
|
304
|
+
if (!Array.isArray(groups)) {
|
|
303
305
|
continue;
|
|
304
306
|
}
|
|
305
307
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
materialized[eventName] = groups.map(group => {
|
|
309
|
+
if (!group || typeof group !== 'object' || Array.isArray(group)) {
|
|
310
|
+
return group;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const hookList = Array.isArray(group.hooks) ? group.hooks.map(hook => {
|
|
314
|
+
if (!hook || typeof hook !== 'object' || Array.isArray(hook)) {
|
|
315
|
+
return hook;
|
|
316
|
+
}
|
|
317
|
+
if (hook.type !== 'command' || typeof hook.command !== 'string') {
|
|
318
|
+
return hook;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
...hook,
|
|
323
|
+
command: materializeHookCommand(hook.command, runtimeRoot)
|
|
324
|
+
};
|
|
325
|
+
}) : group.hooks;
|
|
326
|
+
|
|
327
|
+
return {
|
|
309
328
|
...group,
|
|
310
329
|
hooks: hookList
|
|
311
|
-
}
|
|
312
|
-
}
|
|
330
|
+
};
|
|
331
|
+
});
|
|
313
332
|
}
|
|
314
333
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
334
|
+
return materialized;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function upsertSpectreHooks(runtimeRoot, generatedHooks) {
|
|
338
|
+
const { hooksPath, config } = readHooksConfig();
|
|
339
|
+
const hooks = { ...(config.hooks ?? {}) };
|
|
340
|
+
const materializedHooks = materializeGeneratedHooks(generatedHooks, runtimeRoot);
|
|
341
|
+
|
|
342
|
+
for (const eventName of new Set([...Object.keys(hooks), ...Object.keys(materializedHooks)])) {
|
|
343
|
+
const groups = Array.isArray(hooks[eventName]) ? hooks[eventName] : [];
|
|
344
|
+
const nextGroups = [];
|
|
345
|
+
|
|
346
|
+
for (const group of groups) {
|
|
347
|
+
if (!group || typeof group !== 'object' || Array.isArray(group)) {
|
|
348
|
+
nextGroups.push(group);
|
|
349
|
+
continue;
|
|
321
350
|
}
|
|
322
|
-
]
|
|
323
|
-
});
|
|
324
351
|
|
|
325
|
-
|
|
352
|
+
const hookList = Array.isArray(group.hooks) ? group.hooks.filter(hook => !isSpectreHook(hook)) : [];
|
|
353
|
+
if (hookList.length > 0) {
|
|
354
|
+
nextGroups.push({
|
|
355
|
+
...group,
|
|
356
|
+
hooks: hookList
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (Array.isArray(materializedHooks[eventName])) {
|
|
362
|
+
nextGroups.push(...materializedHooks[eventName]);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (nextGroups.length > 0) {
|
|
366
|
+
hooks[eventName] = nextGroups;
|
|
367
|
+
} else {
|
|
368
|
+
delete hooks[eventName];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
326
372
|
writeHooksConfig(hooksPath, {
|
|
327
373
|
...config,
|
|
328
374
|
hooks
|
|
329
375
|
});
|
|
330
376
|
}
|
|
331
377
|
|
|
332
|
-
function
|
|
378
|
+
function removeSpectreHooks() {
|
|
333
379
|
const hooksPath = codexHooksConfigPath();
|
|
334
380
|
if (!fs.existsSync(hooksPath)) {
|
|
335
381
|
return false;
|
|
@@ -337,35 +383,40 @@ function removeSpectreSessionStartHook() {
|
|
|
337
383
|
|
|
338
384
|
const { config } = readHooksConfig();
|
|
339
385
|
const hooks = { ...(config.hooks ?? {}) };
|
|
340
|
-
const groups = Array.isArray(hooks.SessionStart) ? hooks.SessionStart : [];
|
|
341
386
|
let removed = false;
|
|
342
|
-
const nextGroups = [];
|
|
343
387
|
|
|
344
|
-
for (const
|
|
345
|
-
if (!
|
|
346
|
-
nextGroups.push(group);
|
|
388
|
+
for (const [eventName, groups] of Object.entries(hooks)) {
|
|
389
|
+
if (!Array.isArray(groups)) {
|
|
347
390
|
continue;
|
|
348
391
|
}
|
|
349
392
|
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
393
|
+
const nextGroups = [];
|
|
394
|
+
for (const group of groups) {
|
|
395
|
+
if (!group || typeof group !== 'object' || Array.isArray(group)) {
|
|
396
|
+
nextGroups.push(group);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
356
399
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
400
|
+
const originalHooks = Array.isArray(group.hooks) ? group.hooks : [];
|
|
401
|
+
const filteredHooks = originalHooks.filter(hook => {
|
|
402
|
+
const shouldRemove = isSpectreHook(hook);
|
|
403
|
+
removed ||= shouldRemove;
|
|
404
|
+
return !shouldRemove;
|
|
361
405
|
});
|
|
406
|
+
|
|
407
|
+
if (filteredHooks.length > 0) {
|
|
408
|
+
nextGroups.push({
|
|
409
|
+
...group,
|
|
410
|
+
hooks: filteredHooks
|
|
411
|
+
});
|
|
412
|
+
}
|
|
362
413
|
}
|
|
363
|
-
}
|
|
364
414
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
415
|
+
if (nextGroups.length > 0) {
|
|
416
|
+
hooks[eventName] = nextGroups;
|
|
417
|
+
} else {
|
|
418
|
+
delete hooks[eventName];
|
|
419
|
+
}
|
|
369
420
|
}
|
|
370
421
|
|
|
371
422
|
writeHooksConfig(hooksPath, {
|
|
@@ -448,7 +499,7 @@ export function removeProjectSkillsConfigured(projectDir) {
|
|
|
448
499
|
writeConfig(configPath, content);
|
|
449
500
|
}
|
|
450
501
|
|
|
451
|
-
export function ensureSpectreHooksConfigured(runtimeRoot, agents) {
|
|
502
|
+
export function ensureSpectreHooksConfigured(runtimeRoot, agents, generatedHooks = {}) {
|
|
452
503
|
const { configPath, content: initialContent } = readConfig();
|
|
453
504
|
let content = initialContent;
|
|
454
505
|
|
|
@@ -457,7 +508,8 @@ export function ensureSpectreHooksConfigured(runtimeRoot, agents) {
|
|
|
457
508
|
content = removeScalarKey(content, 'hooks', 'session_start');
|
|
458
509
|
content = removeEntryFromArrayLine(content, 'hooks.blocking', 'pre_session_start', preSessionEntry);
|
|
459
510
|
content = upsertRootScalarKey(content, 'suppress_unstable_features_warning', 'true');
|
|
460
|
-
content =
|
|
511
|
+
content = removeScalarKey(content, 'features', 'codex_hooks');
|
|
512
|
+
content = upsertScalarKey(content, 'features', 'hooks', 'true');
|
|
461
513
|
content = upsertScalarKey(content, 'features', 'skills', 'true');
|
|
462
514
|
content = upsertScalarKey(content, 'features', 'multi_agent', 'true');
|
|
463
515
|
content = removeEmptyTable(content, 'hooks');
|
|
@@ -479,7 +531,7 @@ export function ensureSpectreHooksConfigured(runtimeRoot, agents) {
|
|
|
479
531
|
}
|
|
480
532
|
|
|
481
533
|
writeConfig(configPath, content);
|
|
482
|
-
|
|
534
|
+
upsertSpectreHooks(runtimeRoot, generatedHooks);
|
|
483
535
|
}
|
|
484
536
|
|
|
485
537
|
export function removeSpectreHooksConfigured(runtimeRoot, agents) {
|
|
@@ -491,7 +543,7 @@ export function removeSpectreHooksConfigured(runtimeRoot, agents) {
|
|
|
491
543
|
let content = fs.readFileSync(configPath, 'utf8');
|
|
492
544
|
const preSessionEntry = `{ command = ["node", "${escapeTomlString(path.join(runtimeRoot, 'hooks', 'pre-session-start.mjs'))}"] }`;
|
|
493
545
|
|
|
494
|
-
const
|
|
546
|
+
const removedSpectreHooks = removeSpectreHooks();
|
|
495
547
|
|
|
496
548
|
content = removeScalarKey(content, 'hooks', 'session_start');
|
|
497
549
|
content = removeEntryFromArrayLine(content, 'hooks.blocking', 'pre_session_start', preSessionEntry);
|
|
@@ -502,7 +554,8 @@ export function removeSpectreHooksConfigured(runtimeRoot, agents) {
|
|
|
502
554
|
content = removeTable(content, `agents.spectre_${agent.id}`);
|
|
503
555
|
}
|
|
504
556
|
|
|
505
|
-
if (
|
|
557
|
+
if (removedSpectreHooks && !hasRemainingHookDefinitions()) {
|
|
558
|
+
content = removeScalarKey(content, 'features', 'hooks');
|
|
506
559
|
content = removeScalarKey(content, 'features', 'codex_hooks');
|
|
507
560
|
}
|
|
508
561
|
|
package/src/lib/constants.js
CHANGED
|
@@ -12,28 +12,14 @@ export const SESSION_OVERRIDE_END = '<!-- spectre-session:end -->';
|
|
|
12
12
|
export const KNOWLEDGE_OVERRIDE_START = '<!-- spectre-knowledge:start -->';
|
|
13
13
|
export const KNOWLEDGE_OVERRIDE_END = '<!-- spectre-knowledge:end -->';
|
|
14
14
|
|
|
15
|
-
export function
|
|
16
|
-
const
|
|
17
|
-
return fs.readdirSync(
|
|
18
|
-
.filter(
|
|
19
|
-
.map(
|
|
15
|
+
export function listSpectreSkills() {
|
|
16
|
+
const skillsDir = path.join(spectrePluginRoot(), 'skills');
|
|
17
|
+
return fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
18
|
+
.filter(entry => entry.isDirectory() && fs.existsSync(path.join(skillsDir, entry.name, 'SKILL.md')))
|
|
19
|
+
.map(entry => entry.name)
|
|
20
20
|
.sort();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function codexCommandSkillName(commandName) {
|
|
24
|
-
if (commandName === 'learn') {
|
|
25
|
-
return 'spectre-learn';
|
|
26
|
-
}
|
|
27
|
-
if (commandName === 'recall') {
|
|
28
|
-
return 'spectre-recall';
|
|
29
|
-
}
|
|
30
|
-
return `spectre-${commandName}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function listCodexWorkflowCommands() {
|
|
34
|
-
return listSpectreCommands().filter(commandName => !['learn', 'recall'].includes(commandName));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
23
|
export function listSpectreAgents() {
|
|
38
24
|
const agentsDir = path.join(spectrePluginRoot(), 'agents');
|
|
39
25
|
return fs.readdirSync(agentsDir)
|
|
@@ -43,10 +29,18 @@ export function listSpectreAgents() {
|
|
|
43
29
|
}
|
|
44
30
|
|
|
45
31
|
export const SHARED_SKILLS = [
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
32
|
+
'apply',
|
|
33
|
+
'guide',
|
|
34
|
+
'learn',
|
|
35
|
+
'tdd'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export const WORKFLOW_PROBE_SKILLS = [
|
|
39
|
+
'scope',
|
|
40
|
+
'plan',
|
|
41
|
+
'execute',
|
|
42
|
+
'clean',
|
|
43
|
+
'test'
|
|
50
44
|
];
|
|
51
45
|
|
|
52
46
|
export function repoMetadata() {
|
package/src/lib/doctor.js
CHANGED
|
@@ -2,18 +2,17 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { execFileSync } from 'child_process';
|
|
4
4
|
import {
|
|
5
|
-
codexCommandSkillName,
|
|
6
|
-
listCodexWorkflowCommands,
|
|
7
5
|
listSpectreAgents,
|
|
8
|
-
|
|
9
|
-
MIN_CODEX_VERSION
|
|
6
|
+
listSpectreSkills,
|
|
7
|
+
MIN_CODEX_VERSION,
|
|
8
|
+
SHARED_SKILLS,
|
|
9
|
+
WORKFLOW_PROBE_SKILLS
|
|
10
10
|
} from './constants.js';
|
|
11
11
|
import {
|
|
12
12
|
codexConfigPath,
|
|
13
13
|
codexHooksConfigPath,
|
|
14
14
|
codexRuntimeRoot,
|
|
15
15
|
codexSkillsDir,
|
|
16
|
-
projectPaths,
|
|
17
16
|
resolveCodexHome
|
|
18
17
|
} from './paths.js';
|
|
19
18
|
|
|
@@ -55,7 +54,10 @@ function sessionStartHookConfigured() {
|
|
|
55
54
|
Array.isArray(group?.hooks) && group.hooks.some(hook =>
|
|
56
55
|
hook?.type === 'command'
|
|
57
56
|
&& typeof hook.command === 'string'
|
|
58
|
-
&&
|
|
57
|
+
&& (
|
|
58
|
+
hook.command.includes('spectre/hooks/session-start.mjs')
|
|
59
|
+
|| hook.command.includes('spectre/hooks/scripts/load-knowledge.mjs')
|
|
60
|
+
)
|
|
59
61
|
)
|
|
60
62
|
);
|
|
61
63
|
|
|
@@ -68,12 +70,7 @@ function sessionStartHookConfigured() {
|
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
function
|
|
72
|
-
const skillName = codexCommandSkillName(commandName);
|
|
73
|
-
if (skillName === 'spectre-recall') {
|
|
74
|
-
return projectPaths(projectDir).recallSkillPath;
|
|
75
|
-
}
|
|
76
|
-
|
|
73
|
+
function skillPath(skillName) {
|
|
77
74
|
return path.join(codexSkillsDir(), skillName, 'SKILL.md');
|
|
78
75
|
}
|
|
79
76
|
|
|
@@ -98,7 +95,7 @@ export function runDoctor({ verifyHooks = false, json = false, projectDir = proc
|
|
|
98
95
|
hooks: {
|
|
99
96
|
verifyRequested: verifyHooks,
|
|
100
97
|
sessionStartConfigured: false,
|
|
101
|
-
|
|
98
|
+
hooksFeatureEnabled: false,
|
|
102
99
|
hiddenContextInjection: 'unconfigured',
|
|
103
100
|
hooksConfigPath: codexHooksConfigPath(),
|
|
104
101
|
hooksConfigPresent: fs.existsSync(codexHooksConfigPath())
|
|
@@ -114,12 +111,12 @@ export function runDoctor({ verifyHooks = false, json = false, projectDir = proc
|
|
|
114
111
|
|
|
115
112
|
if (fs.existsSync(codexConfigPath())) {
|
|
116
113
|
const config = fs.readFileSync(codexConfigPath(), 'utf8');
|
|
117
|
-
result.hooks.
|
|
114
|
+
result.hooks.hooksFeatureEnabled = /^hooks\s*=\s*true\s*$/m.test(config);
|
|
118
115
|
result.hooks.sessionStartConfigured = hookConfigStatus.configured;
|
|
119
116
|
if (hookConfigStatus.error) {
|
|
120
117
|
result.hooks.configError = hookConfigStatus.error;
|
|
121
118
|
}
|
|
122
|
-
if (result.hooks.sessionStartConfigured && result.hooks.
|
|
119
|
+
if (result.hooks.sessionStartConfigured && result.hooks.hooksFeatureEnabled) {
|
|
123
120
|
result.hooks.hiddenContextInjection = 'agents_override_managed_block';
|
|
124
121
|
} else if (result.hooks.sessionStartConfigured) {
|
|
125
122
|
result.hooks.hiddenContextInjection = 'configured_but_feature_disabled';
|
|
@@ -130,13 +127,13 @@ export function runDoctor({ verifyHooks = false, json = false, projectDir = proc
|
|
|
130
127
|
result.capabilities.multiAgentEnabled = config.includes('multi_agent = true');
|
|
131
128
|
}
|
|
132
129
|
|
|
133
|
-
const
|
|
134
|
-
result.capabilities.workflowSkillsInstalled =
|
|
135
|
-
.some(name => fs.existsSync(
|
|
136
|
-
result.capabilities.exactWorkflowSkillsInstalled =
|
|
130
|
+
const expectedSkillFiles = listSpectreSkills().map(name => skillPath(name));
|
|
131
|
+
result.capabilities.workflowSkillsInstalled = WORKFLOW_PROBE_SKILLS
|
|
132
|
+
.some(name => fs.existsSync(skillPath(name)));
|
|
133
|
+
result.capabilities.exactWorkflowSkillsInstalled = expectedSkillFiles.every(filePath => fs.existsSync(filePath));
|
|
137
134
|
|
|
138
|
-
result.capabilities.sharedSkillsInstalled =
|
|
139
|
-
.every(skill => fs.existsSync(
|
|
135
|
+
result.capabilities.sharedSkillsInstalled = SHARED_SKILLS
|
|
136
|
+
.every(skill => fs.existsSync(skillPath(skill)));
|
|
140
137
|
|
|
141
138
|
if (verifyHooks) {
|
|
142
139
|
result.hooks.manualVerification = 'Use an interactive Codex session to verify SessionStart context injection. `codex exec` is not treated as authoritative for this hook lifecycle.';
|
|
@@ -154,7 +151,7 @@ export function runDoctor({ verifyHooks = false, json = false, projectDir = proc
|
|
|
154
151
|
process.stdout.write(`Runtime present: ${result.installed.runtimeDir ? 'yes' : 'no'}\n`);
|
|
155
152
|
process.stdout.write(`session_start hook configured: ${result.hooks.sessionStartConfigured ? 'yes' : 'no'}\n`);
|
|
156
153
|
process.stdout.write(`hooks.json present: ${result.hooks.hooksConfigPresent ? 'yes' : 'no'}\n`);
|
|
157
|
-
process.stdout.write(`
|
|
154
|
+
process.stdout.write(`Hooks feature enabled: ${result.hooks.hooksFeatureEnabled ? 'yes' : 'no'}\n`);
|
|
158
155
|
process.stdout.write(`Hidden context injection: ${result.hooks.hiddenContextInjection}\n`);
|
|
159
156
|
if (result.hooks.configError) {
|
|
160
157
|
process.stdout.write(`Hook config error: ${result.hooks.configError}\n`);
|