@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.
Files changed (62) hide show
  1. package/dist/scaffolder.js +166 -3
  2. package/dist/scaffolder.js.map +1 -1
  3. package/dist/skills/brain-debrief.md +186 -0
  4. package/dist/skills/brainstorming.md +170 -0
  5. package/dist/skills/code-patrol.md +176 -0
  6. package/dist/skills/context-resume.md +143 -0
  7. package/dist/skills/executing-plans.md +201 -0
  8. package/dist/skills/fix-and-learn.md +164 -0
  9. package/dist/skills/health-check.md +225 -0
  10. package/dist/skills/knowledge-harvest.md +178 -0
  11. package/dist/skills/onboard-me.md +197 -0
  12. package/dist/skills/retrospective.md +189 -0
  13. package/dist/skills/second-opinion.md +142 -0
  14. package/dist/skills/systematic-debugging.md +230 -0
  15. package/dist/skills/test-driven-development.md +266 -0
  16. package/dist/skills/vault-capture.md +154 -0
  17. package/dist/skills/vault-navigator.md +129 -0
  18. package/dist/skills/verification-before-completion.md +170 -0
  19. package/dist/skills/writing-plans.md +207 -0
  20. package/dist/templates/claude-md-template.js +90 -1
  21. package/dist/templates/claude-md-template.js.map +1 -1
  22. package/dist/templates/domain-facade.d.ts +4 -0
  23. package/dist/templates/domain-facade.js +4 -0
  24. package/dist/templates/domain-facade.js.map +1 -1
  25. package/dist/templates/entry-point.js +32 -0
  26. package/dist/templates/entry-point.js.map +1 -1
  27. package/dist/templates/readme.js +38 -0
  28. package/dist/templates/readme.js.map +1 -1
  29. package/dist/templates/setup-script.js +52 -1
  30. package/dist/templates/setup-script.js.map +1 -1
  31. package/dist/templates/skills.d.ts +16 -0
  32. package/dist/templates/skills.js +73 -0
  33. package/dist/templates/skills.js.map +1 -0
  34. package/dist/templates/test-facades.js +173 -3
  35. package/dist/templates/test-facades.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/__tests__/scaffolder.test.ts +115 -2
  38. package/src/scaffolder.ts +171 -3
  39. package/src/skills/brain-debrief.md +186 -0
  40. package/src/skills/brainstorming.md +170 -0
  41. package/src/skills/code-patrol.md +176 -0
  42. package/src/skills/context-resume.md +143 -0
  43. package/src/skills/executing-plans.md +201 -0
  44. package/src/skills/fix-and-learn.md +164 -0
  45. package/src/skills/health-check.md +225 -0
  46. package/src/skills/knowledge-harvest.md +178 -0
  47. package/src/skills/onboard-me.md +197 -0
  48. package/src/skills/retrospective.md +189 -0
  49. package/src/skills/second-opinion.md +142 -0
  50. package/src/skills/systematic-debugging.md +230 -0
  51. package/src/skills/test-driven-development.md +266 -0
  52. package/src/skills/vault-capture.md +154 -0
  53. package/src/skills/vault-navigator.md +129 -0
  54. package/src/skills/verification-before-completion.md +170 -0
  55. package/src/skills/writing-plans.md +207 -0
  56. package/src/templates/claude-md-template.ts +181 -0
  57. package/src/templates/domain-facade.ts +4 -0
  58. package/src/templates/entry-point.ts +32 -0
  59. package/src/templates/readme.ts +38 -0
  60. package/src/templates/setup-script.ts +54 -1
  61. package/src/templates/skills.ts +82 -0
  62. package/src/templates/test-facades.ts +173 -3
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BF,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"}
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
- echo ""
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;;;;;;;;;;;;;4BAalE,MAAM,CAAC,IAAI;;QAE/B,MAAM,CAAC,IAAI;CAClB,CAAC;AACF,CAAC"}
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
- // Total: 50
188
- expect(facade.ops.length).toBe(50);
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAsEe,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;iBAehC,MAAM,CAAC,EAAE;;;;;;;;kCAQQ,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA8CM,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;;;;;;;;;;;;;;wCAcN,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDhE,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"}
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.2.0",
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 31 ops
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(31);
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);