@codename_inc/spectre 3.7.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/package.json +3 -2
- package/plugins/spectre/.claude-plugin/plugin.json +1 -1
- package/plugins/spectre/bin/spectre-register +5 -0
- package/plugins/spectre/hooks/hooks.json +3 -14
- package/plugins/spectre/hooks/scripts/bootstrap.mjs +98 -0
- package/plugins/spectre/hooks/scripts/handoff-resume.mjs +404 -0
- package/plugins/spectre/hooks/scripts/lib.mjs +82 -0
- package/plugins/spectre/hooks/scripts/load-knowledge.mjs +189 -0
- package/plugins/spectre/hooks/scripts/register_learning.mjs +264 -0
- package/plugins/spectre/hooks/scripts/{test_bootstrap.cjs → test_bootstrap.mjs} +12 -7
- package/plugins/spectre/hooks/scripts/{test_handoff-resume.cjs → test_handoff-resume.mjs} +13 -11
- package/plugins/spectre/hooks/scripts/{test_load-knowledge.cjs → test_load-knowledge.mjs} +103 -22
- package/plugins/spectre/hooks/scripts/test_register-learning.mjs +335 -0
- package/plugins/spectre/skills/apply/SKILL.md +87 -0
- package/plugins/spectre/{commands/architecture_review.md → skills/architecture_review/SKILL.md} +9 -0
- package/plugins/spectre/{commands/clean.md → skills/clean/SKILL.md} +9 -0
- package/plugins/spectre/{commands/code_review.md → skills/code_review/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_plan.md → skills/create_plan/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_tasks.md → skills/create_tasks/SKILL.md} +9 -0
- package/plugins/spectre/{commands/create_test_guide.md → skills/create_test_guide/SKILL.md} +9 -0
- package/plugins/spectre/{commands/evaluate.md → skills/evaluate/SKILL.md} +11 -2
- package/plugins/spectre/{commands/execute.md → skills/execute/SKILL.md} +12 -3
- package/plugins/spectre/{commands/fix.md → skills/fix/SKILL.md} +9 -0
- package/plugins/spectre/{commands/forget.md → skills/forget/SKILL.md} +9 -0
- package/plugins/spectre/skills/{spectre-guide → guide}/SKILL.md +6 -5
- package/plugins/spectre/{commands/handoff.md → skills/handoff/SKILL.md} +9 -0
- package/plugins/spectre/{commands/kickoff.md → skills/kickoff/SKILL.md} +9 -0
- package/plugins/spectre/skills/{spectre-learn → learn}/SKILL.md +19 -59
- package/plugins/spectre/skills/learn/references/recall-template.md +34 -0
- package/plugins/spectre/{commands/plan.md → skills/plan/SKILL.md} +66 -25
- package/plugins/spectre/{commands/plan_review.md → skills/plan_review/SKILL.md} +9 -0
- package/plugins/spectre/skills/prototype/SKILL.md +314 -0
- package/plugins/spectre/{commands/quick_dev.md → skills/quick_dev/SKILL.md} +9 -0
- package/plugins/spectre/{commands/rebase.md → skills/rebase/SKILL.md} +9 -0
- package/plugins/spectre/skills/recall/SKILL.md +17 -0
- package/plugins/spectre/{commands/research.md → skills/research/SKILL.md} +9 -0
- package/plugins/spectre/skills/scope/SKILL.md +174 -0
- package/plugins/spectre/{commands/ship.md → skills/ship/SKILL.md} +9 -0
- package/plugins/spectre/{commands/sweep.md → skills/sweep/SKILL.md} +9 -0
- package/plugins/spectre/skills/tdd/SKILL.md +111 -0
- package/plugins/spectre/{commands/test.md → skills/test/SKILL.md} +9 -0
- package/plugins/spectre/skills/ux/SKILL.md +121 -0
- package/plugins/spectre/{commands/validate.md → skills/validate/SKILL.md} +9 -0
- package/plugins/spectre-codex/agents/analyst.toml +117 -0
- package/plugins/spectre-codex/agents/dev.toml +65 -0
- package/plugins/spectre-codex/agents/finder.toml +101 -0
- package/plugins/spectre-codex/agents/patterns.toml +203 -0
- package/plugins/spectre-codex/agents/reviewer.toml +123 -0
- package/plugins/spectre-codex/agents/sync.toml +146 -0
- package/plugins/spectre-codex/agents/tester.toml +205 -0
- package/plugins/spectre-codex/agents/web-research.toml +104 -0
- package/plugins/spectre-codex/hooks/hooks.json +23 -0
- package/plugins/{spectre/hooks/scripts/bootstrap.cjs → spectre-codex/hooks/scripts/bootstrap.mjs} +15 -16
- package/plugins/{spectre/hooks/scripts/handoff-resume.cjs → spectre-codex/hooks/scripts/handoff-resume.mjs} +21 -27
- package/plugins/{spectre/hooks/scripts/lib.cjs → spectre-codex/hooks/scripts/lib.mjs} +3 -4
- package/plugins/spectre-codex/hooks/scripts/load-knowledge.mjs +189 -0
- package/plugins/spectre-codex/hooks/scripts/register_learning.mjs +264 -0
- package/plugins/spectre-codex/skills/apply/SKILL.md +87 -0
- package/plugins/spectre-codex/skills/architecture_review/SKILL.md +129 -0
- package/plugins/spectre-codex/skills/clean/SKILL.md +322 -0
- package/plugins/spectre-codex/skills/code_review/SKILL.md +417 -0
- package/plugins/spectre-codex/skills/create_plan/SKILL.md +126 -0
- package/plugins/spectre-codex/skills/create_tasks/SKILL.md +383 -0
- package/plugins/spectre-codex/skills/create_test_guide/SKILL.md +129 -0
- package/plugins/spectre-codex/skills/evaluate/SKILL.md +59 -0
- package/plugins/spectre-codex/skills/execute/SKILL.md +96 -0
- package/plugins/spectre-codex/skills/fix/SKILL.md +70 -0
- package/plugins/spectre-codex/skills/forget/SKILL.md +67 -0
- package/plugins/spectre-codex/skills/guide/SKILL.md +359 -0
- package/plugins/spectre-codex/skills/handoff/SKILL.md +170 -0
- package/plugins/spectre-codex/skills/kickoff/SKILL.md +124 -0
- package/plugins/spectre-codex/skills/learn/SKILL.md +595 -0
- package/plugins/{spectre/skills/spectre-learn → spectre-codex/skills/learn}/references/recall-template.md +4 -1
- package/plugins/spectre-codex/skills/plan/SKILL.md +211 -0
- package/plugins/spectre-codex/skills/plan_review/SKILL.md +42 -0
- package/plugins/spectre-codex/skills/prototype/SKILL.md +314 -0
- package/plugins/spectre-codex/skills/quick_dev/SKILL.md +110 -0
- package/plugins/spectre-codex/skills/rebase/SKILL.md +82 -0
- package/plugins/spectre-codex/skills/recall/SKILL.md +17 -0
- package/plugins/spectre-codex/skills/research/SKILL.md +168 -0
- package/plugins/spectre-codex/skills/scope/SKILL.md +174 -0
- package/plugins/spectre-codex/skills/ship/SKILL.md +181 -0
- package/plugins/spectre-codex/skills/sweep/SKILL.md +91 -0
- package/plugins/{spectre/skills/spectre-tdd → spectre-codex/skills/tdd}/SKILL.md +1 -1
- package/plugins/spectre-codex/skills/test/SKILL.md +389 -0
- package/plugins/spectre-codex/skills/ux/SKILL.md +121 -0
- package/plugins/spectre-codex/skills/validate/SKILL.md +352 -0
- package/src/config.test.js +6 -5
- package/src/install.test.js +100 -11
- package/src/lib/config.js +107 -54
- package/src/lib/constants.js +17 -23
- package/src/lib/doctor.js +19 -22
- package/src/lib/install.js +98 -313
- package/src/lib/knowledge.js +7 -37
- package/src/lib/paths.js +0 -12
- package/src/pack.test.js +87 -0
- package/plugins/spectre/commands/learn.md +0 -15
- package/plugins/spectre/commands/recall.md +0 -5
- package/plugins/spectre/commands/scope.md +0 -119
- package/plugins/spectre/commands/ux_spec.md +0 -91
- package/plugins/spectre/hooks/scripts/load-knowledge.cjs +0 -120
- package/plugins/spectre/hooks/scripts/precompact-warning.cjs +0 -19
- package/plugins/spectre/hooks/scripts/register_learning.cjs +0 -144
- package/plugins/spectre/hooks/scripts/test_register-learning.cjs +0 -146
- package/plugins/spectre/skills/spectre-apply/SKILL.md +0 -189
package/src/pack.test.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { execFileSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
function makeTempDir(prefix) {
|
|
9
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function exec(command, args, options = {}) {
|
|
13
|
+
return execFileSync(command, args, {
|
|
14
|
+
encoding: 'utf8',
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
16
|
+
...options
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectFiles(root) {
|
|
21
|
+
const files = [];
|
|
22
|
+
if (!fs.existsSync(root)) return files;
|
|
23
|
+
|
|
24
|
+
function walk(dir) {
|
|
25
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
26
|
+
const fullPath = path.join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
walk(fullPath);
|
|
29
|
+
} else if (entry.isFile()) {
|
|
30
|
+
files.push(fullPath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
walk(root);
|
|
36
|
+
return files;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test('packed npm artifact installs Codex assets from generated tree', { concurrency: false }, () => {
|
|
40
|
+
const repoRoot = path.resolve('.');
|
|
41
|
+
const packDir = makeTempDir('spectre-pack-');
|
|
42
|
+
const projectDir = makeTempDir('spectre-pack-install-');
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const packOutput = exec('npm', ['pack', '--pack-destination', packDir], { cwd: repoRoot }).trim();
|
|
46
|
+
const tarball = path.join(packDir, packOutput.split('\n').at(-1));
|
|
47
|
+
|
|
48
|
+
exec('npm', ['init', '-y'], { cwd: projectDir });
|
|
49
|
+
exec('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', tarball], { cwd: projectDir });
|
|
50
|
+
exec('git', ['init', '-b', 'main'], { cwd: projectDir });
|
|
51
|
+
|
|
52
|
+
const env = { ...process.env };
|
|
53
|
+
delete env.CODEX_HOME;
|
|
54
|
+
|
|
55
|
+
const unscopedHelp = exec('npx', ['spectre', 'help'], {
|
|
56
|
+
cwd: projectDir,
|
|
57
|
+
env
|
|
58
|
+
});
|
|
59
|
+
assert.match(unscopedHelp, /spectre install codex/);
|
|
60
|
+
|
|
61
|
+
exec('npx', ['@codename_inc/spectre', 'install', 'codex', '--scope', 'project', '--project-dir', projectDir], {
|
|
62
|
+
cwd: projectDir,
|
|
63
|
+
env
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const codexHome = path.join(projectDir, '.codex');
|
|
67
|
+
assert.ok(fs.existsSync(path.join(codexHome, 'skills', 'plan', 'SKILL.md')));
|
|
68
|
+
assert.ok(fs.existsSync(path.join(codexHome, 'spectre', 'agents', 'dev.toml')));
|
|
69
|
+
assert.ok(fs.existsSync(path.join(codexHome, 'spectre', 'hooks', 'scripts', 'load-knowledge.mjs')));
|
|
70
|
+
assert.ok(!fs.existsSync(path.join(codexHome, 'spectre', 'hooks', 'session-start.mjs')));
|
|
71
|
+
|
|
72
|
+
const hooksConfig = JSON.parse(fs.readFileSync(path.join(codexHome, 'hooks.json'), 'utf8'));
|
|
73
|
+
assert.ok(hooksConfig.hooks.SessionStart.some(group =>
|
|
74
|
+
Array.isArray(group.hooks) && group.hooks.some(hook => hook.command.includes('spectre/hooks/scripts/load-knowledge.mjs'))
|
|
75
|
+
));
|
|
76
|
+
|
|
77
|
+
const runtimeFiles = collectFiles(path.join(codexHome, 'spectre'));
|
|
78
|
+
for (const filePath of runtimeFiles) {
|
|
79
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
80
|
+
assert.doesNotMatch(content, /file:\/\//, `${filePath} should not contain package-cache file:// imports`);
|
|
81
|
+
assert.doesNotMatch(content, new RegExp(repoRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')), `${filePath} should not reference the repo checkout`);
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
fs.rmSync(packDir, { recursive: true, force: true });
|
|
85
|
+
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "\ud83d\udc7b | Capture knowledge for future sessions"
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# /learn - Capture Project Knowledge
|
|
6
|
-
|
|
7
|
-
Load the `spectre-learn` skill via `Skill(spectre-learn)` and **execute its workflow step-by-step**.
|
|
8
|
-
|
|
9
|
-
<CRITICAL>
|
|
10
|
-
This skill supersedes auto-memory. Do NOT write to MEMORY.md or any auto-memory directory. The skill defines the exclusive storage location, format, and registration workflow for captured knowledge.
|
|
11
|
-
|
|
12
|
-
Treat the skill output as a binding directive, not informational context.
|
|
13
|
-
</CRITICAL>
|
|
14
|
-
|
|
15
|
-
**Topic**: $ARGUMENTS
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 👻 | Interactively scope a feature or improvement, generating a complete Scope document that clarifies what's IN and OUT -- primary agent
|
|
3
|
-
---
|
|
4
|
-
# scope: Interactive Feature Scoping
|
|
5
|
-
|
|
6
|
-
Collaborative workflow for structuring unstructured thoughts into clear scope boundaries through contextual brainstorming and targeted clarifications. Focuses on user value and scope boundaries before technical considerations. Output: comprehensive scope document with clear boundaries, user value, and key decisions saved to `{OUT_DIR}/concepts/scope.md`.
|
|
7
|
-
|
|
8
|
-
## ARGUMENTS
|
|
9
|
-
|
|
10
|
-
<ARGUMENTS> $ARGUMENTS </ARGUMENTS>
|
|
11
|
-
|
|
12
|
-
## Step 1: Immediate Reply & Gather Context
|
|
13
|
-
|
|
14
|
-
- **Action** — ImmediateReply: Respond before any tools.
|
|
15
|
-
- **If** `FROM_KICKOFF=true` → acknowledge kickoff context, read `KICKOFF_DOC`, extract (Core Problem, User Value, Decisions Made, Remaining Ambiguities, Key Code Refs), **SKIP to Step 3**
|
|
16
|
-
- **Else If** ARGUMENTS empty → probe for context enthusiastically
|
|
17
|
-
- **Else** → proceed to Step 2
|
|
18
|
-
- **CRITICAL**: No tool calls except reading kickoff doc when FROM_KICKOFF=true
|
|
19
|
-
|
|
20
|
-
## Step 2: Interactive Scope Exploration
|
|
21
|
-
|
|
22
|
-
**SKIP IF FROM_KICKOFF=true**
|
|
23
|
-
|
|
24
|
-
- **Action** — ExploreScope: Collaborative dialogue focused on boundaries and user experience.
|
|
25
|
-
|
|
26
|
-
**CRITICAL**: Focus on WHAT, not HOW. Defer all technical/implementation questions until scope boundaries are finalized. Only ask technical questions if the scope itself is inherently technical (e.g., "migrate database from X to Y").
|
|
27
|
-
|
|
28
|
-
**PATTERN**: Lead with a rich initial exploration. In your FIRST response, propose concrete hypotheses AND ask 5-8 questions across multiple dimensions. Give the user a full landscape to react to, not a single thread to follow.
|
|
29
|
-
|
|
30
|
-
**FIRST RESPONSE FORMAT**:
|
|
31
|
-
|
|
32
|
-
> Here's my initial read on this, plus questions to help us explore the bounds:
|
|
33
|
-
>
|
|
34
|
-
> **My hypothesis**: [problem statement, who it affects, proposed IN/OUT]
|
|
35
|
-
>
|
|
36
|
-
> **Questions to explore**:
|
|
37
|
-
> 1. [User problem question]
|
|
38
|
-
> 2. [UX flow question]
|
|
39
|
-
> 3. [Boundary edge question]
|
|
40
|
-
> 4. [Alternative approach question]
|
|
41
|
-
> 5. [Success criteria question]
|
|
42
|
-
> 6. [Edge case question]
|
|
43
|
-
> ...
|
|
44
|
-
>
|
|
45
|
-
> Answer any/all that spark thoughts. Skip what's obvious.
|
|
46
|
-
|
|
47
|
-
**Question types to include** (aim for 5-8 total, mix from these):
|
|
48
|
-
|
|
49
|
-
- **User & Problem**: Who feels this most? What triggers the need? What's the cost of not solving it?
|
|
50
|
-
- **UX & Feel**: Should this feel fast or thorough? Guided or flexible? What's the ideal flow?
|
|
51
|
-
- **Boundaries**: What about [adjacent thing]—IN or OUT? If unlimited time, what else? What's essential for v1?
|
|
52
|
-
- **Alternatives**: I could see this as [A] or [B]. Which direction?
|
|
53
|
-
- **Edge cases**: What happens when [unusual situation]? Should we handle [error state]?
|
|
54
|
-
- **Success**: What makes you say "this shipped well"? What's the one thing we can't get wrong?
|
|
55
|
-
|
|
56
|
-
**DO NOT ask about**: implementation approach, technical trade-offs, architecture, or integration details until boundaries are confirmed.
|
|
57
|
-
|
|
58
|
-
- **Action** — IterateBoundaries: After user responds, refine boundaries and ask targeted follow-ups on gaps.
|
|
59
|
-
|
|
60
|
-
> **Current Boundaries**: ✅ **IN**: \[list\] ❌ **OUT**: \[list\] ⚠️ **Unsure**: \[edge cases\]
|
|
61
|
-
>
|
|
62
|
-
> Any items to move? Add exclusions? Clarify edges? Reply 'looks good' to continue.
|
|
63
|
-
|
|
64
|
-
- **Wait** — User confirms Scope boundaries are accurate
|
|
65
|
-
|
|
66
|
-
## Step 3: Generate Targeted Clarifications
|
|
67
|
-
|
|
68
|
-
- **Action** — DetermineOutputDir:
|
|
69
|
-
|
|
70
|
-
- **If** FROM_KICKOFF → use same dir as kickoff doc
|
|
71
|
-
- **Else** → `OUT_DIR = user_specified || docs/tasks/{branch_name}`
|
|
72
|
-
- `mkdir -p "$OUT_DIR"`
|
|
73
|
-
|
|
74
|
-
- **Action** — GenerateTargetedQuestions: Create 3-6 questions based ONLY on remaining scope ambiguities from Step 2 (or kickoff's "Remaining Ambiguities").
|
|
75
|
-
|
|
76
|
-
**CRITICAL**: Only ask about unresolved scope ambiguities. Technical questions (architecture, trade-offs, integration) belong in `/spectre:plan`, not here.
|
|
77
|
-
|
|
78
|
-
- **Action** — SaveClarifications: Create `{OUT_DIR}/clarifications/scope_clarifications_{timestamp}.md`:
|
|
79
|
-
|
|
80
|
-
- Header: concept name, confirmed boundaries so far
|
|
81
|
-
- Questions: Each focused on a scope edge case with `<response></response>` block
|
|
82
|
-
|
|
83
|
-
- **Action** — PromptUser: "Saved clarifications to `{path}`. Answer in `<response>` blocks. Reply 'Read it' when ready."
|
|
84
|
-
|
|
85
|
-
- **Wait** — User replies
|
|
86
|
-
|
|
87
|
-
- **Action** — ReadClarifications: Read file, use responses (proceed with assumptions if empty)
|
|
88
|
-
|
|
89
|
-
## Step 4: Create Scope Document
|
|
90
|
-
|
|
91
|
-
- **Action** — CreateScopeDoc: Generate `{OUT_DIR}/concepts/scope.md` (use scoped filename if exists).
|
|
92
|
-
|
|
93
|
-
**Priority**: User value and boundaries BEFORE technical details.
|
|
94
|
-
|
|
95
|
-
**Sections**: The Problem (pain, impact, current state) → Target Users (primary, secondary, needs) → Success Criteria (outcomes, metrics) → User Experience (journeys, principles, trade-offs) → Scope Boundaries (in/out/maybe/future) → Constraints (platform, perf, a11y, scale) → Integration (touches, avoids, dependencies) → Decisions (from clarifications + rationale) → Risks (UX, scope creep, open questions) → Next Steps (`/spectre:plan` or `/spectre:create_tasks`, complexity S/M/L)
|
|
96
|
-
|
|
97
|
-
## Step 5: Light Technical Context (Optional)
|
|
98
|
-
|
|
99
|
-
**Only if scope identifies specific technical/architecture integration points.**
|
|
100
|
-
|
|
101
|
-
- **Action** — IdentifyTouchpoints: Identify desired areas of research, and dispatch parallel @analyst subagents to research each area. Surface-level only (component names, NOT implementation). List features this interacts with, constraints worth documenting, areas to avoid.
|
|
102
|
-
|
|
103
|
-
- **Action** — UpdateScopeDoc: Add findings to Integration & Constraints sections if relevant.
|
|
104
|
-
|
|
105
|
-
## Step 6: Final Review & Next Steps
|
|
106
|
-
|
|
107
|
-
- **Action** — PresentDocForReview: Show final boundaries and next steps together.
|
|
108
|
-
|
|
109
|
-
> **Scope Complete**: `{OUT_DIR}/concepts/scope.md`
|
|
110
|
-
>
|
|
111
|
-
> **Final Boundaries**: ✅ **IN**: \[from doc\] ❌ **OUT**: \[from doc\] ⚠️ **Maybe/Future**: \[from doc\]
|
|
112
|
-
>
|
|
113
|
-
> Docs saved: `{OUT_DIR}/concepts/scope.md`, `{OUT_DIR}/clarifications/scope_clarifications_{timestamp}.md`
|
|
114
|
-
>
|
|
115
|
-
> Reply with any edits, updates, or clarifications — otherwise pick a next step:
|
|
116
|
-
|
|
117
|
-
- **Action** — RenderFooter: Render Next Steps using `@skill-spectre:spectre-guide` skill.
|
|
118
|
-
|
|
119
|
-
> **NOTE**: Do NOT wait for explicit approval. Present next steps immediately inline with the review. User can reply with scope edits OR jump straight into a next step command. If user replies with edits, apply them to the scope doc and re-present.
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Define the User Flows and generate a UX Spec - primary agent
|
|
3
|
-
---
|
|
4
|
-
# ux_spec: Define Exactly How the Feature Works
|
|
5
|
-
|
|
6
|
-
Transform product requirements into a definitive behavioral specification. Two stages: align on user flows, then generate detailed spec. Output: `ux.md` ready for implementation.
|
|
7
|
-
|
|
8
|
-
<ARGUMENTS> $ARGUMENTS </ARGUMENTS>
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# STAGE 1: Flow Discovery & Alignment
|
|
13
|
-
|
|
14
|
-
**Goal**: Align on HOW the feature works before specifying details.
|
|
15
|
-
|
|
16
|
-
## Step 1 — Understand the Feature
|
|
17
|
-
|
|
18
|
-
1. **Read requirements**: `docs/tasks/{branch}/task_summary.md` and `docs/tasks/{branch}/specs/prd.md`
|
|
19
|
-
2. **Research patterns**: Find existing screens/components similar to what we're building, note conventions
|
|
20
|
-
3. **Identify journeys**: List user goals, entry points, and completion states
|
|
21
|
-
|
|
22
|
-
## Step 2 — Present User Flows
|
|
23
|
-
|
|
24
|
-
Write each flow as a narrative walkthrough:
|
|
25
|
-
|
|
26
|
-
**Per flow include**: Goal, Entry point, Journey steps (User sees → User does → System responds), Decision points with branches, Success state, Questions where ambiguity exists
|
|
27
|
-
|
|
28
|
-
After writing all flows, prompt:
|
|
29
|
-
|
|
30
|
-
> **User Flows for Review**
|
|
31
|
-
>
|
|
32
|
-
> I've mapped {N} flows: {list with one-line summaries}
|
|
33
|
-
>
|
|
34
|
-
> Please review: Are these the right flows? Any missing? Do journeys feel right? Answer flagged questions.
|
|
35
|
-
>
|
|
36
|
-
> Reply with feedback, or **"Flows approved"** to proceed.
|
|
37
|
-
|
|
38
|
-
**Wait for approval. If feedback → revise and re-present. If approved → Stage 2.**
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
# STAGE 2: Detailed Specification
|
|
43
|
-
|
|
44
|
-
**Gate**: Only proceed after explicit flow approval.
|
|
45
|
-
|
|
46
|
-
## Step 3 — Clarify Remaining Details
|
|
47
|
-
|
|
48
|
-
Review approved flows for gaps: component behaviors, edge cases, state definitions.
|
|
49
|
-
|
|
50
|
-
If significant gaps, ask 3-5 targeted questions (empty states, error handling, loading, limits). Save to `clarifications/ux_clarifications_{timestamp}.md`, prompt user to read, incorporate answers.
|
|
51
|
-
|
|
52
|
-
## Step 4 — Write the Specification
|
|
53
|
-
|
|
54
|
-
Generate complete spec with these sections:
|
|
55
|
-
|
|
56
|
-
### Required Sections
|
|
57
|
-
|
|
58
|
-
1. **Overview** — What this feature is, problem it solves, primary user goal (1 paragraph)
|
|
59
|
-
2. **Screens** — Every screen: name, purpose (1 line), navigation relationships
|
|
60
|
-
3. **Flows** — Formalized from Stage 1 with alternate paths (validation fail, cancel, network error)
|
|
61
|
-
4. **Layouts** — Per screen: header/main/footer structure + responsive behavior (desktop >1024, tablet 768-1024, mobile <768)
|
|
62
|
-
5. **Components** — Each interactive element: purpose, location, states (default, hover, active, disabled, loading, error)
|
|
63
|
-
6. **Interactions** — Table format: Element | Action | Result (exhaustive)
|
|
64
|
-
7. **States** — Table format: State | Trigger | Appearance | Available Actions (empty, loading, loaded, error)
|
|
65
|
-
8. **Content** — Exact copy: page titles, buttons, empty states, error messages, confirmation dialogs
|
|
66
|
-
9. **Edge Cases** — Limits/boundaries, null/long data handling, permissions, offline/network failures
|
|
67
|
-
10. **Accessibility** — Tab order, keyboard actions (Enter/Space/Escape), screen reader announcements, focus management
|
|
68
|
-
|
|
69
|
-
Save to `docs/tasks/{branch}/ux.md`
|
|
70
|
-
|
|
71
|
-
Prompt:
|
|
72
|
-
|
|
73
|
-
> **UX Specification Complete**
|
|
74
|
-
>
|
|
75
|
-
> Written to `{path}`. Please review: Any behaviors wrong or missing? Edge cases not covered?
|
|
76
|
-
>
|
|
77
|
-
> Reply with feedback, or **"Approved"** to finalize.
|
|
78
|
-
|
|
79
|
-
**Wait for approval.**
|
|
80
|
-
|
|
81
|
-
## Step 5 — Handoff
|
|
82
|
-
|
|
83
|
-
Confirm completion with summary: screens specified, flows documented, components with states, edge cases and accessibility covered.
|
|
84
|
-
|
|
85
|
-
Read `.spectre/next_steps_guide.md` and render Next Steps footer:
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
Next Steps | Phase: Scope | Status: UX Complete
|
|
89
|
-
Recommendation: {contextual next action}
|
|
90
|
-
Options: /create_plan, /create_tasks, /tdd
|
|
91
|
-
```
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* load-knowledge.cjs
|
|
6
|
-
*
|
|
7
|
-
* SessionStart hook that injects the apply skill content with embedded registry
|
|
8
|
-
* directly into Claude's context.
|
|
9
|
-
*
|
|
10
|
-
* Reads:
|
|
11
|
-
* - Apply skill from plugin: skills/spectre-apply/SKILL.md
|
|
12
|
-
* - Registry from project: .claude/skills/spectre-recall/references/registry.toon
|
|
13
|
-
*
|
|
14
|
-
* Combines them by replacing the Registry Location section with actual registry content.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
|
|
20
|
-
function countRegistryEntries(lines) {
|
|
21
|
-
let count = 0;
|
|
22
|
-
for (const line of lines) {
|
|
23
|
-
if (line.trim() && line.includes('|') && !line.startsWith('#')) {
|
|
24
|
-
count++;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return count;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function stripFrontmatter(content) {
|
|
31
|
-
if (content.startsWith('---')) {
|
|
32
|
-
const end = content.indexOf('---', 3);
|
|
33
|
-
if (end !== -1) {
|
|
34
|
-
return content.slice(end + 3).trim();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return content;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function main() {
|
|
41
|
-
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
42
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || '';
|
|
43
|
-
|
|
44
|
-
const applySkillPath = path.join(pluginRoot, 'skills', 'spectre-apply', 'SKILL.md');
|
|
45
|
-
|
|
46
|
-
if (!fs.existsSync(applySkillPath)) {
|
|
47
|
-
process.exit(0);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Paths - check new name first, fall back to old names for migration
|
|
51
|
-
let registryPath = path.join(projectDir, '.claude', 'skills', 'spectre-recall', 'references', 'registry.toon');
|
|
52
|
-
const oldRegistryPath = path.join(projectDir, '.claude', 'skills', 'spectre-find', 'references', 'registry.toon');
|
|
53
|
-
|
|
54
|
-
// Support old "spectre-find" path for projects that haven't migrated
|
|
55
|
-
if (!fs.existsSync(registryPath) && fs.existsSync(oldRegistryPath)) {
|
|
56
|
-
registryPath = oldRegistryPath;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Read registry if it exists
|
|
60
|
-
let registryContent = '';
|
|
61
|
-
let entryCount = 0;
|
|
62
|
-
if (fs.existsSync(registryPath)) {
|
|
63
|
-
registryContent = fs.readFileSync(registryPath, 'utf8').trim();
|
|
64
|
-
const lines = registryContent ? registryContent.split('\n') : [];
|
|
65
|
-
entryCount = countRegistryEntries(lines);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Read apply skill and strip frontmatter
|
|
69
|
-
let applyContent = fs.readFileSync(applySkillPath, 'utf8');
|
|
70
|
-
applyContent = stripFrontmatter(applyContent);
|
|
71
|
-
|
|
72
|
-
// Replace the Registry Location section with embedded registry or empty notice
|
|
73
|
-
let registrySection;
|
|
74
|
-
if (entryCount > 0) {
|
|
75
|
-
registrySection =
|
|
76
|
-
'## Registry\n\n' +
|
|
77
|
-
'**Format**: `skill-name|category|triggers|description`\n\n' +
|
|
78
|
-
'```\n' +
|
|
79
|
-
registryContent + '\n' +
|
|
80
|
-
'```\n\n' +
|
|
81
|
-
'Each entry corresponds to a skill that can be loaded via `Skill({skill-name})`\n\n' +
|
|
82
|
-
'**Categories:** feature, gotchas, patterns, decisions, procedures, integration, performance, testing, ux, strategy';
|
|
83
|
-
} else {
|
|
84
|
-
registrySection =
|
|
85
|
-
'## Registry\n\n' +
|
|
86
|
-
'No knowledge has been captured for this project yet. The behavioral rules in this document still apply.\n\n' +
|
|
87
|
-
'To capture knowledge from this session, use `/spectre:learn` after completing significant work.\n\n' +
|
|
88
|
-
'**Categories:** feature, gotchas, patterns, decisions, procedures, integration, performance, testing, ux, strategy';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Replace the Registry Location section
|
|
92
|
-
applyContent = applyContent.replace(
|
|
93
|
-
/## Registry Location[\s\S]*?(?=## Workflow)/,
|
|
94
|
-
registrySection + '\n\n'
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
// Build final context
|
|
98
|
-
const context = `<spectre-knowledge>\n${applyContent}\n</spectre-knowledge>`;
|
|
99
|
-
|
|
100
|
-
// Visible notice
|
|
101
|
-
let visibleNotice;
|
|
102
|
-
if (entryCount > 0) {
|
|
103
|
-
visibleNotice = `\ud83d\udc7b spectre: ${entryCount} knowledge skills available`;
|
|
104
|
-
} else {
|
|
105
|
-
visibleNotice = '\ud83d\udc7b spectre: ready \u2014 capture knowledge with /spectre:learn';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const output = {
|
|
109
|
-
systemMessage: visibleNotice,
|
|
110
|
-
hookSpecificOutput: {
|
|
111
|
-
hookEventName: 'SessionStart',
|
|
112
|
-
additionalContext: context
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
117
|
-
process.exit(0);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
main();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* precompact-warning.cjs
|
|
6
|
-
*
|
|
7
|
-
* PreCompact hook that suggests using /spectre:handoff + /clear
|
|
8
|
-
* instead of auto-compact for better context continuity.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const output = {
|
|
12
|
-
systemMessage:
|
|
13
|
-
'\u26a0\ufe0f Auto-compact can cause context loss. ' +
|
|
14
|
-
'For full continuity: /spectre:handoff \u2192 /clear \u2192 new session. ' +
|
|
15
|
-
'Consider disabling auto-compact in /config.'
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
19
|
-
process.exit(0);
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* register_learning.cjs
|
|
6
|
-
*
|
|
7
|
-
* Registers a spectre learning and manages the project-level recall skill.
|
|
8
|
-
*
|
|
9
|
-
* Responsibilities:
|
|
10
|
-
* 1. Create/update registry at .claude/skills/spectre-recall/references/registry.toon
|
|
11
|
-
* 2. Read recall-template.md from plugin
|
|
12
|
-
* 3. Generate .claude/skills/spectre-recall/SKILL.md with embedded registry
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* node register_learning.cjs \
|
|
16
|
-
* --project-root "/path/to/project" \
|
|
17
|
-
* --skill-name "feature-my-feature" \
|
|
18
|
-
* --category "feature" \
|
|
19
|
-
* --triggers "keyword1, keyword2" \
|
|
20
|
-
* --description "Use when doing X or Y"
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const path = require('path');
|
|
25
|
-
|
|
26
|
-
function getRegistryHeader() {
|
|
27
|
-
return [
|
|
28
|
-
'# SPECTRE Knowledge Registry',
|
|
29
|
-
'# Format: skill-name|category|triggers|description',
|
|
30
|
-
''
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function updateRegistry(registryPath, entry, skillName) {
|
|
35
|
-
const entryPrefix = skillName + '|';
|
|
36
|
-
let lines;
|
|
37
|
-
|
|
38
|
-
if (fs.existsSync(registryPath)) {
|
|
39
|
-
const content = fs.readFileSync(registryPath, 'utf8').trim();
|
|
40
|
-
lines = content ? content.split('\n') : [];
|
|
41
|
-
} else {
|
|
42
|
-
lines = getRegistryHeader();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let entryExists = false;
|
|
46
|
-
const updatedLines = [];
|
|
47
|
-
|
|
48
|
-
for (const line of lines) {
|
|
49
|
-
if (line.startsWith(entryPrefix)) {
|
|
50
|
-
updatedLines.push(entry);
|
|
51
|
-
entryExists = true;
|
|
52
|
-
} else {
|
|
53
|
-
updatedLines.push(line);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!entryExists) {
|
|
58
|
-
updatedLines.push(entry);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let content = updatedLines.join('\n');
|
|
62
|
-
if (!content.endsWith('\n')) {
|
|
63
|
-
content += '\n';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fs.writeFileSync(registryPath, content);
|
|
67
|
-
return content;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function generateFindSkill(findSkillPath, templatePath, registryContent) {
|
|
71
|
-
if (!fs.existsSync(templatePath)) {
|
|
72
|
-
process.stderr.write(`Warning: Template not found at ${templatePath}\n`);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const template = fs.readFileSync(templatePath, 'utf8');
|
|
77
|
-
const skillContent = template.replace('{{REGISTRY}}', registryContent.trim());
|
|
78
|
-
|
|
79
|
-
fs.mkdirSync(path.dirname(findSkillPath), { recursive: true });
|
|
80
|
-
fs.writeFileSync(findSkillPath, skillContent);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function parseArgs(argv) {
|
|
84
|
-
const args = {};
|
|
85
|
-
const flags = ['--project-root', '--skill-name', '--category', '--triggers', '--description'];
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < argv.length; i++) {
|
|
88
|
-
if (flags.includes(argv[i]) && i + 1 < argv.length) {
|
|
89
|
-
// Convert --project-root to projectRoot
|
|
90
|
-
const key = argv[i].slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
91
|
-
args[key] = argv[i + 1];
|
|
92
|
-
i++;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return args;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function main() {
|
|
100
|
-
const args = parseArgs(process.argv.slice(2));
|
|
101
|
-
|
|
102
|
-
const required = ['projectRoot', 'skillName', 'category', 'triggers', 'description'];
|
|
103
|
-
for (const key of required) {
|
|
104
|
-
if (!args[key]) {
|
|
105
|
-
process.stderr.write(`Error: missing required argument --${key.replace(/[A-Z]/g, c => '-' + c.toLowerCase())}\n`);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const projectRoot = args.projectRoot;
|
|
111
|
-
|
|
112
|
-
// New paths: registry lives inside spectre-recall skill
|
|
113
|
-
const recallDir = path.join(projectRoot, '.claude', 'skills', 'spectre-recall');
|
|
114
|
-
const registryDir = path.join(recallDir, 'references');
|
|
115
|
-
const registryPath = path.join(registryDir, 'registry.toon');
|
|
116
|
-
const recallSkillPath = path.join(recallDir, 'SKILL.md');
|
|
117
|
-
|
|
118
|
-
// Template is in the plugin — resolve via env var (hooks) or __filename (manual invocation)
|
|
119
|
-
let pluginRoot;
|
|
120
|
-
if (process.env.CLAUDE_PLUGIN_ROOT) {
|
|
121
|
-
pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
122
|
-
} else {
|
|
123
|
-
// Fallback: resolve relative to this script
|
|
124
|
-
// Script is at: <plugin_root>/hooks/scripts/register_learning.cjs
|
|
125
|
-
pluginRoot = path.resolve(__dirname, '..', '..');
|
|
126
|
-
}
|
|
127
|
-
const templatePath = path.join(pluginRoot, 'skills', 'spectre-learn', 'references', 'recall-template.md');
|
|
128
|
-
|
|
129
|
-
// Ensure directories exist
|
|
130
|
-
fs.mkdirSync(registryDir, { recursive: true });
|
|
131
|
-
|
|
132
|
-
// Build the registry entry
|
|
133
|
-
const entry = `${args.skillName}|${args.category}|${args.triggers}|${args.description}`;
|
|
134
|
-
|
|
135
|
-
// Update registry and get full content
|
|
136
|
-
const registryContent = updateRegistry(registryPath, entry, args.skillName);
|
|
137
|
-
|
|
138
|
-
// Generate recall skill with embedded registry
|
|
139
|
-
generateFindSkill(recallSkillPath, templatePath, registryContent);
|
|
140
|
-
|
|
141
|
-
process.stdout.write(`Registered: ${entry}\n`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
main();
|