@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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "ux"
|
|
3
|
+
description: "👻 | Define user flows, components, and UX behavior — generates the UX spec for a feature - primary agent"
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ux
|
|
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
|
+
# ux: Define Exactly How the Feature Works
|
|
14
|
+
|
|
15
|
+
Transform product requirements into a definitive behavioral specification. Two stages: align on user flows, then generate detailed spec. Output: `ux.md` ready for implementation.
|
|
16
|
+
|
|
17
|
+
<ARGUMENTS> $ARGUMENTS </ARGUMENTS>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# STAGE 1: Flow Discovery & Alignment
|
|
22
|
+
|
|
23
|
+
**Goal**: Align on HOW the feature works before specifying details.
|
|
24
|
+
|
|
25
|
+
## Step 1 — Understand the Feature
|
|
26
|
+
|
|
27
|
+
1. **Read scope and requirements** in this precedence (read whichever exist FULLY, no offset/limit):
|
|
28
|
+
- `docs/tasks/{branch}/concepts/scope.md` — the canonical scope doc (preferred)
|
|
29
|
+
- `docs/tasks/{branch}/specs/prd.md` — if a PRD was generated separately
|
|
30
|
+
- `docs/tasks/{branch}/task_summary.md` — if present
|
|
31
|
+
- **If none exist** → ask the user for scope context (or recommend `scope` first) before proceeding.
|
|
32
|
+
2. **Research patterns**: Dispatch `@patterns` to find existing screens/components similar to what we're building. Note conventions, reusable elements, and any design tokens.
|
|
33
|
+
3. **Identify user segments**: List the user segments this feature serves — first-time vs returning, anonymous vs signed-in, free vs paid, role-based variants. UX often diverges across segments and missing this is a common cause of rework.
|
|
34
|
+
4. **Identify journeys**: List user goals, entry points, and completion states.
|
|
35
|
+
|
|
36
|
+
## Step 2 — Present User Flows
|
|
37
|
+
|
|
38
|
+
Write each flow as a narrative walkthrough.
|
|
39
|
+
|
|
40
|
+
**Per flow include**: Goal, Entry point, Journey steps (User sees → User does → System responds), Decision points with branches, Success state, Questions where ambiguity exists.
|
|
41
|
+
|
|
42
|
+
**Per user segment**: Call out where flows diverge (e.g., "First-time users see X tour; returning users skip directly to Y").
|
|
43
|
+
|
|
44
|
+
After writing all flows, propose a specific take rather than asking open-ended:
|
|
45
|
+
|
|
46
|
+
> **User Flows — Proposed**
|
|
47
|
+
>
|
|
48
|
+
> I've mapped {N} flows across {M} user segments: {list with one-line summaries}
|
|
49
|
+
>
|
|
50
|
+
> **Key segmentation calls**: [where flows diverge by user state and why]
|
|
51
|
+
>
|
|
52
|
+
> Push back on anything wrong, missing, or over-/under-segmented. Reply with feedback or **"Flows approved"** to proceed.
|
|
53
|
+
|
|
54
|
+
**Wait for approval. If feedback → revise and re-present. If approved → Stage 2.**
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
# STAGE 2: Detailed Specification
|
|
59
|
+
|
|
60
|
+
**Gate**: Only proceed after explicit flow approval.
|
|
61
|
+
|
|
62
|
+
## Step 3 — Clarify Remaining Details
|
|
63
|
+
|
|
64
|
+
Review approved flows for gaps: component behaviors, edge cases, state definitions, segment variants.
|
|
65
|
+
|
|
66
|
+
If significant gaps, ask 3–5 targeted questions (empty states, error handling, loading, limits, segment differences). Save to `clarifications/ux_clarifications_{timestamp}.md`, prompt user to read, incorporate answers.
|
|
67
|
+
|
|
68
|
+
## Step 4 — Write the Specification
|
|
69
|
+
|
|
70
|
+
Generate complete spec with these sections:
|
|
71
|
+
|
|
72
|
+
### Required Sections
|
|
73
|
+
|
|
74
|
+
1. **Overview** — What this feature is, problem it solves, primary user goal (1 paragraph)
|
|
75
|
+
2. **User Segments** — Each segment served, what's different about their UX (e.g., first-time onboarding, role-based permissions, free vs paid limits, anon vs signed-in)
|
|
76
|
+
3. **Screens** — Every screen: name, purpose (1 line), navigation relationships
|
|
77
|
+
4. **Flows** — Formalized from Stage 1 with alternate paths (validation fail, cancel, network error). Include per-segment branches where they diverge.
|
|
78
|
+
5. **Layouts** — Per screen: header/main/footer structure + responsive behavior (desktop >1024, tablet 768–1024, mobile <768)
|
|
79
|
+
6. **Components** — Each interactive element: purpose, location, applicable states (see State Vocabulary below)
|
|
80
|
+
7. **Interactions** — Table format: Element | Action | Result (exhaustive)
|
|
81
|
+
8. **States** — Table format: State | Trigger | Appearance | Available Actions
|
|
82
|
+
9. **Content** — Exact copy: page titles, buttons, empty states, error messages, confirmation dialogs
|
|
83
|
+
10. **Edge Cases** — Limits/boundaries, null/long data handling, permissions, offline/network failures, segment-specific edge cases
|
|
84
|
+
11. **Accessibility** — Tab order, keyboard actions (Enter/Space/Escape), screen reader announcements, focus management
|
|
85
|
+
|
|
86
|
+
### State Vocabulary
|
|
87
|
+
|
|
88
|
+
Use these state categories where applicable. Not every component needs every state — pick what's relevant for the feature.
|
|
89
|
+
|
|
90
|
+
- **Visual states** (per interactive element): default, hover, focus, active/pressed, disabled
|
|
91
|
+
- **Data states** (per data view): empty, loading, partial-loaded, loaded, error, stale/refreshing
|
|
92
|
+
- **Form states**: pristine, dirty, touched, submitting, submitted-success, submitted-error, validation-error per field
|
|
93
|
+
- **Selection states**: none, single, multi, partial-selection, all-selected
|
|
94
|
+
- **Sync states** (collaborative or async UI): optimistic, pending, conflict, resolved
|
|
95
|
+
- **Network states** (where relevant): online, offline, reconnecting
|
|
96
|
+
|
|
97
|
+
Save to `docs/tasks/{branch}/ux.md`
|
|
98
|
+
|
|
99
|
+
Prompt:
|
|
100
|
+
|
|
101
|
+
> **UX Specification Complete**
|
|
102
|
+
>
|
|
103
|
+
> Written to `{path}`. Please review: Any behaviors wrong or missing? Edge cases not covered? Segment differences captured?
|
|
104
|
+
>
|
|
105
|
+
> **Want a prototype to validate visually before approving?** `prototype` will render this spec (high-fi, no synthesis) and flag assumptions where I had to fill in details — catches issues prose review misses. Reply `prototype` to run it now.
|
|
106
|
+
>
|
|
107
|
+
> Otherwise, reply with feedback, or **"Approved"** to finalize.
|
|
108
|
+
|
|
109
|
+
**Wait for approval, feedback, or prototype request.** If user replies `prototype`, invoke `prototype` (the post-ux mode auto-detects the complete ux.md). Once prototype completes and any spec updates from filled assumptions are applied, return here for final approval.
|
|
110
|
+
|
|
111
|
+
## Step 5 — Handoff
|
|
112
|
+
|
|
113
|
+
Confirm completion with summary: screens specified, segments addressed, flows documented, components with states, edge cases and accessibility covered.
|
|
114
|
+
|
|
115
|
+
Read `.spectre/next_steps_guide.md` and render Next Steps footer:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Next Steps | Phase: Scope | Status: UX Complete
|
|
119
|
+
Recommendation: {contextual next action — if no prototype was generated, suggest prototype to validate the spec visually before /plan}
|
|
120
|
+
Options: prototype (validate spec visually), create_plan, create_tasks, tdd
|
|
121
|
+
```
|
|
@@ -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.
|
package/src/config.test.js
CHANGED
|
@@ -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
|
|
94
|
-
assert.
|
|
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
|
|
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, /
|
|
133
|
-
assert.match(overrideContent, /
|
|
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
|
});
|
package/src/install.test.js
CHANGED
|
@@ -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', '
|
|
41
|
+
const scopeSkillPath = path.join(codeHome, 'skills', 'scope', 'SKILL.md');
|
|
42
42
|
assert.ok(fs.existsSync(scopeSkillPath));
|
|
43
|
-
|
|
44
|
-
assert.match(
|
|
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', '
|
|
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
|
|
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', '
|
|
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,
|
|
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', '
|
|
80
|
-
|
|
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/
|
|
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', '
|
|
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');
|