@soleri/forge 5.2.0 → 5.5.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/dist/scaffolder.js +166 -3
- package/dist/scaffolder.js.map +1 -1
- package/dist/skills/brain-debrief.md +186 -0
- package/dist/skills/brainstorming.md +170 -0
- package/dist/skills/code-patrol.md +176 -0
- package/dist/skills/context-resume.md +143 -0
- package/dist/skills/executing-plans.md +201 -0
- package/dist/skills/fix-and-learn.md +164 -0
- package/dist/skills/health-check.md +225 -0
- package/dist/skills/knowledge-harvest.md +178 -0
- package/dist/skills/onboard-me.md +197 -0
- package/dist/skills/retrospective.md +189 -0
- package/dist/skills/second-opinion.md +142 -0
- package/dist/skills/systematic-debugging.md +230 -0
- package/dist/skills/test-driven-development.md +266 -0
- package/dist/skills/vault-capture.md +154 -0
- package/dist/skills/vault-navigator.md +129 -0
- package/dist/skills/verification-before-completion.md +170 -0
- package/dist/skills/writing-plans.md +207 -0
- package/dist/templates/claude-md-template.js +90 -1
- package/dist/templates/claude-md-template.js.map +1 -1
- package/dist/templates/domain-facade.d.ts +4 -0
- package/dist/templates/domain-facade.js +4 -0
- package/dist/templates/domain-facade.js.map +1 -1
- package/dist/templates/entry-point.js +32 -0
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/readme.js +38 -0
- package/dist/templates/readme.js.map +1 -1
- package/dist/templates/setup-script.js +52 -1
- package/dist/templates/setup-script.js.map +1 -1
- package/dist/templates/skills.d.ts +16 -0
- package/dist/templates/skills.js +73 -0
- package/dist/templates/skills.js.map +1 -0
- package/dist/templates/test-facades.js +173 -3
- package/dist/templates/test-facades.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/scaffolder.test.ts +115 -2
- package/src/scaffolder.ts +171 -3
- package/src/skills/brain-debrief.md +186 -0
- package/src/skills/brainstorming.md +170 -0
- package/src/skills/code-patrol.md +176 -0
- package/src/skills/context-resume.md +143 -0
- package/src/skills/executing-plans.md +201 -0
- package/src/skills/fix-and-learn.md +164 -0
- package/src/skills/health-check.md +225 -0
- package/src/skills/knowledge-harvest.md +178 -0
- package/src/skills/onboard-me.md +197 -0
- package/src/skills/retrospective.md +189 -0
- package/src/skills/second-opinion.md +142 -0
- package/src/skills/systematic-debugging.md +230 -0
- package/src/skills/test-driven-development.md +266 -0
- package/src/skills/vault-capture.md +154 -0
- package/src/skills/vault-navigator.md +129 -0
- package/src/skills/verification-before-completion.md +170 -0
- package/src/skills/writing-plans.md +207 -0
- package/src/templates/claude-md-template.ts +181 -0
- package/src/templates/domain-facade.ts +4 -0
- package/src/templates/entry-point.ts +32 -0
- package/src/templates/readme.ts +38 -0
- package/src/templates/setup-script.ts +54 -1
- package/src/templates/skills.ts +82 -0
- package/src/templates/test-facades.ts +173 -3
package/dist/templates/readme.js
CHANGED
|
@@ -80,6 +80,44 @@ ${domainRows}
|
|
|
80
80
|
|
|
81
81
|
${principleLines}
|
|
82
82
|
|
|
83
|
+
## Built-in Skills
|
|
84
|
+
|
|
85
|
+
${config.name} ships with 17 structured workflow skills, invocable via \`/<skill-name>\` in Claude Code:
|
|
86
|
+
|
|
87
|
+
**Development Workflows:**
|
|
88
|
+
|
|
89
|
+
| Skill | Description |
|
|
90
|
+
|-------|-------------|
|
|
91
|
+
| \`/test-driven-development\` | Red-green-refactor TDD workflow with vault-informed test patterns |
|
|
92
|
+
| \`/systematic-debugging\` | Root cause investigation — vault search, web search, then diagnose |
|
|
93
|
+
| \`/verification-before-completion\` | Evidence-based completion claims with system diagnostics |
|
|
94
|
+
| \`/brainstorming\` | Collaborative design exploration with vault + web research first |
|
|
95
|
+
| \`/writing-plans\` | Implementation plans with quality grading and auto-improvement |
|
|
96
|
+
| \`/executing-plans\` | Batch execution with loop tracking and plan reconciliation |
|
|
97
|
+
| \`/fix-and-learn\` | Fix bugs and capture root cause — vault search before any fix |
|
|
98
|
+
| \`/code-patrol\` | Review code against YOUR vault patterns, not generic lint rules |
|
|
99
|
+
|
|
100
|
+
**Knowledge Management:**
|
|
101
|
+
|
|
102
|
+
| Skill | Description |
|
|
103
|
+
|-------|-------------|
|
|
104
|
+
| \`/vault-navigator\` | Intelligent vault search — tags, domains, age reports, cross-project |
|
|
105
|
+
| \`/vault-capture\` | Persist knowledge with curator grooming and governance |
|
|
106
|
+
| \`/knowledge-harvest\` | Point at any doc/code — auto-extract patterns into vault |
|
|
107
|
+
| \`/brain-debrief\` | Intelligence report — strengths, gaps, cross-project insights |
|
|
108
|
+
|
|
109
|
+
**Magic UX:**
|
|
110
|
+
|
|
111
|
+
| Skill | Description |
|
|
112
|
+
|-------|-------------|
|
|
113
|
+
| \`/context-resume\` | "What was I working on?" — full context reconstruction |
|
|
114
|
+
| \`/second-opinion\` | Decision support from vault + brain + web combined |
|
|
115
|
+
| \`/retrospective\` | Data-driven sprint/week retro from actual metrics |
|
|
116
|
+
| \`/onboard-me\` | Instant project knowledge tour for newcomers |
|
|
117
|
+
| \`/health-check\` | Vault maintenance — duplicates, contradictions, stale entries |
|
|
118
|
+
|
|
119
|
+
Skills are installed to \`~/.claude/commands/\` during setup. Run \`./scripts/setup.sh\` to install or reinstall.
|
|
120
|
+
|
|
83
121
|
## Features
|
|
84
122
|
|
|
85
123
|
### Knowledge Vault
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readme.js","sourceRoot":"","sources":["../../src/templates/readme.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,oCAAoC,CAAC;SACtD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzE,OAAO,KAAK,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI;;EAExC,MAAM,CAAC,WAAW;;SAEX,MAAM,CAAC,IAAI;;;;EAIlB,MAAM,CAAC,IAAI;;;;;;;6EAOgE,MAAM,CAAC,IAAI;;;;;;KAMnF,MAAM,CAAC,EAAE;;;;;;;;eAQC,MAAM,CAAC,IAAI;;;aAGb,MAAM,CAAC,IAAI;;;;;;;uCAOe,MAAM,CAAC,IAAI;;KAE7C,MAAM,CAAC,IAAI;uBACO,MAAM,CAAC,IAAI;KAC7B,MAAM,CAAC,IAAI;;;;;;;;;;OAUT,MAAM,CAAC,EAAE;;;kCAGkB,MAAM,CAAC,EAAE;;;;;;;;;;;;EAYzC,UAAU;;;;EAIV,cAAc
|
|
1
|
+
{"version":3,"file":"readme.js","sourceRoot":"","sources":["../../src/templates/readme.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,oCAAoC,CAAC;SACtD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzE,OAAO,KAAK,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI;;EAExC,MAAM,CAAC,WAAW;;SAEX,MAAM,CAAC,IAAI;;;;EAIlB,MAAM,CAAC,IAAI;;;;;;;6EAOgE,MAAM,CAAC,IAAI;;;;;;KAMnF,MAAM,CAAC,EAAE;;;;;;;;eAQC,MAAM,CAAC,IAAI;;;aAGb,MAAM,CAAC,IAAI;;;;;;;uCAOe,MAAM,CAAC,IAAI;;KAE7C,MAAM,CAAC,IAAI;uBACO,MAAM,CAAC,IAAI;KAC7B,MAAM,CAAC,IAAI;;;;;;;;;;OAUT,MAAM,CAAC,EAAE;;;kCAGkB,MAAM,CAAC,EAAE;;;;;;;;;;;;EAYzC,UAAU;;;;EAIV,cAAc;;;;EAId,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiEC,MAAM,CAAC,EAAE;;;;;;;;;;sCAUe,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;cAejC,MAAM,CAAC,IAAI,8CAA8C,MAAM,CAAC,IAAI;gBAClE,MAAM,CAAC,IAAI;;;;eAIZ,MAAM,CAAC,IAAI;;yBAED,MAAM,CAAC,IAAI;;;EAGlC,MAAM,CAAC,IAAI;;;8EAGiE,MAAM,CAAC,IAAI;;;EAGvF,MAAM,CAAC,IAAI;;;+CAGkC,MAAM,CAAC,IAAI;;;EAGxD,MAAM,CAAC,IAAI;;;;;;;;2BAQc,MAAM,CAAC,IAAI;;;EAGpC,MAAM,CAAC,IAAI;;;;;;EAMX,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;EAgBX,MAAM,CAAC,IAAI;;;;0BAIa,MAAM,CAAC,IAAI;;;;;;EAMnC,MAAM,CAAC,IAAI;;;;;;;mJAOsI,MAAM,CAAC,IAAI;;;;;;;EAO5J,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CX,MAAM,CAAC,IAAI;;;;;sCAKyB,MAAM,CAAC,IAAI;;;2CAGN,MAAM,CAAC,IAAI;;;;EAIpD,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCZ,CAAC;AACF,CAAC"}
|
|
@@ -99,7 +99,58 @@ else
|
|
|
99
99
|
fi
|
|
100
100
|
fi
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
# Install skills to ~/.claude/commands/
|
|
103
|
+
SKILLS_DIR="$AGENT_DIR/skills"
|
|
104
|
+
COMMANDS_DIR="$HOME/.claude/commands"
|
|
105
|
+
|
|
106
|
+
if [ -d "$SKILLS_DIR" ]; then
|
|
107
|
+
echo ""
|
|
108
|
+
echo "Installing skills..."
|
|
109
|
+
mkdir -p "$COMMANDS_DIR"
|
|
110
|
+
skill_installed=0
|
|
111
|
+
skill_skipped=0
|
|
112
|
+
for skill_dir in "$SKILLS_DIR"/*/; do
|
|
113
|
+
[ -d "$skill_dir" ] || continue
|
|
114
|
+
skill_file="$skill_dir/SKILL.md"
|
|
115
|
+
[ -f "$skill_file" ] || continue
|
|
116
|
+
skill_name="$(basename "$skill_dir")"
|
|
117
|
+
dest="$COMMANDS_DIR/$skill_name.md"
|
|
118
|
+
if [ -f "$dest" ]; then
|
|
119
|
+
skill_skipped=$((skill_skipped + 1))
|
|
120
|
+
else
|
|
121
|
+
cp "$skill_file" "$dest"
|
|
122
|
+
skill_installed=$((skill_installed + 1))
|
|
123
|
+
fi
|
|
124
|
+
done
|
|
125
|
+
echo "[ok] Skills: $skill_installed installed, $skill_skipped already present"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
${config.hookPacks?.length
|
|
129
|
+
? `# Install hook packs to global ~/.claude/
|
|
130
|
+
AGENT_CLAUDE_DIR="$AGENT_DIR/.claude"
|
|
131
|
+
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
132
|
+
|
|
133
|
+
if [ -d "$AGENT_CLAUDE_DIR" ]; then
|
|
134
|
+
echo ""
|
|
135
|
+
echo "Installing hook packs..."
|
|
136
|
+
mkdir -p "$GLOBAL_CLAUDE_DIR"
|
|
137
|
+
installed=0
|
|
138
|
+
skipped=0
|
|
139
|
+
for hook_file in "$AGENT_CLAUDE_DIR"/hookify.*.local.md; do
|
|
140
|
+
[ -f "$hook_file" ] || continue
|
|
141
|
+
dest="$GLOBAL_CLAUDE_DIR/$(basename "$hook_file")"
|
|
142
|
+
if [ -f "$dest" ]; then
|
|
143
|
+
skipped=$((skipped + 1))
|
|
144
|
+
else
|
|
145
|
+
cp "$hook_file" "$dest"
|
|
146
|
+
installed=$((installed + 1))
|
|
147
|
+
fi
|
|
148
|
+
done
|
|
149
|
+
echo "[ok] Hooks: $installed installed, $skipped already present"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
`
|
|
153
|
+
: ''}echo ""
|
|
103
154
|
echo "=== Setup Complete ==="
|
|
104
155
|
echo ""
|
|
105
156
|
echo "Next:"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-script.js","sourceRoot":"","sources":["../../src/templates/setup-script.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,OAAO;;;;cAIK,MAAM,CAAC,EAAE;;YAEX,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;mBAmBJ,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;gCAeE,MAAM,CAAC,IAAI;;;;;;;;oBAQvB,MAAM,CAAC,IAAI;;;wBAGP,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;uFAkBoD,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;qFAmBX,MAAM,CAAC,EAAE
|
|
1
|
+
{"version":3,"file":"setup-script.js","sourceRoot":"","sources":["../../src/templates/setup-script.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,OAAO;;;;cAIK,MAAM,CAAC,EAAE;;YAEX,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;mBAmBJ,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;gCAeE,MAAM,CAAC,IAAI;;;;;;;;oBAQvB,MAAM,CAAC,IAAI;;;wBAGP,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;uFAkBoD,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;qFAmBX,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmC5F,MAAM,CAAC,SAAS,EAAE,MAAM;QACtB,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAuBL;QACG,CAAC,CAAC,EACN;;;;;4BAK4B,MAAM,CAAC,IAAI;;QAE/B,MAAM,CAAC,IAAI;CAClB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate skill files for the scaffolded agent.
|
|
4
|
+
* Returns [relativePath, content] tuples for each skill.
|
|
5
|
+
*
|
|
6
|
+
* - Superpowers-adapted skills (MIT): copied as-is
|
|
7
|
+
* - Engine-adapted skills: YOUR_AGENT_core → {config.id}_core
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateSkills(config: AgentConfig): Array<[string, string]>;
|
|
10
|
+
/**
|
|
11
|
+
* List all bundled skill names with their descriptions (from YAML frontmatter).
|
|
12
|
+
*/
|
|
13
|
+
export declare function listSkillDescriptions(): Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const SKILLS_DIR = join(__dirname, '..', 'skills');
|
|
6
|
+
/** Skills that use YOUR_AGENT_core placeholder and need agent-specific substitution. */
|
|
7
|
+
const AGENT_SPECIFIC_SKILLS = new Set([
|
|
8
|
+
'brain-debrief',
|
|
9
|
+
'brainstorming',
|
|
10
|
+
'code-patrol',
|
|
11
|
+
'context-resume',
|
|
12
|
+
'executing-plans',
|
|
13
|
+
'fix-and-learn',
|
|
14
|
+
'health-check',
|
|
15
|
+
'knowledge-harvest',
|
|
16
|
+
'onboard-me',
|
|
17
|
+
'retrospective',
|
|
18
|
+
'second-opinion',
|
|
19
|
+
'systematic-debugging',
|
|
20
|
+
'test-driven-development',
|
|
21
|
+
'vault-capture',
|
|
22
|
+
'vault-navigator',
|
|
23
|
+
'verification-before-completion',
|
|
24
|
+
'writing-plans',
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Generate skill files for the scaffolded agent.
|
|
28
|
+
* Returns [relativePath, content] tuples for each skill.
|
|
29
|
+
*
|
|
30
|
+
* - Superpowers-adapted skills (MIT): copied as-is
|
|
31
|
+
* - Engine-adapted skills: YOUR_AGENT_core → {config.id}_core
|
|
32
|
+
*/
|
|
33
|
+
export function generateSkills(config) {
|
|
34
|
+
const files = [];
|
|
35
|
+
let skillFiles;
|
|
36
|
+
try {
|
|
37
|
+
skillFiles = readdirSync(SKILLS_DIR).filter((f) => f.endsWith('.md'));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return files;
|
|
41
|
+
}
|
|
42
|
+
for (const file of skillFiles) {
|
|
43
|
+
const skillName = file.replace('.md', '');
|
|
44
|
+
let content = readFileSync(join(SKILLS_DIR, file), 'utf-8');
|
|
45
|
+
if (AGENT_SPECIFIC_SKILLS.has(skillName)) {
|
|
46
|
+
content = content.replace(/YOUR_AGENT_core/g, `${config.id}_core`);
|
|
47
|
+
}
|
|
48
|
+
files.push([`skills/${skillName}/SKILL.md`, content]);
|
|
49
|
+
}
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* List all bundled skill names with their descriptions (from YAML frontmatter).
|
|
54
|
+
*/
|
|
55
|
+
export function listSkillDescriptions() {
|
|
56
|
+
let skillFiles;
|
|
57
|
+
try {
|
|
58
|
+
skillFiles = readdirSync(SKILLS_DIR).filter((f) => f.endsWith('.md'));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return skillFiles.map((file) => {
|
|
64
|
+
const content = readFileSync(join(SKILLS_DIR, file), 'utf-8');
|
|
65
|
+
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
66
|
+
const descMatch = content.match(/^description:\s*"?(.+?)"?\s*$/m);
|
|
67
|
+
return {
|
|
68
|
+
name: nameMatch?.[1]?.trim() ?? file.replace('.md', ''),
|
|
69
|
+
description: descMatch?.[1]?.trim() ?? '',
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/templates/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAEnD,wFAAwF;AACxF,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,eAAe;IACf,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,eAAe;IACf,cAAc;IACd,mBAAmB;IACnB,YAAY;IACZ,eAAe;IACf,gBAAgB;IAChB,sBAAsB;IACtB,yBAAyB;IACzB,eAAe;IACf,iBAAiB;IACjB,gCAAgC;IAChC,eAAe;CAChB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,IAAI,UAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAE5D,IAAI,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,SAAS,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,UAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAClE,OAAO;YACL,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACvD,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE;SAC1C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -123,6 +123,29 @@ ${domainDescribes}
|
|
|
123
123
|
if (stats.totalEntries === 0) {
|
|
124
124
|
recommendations.push('Vault is empty');
|
|
125
125
|
}
|
|
126
|
+
// Check hook status
|
|
127
|
+
const { readdirSync } = await import('node:fs');
|
|
128
|
+
const agentClaudeDir = joinPath(__dirname, '..', '.claude');
|
|
129
|
+
const globalClaudeDir = joinPath(homedir(), '.claude');
|
|
130
|
+
const hookStatus = { agent: [] as string[], global: [] as string[], missing: [] as string[] };
|
|
131
|
+
if (exists(agentClaudeDir)) {
|
|
132
|
+
try {
|
|
133
|
+
const agentHooks = readdirSync(agentClaudeDir)
|
|
134
|
+
.filter((f: string) => f.startsWith('hookify.') && f.endsWith('.local.md'))
|
|
135
|
+
.map((f: string) => f.replace('hookify.', '').replace('.local.md', ''));
|
|
136
|
+
hookStatus.agent = agentHooks;
|
|
137
|
+
for (const hook of agentHooks) {
|
|
138
|
+
if (exists(joinPath(globalClaudeDir, \`hookify.\${hook}.local.md\`))) {
|
|
139
|
+
hookStatus.global.push(hook);
|
|
140
|
+
} else {
|
|
141
|
+
hookStatus.missing.push(hook);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch { /* ignore */ }
|
|
145
|
+
}
|
|
146
|
+
if (hookStatus.missing.length > 0) {
|
|
147
|
+
recommendations.push(\`\${hookStatus.missing.length} hook(s) not installed globally — run scripts/setup.sh\`);
|
|
148
|
+
}
|
|
126
149
|
if (recommendations.length === 0) {
|
|
127
150
|
recommendations.push('${config.name} is fully set up and ready!');
|
|
128
151
|
}
|
|
@@ -133,6 +156,7 @@ ${domainDescribes}
|
|
|
133
156
|
global: { exists: exists(globalClaudeMd), has_agent_section: hasAgentMarker(globalClaudeMd) },
|
|
134
157
|
},
|
|
135
158
|
vault: { entries: stats.totalEntries, domains: Object.keys(stats.byDomain) },
|
|
159
|
+
hooks: hookStatus,
|
|
136
160
|
recommendations,
|
|
137
161
|
};
|
|
138
162
|
},
|
|
@@ -169,6 +193,10 @@ ${domainDescribes}
|
|
|
169
193
|
expect(opNames).toContain('brain_archive_sessions');
|
|
170
194
|
expect(opNames).toContain('brain_promote_proposals');
|
|
171
195
|
expect(opNames).toContain('brain_lifecycle');
|
|
196
|
+
// Enhanced brain ops (3)
|
|
197
|
+
expect(opNames).toContain('brain_feedback');
|
|
198
|
+
expect(opNames).toContain('brain_feedback_stats');
|
|
199
|
+
expect(opNames).toContain('brain_reset_extracted');
|
|
172
200
|
// Agent-specific ops (5)
|
|
173
201
|
expect(opNames).toContain('health');
|
|
174
202
|
expect(opNames).toContain('identity');
|
|
@@ -184,8 +212,122 @@ ${domainDescribes}
|
|
|
184
212
|
expect(opNames).toContain('route_intent');
|
|
185
213
|
expect(opNames).toContain('morph');
|
|
186
214
|
expect(opNames).toContain('get_behavior_rules');
|
|
187
|
-
//
|
|
188
|
-
expect(
|
|
215
|
+
// Cognee ops (5)
|
|
216
|
+
expect(opNames).toContain('cognee_status');
|
|
217
|
+
expect(opNames).toContain('cognee_search');
|
|
218
|
+
expect(opNames).toContain('cognee_add');
|
|
219
|
+
expect(opNames).toContain('cognee_cognify');
|
|
220
|
+
expect(opNames).toContain('cognee_config');
|
|
221
|
+
// LLM ops (2)
|
|
222
|
+
expect(opNames).toContain('llm_rotate');
|
|
223
|
+
expect(opNames).toContain('llm_call');
|
|
224
|
+
// Governance ops (5)
|
|
225
|
+
expect(opNames).toContain('governance_policy');
|
|
226
|
+
expect(opNames).toContain('governance_proposals');
|
|
227
|
+
expect(opNames).toContain('governance_stats');
|
|
228
|
+
expect(opNames).toContain('governance_expire');
|
|
229
|
+
expect(opNames).toContain('governance_dashboard');
|
|
230
|
+
// Planning Extra ops (9)
|
|
231
|
+
expect(opNames).toContain('plan_iterate');
|
|
232
|
+
expect(opNames).toContain('plan_split');
|
|
233
|
+
expect(opNames).toContain('plan_reconcile');
|
|
234
|
+
expect(opNames).toContain('plan_complete_lifecycle');
|
|
235
|
+
expect(opNames).toContain('plan_dispatch');
|
|
236
|
+
expect(opNames).toContain('plan_review');
|
|
237
|
+
expect(opNames).toContain('plan_archive');
|
|
238
|
+
expect(opNames).toContain('plan_list_tasks');
|
|
239
|
+
expect(opNames).toContain('plan_stats');
|
|
240
|
+
// Memory Extra ops (8)
|
|
241
|
+
expect(opNames).toContain('memory_delete');
|
|
242
|
+
expect(opNames).toContain('memory_stats');
|
|
243
|
+
expect(opNames).toContain('memory_export');
|
|
244
|
+
expect(opNames).toContain('memory_import');
|
|
245
|
+
expect(opNames).toContain('memory_prune');
|
|
246
|
+
expect(opNames).toContain('memory_deduplicate');
|
|
247
|
+
expect(opNames).toContain('memory_topics');
|
|
248
|
+
expect(opNames).toContain('memory_by_project');
|
|
249
|
+
// Vault Extra ops (12)
|
|
250
|
+
expect(opNames).toContain('vault_get');
|
|
251
|
+
expect(opNames).toContain('vault_update');
|
|
252
|
+
expect(opNames).toContain('vault_remove');
|
|
253
|
+
expect(opNames).toContain('vault_bulk_add');
|
|
254
|
+
expect(opNames).toContain('vault_bulk_remove');
|
|
255
|
+
expect(opNames).toContain('vault_tags');
|
|
256
|
+
expect(opNames).toContain('vault_domains');
|
|
257
|
+
expect(opNames).toContain('vault_recent');
|
|
258
|
+
expect(opNames).toContain('vault_import');
|
|
259
|
+
expect(opNames).toContain('vault_seed');
|
|
260
|
+
expect(opNames).toContain('vault_backup');
|
|
261
|
+
expect(opNames).toContain('vault_age_report');
|
|
262
|
+
// Admin ops (8)
|
|
263
|
+
expect(opNames).toContain('admin_health');
|
|
264
|
+
expect(opNames).toContain('admin_tool_list');
|
|
265
|
+
expect(opNames).toContain('admin_config');
|
|
266
|
+
expect(opNames).toContain('admin_vault_size');
|
|
267
|
+
expect(opNames).toContain('admin_uptime');
|
|
268
|
+
expect(opNames).toContain('admin_version');
|
|
269
|
+
expect(opNames).toContain('admin_reset_cache');
|
|
270
|
+
expect(opNames).toContain('admin_diagnostic');
|
|
271
|
+
// Loop ops (7)
|
|
272
|
+
expect(opNames).toContain('loop_start');
|
|
273
|
+
expect(opNames).toContain('loop_iterate');
|
|
274
|
+
expect(opNames).toContain('loop_status');
|
|
275
|
+
expect(opNames).toContain('loop_cancel');
|
|
276
|
+
expect(opNames).toContain('loop_history');
|
|
277
|
+
expect(opNames).toContain('loop_is_active');
|
|
278
|
+
expect(opNames).toContain('loop_complete');
|
|
279
|
+
// Orchestrate ops (5)
|
|
280
|
+
expect(opNames).toContain('orchestrate_plan');
|
|
281
|
+
expect(opNames).toContain('orchestrate_execute');
|
|
282
|
+
expect(opNames).toContain('orchestrate_complete');
|
|
283
|
+
expect(opNames).toContain('orchestrate_status');
|
|
284
|
+
expect(opNames).toContain('orchestrate_quick_capture');
|
|
285
|
+
// Capture ops (4)
|
|
286
|
+
expect(opNames).toContain('capture_knowledge');
|
|
287
|
+
expect(opNames).toContain('capture_quick');
|
|
288
|
+
expect(opNames).toContain('search_intelligent');
|
|
289
|
+
expect(opNames).toContain('search_feedback');
|
|
290
|
+
// Grading ops (5)
|
|
291
|
+
expect(opNames).toContain('plan_grade');
|
|
292
|
+
expect(opNames).toContain('plan_check_history');
|
|
293
|
+
expect(opNames).toContain('plan_latest_check');
|
|
294
|
+
expect(opNames).toContain('plan_meets_grade');
|
|
295
|
+
expect(opNames).toContain('plan_auto_improve');
|
|
296
|
+
// Admin Extra ops (10)
|
|
297
|
+
expect(opNames).toContain('admin_telemetry');
|
|
298
|
+
expect(opNames).toContain('admin_telemetry_recent');
|
|
299
|
+
expect(opNames).toContain('admin_telemetry_reset');
|
|
300
|
+
expect(opNames).toContain('admin_permissions');
|
|
301
|
+
expect(opNames).toContain('admin_vault_analytics');
|
|
302
|
+
expect(opNames).toContain('admin_search_insights');
|
|
303
|
+
expect(opNames).toContain('admin_module_status');
|
|
304
|
+
expect(opNames).toContain('admin_env');
|
|
305
|
+
expect(opNames).toContain('admin_gc');
|
|
306
|
+
expect(opNames).toContain('admin_export_config');
|
|
307
|
+
// Curator Extra ops (4)
|
|
308
|
+
expect(opNames).toContain('curator_entry_history');
|
|
309
|
+
expect(opNames).toContain('curator_record_snapshot');
|
|
310
|
+
expect(opNames).toContain('curator_queue_stats');
|
|
311
|
+
expect(opNames).toContain('curator_enrich');
|
|
312
|
+
// Project ops (12)
|
|
313
|
+
expect(opNames).toContain('project_get');
|
|
314
|
+
expect(opNames).toContain('project_list');
|
|
315
|
+
expect(opNames).toContain('project_unregister');
|
|
316
|
+
expect(opNames).toContain('project_get_rules');
|
|
317
|
+
expect(opNames).toContain('project_list_rules');
|
|
318
|
+
expect(opNames).toContain('project_add_rule');
|
|
319
|
+
expect(opNames).toContain('project_remove_rule');
|
|
320
|
+
expect(opNames).toContain('project_link');
|
|
321
|
+
expect(opNames).toContain('project_unlink');
|
|
322
|
+
expect(opNames).toContain('project_get_links');
|
|
323
|
+
expect(opNames).toContain('project_linked_projects');
|
|
324
|
+
expect(opNames).toContain('project_touch');
|
|
325
|
+
// Cross-project memory ops (3)
|
|
326
|
+
expect(opNames).toContain('memory_promote_to_global');
|
|
327
|
+
expect(opNames).toContain('memory_configure');
|
|
328
|
+
expect(opNames).toContain('memory_cross_project_search');
|
|
329
|
+
// Total: 152 (147 core + 5 agent-specific)
|
|
330
|
+
expect(facade.ops.length).toBe(152);
|
|
189
331
|
});
|
|
190
332
|
|
|
191
333
|
it('search should query across all domains with ranked results', async () => {
|
|
@@ -277,7 +419,9 @@ ${domainDescribes}
|
|
|
277
419
|
const setupOp = facade.ops.find((o) => o.name === 'setup')!;
|
|
278
420
|
const result = (await setupOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
279
421
|
agent: { name: string };
|
|
422
|
+
claude_md: { project: { exists: boolean; has_agent_section: boolean }; global: { exists: boolean; has_agent_section: boolean } };
|
|
280
423
|
vault: { entries: number };
|
|
424
|
+
hooks: { agent: string[]; global: string[]; missing: string[] };
|
|
281
425
|
recommendations: string[];
|
|
282
426
|
};
|
|
283
427
|
expect(result.agent.name).toBe('${escapeQuotes(config.name)}');
|
|
@@ -327,6 +471,32 @@ ${domainDescribes}
|
|
|
327
471
|
const result = (await healthOp.handler({})) as { score: number };
|
|
328
472
|
expect(result.score).toBeGreaterThan(0);
|
|
329
473
|
});
|
|
474
|
+
|
|
475
|
+
it('governance_policy get should return default policy', async () => {
|
|
476
|
+
const facade = buildCoreFacade();
|
|
477
|
+
const policyOp = facade.ops.find((o) => o.name === 'governance_policy')!;
|
|
478
|
+
const result = (await policyOp.handler({ action: 'get', projectPath: '/test' })) as {
|
|
479
|
+
projectPath: string;
|
|
480
|
+
quotas: { maxEntriesTotal: number };
|
|
481
|
+
autoCapture: { enabled: boolean };
|
|
482
|
+
};
|
|
483
|
+
expect(result.projectPath).toBe('/test');
|
|
484
|
+
expect(result.quotas.maxEntriesTotal).toBe(500);
|
|
485
|
+
expect(result.autoCapture.enabled).toBe(true);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('governance_dashboard should return combined view', async () => {
|
|
489
|
+
const facade = buildCoreFacade();
|
|
490
|
+
const dashOp = facade.ops.find((o) => o.name === 'governance_dashboard')!;
|
|
491
|
+
const result = (await dashOp.handler({ projectPath: '/test' })) as {
|
|
492
|
+
vaultSize: number;
|
|
493
|
+
quotaPercent: number;
|
|
494
|
+
pendingProposals: number;
|
|
495
|
+
};
|
|
496
|
+
expect(typeof result.vaultSize).toBe('number');
|
|
497
|
+
expect(typeof result.quotaPercent).toBe('number');
|
|
498
|
+
expect(result.pendingProposals).toBe(0);
|
|
499
|
+
});
|
|
330
500
|
});
|
|
331
501
|
});
|
|
332
502
|
`;
|
|
@@ -382,7 +552,7 @@ function generateDomainDescribe(agentId, domain) {
|
|
|
382
552
|
severity: 'warning',
|
|
383
553
|
description: 'A captured pattern.',
|
|
384
554
|
tags: ['captured'],
|
|
385
|
-
})) as { captured: boolean };
|
|
555
|
+
})) as { captured: boolean; governance?: { action: string } };
|
|
386
556
|
expect(result.captured).toBe(true);
|
|
387
557
|
const entry = runtime.vault.get('${domain}-cap1');
|
|
388
558
|
expect(entry).not.toBeNull();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-facades.js","sourceRoot":"","sources":["../../src/templates/test-facades.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;SAChD,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmCS,MAAM,CAAC,EAAE;;;;;;;;;;;EAWzB,eAAe;;cAEH,MAAM,CAAC,EAAE
|
|
1
|
+
{"version":3,"file":"test-facades.js","sourceRoot":"","sources":["../../src/templates/test-facades.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;SAChD,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmCS,MAAM,CAAC,EAAE;;;;;;;;;;;EAWzB,eAAe;;cAEH,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCA6Fe,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;iBAgBhC,MAAM,CAAC,EAAE;;;;;;;;kCAQQ,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDAoKM,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAmCvB,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;mCACzB,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;;;;;;;;;;;0CAWlB,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;qCA0B9B,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;wCAgBN,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EhE,CAAC;AACF,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAe,EAAE,MAAc;IAC7D,MAAM,UAAU,GAAG,GAAG,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;IAE7D,OAAO,eAAe,UAAU;;4CAEU,OAAO,OAAO,MAAM;;;;;kCAK9B,UAAU;;;;;;;;;iDASK,MAAM;;2BAE5B,MAAM,mBAAmB,MAAM;;;;;;kDAMR,MAAM;;;iCAGvB,MAAM;;2BAEZ,MAAM,kBAAkB,MAAM;;;;;;;wDAOD,MAAM;;;wCAGtB,MAAM;;;;eAI/B,MAAM;;;;;;;;yCAQoB,MAAM;;oCAEX,MAAM;;;;6CAIG,MAAM,mBAAmB,MAAM;;;gDAG5B,MAAM;gCACtB,MAAM;;;;6CAIO,MAAM,mBAAmB,MAAM;;;gDAG5B,MAAM;;kCAEpB,MAAM;;MAElC,CAAC;AACP,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soleri/forge",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "Scaffold AI agents that learn, remember, and grow with you.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"dev": "tsx src/index.ts",
|
|
41
|
-
"build": "tsc",
|
|
41
|
+
"build": "tsc && cp -r src/skills dist/skills",
|
|
42
42
|
"start": "node dist/index.js",
|
|
43
43
|
"typecheck": "tsc --noEmit",
|
|
44
44
|
"test": "vitest run",
|
|
@@ -59,9 +59,9 @@ describe('Scaffolder', () => {
|
|
|
59
59
|
expect(preview.facades).toHaveLength(4); // 3 domains + core
|
|
60
60
|
expect(preview.facades[0].name).toBe('atlas_data_pipelines');
|
|
61
61
|
|
|
62
|
-
// Core facade should list all
|
|
62
|
+
// Core facade should list all 152 ops (147 core + 5 agent-specific)
|
|
63
63
|
const coreFacade = preview.facades.find((f) => f.name === 'atlas_core')!;
|
|
64
|
-
expect(coreFacade.ops.length).toBe(
|
|
64
|
+
expect(coreFacade.ops.length).toBe(152);
|
|
65
65
|
expect(coreFacade.ops).toContain('curator_status');
|
|
66
66
|
expect(coreFacade.ops).toContain('health');
|
|
67
67
|
|
|
@@ -248,6 +248,119 @@ describe('Scaffolder', () => {
|
|
|
248
248
|
});
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
+
describe('skills', () => {
|
|
252
|
+
it('should create skills directory with 10 SKILL.md files', () => {
|
|
253
|
+
scaffold(testConfig);
|
|
254
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
255
|
+
|
|
256
|
+
expect(existsSync(skillsDir)).toBe(true);
|
|
257
|
+
|
|
258
|
+
const skillDirs = readdirSync(skillsDir, { withFileTypes: true })
|
|
259
|
+
.filter((e) => e.isDirectory())
|
|
260
|
+
.map((e) => e.name);
|
|
261
|
+
|
|
262
|
+
expect(skillDirs).toHaveLength(17);
|
|
263
|
+
|
|
264
|
+
// Verify each skill dir has a SKILL.md
|
|
265
|
+
for (const dir of skillDirs) {
|
|
266
|
+
expect(existsSync(join(skillsDir, dir, 'SKILL.md'))).toBe(true);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should include all expected skill names', () => {
|
|
271
|
+
scaffold(testConfig);
|
|
272
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
273
|
+
const skillDirs = readdirSync(skillsDir).sort();
|
|
274
|
+
|
|
275
|
+
expect(skillDirs).toEqual([
|
|
276
|
+
'brain-debrief',
|
|
277
|
+
'brainstorming',
|
|
278
|
+
'code-patrol',
|
|
279
|
+
'context-resume',
|
|
280
|
+
'executing-plans',
|
|
281
|
+
'fix-and-learn',
|
|
282
|
+
'health-check',
|
|
283
|
+
'knowledge-harvest',
|
|
284
|
+
'onboard-me',
|
|
285
|
+
'retrospective',
|
|
286
|
+
'second-opinion',
|
|
287
|
+
'systematic-debugging',
|
|
288
|
+
'test-driven-development',
|
|
289
|
+
'vault-capture',
|
|
290
|
+
'vault-navigator',
|
|
291
|
+
'verification-before-completion',
|
|
292
|
+
'writing-plans',
|
|
293
|
+
]);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should have YAML frontmatter in all skills', () => {
|
|
297
|
+
scaffold(testConfig);
|
|
298
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
299
|
+
const skillDirs = readdirSync(skillsDir);
|
|
300
|
+
|
|
301
|
+
for (const dir of skillDirs) {
|
|
302
|
+
const content = readFileSync(join(skillsDir, dir, 'SKILL.md'), 'utf-8');
|
|
303
|
+
expect(content).toMatch(/^---\nname: /);
|
|
304
|
+
expect(content).toContain('description:');
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should substitute YOUR_AGENT_core with agent ID in all skills', () => {
|
|
309
|
+
scaffold(testConfig);
|
|
310
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
311
|
+
const allSkills = readdirSync(skillsDir);
|
|
312
|
+
|
|
313
|
+
for (const name of allSkills) {
|
|
314
|
+
const content = readFileSync(join(skillsDir, name, 'SKILL.md'), 'utf-8');
|
|
315
|
+
expect(content).not.toContain('YOUR_AGENT_core');
|
|
316
|
+
// All skills that reference agent ops should have atlas_core
|
|
317
|
+
if (content.includes('_core')) {
|
|
318
|
+
expect(content).toContain('atlas_core');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should include MIT attribution in superpowers-adapted skills', () => {
|
|
324
|
+
scaffold(testConfig);
|
|
325
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
326
|
+
const superpowersSkills = [
|
|
327
|
+
'test-driven-development',
|
|
328
|
+
'systematic-debugging',
|
|
329
|
+
'verification-before-completion',
|
|
330
|
+
'brainstorming',
|
|
331
|
+
'writing-plans',
|
|
332
|
+
'executing-plans',
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
for (const name of superpowersSkills) {
|
|
336
|
+
const content = readFileSync(join(skillsDir, name, 'SKILL.md'), 'utf-8');
|
|
337
|
+
expect(content).toContain('MIT License');
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should have no YOUR_AGENT_core placeholder remaining in any skill', () => {
|
|
342
|
+
scaffold(testConfig);
|
|
343
|
+
const skillsDir = join(tempDir, 'atlas', 'skills');
|
|
344
|
+
const allSkills = readdirSync(skillsDir);
|
|
345
|
+
|
|
346
|
+
for (const name of allSkills) {
|
|
347
|
+
const content = readFileSync(join(skillsDir, name, 'SKILL.md'), 'utf-8');
|
|
348
|
+
expect(content).not.toContain('YOUR_AGENT_core');
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should include skills in preview', () => {
|
|
353
|
+
const preview = previewScaffold(testConfig);
|
|
354
|
+
const paths = preview.files.map((f) => f.path);
|
|
355
|
+
expect(paths).toContain('skills/');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should mention skills in scaffold summary', () => {
|
|
359
|
+
const result = scaffold(testConfig);
|
|
360
|
+
expect(result.summary).toContain('built-in skills');
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
251
364
|
describe('listAgents', () => {
|
|
252
365
|
it('should list scaffolded agents', () => {
|
|
253
366
|
scaffold(testConfig);
|