@codename_inc/spectre 3.7.0 → 4.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.
Files changed (102) hide show
  1. package/README.md +3 -4
  2. package/package.json +3 -2
  3. package/plugins/spectre/.claude-plugin/plugin.json +1 -1
  4. package/plugins/spectre/bin/spectre-register +5 -0
  5. package/plugins/spectre/hooks/hooks.json +3 -14
  6. package/plugins/spectre/hooks/scripts/bootstrap.mjs +98 -0
  7. package/plugins/spectre/hooks/scripts/handoff-resume.mjs +404 -0
  8. package/plugins/spectre/hooks/scripts/lib.mjs +82 -0
  9. package/plugins/spectre/hooks/scripts/load-knowledge.mjs +189 -0
  10. package/plugins/spectre/hooks/scripts/register_learning.mjs +264 -0
  11. package/plugins/spectre/hooks/scripts/{test_bootstrap.cjs → test_bootstrap.mjs} +12 -7
  12. package/plugins/spectre/hooks/scripts/{test_handoff-resume.cjs → test_handoff-resume.mjs} +13 -11
  13. package/plugins/spectre/hooks/scripts/{test_load-knowledge.cjs → test_load-knowledge.mjs} +103 -22
  14. package/plugins/spectre/hooks/scripts/test_register-learning.mjs +335 -0
  15. package/plugins/spectre/skills/apply/SKILL.md +87 -0
  16. package/plugins/spectre/{commands/architecture_review.md → skills/architecture_review/SKILL.md} +9 -0
  17. package/plugins/spectre/{commands/clean.md → skills/clean/SKILL.md} +9 -0
  18. package/plugins/spectre/{commands/code_review.md → skills/code_review/SKILL.md} +9 -0
  19. package/plugins/spectre/{commands/create_plan.md → skills/create_plan/SKILL.md} +9 -0
  20. package/plugins/spectre/{commands/create_tasks.md → skills/create_tasks/SKILL.md} +9 -0
  21. package/plugins/spectre/{commands/create_test_guide.md → skills/create_test_guide/SKILL.md} +9 -0
  22. package/plugins/spectre/{commands/evaluate.md → skills/evaluate/SKILL.md} +11 -2
  23. package/plugins/spectre/{commands/execute.md → skills/execute/SKILL.md} +12 -3
  24. package/plugins/spectre/{commands/fix.md → skills/fix/SKILL.md} +9 -0
  25. package/plugins/spectre/{commands/forget.md → skills/forget/SKILL.md} +9 -0
  26. package/plugins/spectre/skills/{spectre-guide → guide}/SKILL.md +2 -1
  27. package/plugins/spectre/{commands/handoff.md → skills/handoff/SKILL.md} +9 -0
  28. package/plugins/spectre/{commands/kickoff.md → skills/kickoff/SKILL.md} +9 -0
  29. package/plugins/spectre/skills/{spectre-learn → learn}/SKILL.md +19 -59
  30. package/plugins/spectre/skills/learn/references/recall-template.md +34 -0
  31. package/plugins/spectre/{commands/plan.md → skills/plan/SKILL.md} +66 -25
  32. package/plugins/spectre/{commands/plan_review.md → skills/plan_review/SKILL.md} +9 -0
  33. package/plugins/spectre/{commands/quick_dev.md → skills/quick_dev/SKILL.md} +9 -0
  34. package/plugins/spectre/{commands/rebase.md → skills/rebase/SKILL.md} +9 -0
  35. package/plugins/spectre/skills/recall/SKILL.md +17 -0
  36. package/plugins/spectre/{commands/research.md → skills/research/SKILL.md} +9 -0
  37. package/plugins/spectre/{commands/scope.md → skills/scope/SKILL.md} +9 -0
  38. package/plugins/spectre/{commands/ship.md → skills/ship/SKILL.md} +9 -0
  39. package/plugins/spectre/{commands/sweep.md → skills/sweep/SKILL.md} +9 -0
  40. package/plugins/spectre/skills/tdd/SKILL.md +111 -0
  41. package/plugins/spectre/{commands/test.md → skills/test/SKILL.md} +9 -0
  42. package/plugins/spectre/{commands/ux_spec.md → skills/ux_spec/SKILL.md} +9 -0
  43. package/plugins/spectre/{commands/validate.md → skills/validate/SKILL.md} +9 -0
  44. package/plugins/spectre-codex/agents/analyst.toml +117 -0
  45. package/plugins/spectre-codex/agents/dev.toml +65 -0
  46. package/plugins/spectre-codex/agents/finder.toml +101 -0
  47. package/plugins/spectre-codex/agents/patterns.toml +203 -0
  48. package/plugins/spectre-codex/agents/reviewer.toml +123 -0
  49. package/plugins/spectre-codex/agents/sync.toml +146 -0
  50. package/plugins/spectre-codex/agents/tester.toml +205 -0
  51. package/plugins/spectre-codex/agents/web-research.toml +104 -0
  52. package/plugins/spectre-codex/hooks/hooks.json +23 -0
  53. package/plugins/{spectre/hooks/scripts/bootstrap.cjs → spectre-codex/hooks/scripts/bootstrap.mjs} +15 -16
  54. package/plugins/{spectre/hooks/scripts/handoff-resume.cjs → spectre-codex/hooks/scripts/handoff-resume.mjs} +21 -27
  55. package/plugins/{spectre/hooks/scripts/lib.cjs → spectre-codex/hooks/scripts/lib.mjs} +3 -4
  56. package/plugins/spectre-codex/hooks/scripts/load-knowledge.mjs +189 -0
  57. package/plugins/spectre-codex/hooks/scripts/register_learning.mjs +264 -0
  58. package/plugins/spectre-codex/skills/apply/SKILL.md +87 -0
  59. package/plugins/spectre-codex/skills/architecture_review/SKILL.md +129 -0
  60. package/plugins/spectre-codex/skills/clean/SKILL.md +322 -0
  61. package/plugins/spectre-codex/skills/code_review/SKILL.md +417 -0
  62. package/plugins/spectre-codex/skills/create_plan/SKILL.md +126 -0
  63. package/plugins/spectre-codex/skills/create_tasks/SKILL.md +383 -0
  64. package/plugins/spectre-codex/skills/create_test_guide/SKILL.md +129 -0
  65. package/plugins/spectre-codex/skills/evaluate/SKILL.md +59 -0
  66. package/plugins/spectre-codex/skills/execute/SKILL.md +96 -0
  67. package/plugins/spectre-codex/skills/fix/SKILL.md +70 -0
  68. package/plugins/spectre-codex/skills/forget/SKILL.md +67 -0
  69. package/plugins/spectre-codex/skills/guide/SKILL.md +359 -0
  70. package/plugins/spectre-codex/skills/handoff/SKILL.md +170 -0
  71. package/plugins/spectre-codex/skills/kickoff/SKILL.md +124 -0
  72. package/plugins/spectre-codex/skills/learn/SKILL.md +595 -0
  73. package/plugins/{spectre/skills/spectre-learn → spectre-codex/skills/learn}/references/recall-template.md +4 -1
  74. package/plugins/spectre-codex/skills/plan/SKILL.md +211 -0
  75. package/plugins/spectre-codex/skills/plan_review/SKILL.md +42 -0
  76. package/plugins/spectre-codex/skills/quick_dev/SKILL.md +110 -0
  77. package/plugins/spectre-codex/skills/rebase/SKILL.md +82 -0
  78. package/plugins/spectre-codex/skills/recall/SKILL.md +17 -0
  79. package/plugins/spectre-codex/skills/research/SKILL.md +168 -0
  80. package/plugins/spectre-codex/skills/scope/SKILL.md +128 -0
  81. package/plugins/spectre-codex/skills/ship/SKILL.md +181 -0
  82. package/plugins/spectre-codex/skills/sweep/SKILL.md +91 -0
  83. package/plugins/{spectre/skills/spectre-tdd → spectre-codex/skills/tdd}/SKILL.md +1 -1
  84. package/plugins/spectre-codex/skills/test/SKILL.md +389 -0
  85. package/plugins/spectre-codex/skills/ux_spec/SKILL.md +100 -0
  86. package/plugins/spectre-codex/skills/validate/SKILL.md +352 -0
  87. package/src/config.test.js +6 -5
  88. package/src/install.test.js +100 -11
  89. package/src/lib/config.js +107 -54
  90. package/src/lib/constants.js +17 -23
  91. package/src/lib/doctor.js +19 -22
  92. package/src/lib/install.js +98 -313
  93. package/src/lib/knowledge.js +7 -37
  94. package/src/lib/paths.js +0 -12
  95. package/src/pack.test.js +87 -0
  96. package/plugins/spectre/commands/learn.md +0 -15
  97. package/plugins/spectre/commands/recall.md +0 -5
  98. package/plugins/spectre/hooks/scripts/load-knowledge.cjs +0 -120
  99. package/plugins/spectre/hooks/scripts/precompact-warning.cjs +0 -19
  100. package/plugins/spectre/hooks/scripts/register_learning.cjs +0 -144
  101. package/plugins/spectre/hooks/scripts/test_register-learning.cjs +0 -146
  102. package/plugins/spectre/skills/spectre-apply/SKILL.md +0 -189
@@ -0,0 +1,352 @@
1
+ ---
2
+ name: "validate"
3
+ description: "👻 | Comprehensive post implementation requirement validation using subagents"
4
+ user-invocable: true
5
+ ---
6
+
7
+ # validate
8
+
9
+ ## Input Handling
10
+
11
+ Treat the current command arguments as this workflow's input. When invoked from a slash command, use the forwarded `$ARGUMENTS` value.
12
+
13
+
14
+ # validate: Scope delivery verification and gap analysis
15
+
16
+ ## Description
17
+
18
+ - **What** — Validate implementation against scope/tasks docs, dispatch parallel subagents per area, produce single actionable gap remediation document.
19
+ - **Approach** — Primary agent chunks work by scope items or parent tasks, dispatches one @analyst per area IN PARALLEL. Each subagent validates their area including E2E UX accessibility.
20
+ - **Outcome** — Single `validation_gaps.md` with actionable tasks ready for immediate implementation.
21
+
22
+ ## Core Validation Principle
23
+
24
+ > **"Definition ≠ Connection ≠ Reachability"**
25
+
26
+ Three levels of implementation completeness:
27
+ 1. **Defined**: Code exists in a file
28
+ 2. **Connected**: Code is imported/called by other code
29
+ 3. **Reachable**: A user action can trigger the code path
30
+
31
+ Validation must verify all three levels. A feature with Level 1 but not Level 2 or 3 is NOT complete—it's dead code that happens to match the requirement description.
32
+
33
+ When verifying any implementation:
34
+ - Don't stop at "function X exists in file Y"
35
+ - Continue to "function X is called by Z at file:line"
36
+ - Continue to "Z is triggered when user does W"
37
+
38
+ ## ARGUMENTS Input
39
+
40
+ **REQUIRED**: User must provide scope documents to validate against.
41
+
42
+ <ARGUMENTS> $ARGUMENTS </ARGUMENTS>
43
+
44
+ ## Step (1/4) - Gather Validation Inputs
45
+
46
+ - **Action** — CheckArguments: Verify user provided scope documents.
47
+
48
+ - **If** ARGUMENTS contains file paths or "use thread context" → proceed
49
+
50
+ - **Else** → Immediately reply:
51
+
52
+ > "What should I validate against? Please provide:
53
+ >
54
+ > - Path to scope document (e.g., `docs/tasks/main/scope.md`)
55
+ > - Path to tasks document (e.g., `docs/tasks/main/tasks.md`)
56
+ > - Or say 'use thread context' to validate against our conversation"
57
+
58
+ - **Wait** — User provides validation inputs
59
+
60
+ - **Action** — ReadScopeDocs: Read provided documents completely (no limits).
61
+
62
+ - Extract all requirements, acceptance criteria, deliverables
63
+ - Document scope boundaries (in-scope / out-of-scope)
64
+ - Note constraints and success metrics
65
+
66
+ - **Action** — ChunkIntoValidationAreas: Break scope into discrete validation areas.
67
+
68
+ - **From tasks.md**: Each parent task (e.g., \[1.1\], \[1.2\]) = one validation area
69
+ - **From scope.md**: Each "In Scope" item = one validation area
70
+ - **From thread context**: Each discussed feature/requirement = one validation area
71
+ - Aim for 3-8 validation areas (merge small items, split large ones)
72
+
73
+ - **Action** — CreateValidationManifest: Document chunks before dispatch.
74
+
75
+ ```plaintext
76
+ Validation Areas:
77
+ 1. {Area Name} — {What to validate}
78
+ - Source: {requirement text from scope doc}
79
+ - Expected: {what should exist}
80
+ 2. ...
81
+ ```
82
+
83
+ ## Step (2/4) - Dispatch Parallel Validation Agents
84
+
85
+ **CRITICAL**: Dispatch ALL validation agents in parallel in a SINGLE message with multiple Task tool calls. Do NOT dispatch sequentially.
86
+
87
+ - **Action** — DispatchValidators: Launch one @analyst per validation area IN PARALLEL.
88
+
89
+ **Subagent Prompt Template**:
90
+
91
+ ```plaintext
92
+ You are validating scope delivery for ONE specific area.
93
+
94
+ ## Context Documents
95
+ - Scope: {path or "thread context"}
96
+ - Tasks: {path if provided}
97
+ - Branch: {branch_name}
98
+
99
+ ## Your Validation Area
100
+ **Area**: {area_name}
101
+ **Source Requirement**: {exact text from scope/tasks doc}
102
+ **Expected Deliverables**: {what should exist}
103
+
104
+ ## Your Task
105
+ 1. Investigate YOUR SPECIFIC AREA only
106
+ 2. For each requirement, determine:
107
+ - **Status**: ✅ Delivered | ⚠️ Partial | 🔌 Dead Code | ❌ Missing
108
+ - ✅ **Delivered**: Defined AND connected AND reachable from user action
109
+ - ⚠️ **Partial**: Code exists but has broken/missing connections
110
+ - 🔌 **Dead Code**: Code exists but has zero usage sites
111
+ - ❌ **Missing**: Code does not exist
112
+ - **Evidence**: Must include BOTH:
113
+ 1. Definition site: `file:line` where code is defined
114
+ 2. Usage site: `file:line` where code is called/rendered
115
+ - If you can only cite definition without usage → status is ⚠️ or 🔌
116
+ - **Gap**: What's missing (if any)
117
+
118
+ 3. **CRITICAL - Reachability Verification**:
119
+ - Trace the COMPLETE chain from user action to implementation:
120
+ - Entry point: What user action triggers this? (click, route, event)
121
+ - Call chain: How does execution flow to the implementation?
122
+ - Terminal point: What side effect/UI change occurs?
123
+ - A broken link at ANY point = ⚠️ NOT FULLY DELIVERED
124
+ - For every function/component, grep for USAGE not just DEFINITION:
125
+ - Functions: Search for `functionName(` to find invocations
126
+ - Components: Search for `<ComponentName` to find render sites
127
+ - Hooks: Search for `useHookName(` to find consumers
128
+ - Props: Search for `propName={` to find where passed
129
+ - Zero usage sites = 🔌 Dead Code
130
+
131
+ 4. **CRITICAL - Consumer-First Validation (Render-Backward Trace)**:
132
+ For UI features, start from the FINAL RENDER and trace BACKWARDS:
133
+ - What component renders the feature output? (exact JSX location)
134
+ - What variable does that JSX use? (exact variable name)
135
+ - Where does that variable come from? (hook, prop, computed value)
136
+ - Trace back to the user action. Is every link connected?
137
+
138
+ Example (filter feature):
139
+ - Renders: `BoardColumn` receives `tasks` prop at line 45
140
+ - Variable: `tasks` comes from `getFilteredTasksForBoard(board.id)`
141
+ - Source: `getFilteredTasksForBoard` uses... `getTasksForBoard` from drag hook
142
+ - ❌ BROKEN: This bypasses `displayTasks` from filter hook!
143
+
144
+ If you can only trace forward (definition → usage) but NOT backward
145
+ (render → source), you haven't verified the last mile.
146
+
147
+ 5. **CRITICAL - Dead Computation Detection**:
148
+ List every computed value that is NOT directly consumed by a render:
149
+ - For each hook return value, grep for where it's destructured/used
150
+ - For each computed const, grep for where it's referenced
151
+ - A value that's computed but never reaches JSX = 🔌 Dead Code
152
+
153
+ Example:
154
+ - `displayTasks` from `useKanbanFilters` → used where?
155
+ - Passed to `TasksHeroHeader` ✓
156
+ - Used in `getFilteredTasksForBoard`? ❌ NO — uses different source
157
+ - Computed but not in render chain = BUG
158
+
159
+ 6. **Old Code Path Audit**:
160
+ When new functionality replaces old patterns:
161
+ - What OLD code handled this before? (imports, hooks, functions)
162
+ - Is the old code still being called anywhere?
163
+ - Are there duplicate data sources for the same concern?
164
+ - If old path still active → ⚠️ Partial (new code bypassed)
165
+
166
+ 7. Check for scope creep: anything beyond the requirement
167
+
168
+ ## Output Format
169
+ ```
170
+
171
+ AREA: {area_name} STATUS: {overall: Delivered | Partial | Dead Code | Missing}
172
+
173
+ REQUIREMENTS:
174
+
175
+ - \[REQ-1\] {requirement}
176
+ Status: {✅|⚠️|🔌|❌}
177
+ Definition: {file:line where defined}
178
+ Usage: {file:line where called/rendered, or "NONE FOUND"}
179
+ Reachability: {user action → ... → this code, or "NOT REACHABLE"}
180
+ Render Chain: {JSX ← variable ← source ← user action, or "BROKEN AT {link}"}
181
+ Gap: {what's missing}
182
+ Remediation: {specific fix — what to wire, what to replace, what to add}
183
+ - Produces: {what the fix outputs}
184
+ - Consumed by: {what should use it}
185
+ - Replaces: {old path to remove, or "N/A"}
186
+
187
+ DEAD COMPUTATIONS:
188
+ - {variable name} in {file}: computed but not consumed by render
189
+ - Remediation: {wire to X, or delete if unnecessary}
190
+
191
+ OLD CODE PATHS:
192
+ - {old function/hook}: still active at {file:line}, bypasses new implementation
193
+ - Remediation: {replace calls with new path, or remove}
194
+
195
+ SCOPE CREEP: {any features beyond scope}
196
+
197
+ SUMMARY: {1-2 sentences}
198
+
199
+ ```
200
+
201
+ **Verification Patterns for Subagents**:
202
+
203
+ | Checking | Search Pattern | Meaning |
204
+ |----------|---------------|---------|
205
+ | Function called | `functionName\(` | Invocation exists |
206
+ | Component renders | `<ComponentName` | JSX usage exists |
207
+ | Hook consumed | `useHookName\(` | Hook is used |
208
+ | Prop passed | `propName={` | Parent passes prop |
209
+ | Export imported | `import.*Name` | Module consumed |
210
+
211
+ **Common broken link patterns to check**:
212
+ - Callback defined but never passed as prop
213
+ - Prop received but never used in component body
214
+ - Function exported but never imported elsewhere
215
+ - Type defined but never used in signatures
216
+ - Switch case exists but condition never triggers
217
+ - **Hook returns value that's destructured but never used in JSX**
218
+ - **New data source created but old source still used in render**
219
+ - **Computed value stored in variable but render uses different variable**
220
+ - **Filter/transform function created but rendering bypasses it**
221
+
222
+ **Last-Mile Anti-Patterns** (these cause "works in code, broken in UI"):
223
+ - `displayTasks` computed but `getTasksForBoard` called in render
224
+ - Handler created but onClick still points to old handler
225
+ - New hook created but component still imports old hook
226
+ - State updated correctly but wrong variable passed to child component
227
+
228
+ - **Wait** — All validation agents complete
229
+
230
+ ## Step (3/4) - Consolidate & Create Gap Remediation Tasks
231
+
232
+ - **Action** — ConsolidateFindings: Merge all subagent outputs.
233
+
234
+ - Aggregate status across all areas
235
+ - Compile gaps by priority (Critical/Medium/Low)
236
+ - Note any scope creep findings
237
+
238
+ - **Action** — FinalWiringChecklist: Before marking any area complete, verify:
239
+
240
+ | Check | Question |
241
+ |-------|----------|
242
+ | Consumer connected? | Does every new function/hook have a consumer that uses its output? |
243
+ | Render chain complete? | Can you trace from JSX ← variable ← hook ← user action without breaks? |
244
+ | Old paths removed? | If replacing old code, is the old path dead or redirected? |
245
+ | No orphaned computation? | Is every computed value actually used in a render path? |
246
+ | No duplicate sources? | Is there only ONE data source for each concern (not old + new)? |
247
+
248
+ **CRITICAL**: If any check fails for an area marked ✅, downgrade to ⚠️ and add gap task.
249
+
250
+ - **Action** — DetermineOutputDir:
251
+
252
+ - `branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)`
253
+ - **If** user specifies path → `OUT_DIR={that value}`
254
+ - **Else** → `OUT_DIR=docs/tasks/{branch_name}`
255
+ - `mkdir -p "${OUT_DIR}/validation"`
256
+
257
+ - **Action** — CreateValidationGapsDoc: Generate `{OUT_DIR}/validation/validation_gaps.md`.
258
+
259
+ **Document Structure**:
260
+
261
+ ```markdown
262
+ # Validation Gaps: {task_name}
263
+ *Generated: {timestamp}*
264
+
265
+ ## Summary
266
+ - **Overall Status**: {Complete | Needs Work | Significant Gaps}
267
+ - **Requirements**: {X of Y} delivered
268
+ - **Gaps Found**: {count} requiring remediation
269
+ - **Scope Creep**: {count} items (document or remove)
270
+
271
+ ---
272
+
273
+ ## Gap Remediation Tasks
274
+
275
+ ### 📦 Phase 1: Critical Gaps
276
+
277
+ #### 📋 [1.1] {Gap Title - e.g., "Connect auth flow to login page"}
278
+ **Requirement**: {original requirement text}
279
+ **Current State**: {what exists now — definition site if code exists}
280
+ **Gap**: {what's missing — broken link in the chain}
281
+
282
+ - [ ] **1.1.1** {Specific action - e.g., "Wire LoginButton onClick to auth handler"}
283
+ - **Produces**: {output this creates — e.g., "onClick handler calling authService.login()"}
284
+ - **Consumed by**: {what uses this — e.g., "LoginButton component render"}
285
+ - **Replaces**: {old code path — e.g., "inline console.log in onClick" or "N/A"}
286
+ - [ ] {Verifiable outcome 1}
287
+ - [ ] {Verifiable outcome 2}
288
+
289
+ - [ ] **1.1.2** {Specific action - e.g., "Add login route to app router"}
290
+ - **Produces**: {output — e.g., "/login route rendering LoginPage"}
291
+ - **Consumed by**: {consumer — e.g., "App router, navigated via authService redirect"}
292
+ - [ ] {Verifiable outcome 1}
293
+ - [ ] {Verifiable outcome 2}
294
+
295
+ #### 📋 [1.2] {Next Gap Title}
296
+ ...
297
+
298
+ ### 📦 Phase 2: Medium Priority Gaps
299
+ ...
300
+
301
+ ### 📦 Phase 3: Low Priority / Polish
302
+ ...
303
+
304
+ ---
305
+
306
+ ## Scope Creep Review
307
+ Items implemented beyond original scope:
308
+ - [ ] **{Feature}**: {Keep and document | Remove | Discuss}
309
+ - Evidence: {file:line}
310
+ - Recommendation: {action}
311
+
312
+ ---
313
+
314
+ ## Validation Coverage
315
+ | Area | Status | Definition | Usage | Render Chain |
316
+ |------|--------|------------|-------|--------------|
317
+ | {area 1} | ✅ | {file:line} | {file:line} | JSX ← var ← source ✓ |
318
+ | {area 2} | ⚠️ | {file:line} | {file:line} | Broken at {link} |
319
+ | {area 3} | 🔌 | {file:line} | NONE | Dead computation |
320
+ | {area 4} | ❌ | — | — | — |
321
+
322
+ ## Dead Computations Found
323
+ | Variable | File | Computed By | Should Be Consumed By |
324
+ |----------|------|-------------|----------------------|
325
+ | {displayTasks} | {useKanbanFilters.ts} | {applyTaskFilters} | {KanbanBoard render} |
326
+
327
+ ## Old Code Paths Still Active
328
+ | Old Path | Location | Should Be Replaced By | Impact |
329
+ |----------|----------|----------------------|--------|
330
+ | {getTasksForBoard} | {useKanbanDrag:45} | {displayTasks from useKanbanFilters} | Bypasses new filter |
331
+
332
+ ```
333
+
334
+ ## Step (4/4) - Present Results
335
+
336
+ - **Action** — PresentResults: Show validation summary.
337
+
338
+ > ## Validation Complete
339
+ **> **Status**: {Complete | Needs Work | Significant Gaps}
340
+ >
341
+ > - {X of Y} requirements delivered
342
+ > - {N} gaps requiring remediation
343
+ > - {N} scope creep items to review
344
+ **> **Gap Remediation Doc**: `{OUT_DIR}/validation/validation_gaps.md`
345
+ >
346
+ > {1-2 sentence summary of key findings}
347
+
348
+ - **Action** — RenderFooter: Render Next Steps footer using `Skill(spectre-guide)` skill
349
+
350
+ ## Next Steps
351
+
352
+ See `Skill(spectre)` skill for footer format and command options.
@@ -90,8 +90,9 @@ test('buildSessionStartOutput returns official SessionStart payload from the lat
90
90
  assert.match(overrideContent, /<!-- spectre-session:end -->/);
91
91
  assert.match(overrideContent, /<!-- spectre-knowledge:start -->/);
92
92
  assert.match(overrideContent, /## SPECTRE Knowledge Context/);
93
- assert.match(overrideContent, /If ANY entry's triggers or description match your current task, you MUST load the skill FIRST/);
94
- assert.match(overrideContent, /feature-auth\|feature\|auth, login\|Use when modifying auth flows/);
93
+ assert.match(overrideContent, /If ANY skill's triggers or description match your current task, you MUST load the skill FIRST/);
94
+ assert.doesNotMatch(overrideContent, /feature-auth\|feature\|auth, login\|Use when modifying auth flows/);
95
+ assert.match(overrideContent, /skill list already has what you need/);
95
96
  assert.match(overrideContent, /<!-- spectre-knowledge:end -->/);
96
97
  assert.ok(fs.existsSync(path.join(tmp, '.agents', 'skills', 'spectre-recall', 'SKILL.md')));
97
98
  });
@@ -123,12 +124,12 @@ test('buildSessionStartOutput keeps knowledge active when no handoff exists and
123
124
  const { buildSessionStartOutput } = await import('./lib/project.js');
124
125
  const output = buildSessionStartOutput(tmp, { source: 'clear' });
125
126
  assert.ok(output);
126
- assert.equal(output.systemMessage, '🟢 👻 SPECTRE active | 👻 spectre: ready — capture knowledge with spectre-learn');
127
+ assert.equal(output.systemMessage, '🟢 👻 SPECTRE active | 👻 spectre: ready — capture knowledge with /spectre:learn');
127
128
  const overrideContent = fs.readFileSync(path.join(tmp, 'AGENTS.override.md'), 'utf8');
128
129
  assert.match(overrideContent, /User content before\./);
129
130
  assert.match(overrideContent, /User content after\./);
130
131
  assert.doesNotMatch(overrideContent, /spectre-session:start/);
131
132
  assert.match(overrideContent, /spectre-knowledge:start/);
132
- assert.match(overrideContent, /No knowledge has been captured for this project yet/);
133
- assert.match(overrideContent, /use `spectre-learn` after completing significant work/);
133
+ assert.match(overrideContent, /If ANY skill's triggers or description match your current task, you MUST load the skill FIRST/);
134
+ assert.match(overrideContent, /Capture via `Skill\(learn\)`/);
134
135
  });
@@ -38,32 +38,39 @@ test('project install writes workflow skills, agent config, and official Session
38
38
  await main(['install', 'codex', '--scope', 'project', '--project-dir', projectDir]);
39
39
 
40
40
  const codeHome = path.join(projectDir, '.codex');
41
- const scopeSkillPath = path.join(codeHome, 'skills', 'spectre-scope', 'SKILL.md');
41
+ const scopeSkillPath = path.join(codeHome, 'skills', 'scope', 'SKILL.md');
42
42
  assert.ok(fs.existsSync(scopeSkillPath));
43
- assert.match(fs.readFileSync(scopeSkillPath, 'utf8'), /Treat the current user request as the input arguments for this workflow/);
44
- assert.match(fs.readFileSync(scopeSkillPath, 'utf8'), /This is the Codex skill replacement for the deprecated custom prompt \/spectre:scope/);
43
+ const scopeSkill = fs.readFileSync(scopeSkillPath, 'utf8');
44
+ assert.match(scopeSkill, /# scope: Interactive Feature Scoping/);
45
+ assert.doesNotMatch(scopeSkill, /This is the Codex skill replacement for the deprecated custom prompt \/spectre:scope/);
46
+ assert.doesNotMatch(scopeSkill, /Skill\(spectre-scope\)/);
45
47
 
46
- const applySkillPath = path.join(codeHome, 'skills', 'spectre-apply', 'SKILL.md');
48
+ const applySkillPath = path.join(codeHome, 'skills', 'apply', 'SKILL.md');
47
49
  assert.ok(fs.existsSync(applySkillPath));
48
- assert.match(fs.readFileSync(applySkillPath, 'utf8'), /If ANY entry's triggers or description match your current task, you MUST load the skill FIRST/);
50
+ assert.match(fs.readFileSync(applySkillPath, 'utf8'), /If ANY skill's triggers or description match your current task, you MUST load the skill FIRST/);
49
51
 
50
- const learnSkillPath = path.join(codeHome, 'skills', 'spectre-learn', 'SKILL.md');
52
+ const learnSkillPath = path.join(codeHome, 'skills', 'learn', 'SKILL.md');
51
53
  assert.ok(fs.existsSync(learnSkillPath));
52
54
  assert.match(fs.readFileSync(learnSkillPath, 'utf8'), /### 13\. Register the Learning/);
53
55
  assert.match(fs.readFileSync(learnSkillPath, 'utf8'), /\.agents\/skills\/spectre-recall\/references\/registry\.toon/);
56
+ assert.ok(fs.existsSync(path.join(codeHome, 'skills', 'learn', 'references', 'recall-template.md')));
54
57
 
55
58
  const agentPath = path.join(codeHome, 'spectre', 'agents', 'dev.toml');
56
59
  assert.ok(fs.existsSync(agentPath));
57
60
  const agentConfig = fs.readFileSync(agentPath, 'utf8');
58
61
  assert.match(agentConfig, /name = "dev"/);
59
62
  assert.match(agentConfig, /description = /);
63
+ assert.doesNotMatch(agentConfig, /^model = /m);
64
+ assert.doesNotMatch(agentConfig, /^model_reasoning_effort = /m);
65
+ assert.match(agentConfig, /sandbox_mode = "workspace-write"/);
60
66
  assert.match(agentConfig, /developer_instructions = \"\"\"/);
61
67
  assert.doesNotMatch(agentConfig, /base_instructions = /);
62
68
 
63
69
  const config = fs.readFileSync(path.join(codeHome, 'config.toml'), 'utf8');
64
70
  assert.match(config, /suppress_unstable_features_warning = true/);
65
71
  assert.match(config, /\[agents\.spectre_dev\]/);
66
- assert.match(config, /codex_hooks = true/);
72
+ assert.match(config, /^hooks = true$/m);
73
+ assert.doesNotMatch(config, /codex_hooks = true/);
67
74
  assert.match(config, /multi_agent = true/);
68
75
  assert.doesNotMatch(config, /session_start = /);
69
76
  assert.doesNotMatch(config, /pre_session_start/);
@@ -76,10 +83,22 @@ test('project install writes workflow skills, agent config, and official Session
76
83
  assert.deepEqual(hooksConfig.hooks.SessionStart[0].hooks, [
77
84
  {
78
85
  type: 'command',
79
- command: `node '${path.join(codeHome, 'spectre', 'hooks', 'session-start.mjs')}'`,
80
- statusMessage: 'Spectre: loading session context'
86
+ command: `node '${path.join(codeHome, 'spectre', 'hooks', 'scripts', 'bootstrap.mjs')}'`
87
+ },
88
+ {
89
+ type: 'command',
90
+ command: `node '${path.join(codeHome, 'spectre', 'hooks', 'scripts', 'handoff-resume.mjs')}'`
91
+ },
92
+ {
93
+ type: 'command',
94
+ command: `node '${path.join(codeHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')}'`
81
95
  }
82
96
  ]);
97
+ assert.equal(hooksConfig.hooks.PreCompact, undefined);
98
+ assert.ok(fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'hooks.json')));
99
+ assert.ok(fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')));
100
+ assert.ok(!fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'scripts', 'precompact-warning.mjs')));
101
+ assert.ok(!fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'session-start.mjs')));
83
102
  assert.ok(fs.existsSync(path.join(codeHome, 'spectre', 'tools', 'sync-session-override.mjs')));
84
103
  assert.ok(fs.existsSync(path.join(projectDir, '.agents', 'skills', 'spectre-recall', 'SKILL.md')));
85
104
  assert.ok(fs.existsSync(path.join(projectDir, '.agents', 'skills', 'spectre-recall', 'references', 'registry.toon')));
@@ -93,6 +112,25 @@ test('project install writes workflow skills, agent config, and official Session
93
112
  assert.ok(!fs.existsSync(path.join(projectDir, '.agents', 'skills', 'spectre-session')));
94
113
  assert.ok(!fs.existsSync(path.join(projectDir, '.spectre', 'bin', 'codex')));
95
114
  assert.ok(!fs.existsSync(path.join(codeHome, 'prompts', 'spectre:scope.md')));
115
+
116
+ const knowledgeOutput = execFileSync(process.execPath, [
117
+ path.join(codeHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')
118
+ ], {
119
+ cwd: projectDir,
120
+ env: {
121
+ ...process.env,
122
+ CODEX_HOME: codeHome
123
+ },
124
+ encoding: 'utf8'
125
+ }).trim();
126
+ const parsedKnowledgeOutput = JSON.parse(knowledgeOutput);
127
+ assert.deepEqual(parsedKnowledgeOutput.hookSpecificOutput, { hookEventName: 'SessionStart' });
128
+ assert.match(parsedKnowledgeOutput.systemMessage, /spectre: ready/);
129
+ const knowledgeOverride = fs.readFileSync(path.join(projectDir, 'AGENTS.override.md'), 'utf8');
130
+ assert.match(knowledgeOverride, /<!-- spectre-knowledge:start -->/);
131
+ assert.match(knowledgeOverride, /# Apply Knowledge/);
132
+ assert.doesNotMatch(knowledgeOutput, /additionalContext/);
133
+
96
134
  execFileSync('codex', ['--version'], {
97
135
  env: {
98
136
  ...process.env,
@@ -109,6 +147,56 @@ test('project install writes workflow skills, agent config, and official Session
109
147
  }
110
148
  });
111
149
 
150
+ test('user install wires generated hooks that run outside project manifests', { concurrency: false }, () => {
151
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'spectre-codex-home-'));
152
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'spectre-codex-workspace-'));
153
+ execFileSync('git', ['init', '-b', 'main'], { cwd: workspaceDir, stdio: 'ignore' });
154
+
155
+ execFileSync(process.execPath, [
156
+ path.join(process.cwd(), 'bin', 'spectre.js'),
157
+ 'install',
158
+ 'codex',
159
+ '--scope',
160
+ 'user',
161
+ '--project-dir',
162
+ workspaceDir
163
+ ], {
164
+ cwd: process.cwd(),
165
+ env: {
166
+ ...process.env,
167
+ HOME: homeDir,
168
+ CODEX_HOME: ''
169
+ },
170
+ stdio: 'ignore'
171
+ });
172
+
173
+ const codeHome = path.join(homeDir, '.codex');
174
+ const hooksConfig = JSON.parse(fs.readFileSync(path.join(codeHome, 'hooks.json'), 'utf8'));
175
+ const sessionHooks = hooksConfig.hooks.SessionStart.flatMap(group => group.hooks ?? []);
176
+ assert.ok(sessionHooks.some(hook => hook.command.includes('spectre/hooks/scripts/load-knowledge.mjs')));
177
+ assert.ok(sessionHooks.every(hook => !hook.command.includes('spectre/hooks/session-start.mjs')));
178
+ assert.ok(fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')));
179
+ assert.ok(!fs.existsSync(path.join(codeHome, 'spectre', 'hooks', 'session-start.mjs')));
180
+
181
+ const output = execFileSync(process.execPath, [
182
+ path.join(codeHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')
183
+ ], {
184
+ cwd: workspaceDir,
185
+ env: {
186
+ ...process.env,
187
+ HOME: homeDir
188
+ },
189
+ encoding: 'utf8'
190
+ }).trim();
191
+
192
+ assert.ok(!fs.existsSync(path.join(workspaceDir, '.spectre', 'manifest.json')));
193
+ const parsedOutput = JSON.parse(output);
194
+ assert.match(parsedOutput.systemMessage, /spectre: ready/);
195
+ assert.deepEqual(parsedOutput.hookSpecificOutput, { hookEventName: 'SessionStart' });
196
+ assert.ok(!fs.existsSync(path.join(workspaceDir, 'AGENTS.override.md')));
197
+ assert.doesNotMatch(output, /additionalContext/);
198
+ });
199
+
112
200
  test('project install removes legacy bridge artifacts while preserving non-managed AGENTS content', { concurrency: false }, async () => {
113
201
  const projectDir = makeProject();
114
202
  fs.writeFileSync(
@@ -209,8 +297,9 @@ test('project install preserves unrelated hooks.json handlers while adding Spect
209
297
  ]);
210
298
  assert.ok(Array.isArray(hooksConfig.hooks.SessionStart));
211
299
  assert.ok(hooksConfig.hooks.SessionStart.some(group =>
212
- Array.isArray(group.hooks) && group.hooks.some(hook => hook.command.includes('spectre/hooks/session-start.mjs'))
300
+ Array.isArray(group.hooks) && group.hooks.some(hook => hook.command.includes('spectre/hooks/scripts/load-knowledge.mjs'))
213
301
  ));
302
+ assert.equal(hooksConfig.hooks.PreCompact, undefined);
214
303
  } finally {
215
304
  if (previousCodexHome == null) {
216
305
  delete process.env.CODEX_HOME;
@@ -246,7 +335,7 @@ test('project uninstall removes managed workflow skills, agent config, and proje
246
335
 
247
336
  const codeHome = path.join(projectDir, '.codex');
248
337
 
249
- assert.ok(!fs.existsSync(path.join(codeHome, 'skills', 'spectre-scope')));
338
+ assert.ok(!fs.existsSync(path.join(codeHome, 'skills', 'scope')));
250
339
  assert.ok(!fs.existsSync(path.join(codeHome, 'spectre')));
251
340
 
252
341
  const config = fs.readFileSync(path.join(codeHome, 'config.toml'), 'utf8');