@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,104 @@
|
|
|
1
|
+
name = "web_research"
|
|
2
|
+
description = "Do you find yourself desiring information that you don't quite feel well-trained (confident) on? Information that is modern and potentially only discoverable on the web? Use the researcher subagent_type today to find any and all answers to your questions! It will research deeply to figure out and attempt to answer your questions! If you aren't immediately satisfied you can get your money back! (Not really - but you can re-run researcher with an altered prompt in the event you're not satisfied the first time)"
|
|
3
|
+
sandbox_mode = "read-only"
|
|
4
|
+
developer_instructions = """You are an expert web research specialist focused on finding accurate, relevant information from web sources. Your primary tools are WebSearch and WebFetch, which you use to discover and retrieve information based on user queries.
|
|
5
|
+
|
|
6
|
+
## Core Responsibilities
|
|
7
|
+
|
|
8
|
+
When you receive a research query, you will:
|
|
9
|
+
|
|
10
|
+
1. **Analyze the Query**: Break down the user's request to identify:
|
|
11
|
+
- Key search terms and concepts
|
|
12
|
+
- Types of sources likely to have answers (documentation, blogs, forums, academic papers)
|
|
13
|
+
- Multiple search angles to ensure comprehensive coverage
|
|
14
|
+
|
|
15
|
+
2. **Execute Strategic Searches**:
|
|
16
|
+
- Start with broad searches to understand the landscape
|
|
17
|
+
- Refine with specific technical terms and phrases
|
|
18
|
+
- Use multiple search variations to capture different perspectives
|
|
19
|
+
- Include site-specific searches when targeting known authoritative sources (e.g., "site:docs.stripe.com webhook signature")
|
|
20
|
+
|
|
21
|
+
3. **Fetch and Analyze Content**:
|
|
22
|
+
- Use WebFetch to retrieve full content from promising search results
|
|
23
|
+
- Prioritize official documentation, reputable technical blogs, and authoritative sources
|
|
24
|
+
- Extract specific quotes and sections relevant to the query
|
|
25
|
+
- Note publication dates to ensure currency of information
|
|
26
|
+
|
|
27
|
+
4. **Synthesize Findings**:
|
|
28
|
+
- Organize information by relevance and authority
|
|
29
|
+
- Include exact quotes with proper attribution
|
|
30
|
+
- Provide direct links to sources
|
|
31
|
+
- Highlight any conflicting information or version-specific details
|
|
32
|
+
- Note any gaps in available information
|
|
33
|
+
|
|
34
|
+
## Search Strategies
|
|
35
|
+
|
|
36
|
+
### For API/Library Documentation:
|
|
37
|
+
- Search for official docs first: "[library name] official documentation [specific feature]"
|
|
38
|
+
- Look for changelog or release notes for version-specific information
|
|
39
|
+
- Find code examples in official repositories or trusted tutorials
|
|
40
|
+
|
|
41
|
+
### For Best Practices:
|
|
42
|
+
- Search for recent articles (include year in search when relevant)
|
|
43
|
+
- Look for content from recognized experts or organizations
|
|
44
|
+
- Cross-reference multiple sources to identify consensus
|
|
45
|
+
- Search for both "best practices" and "anti-patterns" to get full picture
|
|
46
|
+
|
|
47
|
+
### For Technical Solutions:
|
|
48
|
+
- Use specific error messages or technical terms in quotes
|
|
49
|
+
- Search Stack Overflow and technical forums for real-world solutions
|
|
50
|
+
- Look for GitHub issues and discussions in relevant repositories
|
|
51
|
+
- Find blog posts describing similar implementations
|
|
52
|
+
|
|
53
|
+
### For Comparisons:
|
|
54
|
+
- Search for "X vs Y" comparisons
|
|
55
|
+
- Look for migration guides between technologies
|
|
56
|
+
- Find benchmarks and performance comparisons
|
|
57
|
+
- Search for decision matrices or evaluation criteria
|
|
58
|
+
|
|
59
|
+
## Output Format
|
|
60
|
+
|
|
61
|
+
Structure your findings as:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
## Summary
|
|
65
|
+
[Brief overview of key findings]
|
|
66
|
+
|
|
67
|
+
## Detailed Findings
|
|
68
|
+
|
|
69
|
+
### [Topic/Source 1]
|
|
70
|
+
**Source**: [Name with link]
|
|
71
|
+
**Relevance**: [Why this source is authoritative/useful]
|
|
72
|
+
**Key Information**:
|
|
73
|
+
- Direct quote or finding (with link to specific section if possible)
|
|
74
|
+
- Another relevant point
|
|
75
|
+
|
|
76
|
+
### [Topic/Source 2]
|
|
77
|
+
[Continue pattern...]
|
|
78
|
+
|
|
79
|
+
## Additional Resources
|
|
80
|
+
- [Relevant link 1] - Brief description
|
|
81
|
+
- [Relevant link 2] - Brief description
|
|
82
|
+
|
|
83
|
+
## Gaps or Limitations
|
|
84
|
+
[Note any information that couldn't be found or requires further investigation]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Quality Guidelines
|
|
88
|
+
|
|
89
|
+
- **Accuracy**: Always quote sources accurately and provide direct links
|
|
90
|
+
- **Relevance**: Focus on information that directly addresses the user's query
|
|
91
|
+
- **Currency**: Note publication dates and version information when relevant
|
|
92
|
+
- **Authority**: Prioritize official sources, recognized experts, and peer-reviewed content
|
|
93
|
+
- **Completeness**: Search from multiple angles to ensure comprehensive coverage
|
|
94
|
+
- **Transparency**: Clearly indicate when information is outdated, conflicting, or uncertain
|
|
95
|
+
|
|
96
|
+
## Search Efficiency
|
|
97
|
+
|
|
98
|
+
- Start with 2-3 well-crafted searches before fetching content
|
|
99
|
+
- Fetch only the most promising 3-5 pages initially
|
|
100
|
+
- If initial results are insufficient, refine search terms and try again
|
|
101
|
+
- Use search operators effectively: quotes for exact phrases, minus for exclusions, site: for specific domains
|
|
102
|
+
- Consider searching in different forms: tutorials, documentation, Q&A sites, and discussion forums
|
|
103
|
+
|
|
104
|
+
Remember: You are the user's expert guide to web information. Be thorough but efficient, always cite your sources, and provide actionable information that directly addresses their needs. Think deeply as you work."""
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|clear|compact",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node ${CODEX_HOME}/spectre/hooks/scripts/bootstrap.mjs"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "node ${CODEX_HOME}/spectre/hooks/scripts/handoff-resume.mjs"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "command",
|
|
17
|
+
"command": "node ${CODEX_HOME}/spectre/hooks/scripts/load-knowledge.mjs"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
package/plugins/{spectre/hooks/scripts/bootstrap.cjs → spectre-codex/hooks/scripts/bootstrap.mjs}
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* bootstrap.
|
|
4
|
+
* bootstrap.mjs
|
|
6
5
|
*
|
|
7
6
|
* SessionStart hook that removes stale files from older plugin versions.
|
|
8
7
|
*
|
|
@@ -14,20 +13,25 @@
|
|
|
14
13
|
* Paths are relative to CLAUDE_PLUGIN_ROOT.
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { readStdinWithTimeout } from './lib.mjs';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
20
23
|
|
|
21
24
|
// ──────────────────────────────────────────────────────────────────
|
|
22
25
|
// Stale paths to remove (relative to CLAUDE_PLUGIN_ROOT)
|
|
23
26
|
// ──────────────────────────────────────────────────────────────────
|
|
24
27
|
|
|
25
28
|
const STALE_PATHS = [
|
|
26
|
-
// Python scripts replaced by
|
|
29
|
+
// Python scripts replaced by JS hook equivalents (v3.x migration)
|
|
27
30
|
'hooks/scripts/capture-todos.py',
|
|
28
31
|
'hooks/scripts/handoff-resume.py',
|
|
29
32
|
'hooks/scripts/load-knowledge.py',
|
|
30
33
|
'hooks/scripts/precompact-warning.py',
|
|
34
|
+
'hooks/scripts/precompact-warning.mjs',
|
|
31
35
|
'hooks/scripts/register_learning.py',
|
|
32
36
|
'hooks/scripts/test_handoff_resume.py',
|
|
33
37
|
'hooks/scripts/test_load_knowledge.py',
|
|
@@ -72,11 +76,7 @@ async function main() {
|
|
|
72
76
|
// Drain stdin so the hook system doesn't hang
|
|
73
77
|
await readStdinWithTimeout();
|
|
74
78
|
|
|
75
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
76
|
-
if (!pluginRoot) {
|
|
77
|
-
process.stdout.write(JSON.stringify({}) + '\n');
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
79
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..', '..');
|
|
80
80
|
|
|
81
81
|
const removed = cleanupStalePaths(pluginRoot);
|
|
82
82
|
|
|
@@ -91,9 +91,8 @@ async function main() {
|
|
|
91
91
|
process.exit(0);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
if (typeof module !== 'undefined') {
|
|
96
|
-
module.exports = { cleanupStalePaths, STALE_PATHS };
|
|
97
|
-
}
|
|
94
|
+
export { cleanupStalePaths, STALE_PATHS };
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(__filename)) {
|
|
97
|
+
main();
|
|
98
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* handoff-resume.
|
|
4
|
+
* handoff-resume.mjs
|
|
6
5
|
*
|
|
7
6
|
* SessionStart hook that injects context from the last /spectre:handoff.
|
|
8
7
|
* Consolidates the previous session-resume-hook.sh + format-resume-context.py.
|
|
@@ -15,17 +14,25 @@
|
|
|
15
14
|
* - --bg-copy-refs: Copy plugin references (fork #1)
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { fork } from 'node:child_process';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { readStdinWithTimeout, getGitBranch } from './lib.mjs';
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
function getPluginRoot() {
|
|
27
|
+
return process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..', '..');
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
// ──────────────────────────────────────────────────────────────────
|
|
24
31
|
// Plugin reference copying (background fork #1)
|
|
25
32
|
// ──────────────────────────────────────────────────────────────────
|
|
26
33
|
|
|
27
34
|
function copyPluginReferences() {
|
|
28
|
-
const pluginRoot =
|
|
35
|
+
const pluginRoot = getPluginRoot();
|
|
29
36
|
if (!pluginRoot) return;
|
|
30
37
|
|
|
31
38
|
const referencesSrc = path.join(pluginRoot, 'references');
|
|
@@ -198,14 +205,7 @@ function formatContext(data, opts) {
|
|
|
198
205
|
const checkboxTree = (beadsAvailable && tasks.length) ? buildCheckboxTree(tasks) : '';
|
|
199
206
|
|
|
200
207
|
// User-visible notice
|
|
201
|
-
const
|
|
202
|
-
'',
|
|
203
|
-
'\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2588\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2580\u2588\u2580\u2591\u2588\u2580\u2584\u2591\u2588\u2580\u2580',
|
|
204
|
-
'\u2591\u2580\u2580\u2588\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2588\u2591\u2591\u2591\u2591\u2588\u2591\u2591\u2588\u2580\u2584\u2591\u2588\u2580\u2580',
|
|
205
|
-
'\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2591\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2591\u2580\u2591\u2591\u2580\u2591\u2580\u2591\u2580\u2580\u2580'
|
|
206
|
-
].join('\n');
|
|
207
|
-
|
|
208
|
-
const noticeLines = [asciiBanner];
|
|
208
|
+
const noticeLines = ['\ud83d\udc7b SPECTRE'];
|
|
209
209
|
noticeLines.push(`\n\ud83d\udd04 Session Resumed: ${taskName} | Branch: ${branchName}`);
|
|
210
210
|
|
|
211
211
|
if (goal) {
|
|
@@ -353,12 +353,7 @@ async function main() {
|
|
|
353
353
|
|
|
354
354
|
if (!latestHandoff) {
|
|
355
355
|
// No session to resume - show welcome banner with tips
|
|
356
|
-
const
|
|
357
|
-
'',
|
|
358
|
-
'\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2588\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2580\u2588\u2580\u2591\u2588\u2580\u2584\u2591\u2588\u2580\u2580',
|
|
359
|
-
'\u2591\u2580\u2580\u2588\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2588\u2591\u2591\u2591\u2591\u2588\u2591\u2591\u2588\u2580\u2584\u2591\u2588\u2580\u2580',
|
|
360
|
-
'\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2591\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2591\u2580\u2591\u2591\u2580\u2591\u2580\u2591\u2580\u2580\u2580'
|
|
361
|
-
].join('\n');
|
|
356
|
+
const banner = '\ud83d\udc7b SPECTRE';
|
|
362
357
|
const tips = [
|
|
363
358
|
'',
|
|
364
359
|
'Getting Started with SPECTRE:',
|
|
@@ -371,7 +366,7 @@ async function main() {
|
|
|
371
366
|
].join('\n');
|
|
372
367
|
|
|
373
368
|
const welcome = {
|
|
374
|
-
systemMessage:
|
|
369
|
+
systemMessage: banner + '\n' + tips
|
|
375
370
|
};
|
|
376
371
|
process.stdout.write(JSON.stringify(welcome) + '\n');
|
|
377
372
|
process.exit(0);
|
|
@@ -402,9 +397,8 @@ async function main() {
|
|
|
402
397
|
process.exit(0);
|
|
403
398
|
}
|
|
404
399
|
|
|
405
|
-
|
|
406
|
-
if (typeof module !== 'undefined') {
|
|
407
|
-
module.exports = { copyPluginReferences, formatContext, buildCheckboxTree };
|
|
408
|
-
}
|
|
400
|
+
export { copyPluginReferences, formatContext, buildCheckboxTree };
|
|
409
401
|
|
|
410
|
-
|
|
402
|
+
if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(__filename)) {
|
|
403
|
+
main();
|
|
404
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* lib.
|
|
4
|
+
* lib.mjs
|
|
6
5
|
*
|
|
7
6
|
* Shared utilities for spectre hook scripts.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
11
10
|
|
|
12
11
|
const STDIN_TIMEOUT = 2000; // milliseconds
|
|
13
12
|
|
|
@@ -80,4 +79,4 @@ function getGitBranch(cwd) {
|
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
export { readStdinWithTimeout, getGitBranch, STDIN_TIMEOUT };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* load-knowledge.mjs
|
|
5
|
+
*
|
|
6
|
+
* SessionStart hook that refreshes the managed SPECTRE knowledge block in
|
|
7
|
+
* AGENTS.override.md and returns a short visible status line.
|
|
8
|
+
*
|
|
9
|
+
* Reads:
|
|
10
|
+
* - Apply skill from plugin: skills/apply/SKILL.md
|
|
11
|
+
* - Registry from project: .agents/skills/spectre-recall/references/registry.toon
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
function getPluginRoot() {
|
|
22
|
+
return process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..', '..');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolvePluginSkillPath(pluginRoot, skillName, ...parts) {
|
|
26
|
+
const candidates = [
|
|
27
|
+
path.join(pluginRoot, 'skills', skillName, ...parts),
|
|
28
|
+
path.join(pluginRoot, '..', 'skills', skillName, ...parts),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
if (fs.existsSync(candidate)) {
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return candidates[0];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function skillBaseDir(projectDir) {
|
|
41
|
+
const agentsDir = path.join(projectDir, '.agents', 'skills');
|
|
42
|
+
if (fs.existsSync(agentsDir)) return agentsDir;
|
|
43
|
+
return path.join(projectDir, '.claude', 'skills');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function countRegistryEntries(lines) {
|
|
47
|
+
let count = 0;
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (line.trim() && line.includes('|') && !line.startsWith('#')) {
|
|
50
|
+
count++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return count;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function stripFrontmatter(content) {
|
|
57
|
+
if (content.startsWith('---')) {
|
|
58
|
+
const end = content.indexOf('---', 3);
|
|
59
|
+
if (end !== -1) {
|
|
60
|
+
return content.slice(end + 3).trim();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return content;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizeOverrideFile(content) {
|
|
67
|
+
return content
|
|
68
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
69
|
+
.trim();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function escapeRegExp(value) {
|
|
73
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function managedOverridePattern(startMarker, endMarker) {
|
|
77
|
+
return new RegExp(`\\n?${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}\\n?`, 'm');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeManagedOverride(overridePath, startMarker, endMarker, bodyContent) {
|
|
81
|
+
const current = fs.existsSync(overridePath) ? fs.readFileSync(overridePath, 'utf8') : '';
|
|
82
|
+
const pattern = managedOverridePattern(startMarker, endMarker);
|
|
83
|
+
const blockContent = `${startMarker}\n${bodyContent}\n${endMarker}`;
|
|
84
|
+
let updated;
|
|
85
|
+
|
|
86
|
+
if (pattern.test(current)) {
|
|
87
|
+
updated = current.replace(pattern, `${blockContent}\n`);
|
|
88
|
+
} else if (current.trim()) {
|
|
89
|
+
updated = `${current.trimEnd()}\n\n${blockContent}\n`;
|
|
90
|
+
} else {
|
|
91
|
+
updated = `${blockContent}\n`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const normalized = normalizeOverrideFile(updated);
|
|
95
|
+
fs.writeFileSync(overridePath, normalized ? `${normalized}\n` : '');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasProjectKnowledgeSurface(projectDir, registryPath) {
|
|
99
|
+
const overridePath = path.join(projectDir, 'AGENTS.override.md');
|
|
100
|
+
const overrideContent = fs.existsSync(overridePath) ? fs.readFileSync(overridePath, 'utf8') : '';
|
|
101
|
+
return fs.existsSync(path.join(projectDir, '.spectre', 'manifest.json'))
|
|
102
|
+
|| fs.existsSync(registryPath)
|
|
103
|
+
|| overrideContent.includes('<!-- spectre-knowledge:start -->')
|
|
104
|
+
|| overrideContent.includes('<!-- spectre-session:start -->');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildKnowledgeOverrideBody(applyContent) {
|
|
108
|
+
return [
|
|
109
|
+
'## SPECTRE Knowledge Context',
|
|
110
|
+
'',
|
|
111
|
+
'This block is managed by SPECTRE and replaced automatically on session start.',
|
|
112
|
+
'Use it before searching or implementing work in this repository.',
|
|
113
|
+
'',
|
|
114
|
+
applyContent.trim()
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function main() {
|
|
119
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
120
|
+
const pluginRoot = getPluginRoot();
|
|
121
|
+
|
|
122
|
+
const applySkillPath = resolvePluginSkillPath(pluginRoot, 'apply', 'SKILL.md');
|
|
123
|
+
|
|
124
|
+
if (!fs.existsSync(applySkillPath)) {
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Paths - check new name first, fall back to old names for migration
|
|
129
|
+
let registryPath = path.join(skillBaseDir(projectDir), 'spectre-recall', 'references', 'registry.toon');
|
|
130
|
+
const oldRegistryPath = path.join(projectDir, '.claude', 'skills', 'spectre-find', 'references', 'registry.toon');
|
|
131
|
+
|
|
132
|
+
// Support old "spectre-find" path for projects that haven't migrated
|
|
133
|
+
if (!fs.existsSync(registryPath) && fs.existsSync(oldRegistryPath)) {
|
|
134
|
+
registryPath = oldRegistryPath;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Read registry if it exists
|
|
138
|
+
let registryContent = '';
|
|
139
|
+
let entryCount = 0;
|
|
140
|
+
if (fs.existsSync(registryPath)) {
|
|
141
|
+
registryContent = fs.readFileSync(registryPath, 'utf8').trim();
|
|
142
|
+
const lines = registryContent ? registryContent.split('\n') : [];
|
|
143
|
+
entryCount = countRegistryEntries(lines);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Read apply skill and strip frontmatter
|
|
147
|
+
let applyContent = fs.readFileSync(applySkillPath, 'utf8');
|
|
148
|
+
applyContent = stripFrontmatter(applyContent);
|
|
149
|
+
applyContent = applyContent.replaceAll('.claude/skills/', '.agents/skills/').replaceAll('/spectre:', '');
|
|
150
|
+
|
|
151
|
+
if (hasProjectKnowledgeSurface(projectDir, registryPath)) {
|
|
152
|
+
writeManagedOverride(
|
|
153
|
+
path.join(projectDir, 'AGENTS.override.md'),
|
|
154
|
+
'<!-- spectre-knowledge:start -->',
|
|
155
|
+
'<!-- spectre-knowledge:end -->',
|
|
156
|
+
buildKnowledgeOverrideBody(applyContent)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Visible notice
|
|
161
|
+
let visibleNotice;
|
|
162
|
+
if (entryCount > 0) {
|
|
163
|
+
visibleNotice = `\ud83d\udc7b spectre: ${entryCount} knowledge skills available`;
|
|
164
|
+
} else {
|
|
165
|
+
visibleNotice = '\ud83d\udc7b spectre: ready \u2014 capture knowledge with /spectre:learn';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const output = {
|
|
169
|
+
systemMessage: visibleNotice,
|
|
170
|
+
hookSpecificOutput: {
|
|
171
|
+
hookEventName: 'SessionStart'
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
process.stdout.write(JSON.stringify(output) + '\n');
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export {
|
|
180
|
+
buildKnowledgeOverrideBody,
|
|
181
|
+
countRegistryEntries,
|
|
182
|
+
hasProjectKnowledgeSurface,
|
|
183
|
+
resolvePluginSkillPath,
|
|
184
|
+
stripFrontmatter
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(__filename)) {
|
|
188
|
+
main();
|
|
189
|
+
}
|