bmad-module-skill-forge 0.2.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 (154) hide show
  1. package/.nvmrc +1 -0
  2. package/LICENSE +26 -0
  3. package/README.md +213 -0
  4. package/docs/404.md +20 -0
  5. package/docs/agents.md +73 -0
  6. package/docs/architecture.md +301 -0
  7. package/docs/examples.md +144 -0
  8. package/docs/getting-started.md +198 -0
  9. package/docs/index.md +75 -0
  10. package/docs/workflows.md +190 -0
  11. package/package.json +109 -0
  12. package/src/agents/forger.agent.yaml +93 -0
  13. package/src/forger/README.md +17 -0
  14. package/src/forger/forge-tier.yaml +13 -0
  15. package/src/forger/preferences.yaml +10 -0
  16. package/src/knowledge/agentskills-spec.md +158 -0
  17. package/src/knowledge/confidence-tiers.md +153 -0
  18. package/src/knowledge/manual-section-integrity.md +156 -0
  19. package/src/knowledge/overview.md +56 -0
  20. package/src/knowledge/progressive-capability.md +120 -0
  21. package/src/knowledge/provenance-tracking.md +163 -0
  22. package/src/knowledge/skf-knowledge-index.csv +9 -0
  23. package/src/knowledge/skill-lifecycle.md +150 -0
  24. package/src/knowledge/zero-hallucination.md +101 -0
  25. package/src/module-help.csv +12 -0
  26. package/src/module.yaml +22 -0
  27. package/src/workflows/README.md +148 -0
  28. package/src/workflows/analyze-source/data/skill-brief-schema.md +75 -0
  29. package/src/workflows/analyze-source/data/unit-detection-heuristics.md +88 -0
  30. package/src/workflows/analyze-source/steps-c/step-01-init.md +191 -0
  31. package/src/workflows/analyze-source/steps-c/step-01b-continue.md +144 -0
  32. package/src/workflows/analyze-source/steps-c/step-02-scan-project.md +208 -0
  33. package/src/workflows/analyze-source/steps-c/step-03-identify-units.md +199 -0
  34. package/src/workflows/analyze-source/steps-c/step-04-map-and-detect.md +222 -0
  35. package/src/workflows/analyze-source/steps-c/step-05-recommend.md +236 -0
  36. package/src/workflows/analyze-source/steps-c/step-06-generate-briefs.md +239 -0
  37. package/src/workflows/analyze-source/templates/analysis-report-template.md +41 -0
  38. package/src/workflows/analyze-source/validation-report.md +657 -0
  39. package/src/workflows/analyze-source/workflow-plan-analyze-source.md +385 -0
  40. package/src/workflows/analyze-source/workflow.md +61 -0
  41. package/src/workflows/audit-skill/data/drift-report-template.md +62 -0
  42. package/src/workflows/audit-skill/data/severity-rules.md +52 -0
  43. package/src/workflows/audit-skill/steps-c/step-01-init.md +195 -0
  44. package/src/workflows/audit-skill/steps-c/step-02-re-index.md +191 -0
  45. package/src/workflows/audit-skill/steps-c/step-03-structural-diff.md +194 -0
  46. package/src/workflows/audit-skill/steps-c/step-04-semantic-diff.md +199 -0
  47. package/src/workflows/audit-skill/steps-c/step-05-severity-classify.md +210 -0
  48. package/src/workflows/audit-skill/steps-c/step-06-report.md +217 -0
  49. package/src/workflows/audit-skill/validation-report.md +545 -0
  50. package/src/workflows/audit-skill/workflow-plan-audit-skill.md +318 -0
  51. package/src/workflows/audit-skill/workflow.md +63 -0
  52. package/src/workflows/brief-skill/data/skill-brief-schema.md +82 -0
  53. package/src/workflows/brief-skill/steps-c/step-01-gather-intent.md +184 -0
  54. package/src/workflows/brief-skill/steps-c/step-02-analyze-target.md +203 -0
  55. package/src/workflows/brief-skill/steps-c/step-03-scope-definition.md +225 -0
  56. package/src/workflows/brief-skill/steps-c/step-04-confirm-brief.md +185 -0
  57. package/src/workflows/brief-skill/steps-c/step-05-write-brief.md +156 -0
  58. package/src/workflows/brief-skill/validation-report.md +630 -0
  59. package/src/workflows/brief-skill/workflow-plan-brief-skill.md +360 -0
  60. package/src/workflows/brief-skill/workflow.md +57 -0
  61. package/src/workflows/create-skill/data/extraction-patterns.md +63 -0
  62. package/src/workflows/create-skill/data/skill-sections.md +166 -0
  63. package/src/workflows/create-skill/steps-c/step-01-load-brief.md +176 -0
  64. package/src/workflows/create-skill/steps-c/step-02-ecosystem-check.md +143 -0
  65. package/src/workflows/create-skill/steps-c/step-03-extract.md +184 -0
  66. package/src/workflows/create-skill/steps-c/step-04-enrich.md +152 -0
  67. package/src/workflows/create-skill/steps-c/step-05-compile.md +198 -0
  68. package/src/workflows/create-skill/steps-c/step-06-validate.md +162 -0
  69. package/src/workflows/create-skill/steps-c/step-07-generate-artifacts.md +165 -0
  70. package/src/workflows/create-skill/steps-c/step-08-report.md +160 -0
  71. package/src/workflows/create-skill/validation-report.md +189 -0
  72. package/src/workflows/create-skill/workflow-plan-create-skill.md +491 -0
  73. package/src/workflows/create-skill/workflow.md +52 -0
  74. package/src/workflows/create-stack-skill/data/integration-patterns.md +61 -0
  75. package/src/workflows/create-stack-skill/data/manifest-patterns.md +35 -0
  76. package/src/workflows/create-stack-skill/data/stack-skill-template.md +110 -0
  77. package/src/workflows/create-stack-skill/steps-c/step-01-init.md +142 -0
  78. package/src/workflows/create-stack-skill/steps-c/step-02-detect-manifests.md +145 -0
  79. package/src/workflows/create-stack-skill/steps-c/step-03-rank-and-confirm.md +161 -0
  80. package/src/workflows/create-stack-skill/steps-c/step-04-parallel-extract.md +155 -0
  81. package/src/workflows/create-stack-skill/steps-c/step-05-detect-integrations.md +154 -0
  82. package/src/workflows/create-stack-skill/steps-c/step-06-compile-stack.md +160 -0
  83. package/src/workflows/create-stack-skill/steps-c/step-07-generate-output.md +202 -0
  84. package/src/workflows/create-stack-skill/steps-c/step-08-validate.md +193 -0
  85. package/src/workflows/create-stack-skill/steps-c/step-09-report.md +137 -0
  86. package/src/workflows/create-stack-skill/validation-report.md +401 -0
  87. package/src/workflows/create-stack-skill/workflow-plan-create-stack-skill.md +481 -0
  88. package/src/workflows/create-stack-skill/workflow.md +59 -0
  89. package/src/workflows/export-skill/data/managed-section-format.md +70 -0
  90. package/src/workflows/export-skill/data/snippet-format.md +32 -0
  91. package/src/workflows/export-skill/steps-c/step-01-load-skill.md +184 -0
  92. package/src/workflows/export-skill/steps-c/step-02-package.md +153 -0
  93. package/src/workflows/export-skill/steps-c/step-03-generate-snippet.md +166 -0
  94. package/src/workflows/export-skill/steps-c/step-04-update-context.md +222 -0
  95. package/src/workflows/export-skill/steps-c/step-05-token-report.md +126 -0
  96. package/src/workflows/export-skill/steps-c/step-06-summary.md +167 -0
  97. package/src/workflows/export-skill/validation-report.md +263 -0
  98. package/src/workflows/export-skill/workflow-plan-export-skill.md +369 -0
  99. package/src/workflows/export-skill/workflow.md +56 -0
  100. package/src/workflows/quick-skill/data/registry-resolution.md +91 -0
  101. package/src/workflows/quick-skill/data/skill-template.md +66 -0
  102. package/src/workflows/quick-skill/steps-c/step-01-resolve-target.md +165 -0
  103. package/src/workflows/quick-skill/steps-c/step-02-ecosystem-check.md +143 -0
  104. package/src/workflows/quick-skill/steps-c/step-03-quick-extract.md +177 -0
  105. package/src/workflows/quick-skill/steps-c/step-04-compile.md +180 -0
  106. package/src/workflows/quick-skill/steps-c/step-05-validate.md +150 -0
  107. package/src/workflows/quick-skill/steps-c/step-06-write.md +160 -0
  108. package/src/workflows/quick-skill/validation-report.md +543 -0
  109. package/src/workflows/quick-skill/workflow-plan-quick-skill.md +405 -0
  110. package/src/workflows/quick-skill/workflow.md +56 -0
  111. package/src/workflows/setup-forge/data/tier-rules.md +50 -0
  112. package/src/workflows/setup-forge/steps-c/step-01-detect-and-tier.md +140 -0
  113. package/src/workflows/setup-forge/steps-c/step-02-write-config.md +141 -0
  114. package/src/workflows/setup-forge/steps-c/step-03-auto-index.md +116 -0
  115. package/src/workflows/setup-forge/steps-c/step-04-report.md +131 -0
  116. package/src/workflows/setup-forge/validation-report.md +504 -0
  117. package/src/workflows/setup-forge/workflow-plan-setup-forge.md +428 -0
  118. package/src/workflows/setup-forge/workflow.md +49 -0
  119. package/src/workflows/test-skill/data/output-section-formats.md +96 -0
  120. package/src/workflows/test-skill/data/scoring-rules.md +74 -0
  121. package/src/workflows/test-skill/steps-c/step-01-init.md +185 -0
  122. package/src/workflows/test-skill/steps-c/step-02-detect-mode.md +162 -0
  123. package/src/workflows/test-skill/steps-c/step-03-coverage-check.md +222 -0
  124. package/src/workflows/test-skill/steps-c/step-04-coherence-check.md +249 -0
  125. package/src/workflows/test-skill/steps-c/step-05-score.md +222 -0
  126. package/src/workflows/test-skill/steps-c/step-06-report.md +175 -0
  127. package/src/workflows/test-skill/templates/test-report-template.md +24 -0
  128. package/src/workflows/test-skill/validation-report.md +390 -0
  129. package/src/workflows/test-skill/workflow-plan-test-skill.md +288 -0
  130. package/src/workflows/test-skill/workflow.md +57 -0
  131. package/src/workflows/update-skill/data/manual-section-rules.md +44 -0
  132. package/src/workflows/update-skill/data/merge-conflict-rules.md +62 -0
  133. package/src/workflows/update-skill/steps-c/step-01-init.md +208 -0
  134. package/src/workflows/update-skill/steps-c/step-02-detect-changes.md +184 -0
  135. package/src/workflows/update-skill/steps-c/step-03-re-extract.md +187 -0
  136. package/src/workflows/update-skill/steps-c/step-04-merge.md +230 -0
  137. package/src/workflows/update-skill/steps-c/step-05-validate.md +187 -0
  138. package/src/workflows/update-skill/steps-c/step-06-write.md +204 -0
  139. package/src/workflows/update-skill/steps-c/step-07-report.md +201 -0
  140. package/src/workflows/update-skill/validation-report.md +559 -0
  141. package/src/workflows/update-skill/workflow-plan-update-skill.md +441 -0
  142. package/src/workflows/update-skill/workflow.md +56 -0
  143. package/tools/cli/commands/install.js +33 -0
  144. package/tools/cli/commands/status.js +195 -0
  145. package/tools/cli/commands/uninstall.js +226 -0
  146. package/tools/cli/commands/update.js +63 -0
  147. package/tools/cli/lib/compiler.js +306 -0
  148. package/tools/cli/lib/ide-commands.js +244 -0
  149. package/tools/cli/lib/installer.js +307 -0
  150. package/tools/cli/lib/manifest.js +109 -0
  151. package/tools/cli/lib/ui.js +277 -0
  152. package/tools/cli/lib/version-check.js +86 -0
  153. package/tools/cli/skf-cli.js +45 -0
  154. package/tools/skf-npx-wrapper.js +36 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * SKF Status Command
3
+ * Shows installation state, version, tier, configured IDEs, and sidecar status.
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+ const path = require('node:path');
8
+ const fs = require('fs-extra');
9
+ const yaml = require('js-yaml');
10
+ const { readManifest } = require('../lib/manifest');
11
+
12
+ const SKF_FOLDER = '_bmad/skf';
13
+ const SIDECAR_FOLDER = '_bmad/_memory/forger-sidecar';
14
+
15
+ const IDE_NAMES = {
16
+ 'claude-code': 'Claude Code',
17
+ cline: 'Cline',
18
+ codex: 'Codex',
19
+ cursor: 'Cursor',
20
+ 'github-copilot': 'GitHub Copilot',
21
+ roo: 'Roo Code',
22
+ windsurf: 'Windsurf',
23
+ other: 'Other',
24
+ };
25
+
26
+ async function readYaml(filePath) {
27
+ try {
28
+ const content = await fs.readFile(filePath, 'utf8');
29
+ return yaml.load(content) || {};
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ async function getStatus(projectDir) {
36
+ const skfDir = path.join(projectDir, SKF_FOLDER);
37
+ const sidecarDir = path.join(projectDir, SIDECAR_FOLDER);
38
+
39
+ const installed = await fs.pathExists(skfDir);
40
+ if (!installed) {
41
+ return { installed: false };
42
+ }
43
+
44
+ // Read config
45
+ const config = await readYaml(path.join(skfDir, 'config.yaml'));
46
+
47
+ // Read forge tier
48
+ const forgeTier = await readYaml(path.join(sidecarDir, 'forge-tier.yaml'));
49
+
50
+ // Read preferences
51
+ const preferences = await readYaml(path.join(sidecarDir, 'preferences.yaml'));
52
+
53
+ // Check agent file
54
+ const agentFile = path.join(skfDir, 'agents', 'forger.md');
55
+ const agentCompiled = await fs.pathExists(agentFile);
56
+
57
+ // Count workflows
58
+ const workflowsDir = path.join(skfDir, 'workflows', 'skillforge');
59
+ let workflowCount = 0;
60
+ if (await fs.pathExists(workflowsDir)) {
61
+ const entries = await fs.readdir(workflowsDir);
62
+ for (const entry of entries) {
63
+ const wfPath = path.join(workflowsDir, entry, 'workflow.md');
64
+ if (await fs.pathExists(wfPath)) {
65
+ workflowCount++;
66
+ }
67
+ }
68
+ }
69
+
70
+ // Check output folders
71
+ const skillsFolder = config?.skills_output_folder || 'skills';
72
+ const forgeDataFolder = config?.forge_data_folder || 'forge-data';
73
+ const skillsFolderExists = await fs.pathExists(path.join(projectDir, skillsFolder));
74
+ const forgeDataFolderExists = await fs.pathExists(path.join(projectDir, forgeDataFolder));
75
+
76
+ // Check sidecar state
77
+ const sidecarExists = await fs.pathExists(sidecarDir);
78
+ const tierDetected = forgeTier?.tier != null;
79
+
80
+ // Read manifest
81
+ const manifest = await readManifest(projectDir);
82
+
83
+ return {
84
+ installed: true,
85
+ config,
86
+ forgeTier,
87
+ preferences,
88
+ agentCompiled,
89
+ workflowCount,
90
+ skillsFolder,
91
+ skillsFolderExists,
92
+ forgeDataFolder,
93
+ forgeDataFolderExists,
94
+ sidecarExists,
95
+ tierDetected,
96
+ manifest,
97
+ };
98
+ }
99
+
100
+ function displayStatus(status, version) {
101
+ console.log('');
102
+ console.log(chalk.cyan.bold(' Skill Forge — Status'));
103
+ console.log(chalk.dim(` v${version}`));
104
+ console.log('');
105
+
106
+ if (!status.installed) {
107
+ console.log(chalk.yellow(' Not installed.'));
108
+ console.log(chalk.dim(' Run: npx skill-forge install'));
109
+ console.log('');
110
+ return;
111
+ }
112
+
113
+ const config = status.config || {};
114
+
115
+ // Installation
116
+ const manifest = status.manifest;
117
+ console.log(chalk.white.bold(' Installation'));
118
+ console.log(` Project: ${chalk.cyan(config.project_name || '(unknown)')}`);
119
+ console.log(` SKF folder: ${chalk.dim(SKF_FOLDER + '/')}`);
120
+ console.log(` Agent: ${status.agentCompiled ? chalk.green('compiled') : chalk.yellow('not compiled')}`);
121
+ console.log(` Workflows: ${chalk.white(status.workflowCount)}`);
122
+ if (manifest) {
123
+ console.log(
124
+ ` Installed: ${chalk.dim(manifest.installed_at ? new Date(manifest.installed_at).toLocaleDateString() : '(unknown)')}`,
125
+ );
126
+ console.log(` Manifest: ${chalk.green('present')}`);
127
+ } else {
128
+ console.log(` Manifest: ${chalk.yellow('missing')} ${chalk.dim('(reinstall to generate)')}`);
129
+ }
130
+ console.log('');
131
+
132
+ // IDEs
133
+ const ides = config.ides || [];
134
+ console.log(chalk.white.bold(' IDEs'));
135
+ if (ides.length > 0) {
136
+ for (const ide of ides) {
137
+ console.log(` ${chalk.green('●')} ${IDE_NAMES[ide] || ide}`);
138
+ }
139
+ } else {
140
+ console.log(chalk.dim(' None configured'));
141
+ }
142
+ console.log('');
143
+
144
+ // Forge Tier
145
+ const ft = status.forgeTier || {};
146
+ const tools = ft.tools || {};
147
+ console.log(chalk.white.bold(' Forge Tier'));
148
+ if (status.tierDetected) {
149
+ const tierColors = { Quick: chalk.yellow, Forge: chalk.cyan, Deep: chalk.magenta };
150
+ const tierColor = tierColors[ft.tier] || chalk.white;
151
+ console.log(` Tier: ${tierColor(ft.tier)}`);
152
+ console.log(` Detected: ${chalk.dim(ft.tier_detected_at || '(unknown)')}`);
153
+ } else {
154
+ console.log(chalk.dim(' Not detected yet — run @Ferris SF'));
155
+ }
156
+ console.log(` ast-grep: ${formatTool(tools.ast_grep)}`);
157
+ console.log(` gh CLI: ${formatTool(tools.gh_cli)}`);
158
+ console.log(` QMD: ${formatTool(tools.qmd)}`);
159
+ console.log('');
160
+
161
+ // Output Folders
162
+ console.log(chalk.white.bold(' Output Folders'));
163
+ console.log(` Skills: ${status.skillsFolder}/ ${status.skillsFolderExists ? chalk.green('✓') : chalk.yellow('missing')}`);
164
+ console.log(` Forge data: ${status.forgeDataFolder}/ ${status.forgeDataFolderExists ? chalk.green('✓') : chalk.yellow('missing')}`);
165
+ console.log('');
166
+
167
+ // Sidecar
168
+ console.log(chalk.white.bold(' Sidecar'));
169
+ console.log(` State: ${status.sidecarExists ? chalk.green('initialized') : chalk.yellow('missing')}`);
170
+ console.log(` Location: ${chalk.dim(SIDECAR_FOLDER + '/')}`);
171
+ console.log('');
172
+ }
173
+
174
+ function formatTool(value) {
175
+ if (value === true || value === 'available') return chalk.green('available');
176
+ if (value == null) return chalk.dim('not detected');
177
+ return chalk.yellow(String(value));
178
+ }
179
+
180
+ module.exports = {
181
+ command: 'status',
182
+ description: 'Show SKF installation state, version, tier, and configuration',
183
+ options: [],
184
+ action: async () => {
185
+ try {
186
+ const projectDir = process.cwd();
187
+ const packageJson = require('../../../package.json');
188
+ const status = await getStatus(projectDir);
189
+ displayStatus(status, packageJson.version);
190
+ } catch (error) {
191
+ console.error(chalk.red('\nFailed to read status:'), error.message);
192
+ process.exit(1);
193
+ }
194
+ },
195
+ };
@@ -0,0 +1,226 @@
1
+ /**
2
+ * SKF Uninstall Command
3
+ * Clean removal of SKF files, IDE commands, sidecar, and output folders.
4
+ * Uses the manifest to know exactly what to remove.
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('node:path');
9
+ const fs = require('fs-extra');
10
+ const { confirm, isCancel, spinner, log, outro } = require('@clack/prompts');
11
+ const { readManifest, MANIFEST_DIR, MANIFEST_FILE } = require('../lib/manifest');
12
+
13
+ /**
14
+ * Count files that still exist on disk from manifest lists.
15
+ */
16
+ async function countExistingFiles(projectDir, manifest) {
17
+ const allFiles = [
18
+ ...(manifest.files.skf || []),
19
+ ...(manifest.files.sidecar || []),
20
+ ...(manifest.files.ide_commands || []),
21
+ ...(manifest.files.learning || []),
22
+ ...(manifest.files.output || []),
23
+ ];
24
+
25
+ let existing = 0;
26
+ for (const file of allFiles) {
27
+ if (await fs.pathExists(path.join(projectDir, file))) {
28
+ existing++;
29
+ }
30
+ }
31
+ return { total: allFiles.length, existing };
32
+ }
33
+
34
+ /**
35
+ * Display what will be removed, grouped by category.
36
+ */
37
+ async function displayRemovalPlan(projectDir, manifest) {
38
+ const categories = [
39
+ { key: 'skf', label: 'SKF module files', dir: manifest.skf_folder },
40
+ { key: 'sidecar', label: 'Agent sidecar state', dir: '_bmad/_memory/forger-sidecar' },
41
+ { key: 'ide_commands', label: 'IDE command files' },
42
+ { key: 'learning', label: 'Learning material', dir: '_skf-learn' },
43
+ { key: 'output', label: 'Output folder scaffolding' },
44
+ ];
45
+
46
+ const lines = [];
47
+ for (const cat of categories) {
48
+ const files = manifest.files[cat.key] || [];
49
+ if (files.length === 0) continue;
50
+
51
+ // Count how many still exist
52
+ let existCount = 0;
53
+ for (const f of files) {
54
+ if (await fs.pathExists(path.join(projectDir, f))) existCount++;
55
+ }
56
+
57
+ if (existCount === 0) continue;
58
+
59
+ if (cat.dir) {
60
+ lines.push(`${chalk.red('×')} ${cat.label} ${chalk.dim(`(${cat.dir}/ — ${existCount} files)`)}`);
61
+ } else {
62
+ lines.push(`${chalk.red('×')} ${cat.label} ${chalk.dim(`(${existCount} files)`)}`);
63
+ }
64
+ }
65
+
66
+ // Manifest itself
67
+ lines.push(`${chalk.red('×')} Installation manifest ${chalk.dim(`(${MANIFEST_DIR}/${MANIFEST_FILE})`)}`);
68
+
69
+ log.warn(`The following will be removed:\n${lines.join('\n')}`);
70
+ }
71
+
72
+ /**
73
+ * Remove files listed in the manifest, then clean empty parent directories.
74
+ */
75
+ async function removeFiles(projectDir, fileList) {
76
+ let removed = 0;
77
+ for (const file of fileList) {
78
+ const fullPath = path.join(projectDir, file);
79
+ if (await fs.pathExists(fullPath)) {
80
+ await fs.remove(fullPath);
81
+ removed++;
82
+ }
83
+ }
84
+ return removed;
85
+ }
86
+
87
+ /**
88
+ * Remove a directory if it exists and is empty (or force remove).
89
+ */
90
+ async function removeEmptyDir(dirPath) {
91
+ if (!(await fs.pathExists(dirPath))) return;
92
+ try {
93
+ const entries = await fs.readdir(dirPath);
94
+ if (entries.length === 0) {
95
+ await fs.remove(dirPath);
96
+ }
97
+ } catch {
98
+ // ignore
99
+ }
100
+ }
101
+
102
+ module.exports = {
103
+ command: 'uninstall',
104
+ description: 'Remove SKF installation from the current project',
105
+ options: [],
106
+ action: async () => {
107
+ try {
108
+ const projectDir = process.cwd();
109
+ const manifest = await readManifest(projectDir);
110
+
111
+ if (!manifest) {
112
+ // Check if SKF exists without manifest
113
+ const skfExists = await fs.pathExists(path.join(projectDir, '_bmad/skf'));
114
+ if (skfExists) {
115
+ log.warn(
116
+ 'No manifest found. Reinstall first to generate one,\nthen run uninstall again for clean removal.\nRun: npx skill-forge install',
117
+ );
118
+ } else {
119
+ log.warn('SKF is not installed in this directory.');
120
+ }
121
+ process.exit(0);
122
+ return;
123
+ }
124
+
125
+ const { existing } = await countExistingFiles(projectDir, manifest);
126
+ if (existing === 0) {
127
+ log.warn('No SKF files found to remove.');
128
+ // Clean up stale manifest
129
+ await fs.remove(path.join(projectDir, MANIFEST_DIR, MANIFEST_FILE));
130
+ process.exit(0);
131
+ return;
132
+ }
133
+
134
+ await displayRemovalPlan(projectDir, manifest);
135
+
136
+ const shouldProceed = await confirm({
137
+ message: 'Proceed with uninstall?',
138
+ initialValue: false,
139
+ });
140
+
141
+ if (isCancel(shouldProceed) || !shouldProceed) {
142
+ log.warn('Uninstall cancelled.');
143
+ process.exit(0);
144
+ return;
145
+ }
146
+
147
+ const s = spinner();
148
+
149
+ // Remove IDE commands first (scattered across project)
150
+ const ideFiles = manifest.files.ide_commands || [];
151
+ if (ideFiles.length > 0) {
152
+ s.start('Removing IDE commands...');
153
+ const ideRemoved = await removeFiles(projectDir, ideFiles);
154
+ // Clean empty IDE directories
155
+ const cleanedDirs = new Set();
156
+ for (const f of ideFiles) {
157
+ const dir = path.dirname(f);
158
+ if (!cleanedDirs.has(dir)) {
159
+ cleanedDirs.add(dir);
160
+ await removeEmptyDir(path.join(projectDir, dir));
161
+ await removeEmptyDir(path.join(projectDir, path.dirname(dir)));
162
+ }
163
+ }
164
+ s.stop(`Removed ${ideRemoved} IDE command files`);
165
+ }
166
+
167
+ // Remove learning material directory
168
+ const learnFiles = manifest.files.learning || [];
169
+ if (learnFiles.length > 0) {
170
+ s.start('Removing learning material...');
171
+ const learnDir = path.join(projectDir, '_skf-learn');
172
+ if (await fs.pathExists(learnDir)) {
173
+ await fs.remove(learnDir);
174
+ }
175
+ s.stop('Learning material removed');
176
+ }
177
+
178
+ // Remove output folder scaffolding (.gitkeep only)
179
+ const outputFiles = manifest.files.output || [];
180
+ if (outputFiles.length > 0) {
181
+ s.start('Removing output scaffolding...');
182
+ await removeFiles(projectDir, outputFiles);
183
+ // Remove empty output folders
184
+ for (const folder of [manifest.skills_output_folder, manifest.forge_data_folder]) {
185
+ if (folder) {
186
+ await removeEmptyDir(path.join(projectDir, folder));
187
+ }
188
+ }
189
+ s.stop('Output scaffolding removed');
190
+ }
191
+
192
+ // Remove sidecar
193
+ s.start('Removing agent sidecar...');
194
+ const sidecarDir = path.join(projectDir, '_bmad', '_memory', 'forger-sidecar');
195
+ if (await fs.pathExists(sidecarDir)) {
196
+ await fs.remove(sidecarDir);
197
+ }
198
+ await removeEmptyDir(path.join(projectDir, '_bmad', '_memory'));
199
+ s.stop('Agent sidecar removed');
200
+
201
+ // Remove SKF module directory
202
+ s.start('Removing SKF module...');
203
+ const skfDir = path.join(projectDir, manifest.skf_folder);
204
+ if (await fs.pathExists(skfDir)) {
205
+ await fs.remove(skfDir);
206
+ }
207
+ s.stop('SKF module removed');
208
+
209
+ // Remove manifest
210
+ const manifestPath = path.join(projectDir, MANIFEST_DIR, MANIFEST_FILE);
211
+ if (await fs.pathExists(manifestPath)) {
212
+ await fs.remove(manifestPath);
213
+ }
214
+ // Clean empty _bmad/_config/ and _bmad/ if we were the only occupant
215
+ await removeEmptyDir(path.join(projectDir, MANIFEST_DIR));
216
+ await removeEmptyDir(path.join(projectDir, '_bmad'));
217
+
218
+ outro('SKF uninstalled successfully.');
219
+
220
+ process.exit(0);
221
+ } catch (error) {
222
+ console.error(chalk.red('\nUninstall failed:'), error.message);
223
+ process.exit(1);
224
+ }
225
+ },
226
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * SKF Quick Update Command
3
+ * Replaces SKF files and recompiles agents without re-prompting.
4
+ * Preserves config.yaml and sidecar state.
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('node:path');
9
+ const fs = require('fs-extra');
10
+ const yaml = require('js-yaml');
11
+ const { Installer } = require('../lib/installer');
12
+ const { UI } = require('../lib/ui');
13
+
14
+ const SKF_FOLDER = '_bmad/skf';
15
+
16
+ module.exports = {
17
+ command: 'update',
18
+ description: 'Update SKF files and recompile agents (preserves config and sidecar)',
19
+ options: [],
20
+ action: async () => {
21
+ try {
22
+ const projectDir = process.cwd();
23
+ const skfDir = path.join(projectDir, SKF_FOLDER);
24
+
25
+ if (!(await fs.pathExists(skfDir))) {
26
+ console.log(chalk.yellow('\n SKF is not installed in this directory.'));
27
+ console.log(chalk.dim(' Run: npx skill-forge install\n'));
28
+ process.exit(0);
29
+ return;
30
+ }
31
+
32
+ console.log('');
33
+ console.log(chalk.cyan.bold(' Skill Forge — Quick Update'));
34
+ console.log(chalk.dim(' Replacing SKF files, preserving config and sidecar.\n'));
35
+
36
+ const installer = new Installer();
37
+ const result = await installer.install({
38
+ projectDir,
39
+ skfFolder: SKF_FOLDER,
40
+ _action: 'update',
41
+ });
42
+
43
+ if (result && result.success) {
44
+ // Read config to get IDEs for post-update notes
45
+ let ides = [];
46
+ try {
47
+ const configContent = await fs.readFile(path.join(skfDir, 'config.yaml'), 'utf8');
48
+ const config = yaml.load(configContent);
49
+ ides = config?.ides || [];
50
+ } catch {
51
+ /* use empty */
52
+ }
53
+ const ui = new UI();
54
+ ui.displaySuccess(SKF_FOLDER, ides, 'update');
55
+ }
56
+
57
+ process.exit(0);
58
+ } catch (error) {
59
+ console.error(chalk.red('\nUpdate failed:'), error.message);
60
+ process.exit(1);
61
+ }
62
+ },
63
+ };