@codename_inc/spectre 3.7.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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +411 -0
  3. package/bin/spectre.js +8 -0
  4. package/package.json +23 -0
  5. package/plugins/spectre/.claude-plugin/plugin.json +5 -0
  6. package/plugins/spectre/agents/analyst.md +122 -0
  7. package/plugins/spectre/agents/dev.md +70 -0
  8. package/plugins/spectre/agents/finder.md +105 -0
  9. package/plugins/spectre/agents/patterns.md +207 -0
  10. package/plugins/spectre/agents/reviewer.md +128 -0
  11. package/plugins/spectre/agents/sync.md +151 -0
  12. package/plugins/spectre/agents/tester.md +209 -0
  13. package/plugins/spectre/agents/web-research.md +109 -0
  14. package/plugins/spectre/commands/architecture_review.md +120 -0
  15. package/plugins/spectre/commands/clean.md +313 -0
  16. package/plugins/spectre/commands/code_review.md +408 -0
  17. package/plugins/spectre/commands/create_plan.md +117 -0
  18. package/plugins/spectre/commands/create_tasks.md +374 -0
  19. package/plugins/spectre/commands/create_test_guide.md +120 -0
  20. package/plugins/spectre/commands/evaluate.md +50 -0
  21. package/plugins/spectre/commands/execute.md +87 -0
  22. package/plugins/spectre/commands/fix.md +61 -0
  23. package/plugins/spectre/commands/forget.md +58 -0
  24. package/plugins/spectre/commands/handoff.md +161 -0
  25. package/plugins/spectre/commands/kickoff.md +115 -0
  26. package/plugins/spectre/commands/learn.md +15 -0
  27. package/plugins/spectre/commands/plan.md +170 -0
  28. package/plugins/spectre/commands/plan_review.md +33 -0
  29. package/plugins/spectre/commands/quick_dev.md +101 -0
  30. package/plugins/spectre/commands/rebase.md +73 -0
  31. package/plugins/spectre/commands/recall.md +5 -0
  32. package/plugins/spectre/commands/research.md +159 -0
  33. package/plugins/spectre/commands/scope.md +119 -0
  34. package/plugins/spectre/commands/ship.md +172 -0
  35. package/plugins/spectre/commands/sweep.md +82 -0
  36. package/plugins/spectre/commands/test.md +380 -0
  37. package/plugins/spectre/commands/ux_spec.md +91 -0
  38. package/plugins/spectre/commands/validate.md +343 -0
  39. package/plugins/spectre/hooks/hooks.json +34 -0
  40. package/plugins/spectre/hooks/scripts/bootstrap.cjs +99 -0
  41. package/plugins/spectre/hooks/scripts/handoff-resume.cjs +410 -0
  42. package/plugins/spectre/hooks/scripts/lib.cjs +83 -0
  43. package/plugins/spectre/hooks/scripts/load-knowledge.cjs +120 -0
  44. package/plugins/spectre/hooks/scripts/precompact-warning.cjs +19 -0
  45. package/plugins/spectre/hooks/scripts/register_learning.cjs +144 -0
  46. package/plugins/spectre/hooks/scripts/test_bootstrap.cjs +84 -0
  47. package/plugins/spectre/hooks/scripts/test_handoff-resume.cjs +858 -0
  48. package/plugins/spectre/hooks/scripts/test_load-knowledge.cjs +285 -0
  49. package/plugins/spectre/hooks/scripts/test_register-learning.cjs +146 -0
  50. package/plugins/spectre/skills/spectre-apply/SKILL.md +189 -0
  51. package/plugins/spectre/skills/spectre-guide/SKILL.md +358 -0
  52. package/plugins/spectre/skills/spectre-learn/SKILL.md +635 -0
  53. package/plugins/spectre/skills/spectre-learn/references/recall-template.md +31 -0
  54. package/plugins/spectre/skills/spectre-tdd/SKILL.md +111 -0
  55. package/src/config.test.js +134 -0
  56. package/src/install.test.js +273 -0
  57. package/src/lib/config.js +516 -0
  58. package/src/lib/constants.js +60 -0
  59. package/src/lib/doctor.js +168 -0
  60. package/src/lib/install.js +482 -0
  61. package/src/lib/knowledge.js +217 -0
  62. package/src/lib/paths.js +98 -0
  63. package/src/lib/project.js +473 -0
  64. package/src/main.js +150 -0
@@ -0,0 +1,343 @@
1
+ ---
2
+ description: πŸ‘» | Comprehensive post implementation requirement validation using subagents
3
+ ---
4
+
5
+ # validate: Scope delivery verification and gap analysis
6
+
7
+ ## Description
8
+
9
+ - **What** β€” Validate implementation against scope/tasks docs, dispatch parallel subagents per area, produce single actionable gap remediation document.
10
+ - **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.
11
+ - **Outcome** β€” Single `validation_gaps.md` with actionable tasks ready for immediate implementation.
12
+
13
+ ## Core Validation Principle
14
+
15
+ > **"Definition β‰  Connection β‰  Reachability"**
16
+
17
+ Three levels of implementation completeness:
18
+ 1. **Defined**: Code exists in a file
19
+ 2. **Connected**: Code is imported/called by other code
20
+ 3. **Reachable**: A user action can trigger the code path
21
+
22
+ 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.
23
+
24
+ When verifying any implementation:
25
+ - Don't stop at "function X exists in file Y"
26
+ - Continue to "function X is called by Z at file:line"
27
+ - Continue to "Z is triggered when user does W"
28
+
29
+ ## ARGUMENTS Input
30
+
31
+ **REQUIRED**: User must provide scope documents to validate against.
32
+
33
+ <ARGUMENTS> $ARGUMENTS </ARGUMENTS>
34
+
35
+ ## Step (1/4) - Gather Validation Inputs
36
+
37
+ - **Action** β€” CheckArguments: Verify user provided scope documents.
38
+
39
+ - **If** ARGUMENTS contains file paths or "use thread context" β†’ proceed
40
+
41
+ - **Else** β†’ Immediately reply:
42
+
43
+ > "What should I validate against? Please provide:
44
+ >
45
+ > - Path to scope document (e.g., `docs/tasks/main/scope.md`)
46
+ > - Path to tasks document (e.g., `docs/tasks/main/tasks.md`)
47
+ > - Or say 'use thread context' to validate against our conversation"
48
+
49
+ - **Wait** β€” User provides validation inputs
50
+
51
+ - **Action** β€” ReadScopeDocs: Read provided documents completely (no limits).
52
+
53
+ - Extract all requirements, acceptance criteria, deliverables
54
+ - Document scope boundaries (in-scope / out-of-scope)
55
+ - Note constraints and success metrics
56
+
57
+ - **Action** β€” ChunkIntoValidationAreas: Break scope into discrete validation areas.
58
+
59
+ - **From tasks.md**: Each parent task (e.g., \[1.1\], \[1.2\]) = one validation area
60
+ - **From scope.md**: Each "In Scope" item = one validation area
61
+ - **From thread context**: Each discussed feature/requirement = one validation area
62
+ - Aim for 3-8 validation areas (merge small items, split large ones)
63
+
64
+ - **Action** β€” CreateValidationManifest: Document chunks before dispatch.
65
+
66
+ ```plaintext
67
+ Validation Areas:
68
+ 1. {Area Name} β€” {What to validate}
69
+ - Source: {requirement text from scope doc}
70
+ - Expected: {what should exist}
71
+ 2. ...
72
+ ```
73
+
74
+ ## Step (2/4) - Dispatch Parallel Validation Agents
75
+
76
+ **CRITICAL**: Dispatch ALL validation agents in parallel in a SINGLE message with multiple Task tool calls. Do NOT dispatch sequentially.
77
+
78
+ - **Action** β€” DispatchValidators: Launch one @analyst per validation area IN PARALLEL.
79
+
80
+ **Subagent Prompt Template**:
81
+
82
+ ```plaintext
83
+ You are validating scope delivery for ONE specific area.
84
+
85
+ ## Context Documents
86
+ - Scope: {path or "thread context"}
87
+ - Tasks: {path if provided}
88
+ - Branch: {branch_name}
89
+
90
+ ## Your Validation Area
91
+ **Area**: {area_name}
92
+ **Source Requirement**: {exact text from scope/tasks doc}
93
+ **Expected Deliverables**: {what should exist}
94
+
95
+ ## Your Task
96
+ 1. Investigate YOUR SPECIFIC AREA only
97
+ 2. For each requirement, determine:
98
+ - **Status**: βœ… Delivered | ⚠️ Partial | πŸ”Œ Dead Code | ❌ Missing
99
+ - βœ… **Delivered**: Defined AND connected AND reachable from user action
100
+ - ⚠️ **Partial**: Code exists but has broken/missing connections
101
+ - πŸ”Œ **Dead Code**: Code exists but has zero usage sites
102
+ - ❌ **Missing**: Code does not exist
103
+ - **Evidence**: Must include BOTH:
104
+ 1. Definition site: `file:line` where code is defined
105
+ 2. Usage site: `file:line` where code is called/rendered
106
+ - If you can only cite definition without usage β†’ status is ⚠️ or πŸ”Œ
107
+ - **Gap**: What's missing (if any)
108
+
109
+ 3. **CRITICAL - Reachability Verification**:
110
+ - Trace the COMPLETE chain from user action to implementation:
111
+ - Entry point: What user action triggers this? (click, route, event)
112
+ - Call chain: How does execution flow to the implementation?
113
+ - Terminal point: What side effect/UI change occurs?
114
+ - A broken link at ANY point = ⚠️ NOT FULLY DELIVERED
115
+ - For every function/component, grep for USAGE not just DEFINITION:
116
+ - Functions: Search for `functionName(` to find invocations
117
+ - Components: Search for `<ComponentName` to find render sites
118
+ - Hooks: Search for `useHookName(` to find consumers
119
+ - Props: Search for `propName={` to find where passed
120
+ - Zero usage sites = πŸ”Œ Dead Code
121
+
122
+ 4. **CRITICAL - Consumer-First Validation (Render-Backward Trace)**:
123
+ For UI features, start from the FINAL RENDER and trace BACKWARDS:
124
+ - What component renders the feature output? (exact JSX location)
125
+ - What variable does that JSX use? (exact variable name)
126
+ - Where does that variable come from? (hook, prop, computed value)
127
+ - Trace back to the user action. Is every link connected?
128
+
129
+ Example (filter feature):
130
+ - Renders: `BoardColumn` receives `tasks` prop at line 45
131
+ - Variable: `tasks` comes from `getFilteredTasksForBoard(board.id)`
132
+ - Source: `getFilteredTasksForBoard` uses... `getTasksForBoard` from drag hook
133
+ - ❌ BROKEN: This bypasses `displayTasks` from filter hook!
134
+
135
+ If you can only trace forward (definition β†’ usage) but NOT backward
136
+ (render β†’ source), you haven't verified the last mile.
137
+
138
+ 5. **CRITICAL - Dead Computation Detection**:
139
+ List every computed value that is NOT directly consumed by a render:
140
+ - For each hook return value, grep for where it's destructured/used
141
+ - For each computed const, grep for where it's referenced
142
+ - A value that's computed but never reaches JSX = πŸ”Œ Dead Code
143
+
144
+ Example:
145
+ - `displayTasks` from `useKanbanFilters` β†’ used where?
146
+ - Passed to `TasksHeroHeader` βœ“
147
+ - Used in `getFilteredTasksForBoard`? ❌ NO β€” uses different source
148
+ - Computed but not in render chain = BUG
149
+
150
+ 6. **Old Code Path Audit**:
151
+ When new functionality replaces old patterns:
152
+ - What OLD code handled this before? (imports, hooks, functions)
153
+ - Is the old code still being called anywhere?
154
+ - Are there duplicate data sources for the same concern?
155
+ - If old path still active β†’ ⚠️ Partial (new code bypassed)
156
+
157
+ 7. Check for scope creep: anything beyond the requirement
158
+
159
+ ## Output Format
160
+ ```
161
+
162
+ AREA: {area_name} STATUS: {overall: Delivered | Partial | Dead Code | Missing}
163
+
164
+ REQUIREMENTS:
165
+
166
+ - \[REQ-1\] {requirement}
167
+ Status: {βœ…|⚠️|πŸ”Œ|❌}
168
+ Definition: {file:line where defined}
169
+ Usage: {file:line where called/rendered, or "NONE FOUND"}
170
+ Reachability: {user action β†’ ... β†’ this code, or "NOT REACHABLE"}
171
+ Render Chain: {JSX ← variable ← source ← user action, or "BROKEN AT {link}"}
172
+ Gap: {what's missing}
173
+ Remediation: {specific fix β€” what to wire, what to replace, what to add}
174
+ - Produces: {what the fix outputs}
175
+ - Consumed by: {what should use it}
176
+ - Replaces: {old path to remove, or "N/A"}
177
+
178
+ DEAD COMPUTATIONS:
179
+ - {variable name} in {file}: computed but not consumed by render
180
+ - Remediation: {wire to X, or delete if unnecessary}
181
+
182
+ OLD CODE PATHS:
183
+ - {old function/hook}: still active at {file:line}, bypasses new implementation
184
+ - Remediation: {replace calls with new path, or remove}
185
+
186
+ SCOPE CREEP: {any features beyond scope}
187
+
188
+ SUMMARY: {1-2 sentences}
189
+
190
+ ```
191
+
192
+ **Verification Patterns for Subagents**:
193
+
194
+ | Checking | Search Pattern | Meaning |
195
+ |----------|---------------|---------|
196
+ | Function called | `functionName\(` | Invocation exists |
197
+ | Component renders | `<ComponentName` | JSX usage exists |
198
+ | Hook consumed | `useHookName\(` | Hook is used |
199
+ | Prop passed | `propName={` | Parent passes prop |
200
+ | Export imported | `import.*Name` | Module consumed |
201
+
202
+ **Common broken link patterns to check**:
203
+ - Callback defined but never passed as prop
204
+ - Prop received but never used in component body
205
+ - Function exported but never imported elsewhere
206
+ - Type defined but never used in signatures
207
+ - Switch case exists but condition never triggers
208
+ - **Hook returns value that's destructured but never used in JSX**
209
+ - **New data source created but old source still used in render**
210
+ - **Computed value stored in variable but render uses different variable**
211
+ - **Filter/transform function created but rendering bypasses it**
212
+
213
+ **Last-Mile Anti-Patterns** (these cause "works in code, broken in UI"):
214
+ - `displayTasks` computed but `getTasksForBoard` called in render
215
+ - Handler created but onClick still points to old handler
216
+ - New hook created but component still imports old hook
217
+ - State updated correctly but wrong variable passed to child component
218
+
219
+ - **Wait** β€” All validation agents complete
220
+
221
+ ## Step (3/4) - Consolidate & Create Gap Remediation Tasks
222
+
223
+ - **Action** β€” ConsolidateFindings: Merge all subagent outputs.
224
+
225
+ - Aggregate status across all areas
226
+ - Compile gaps by priority (Critical/Medium/Low)
227
+ - Note any scope creep findings
228
+
229
+ - **Action** β€” FinalWiringChecklist: Before marking any area complete, verify:
230
+
231
+ | Check | Question |
232
+ |-------|----------|
233
+ | Consumer connected? | Does every new function/hook have a consumer that uses its output? |
234
+ | Render chain complete? | Can you trace from JSX ← variable ← hook ← user action without breaks? |
235
+ | Old paths removed? | If replacing old code, is the old path dead or redirected? |
236
+ | No orphaned computation? | Is every computed value actually used in a render path? |
237
+ | No duplicate sources? | Is there only ONE data source for each concern (not old + new)? |
238
+
239
+ **CRITICAL**: If any check fails for an area marked βœ…, downgrade to ⚠️ and add gap task.
240
+
241
+ - **Action** β€” DetermineOutputDir:
242
+
243
+ - `branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)`
244
+ - **If** user specifies path β†’ `OUT_DIR={that value}`
245
+ - **Else** β†’ `OUT_DIR=docs/tasks/{branch_name}`
246
+ - `mkdir -p "${OUT_DIR}/validation"`
247
+
248
+ - **Action** β€” CreateValidationGapsDoc: Generate `{OUT_DIR}/validation/validation_gaps.md`.
249
+
250
+ **Document Structure**:
251
+
252
+ ```markdown
253
+ # Validation Gaps: {task_name}
254
+ *Generated: {timestamp}*
255
+
256
+ ## Summary
257
+ - **Overall Status**: {Complete | Needs Work | Significant Gaps}
258
+ - **Requirements**: {X of Y} delivered
259
+ - **Gaps Found**: {count} requiring remediation
260
+ - **Scope Creep**: {count} items (document or remove)
261
+
262
+ ---
263
+
264
+ ## Gap Remediation Tasks
265
+
266
+ ### πŸ“¦ Phase 1: Critical Gaps
267
+
268
+ #### πŸ“‹ [1.1] {Gap Title - e.g., "Connect auth flow to login page"}
269
+ **Requirement**: {original requirement text}
270
+ **Current State**: {what exists now β€” definition site if code exists}
271
+ **Gap**: {what's missing β€” broken link in the chain}
272
+
273
+ - [ ] **1.1.1** {Specific action - e.g., "Wire LoginButton onClick to auth handler"}
274
+ - **Produces**: {output this creates β€” e.g., "onClick handler calling authService.login()"}
275
+ - **Consumed by**: {what uses this β€” e.g., "LoginButton component render"}
276
+ - **Replaces**: {old code path β€” e.g., "inline console.log in onClick" or "N/A"}
277
+ - [ ] {Verifiable outcome 1}
278
+ - [ ] {Verifiable outcome 2}
279
+
280
+ - [ ] **1.1.2** {Specific action - e.g., "Add login route to app router"}
281
+ - **Produces**: {output β€” e.g., "/login route rendering LoginPage"}
282
+ - **Consumed by**: {consumer β€” e.g., "App router, navigated via authService redirect"}
283
+ - [ ] {Verifiable outcome 1}
284
+ - [ ] {Verifiable outcome 2}
285
+
286
+ #### πŸ“‹ [1.2] {Next Gap Title}
287
+ ...
288
+
289
+ ### πŸ“¦ Phase 2: Medium Priority Gaps
290
+ ...
291
+
292
+ ### πŸ“¦ Phase 3: Low Priority / Polish
293
+ ...
294
+
295
+ ---
296
+
297
+ ## Scope Creep Review
298
+ Items implemented beyond original scope:
299
+ - [ ] **{Feature}**: {Keep and document | Remove | Discuss}
300
+ - Evidence: {file:line}
301
+ - Recommendation: {action}
302
+
303
+ ---
304
+
305
+ ## Validation Coverage
306
+ | Area | Status | Definition | Usage | Render Chain |
307
+ |------|--------|------------|-------|--------------|
308
+ | {area 1} | βœ… | {file:line} | {file:line} | JSX ← var ← source βœ“ |
309
+ | {area 2} | ⚠️ | {file:line} | {file:line} | Broken at {link} |
310
+ | {area 3} | πŸ”Œ | {file:line} | NONE | Dead computation |
311
+ | {area 4} | ❌ | β€” | β€” | β€” |
312
+
313
+ ## Dead Computations Found
314
+ | Variable | File | Computed By | Should Be Consumed By |
315
+ |----------|------|-------------|----------------------|
316
+ | {displayTasks} | {useKanbanFilters.ts} | {applyTaskFilters} | {KanbanBoard render} |
317
+
318
+ ## Old Code Paths Still Active
319
+ | Old Path | Location | Should Be Replaced By | Impact |
320
+ |----------|----------|----------------------|--------|
321
+ | {getTasksForBoard} | {useKanbanDrag:45} | {displayTasks from useKanbanFilters} | Bypasses new filter |
322
+
323
+ ```
324
+
325
+ ## Step (4/4) - Present Results
326
+
327
+ - **Action** β€” PresentResults: Show validation summary.
328
+
329
+ > ## Validation Complete
330
+ **> **Status**: {Complete | Needs Work | Significant Gaps}
331
+ >
332
+ > - {X of Y} requirements delivered
333
+ > - {N} gaps requiring remediation
334
+ > - {N} scope creep items to review
335
+ **> **Gap Remediation Doc**: `{OUT_DIR}/validation/validation_gaps.md`
336
+ >
337
+ > {1-2 sentence summary of key findings}
338
+
339
+ - **Action** β€” RenderFooter: Render Next Steps footer using `@skill-spectre:spectre-guide` skill
340
+
341
+ ## Next Steps
342
+
343
+ See `/skill-spectre:spectre` skill for footer format and command options.
@@ -0,0 +1,34 @@
1
+ {
2
+ "hooks": {
3
+ "PreCompact": [
4
+ {
5
+ "matcher": "*",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/precompact-warning.cjs"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "SessionStart": [
15
+ {
16
+ "matcher": "startup|clear|compact",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/bootstrap.cjs"
21
+ },
22
+ {
23
+ "type": "command",
24
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/handoff-resume.cjs"
25
+ },
26
+ {
27
+ "type": "command",
28
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/load-knowledge.cjs"
29
+ }
30
+ ]
31
+ }
32
+ ]
33
+ }
34
+ }
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * bootstrap.cjs
6
+ *
7
+ * SessionStart hook that removes stale files from older plugin versions.
8
+ *
9
+ * When users update the plugin via marketplace, old files that were deleted
10
+ * from the repo may still linger in their cached copy. This script runs on
11
+ * every session start and cleans them up.
12
+ *
13
+ * To add new files to the cleanup list, append to STALE_PATHS below.
14
+ * Paths are relative to CLAUDE_PLUGIN_ROOT.
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const { readStdinWithTimeout } = require('./lib.cjs');
20
+
21
+ // ──────────────────────────────────────────────────────────────────
22
+ // Stale paths to remove (relative to CLAUDE_PLUGIN_ROOT)
23
+ // ──────────────────────────────────────────────────────────────────
24
+
25
+ const STALE_PATHS = [
26
+ // Python scripts replaced by .cjs equivalents (v3.x migration)
27
+ 'hooks/scripts/capture-todos.py',
28
+ 'hooks/scripts/handoff-resume.py',
29
+ 'hooks/scripts/load-knowledge.py',
30
+ 'hooks/scripts/precompact-warning.py',
31
+ 'hooks/scripts/register_learning.py',
32
+ 'hooks/scripts/test_handoff_resume.py',
33
+ 'hooks/scripts/test_load_knowledge.py',
34
+
35
+ // Old skill directory replaced by spectre-guide
36
+ 'skills/spectre-next-steps',
37
+ ];
38
+
39
+ // ──────────────────────────────────────────────────────────────────
40
+ // Cleanup logic
41
+ // ──────────────────────────────────────────────────────────────────
42
+
43
+ function cleanupStalePaths(pluginRoot) {
44
+ let removed = 0;
45
+
46
+ for (const relPath of STALE_PATHS) {
47
+ const fullPath = path.join(pluginRoot, relPath);
48
+
49
+ try {
50
+ const stat = fs.statSync(fullPath);
51
+
52
+ if (stat.isDirectory()) {
53
+ fs.rmSync(fullPath, { recursive: true, force: true });
54
+ removed++;
55
+ } else {
56
+ fs.unlinkSync(fullPath);
57
+ removed++;
58
+ }
59
+ } catch (_) {
60
+ // File doesn't exist or can't be removed β€” skip silently
61
+ }
62
+ }
63
+
64
+ return removed;
65
+ }
66
+
67
+ // ──────────────────────────────────────────────────────────────────
68
+ // Main
69
+ // ──────────────────────────────────────────────────────────────────
70
+
71
+ async function main() {
72
+ // Drain stdin so the hook system doesn't hang
73
+ await readStdinWithTimeout();
74
+
75
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
76
+ if (!pluginRoot) {
77
+ process.stdout.write(JSON.stringify({}) + '\n');
78
+ process.exit(0);
79
+ }
80
+
81
+ const removed = cleanupStalePaths(pluginRoot);
82
+
83
+ if (removed > 0) {
84
+ process.stdout.write(JSON.stringify({
85
+ systemMessage: `bootstrap: cleaned ${removed} stale file${removed > 1 ? 's' : ''} from previous plugin version`
86
+ }) + '\n');
87
+ } else {
88
+ process.stdout.write(JSON.stringify({}) + '\n');
89
+ }
90
+
91
+ process.exit(0);
92
+ }
93
+
94
+ // Export for testing
95
+ if (typeof module !== 'undefined') {
96
+ module.exports = { cleanupStalePaths, STALE_PATHS };
97
+ }
98
+
99
+ main();