workspace-maxxing 0.1.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 (198) hide show
  1. package/.agents/skills/workspace-maxxing/.workspace-templates/CONTEXT.md +44 -0
  2. package/.agents/skills/workspace-maxxing/.workspace-templates/SYSTEM.md +44 -0
  3. package/.agents/skills/workspace-maxxing/.workspace-templates/references/anti-patterns.md +16 -0
  4. package/.agents/skills/workspace-maxxing/.workspace-templates/references/iron-laws.md +26 -0
  5. package/.agents/skills/workspace-maxxing/.workspace-templates/references/reporting-format.md +52 -0
  6. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/benchmark.ts +171 -0
  7. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/dispatch.ts +473 -0
  8. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/generate-tests.ts +158 -0
  9. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/install-tool.ts +82 -0
  10. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/iterate.ts +265 -0
  11. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/orchestrator.ts +539 -0
  12. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/scaffold.ts +282 -0
  13. package/.agents/skills/workspace-maxxing/.workspace-templates/scripts/validate.ts +452 -0
  14. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/architecture/SKILL.md +95 -0
  15. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/fixer/SKILL.md +109 -0
  16. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/iteration/SKILL.md +89 -0
  17. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/prompt-engineering/SKILL.md +87 -0
  18. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/research/SKILL.md +94 -0
  19. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/testing/SKILL.md +89 -0
  20. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/tooling/SKILL.md +87 -0
  21. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/validation/SKILL.md +103 -0
  22. package/.agents/skills/workspace-maxxing/.workspace-templates/skills/worker/SKILL.md +79 -0
  23. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/00-meta/CONTEXT.md +6 -0
  24. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/00-meta/execution-log.md +27 -0
  25. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/01-input/CONTEXT.md +29 -0
  26. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/02-process/CONTEXT.md +29 -0
  27. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/03-output/CONTEXT.md +29 -0
  28. package/.agents/skills/workspace-maxxing/.workspace-templates/workspace/README.md +14 -0
  29. package/.agents/skills/workspace-maxxing/SKILL.md +312 -0
  30. package/.agents/skills/workspace-maxxing/scripts/benchmark.ts +171 -0
  31. package/.agents/skills/workspace-maxxing/scripts/dispatch.ts +473 -0
  32. package/.agents/skills/workspace-maxxing/scripts/generate-tests.ts +158 -0
  33. package/.agents/skills/workspace-maxxing/scripts/install-tool.ts +82 -0
  34. package/.agents/skills/workspace-maxxing/scripts/iterate.ts +265 -0
  35. package/.agents/skills/workspace-maxxing/scripts/orchestrator.ts +539 -0
  36. package/.agents/skills/workspace-maxxing/scripts/scaffold.ts +282 -0
  37. package/.agents/skills/workspace-maxxing/scripts/validate.ts +452 -0
  38. package/README.md +144 -0
  39. package/dist/agent-creator.d.ts +9 -0
  40. package/dist/agent-creator.d.ts.map +1 -0
  41. package/dist/agent-creator.js +199 -0
  42. package/dist/agent-creator.js.map +1 -0
  43. package/dist/agent-iterator.d.ts +38 -0
  44. package/dist/agent-iterator.d.ts.map +1 -0
  45. package/dist/agent-iterator.js +327 -0
  46. package/dist/agent-iterator.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +197 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/install.d.ts +18 -0
  52. package/dist/install.d.ts.map +1 -0
  53. package/dist/install.js +117 -0
  54. package/dist/install.js.map +1 -0
  55. package/dist/platforms/claude.d.ts +7 -0
  56. package/dist/platforms/claude.d.ts.map +1 -0
  57. package/dist/platforms/claude.js +70 -0
  58. package/dist/platforms/claude.js.map +1 -0
  59. package/dist/platforms/copilot.d.ts +7 -0
  60. package/dist/platforms/copilot.d.ts.map +1 -0
  61. package/dist/platforms/copilot.js +75 -0
  62. package/dist/platforms/copilot.js.map +1 -0
  63. package/dist/platforms/gemini.d.ts +7 -0
  64. package/dist/platforms/gemini.d.ts.map +1 -0
  65. package/dist/platforms/gemini.js +81 -0
  66. package/dist/platforms/gemini.js.map +1 -0
  67. package/dist/platforms/index.d.ts +8 -0
  68. package/dist/platforms/index.d.ts.map +1 -0
  69. package/dist/platforms/index.js +41 -0
  70. package/dist/platforms/index.js.map +1 -0
  71. package/dist/platforms/opencode.d.ts +7 -0
  72. package/dist/platforms/opencode.d.ts.map +1 -0
  73. package/dist/platforms/opencode.js +70 -0
  74. package/dist/platforms/opencode.js.map +1 -0
  75. package/dist/scripts/benchmark.d.ts +20 -0
  76. package/dist/scripts/benchmark.d.ts.map +1 -0
  77. package/dist/scripts/benchmark.js +170 -0
  78. package/dist/scripts/benchmark.js.map +1 -0
  79. package/dist/scripts/dispatch.d.ts +32 -0
  80. package/dist/scripts/dispatch.d.ts.map +1 -0
  81. package/dist/scripts/dispatch.js +386 -0
  82. package/dist/scripts/dispatch.js.map +1 -0
  83. package/dist/scripts/generate-tests.d.ts +11 -0
  84. package/dist/scripts/generate-tests.d.ts.map +1 -0
  85. package/dist/scripts/generate-tests.js +118 -0
  86. package/dist/scripts/generate-tests.js.map +1 -0
  87. package/dist/scripts/install-tool.d.ts +8 -0
  88. package/dist/scripts/install-tool.d.ts.map +1 -0
  89. package/dist/scripts/install-tool.js +98 -0
  90. package/dist/scripts/install-tool.js.map +1 -0
  91. package/dist/scripts/iterate.d.ts +44 -0
  92. package/dist/scripts/iterate.d.ts.map +1 -0
  93. package/dist/scripts/iterate.js +260 -0
  94. package/dist/scripts/iterate.js.map +1 -0
  95. package/dist/scripts/orchestrator.d.ts +40 -0
  96. package/dist/scripts/orchestrator.d.ts.map +1 -0
  97. package/dist/scripts/orchestrator.js +378 -0
  98. package/dist/scripts/orchestrator.js.map +1 -0
  99. package/dist/scripts/scaffold.d.ts +8 -0
  100. package/dist/scripts/scaffold.d.ts.map +1 -0
  101. package/dist/scripts/scaffold.js +279 -0
  102. package/dist/scripts/scaffold.js.map +1 -0
  103. package/dist/scripts/validate.d.ts +11 -0
  104. package/dist/scripts/validate.d.ts.map +1 -0
  105. package/dist/scripts/validate.js +472 -0
  106. package/dist/scripts/validate.js.map +1 -0
  107. package/docs/superpowers/plans/2026-04-07-autonomous-iteration-plan.md +1123 -0
  108. package/docs/superpowers/plans/2026-04-07-autonomous-iteration-sub-agent-batches.md +1923 -0
  109. package/docs/superpowers/plans/2026-04-07-autonomous-workflow-sub-skill-plan.md +1505 -0
  110. package/docs/superpowers/plans/2026-04-07-benchmarking-multi-agent-plan.md +854 -0
  111. package/docs/superpowers/plans/2026-04-07-workspace-builder-logic-plan.md +1426 -0
  112. package/docs/superpowers/plans/2026-04-07-workspace-maxxing-plan.md +1299 -0
  113. package/docs/superpowers/plans/2026-04-08-session-294c-subagent-invocation-plan.md +320 -0
  114. package/docs/superpowers/plans/2026-04-08-workflow-prompt-hardening-plan.md +1025 -0
  115. package/docs/superpowers/plans/2026-04-12-workspace-agent-creation-plan.md +992 -0
  116. package/docs/superpowers/specs/2026-04-07-autonomous-iteration-design.md +214 -0
  117. package/docs/superpowers/specs/2026-04-07-autonomous-iteration-sub-agent-batches-design.md +188 -0
  118. package/docs/superpowers/specs/2026-04-07-autonomous-workflow-sub-skill-design.md +137 -0
  119. package/docs/superpowers/specs/2026-04-07-benchmarking-multi-agent-design.md +105 -0
  120. package/docs/superpowers/specs/2026-04-07-workspace-builder-logic-design.md +179 -0
  121. package/docs/superpowers/specs/2026-04-07-workspace-maxxing-design.md +227 -0
  122. package/docs/superpowers/specs/2026-04-08-session-294c-subagent-invocation-design.md +265 -0
  123. package/docs/superpowers/specs/2026-04-08-workflow-prompt-hardening-design.md +146 -0
  124. package/docs/superpowers/specs/2026-04-12-workspace-agent-creation-design.md +239 -0
  125. package/jest.config.js +8 -0
  126. package/package.json +32 -0
  127. package/src/agent-creator.ts +180 -0
  128. package/src/agent-iterator.ts +397 -0
  129. package/src/index.ts +189 -0
  130. package/src/install.ts +105 -0
  131. package/src/platforms/claude.ts +40 -0
  132. package/src/platforms/copilot.ts +50 -0
  133. package/src/platforms/gemini.ts +55 -0
  134. package/src/platforms/index.ts +45 -0
  135. package/src/platforms/opencode.ts +41 -0
  136. package/src/scripts/benchmark.ts +171 -0
  137. package/src/scripts/dispatch.ts +473 -0
  138. package/src/scripts/generate-tests.ts +112 -0
  139. package/src/scripts/install-tool.ts +82 -0
  140. package/src/scripts/iterate.ts +271 -0
  141. package/src/scripts/orchestrator.ts +539 -0
  142. package/src/scripts/scaffold.ts +282 -0
  143. package/src/scripts/validate.ts +516 -0
  144. package/templates/.workspace-templates/CONTEXT.md +44 -0
  145. package/templates/.workspace-templates/SYSTEM.md +44 -0
  146. package/templates/.workspace-templates/references/anti-patterns.md +16 -0
  147. package/templates/.workspace-templates/references/iron-laws.md +26 -0
  148. package/templates/.workspace-templates/references/reporting-format.md +52 -0
  149. package/templates/.workspace-templates/scripts/benchmark.ts +171 -0
  150. package/templates/.workspace-templates/scripts/dispatch.ts +473 -0
  151. package/templates/.workspace-templates/scripts/generate-tests.ts +158 -0
  152. package/templates/.workspace-templates/scripts/install-tool.ts +82 -0
  153. package/templates/.workspace-templates/scripts/iterate.ts +265 -0
  154. package/templates/.workspace-templates/scripts/orchestrator.ts +539 -0
  155. package/templates/.workspace-templates/scripts/scaffold.ts +282 -0
  156. package/templates/.workspace-templates/scripts/validate.ts +452 -0
  157. package/templates/.workspace-templates/skills/architecture/SKILL.md +95 -0
  158. package/templates/.workspace-templates/skills/fixer/SKILL.md +109 -0
  159. package/templates/.workspace-templates/skills/iteration/SKILL.md +89 -0
  160. package/templates/.workspace-templates/skills/prompt-engineering/SKILL.md +87 -0
  161. package/templates/.workspace-templates/skills/research/SKILL.md +94 -0
  162. package/templates/.workspace-templates/skills/testing/SKILL.md +89 -0
  163. package/templates/.workspace-templates/skills/tooling/SKILL.md +87 -0
  164. package/templates/.workspace-templates/skills/validation/SKILL.md +103 -0
  165. package/templates/.workspace-templates/skills/worker/SKILL.md +79 -0
  166. package/templates/.workspace-templates/workspace/00-meta/CONTEXT.md +6 -0
  167. package/templates/.workspace-templates/workspace/00-meta/execution-log.md +27 -0
  168. package/templates/.workspace-templates/workspace/01-input/CONTEXT.md +29 -0
  169. package/templates/.workspace-templates/workspace/02-process/CONTEXT.md +29 -0
  170. package/templates/.workspace-templates/workspace/03-output/CONTEXT.md +29 -0
  171. package/templates/.workspace-templates/workspace/README.md +14 -0
  172. package/templates/SKILL.md +347 -0
  173. package/tests/benchmark.test.ts +158 -0
  174. package/tests/cli.test.ts +109 -0
  175. package/tests/dispatch-parallel.test.ts +124 -0
  176. package/tests/dispatch.test.ts +218 -0
  177. package/tests/fixer-skill.test.ts +203 -0
  178. package/tests/generate-tests.test.ts +101 -0
  179. package/tests/install-tool.test.ts +141 -0
  180. package/tests/install.test.ts +144 -0
  181. package/tests/integration.test.ts +324 -0
  182. package/tests/iterate.test.ts +219 -0
  183. package/tests/orchestrator.test.ts +710 -0
  184. package/tests/scaffold.test.ts +238 -0
  185. package/tests/templates-enhanced.test.ts +208 -0
  186. package/tests/templates.test.ts +219 -0
  187. package/tests/validate.test.ts +421 -0
  188. package/tests/validation-enhanced.test.ts +303 -0
  189. package/tests/worker-skill.test.ts +88 -0
  190. package/tsconfig.json +19 -0
  191. package/workspace/00-meta/CONTEXT.md +3 -0
  192. package/workspace/00-meta/execution-log.md +17 -0
  193. package/workspace/00-meta/tools.md +11 -0
  194. package/workspace/01-input/CONTEXT.md +27 -0
  195. package/workspace/CONTEXT.md +35 -0
  196. package/workspace/README.md +14 -0
  197. package/workspace/SYSTEM.md +36 -0
  198. package/workspace-maxxing-0.1.0.tgz +0 -0
@@ -0,0 +1,1426 @@
1
+ # Workspace Builder Logic Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Implement three helper scripts (scaffold, validate, install-tool) that programmatically generate, validate, and equip ICM-compliant workspaces, plus update the installer and SKILL.md to integrate them.
6
+
7
+ **Architecture:** Three zero-dependency TypeScript scripts in `src/scripts/` that export both CLI entry points and testable functions. Scripts are compiled to `dist/scripts/` and copied to `templates/.workspace-templates/scripts/` during install. The installer (`src/install.ts`) is modified to include the scripts directory in its copy list.
8
+
9
+ **Tech Stack:** TypeScript, Node.js builtins only (`fs`, `path`, `process`, `child_process`), Jest for testing
10
+
11
+ ---
12
+
13
+ ## File Structure
14
+
15
+ **New files:**
16
+ - `src/scripts/scaffold.ts` — Generates ICM workspace from JSON plan
17
+ - `src/scripts/validate.ts` — Checks workspace for ICM compliance
18
+ - `src/scripts/install-tool.ts` — Installs packages and updates tool inventory
19
+ - `templates/.workspace-templates/scripts/scaffold.ts` — Copy for distribution
20
+ - `templates/.workspace-templates/scripts/validate.ts` — Copy for distribution
21
+ - `templates/.workspace-templates/scripts/install-tool.ts` — Copy for distribution
22
+ - `tests/scaffold.test.ts` — Tests for scaffold
23
+ - `tests/validate.test.ts` — Tests for validate
24
+ - `tests/install-tool.test.ts` — Tests for install-tool
25
+
26
+ **Modified files:**
27
+ - `src/install.ts` — Add scripts directory to copy list
28
+ - `templates/SKILL.md` — Add "## Available Scripts" section
29
+ - `package.json` — Add `scripts:build` command to compile scripts
30
+
31
+ ---
32
+
33
+ ### Task 1: Scaffold Script — Tests
34
+
35
+ **Files:**
36
+ - Test: `tests/scaffold.test.ts`
37
+
38
+ - [ ] **Step 1: Write scaffold tests**
39
+
40
+ ```typescript
41
+ // tests/scaffold.test.ts
42
+ import * as fs from 'fs';
43
+ import * as path from 'path';
44
+ import * as os from 'os';
45
+ import { scaffoldWorkspace, ScaffoldOptions } from '../src/scripts/scaffold';
46
+
47
+ describe('scaffold', () => {
48
+ let tempDir: string;
49
+
50
+ beforeEach(() => {
51
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scaffold-test-'));
52
+ });
53
+
54
+ afterEach(() => {
55
+ fs.rmSync(tempDir, { recursive: true, force: true });
56
+ });
57
+
58
+ describe('scaffoldWorkspace', () => {
59
+ it('creates output directory', () => {
60
+ const outputDir = path.join(tempDir, 'workspace');
61
+ const options: ScaffoldOptions = {
62
+ name: 'research',
63
+ stages: ['01-research', '02-analysis', '03-report'],
64
+ output: outputDir,
65
+ };
66
+
67
+ scaffoldWorkspace(options);
68
+
69
+ expect(fs.existsSync(outputDir)).toBe(true);
70
+ });
71
+
72
+ it('creates SYSTEM.md with folder map', () => {
73
+ const outputDir = path.join(tempDir, 'workspace');
74
+ const options: ScaffoldOptions = {
75
+ name: 'research',
76
+ stages: ['01-research', '02-analysis', '03-report'],
77
+ output: outputDir,
78
+ };
79
+
80
+ scaffoldWorkspace(options);
81
+
82
+ const systemMd = fs.readFileSync(path.join(outputDir, 'SYSTEM.md'), 'utf-8');
83
+ expect(systemMd).toContain('## Folder Map');
84
+ expect(systemMd).toContain('01-research');
85
+ expect(systemMd).toContain('02-analysis');
86
+ expect(systemMd).toContain('03-report');
87
+ });
88
+
89
+ it('creates CONTEXT.md at root level', () => {
90
+ const outputDir = path.join(tempDir, 'workspace');
91
+ const options: ScaffoldOptions = {
92
+ name: 'research',
93
+ stages: ['01-research', '02-analysis', '03-report'],
94
+ output: outputDir,
95
+ };
96
+
97
+ scaffoldWorkspace(options);
98
+
99
+ const contextMd = fs.readFileSync(path.join(outputDir, 'CONTEXT.md'), 'utf-8');
100
+ expect(contextMd).toContain('## Routing Table');
101
+ expect(contextMd).toContain('01-research');
102
+ expect(contextMd).toContain('02-analysis');
103
+ expect(contextMd).toContain('03-report');
104
+ });
105
+
106
+ it('creates numbered stage folders with CONTEXT.md', () => {
107
+ const outputDir = path.join(tempDir, 'workspace');
108
+ const options: ScaffoldOptions = {
109
+ name: 'research',
110
+ stages: ['01-research', '02-analysis', '03-report'],
111
+ output: outputDir,
112
+ };
113
+
114
+ scaffoldWorkspace(options);
115
+
116
+ for (const stage of options.stages) {
117
+ const stageDir = path.join(outputDir, stage);
118
+ expect(fs.existsSync(stageDir)).toBe(true);
119
+ const contextMd = fs.readFileSync(path.join(stageDir, 'CONTEXT.md'), 'utf-8');
120
+ expect(contextMd.trim().length).toBeGreaterThan(0);
121
+ expect(contextMd).toContain(stage);
122
+ }
123
+ });
124
+
125
+ it('creates 00-meta folder with tools.md', () => {
126
+ const outputDir = path.join(tempDir, 'workspace');
127
+ const options: ScaffoldOptions = {
128
+ name: 'research',
129
+ stages: ['01-research'],
130
+ output: outputDir,
131
+ };
132
+
133
+ scaffoldWorkspace(options);
134
+
135
+ const toolsMd = path.join(outputDir, '00-meta', 'tools.md');
136
+ expect(fs.existsSync(toolsMd)).toBe(true);
137
+ const content = fs.readFileSync(toolsMd, 'utf-8');
138
+ expect(content).toContain('## Tool Inventory');
139
+ });
140
+
141
+ it('creates README.md', () => {
142
+ const outputDir = path.join(tempDir, 'workspace');
143
+ const options: ScaffoldOptions = {
144
+ name: 'research',
145
+ stages: ['01-research'],
146
+ output: outputDir,
147
+ };
148
+
149
+ scaffoldWorkspace(options);
150
+
151
+ const readme = fs.readFileSync(path.join(outputDir, 'README.md'), 'utf-8');
152
+ expect(readme.trim().length).toBeGreaterThan(0);
153
+ });
154
+
155
+ it('throws if output directory already exists', () => {
156
+ const outputDir = path.join(tempDir, 'workspace');
157
+ fs.mkdirSync(outputDir, { recursive: true });
158
+
159
+ const options: ScaffoldOptions = {
160
+ name: 'research',
161
+ stages: ['01-research'],
162
+ output: outputDir,
163
+ };
164
+
165
+ expect(() => scaffoldWorkspace(options)).toThrow('already exists');
166
+ });
167
+
168
+ it('overwrites if force is true', () => {
169
+ const outputDir = path.join(tempDir, 'workspace');
170
+ fs.mkdirSync(outputDir, { recursive: true });
171
+ fs.writeFileSync(path.join(outputDir, 'existing.txt'), 'data');
172
+
173
+ const options: ScaffoldOptions = {
174
+ name: 'research',
175
+ stages: ['01-research'],
176
+ output: outputDir,
177
+ force: true,
178
+ };
179
+
180
+ scaffoldWorkspace(options);
181
+
182
+ const systemMd = path.join(outputDir, 'SYSTEM.md');
183
+ expect(fs.existsSync(systemMd)).toBe(true);
184
+ });
185
+
186
+ it('throws if stages list is empty', () => {
187
+ const outputDir = path.join(tempDir, 'workspace');
188
+ const options: ScaffoldOptions = {
189
+ name: 'research',
190
+ stages: [],
191
+ output: outputDir,
192
+ };
193
+
194
+ expect(() => scaffoldWorkspace(options)).toThrow('stages');
195
+ });
196
+ });
197
+ });
198
+ ```
199
+
200
+ - [ ] **Step 2: Run tests to verify they fail**
201
+
202
+ ```bash
203
+ npm test -- tests/scaffold.test.ts
204
+ ```
205
+
206
+ Expected: FAIL — module not found or function not defined
207
+
208
+ ---
209
+
210
+ ### Task 2: Scaffold Script — Implementation
211
+
212
+ **Files:**
213
+ - Create: `src/scripts/scaffold.ts`
214
+
215
+ - [ ] **Step 1: Implement scaffold script**
216
+
217
+ ```typescript
218
+ // src/scripts/scaffold.ts
219
+ import * as fs from 'fs';
220
+ import * as path from 'path';
221
+
222
+ export interface ScaffoldOptions {
223
+ name: string;
224
+ stages: string[];
225
+ output: string;
226
+ force?: boolean;
227
+ }
228
+
229
+ export function scaffoldWorkspace(options: ScaffoldOptions): void {
230
+ const { name, stages, output, force = false } = options;
231
+
232
+ // Validate inputs
233
+ if (!stages || stages.length === 0) {
234
+ throw new Error('Stages list cannot be empty');
235
+ }
236
+
237
+ const outputDir = path.resolve(output);
238
+
239
+ // Check if output directory exists
240
+ if (fs.existsSync(outputDir)) {
241
+ if (!force) {
242
+ throw new Error(`Output directory already exists: ${outputDir} (use --force to overwrite)`);
243
+ }
244
+ fs.rmSync(outputDir, { recursive: true, force: true });
245
+ }
246
+
247
+ // Create directory structure
248
+ fs.mkdirSync(outputDir, { recursive: true });
249
+
250
+ // Create SYSTEM.md
251
+ const systemMd = generateSystemMd(name, stages);
252
+ fs.writeFileSync(path.join(outputDir, 'SYSTEM.md'), systemMd);
253
+
254
+ // Create CONTEXT.md
255
+ const contextMd = generateContextMd(name, stages);
256
+ fs.writeFileSync(path.join(outputDir, 'CONTEXT.md'), contextMd);
257
+
258
+ // Create 00-meta folder with tools.md
259
+ const metaDir = path.join(outputDir, '00-meta');
260
+ fs.mkdirSync(metaDir, { recursive: true });
261
+ fs.writeFileSync(path.join(metaDir, 'tools.md'), generateToolsMd());
262
+ fs.writeFileSync(path.join(metaDir, 'CONTEXT.md'), `# 00-meta Context\n\nMetadata and tool inventory for the ${name} workspace.\n`);
263
+
264
+ // Create stage folders
265
+ for (const stage of stages) {
266
+ const stageDir = path.join(outputDir, stage);
267
+ fs.mkdirSync(stageDir, { recursive: true });
268
+ fs.writeFileSync(
269
+ path.join(stageDir, 'CONTEXT.md'),
270
+ generateStageContextMd(name, stage),
271
+ );
272
+ }
273
+
274
+ // Create README.md
275
+ fs.writeFileSync(path.join(outputDir, 'README.md'), generateReadmeMd(name, stages));
276
+
277
+ console.log(`Workspace "${name}" scaffolded at: ${outputDir}`);
278
+ }
279
+
280
+ function generateSystemMd(name: string, stages: string[]): string {
281
+ return `# ${name} — System Prompt
282
+
283
+ ## Role
284
+ You are an AI assistant working within the ${name} workspace.
285
+
286
+ ## Folder Map
287
+
288
+ ${stages.map((s) => `- \`${s}/\` — ${stageDescription(s)}`).join('\n')}
289
+ - \`00-meta/\` — Metadata and tool inventory
290
+
291
+ ## Rules
292
+ - Follow ICM methodology: canonical sources, one-way dependencies, selective loading
293
+ - Each numbered folder is a workflow stage with its own CONTEXT.md for routing
294
+ - Do not create content outside the defined structure
295
+ `;
296
+ }
297
+
298
+ function generateContextMd(name: string, stages: string[]): string {
299
+ return `# ${name} — Context Router
300
+
301
+ ## Routing Table
302
+
303
+ ${stages.map((s) => `- \`${s}/\` → \`${s}/CONTEXT.md\``).join('\n')}
304
+ - \`00-meta/\` → \`00-meta/tools.md\`
305
+
306
+ ## How to Use
307
+ When working on a task, load only the CONTEXT.md for the relevant stage.
308
+ Do not load the entire workspace. Route to specific sections.
309
+ `;
310
+ }
311
+
312
+ function generateStageContextMd(name: string, stage: string): string {
313
+ return `# ${stage} — Context
314
+
315
+ ## Purpose
316
+ This folder handles the ${stage} stage of the ${name} workflow.
317
+
318
+ ## Inputs
319
+ - Define what inputs this stage expects
320
+
321
+ ## Outputs
322
+ - Define what outputs this stage produces
323
+
324
+ ## Dependencies
325
+ - List upstream stages this stage depends on
326
+ `;
327
+ }
328
+
329
+ function generateToolsMd(): string {
330
+ return `# Tool Inventory
331
+
332
+ ## Installed Tools
333
+
334
+ | Tool | Version | Manager | Installed |
335
+ |------|---------|---------|-----------|
336
+ | — | — | — | — |
337
+
338
+ ## Pending Tools
339
+
340
+ List tools that are proposed but not yet approved.
341
+ `;
342
+ }
343
+
344
+ function generateReadmeMd(name: string, stages: string[]): string {
345
+ return `# ${name} Workspace
346
+
347
+ ## Structure
348
+
349
+ ${stages.map((s) => `- \`${s}/\``).join('\n')}
350
+ - \`00-meta/\`
351
+
352
+ ## Usage
353
+
354
+ 1. Follow the workflow stages in order
355
+ 2. Load CONTEXT.md files selectively — only what you need
356
+ 3. Update tools.md when installing new tools
357
+ 4. Run validate.ts to check ICM compliance
358
+ `;
359
+ }
360
+
361
+ function stageDescription(stage: string): string {
362
+ const descriptions: Record<string, string> = {
363
+ '01-input': 'Input collection and validation',
364
+ '02-process': 'Processing and transformation',
365
+ '03-output': 'Output generation and delivery',
366
+ };
367
+ return descriptions[stage] || `Stage: ${stage}`;
368
+ }
369
+
370
+ // CLI entry point
371
+ if (require.main === module) {
372
+ const args = process.argv.slice(2);
373
+ const parseArg = (flag: string): string | undefined => {
374
+ const idx = args.indexOf(flag);
375
+ return idx !== -1 ? args[idx + 1] : undefined;
376
+ };
377
+
378
+ const hasFlag = (flag: string): boolean => args.includes(flag);
379
+
380
+ const name = parseArg('--name');
381
+ const stagesStr = parseArg('--stages');
382
+ const output = parseArg('--output');
383
+
384
+ if (!name || !stagesStr || !output) {
385
+ console.error('Usage: node scaffold.ts --name <name> --stages <s1,s2,...> --output <path> [--force]');
386
+ process.exit(1);
387
+ }
388
+
389
+ const stages = stagesStr.split(',').map((s) => s.trim()).filter(Boolean);
390
+
391
+ scaffoldWorkspace({
392
+ name,
393
+ stages,
394
+ output,
395
+ force: hasFlag('--force'),
396
+ });
397
+ }
398
+ ```
399
+
400
+ - [ ] **Step 2: Run tests to verify they pass**
401
+
402
+ ```bash
403
+ npm test -- tests/scaffold.test.ts
404
+ ```
405
+
406
+ Expected: All 9 tests PASS
407
+
408
+ - [ ] **Step 3: Commit**
409
+
410
+ ```bash
411
+ git add src/scripts/scaffold.ts tests/scaffold.test.ts
412
+ git commit -m "feat: add scaffold script with tests"
413
+ ```
414
+
415
+ ---
416
+
417
+ ### Task 3: Validate Script — Tests
418
+
419
+ **Files:**
420
+ - Test: `tests/validate.test.ts`
421
+
422
+ - [ ] **Step 1: Write validate tests**
423
+
424
+ ```typescript
425
+ // tests/validate.test.ts
426
+ import * as fs from 'fs';
427
+ import * as path from 'path';
428
+ import * as os from 'os';
429
+ import { validateWorkspace, ValidationResult } from '../src/scripts/validate';
430
+
431
+ describe('validate', () => {
432
+ let tempDir: string;
433
+
434
+ beforeEach(() => {
435
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'validate-test-'));
436
+ });
437
+
438
+ afterEach(() => {
439
+ fs.rmSync(tempDir, { recursive: true, force: true });
440
+ });
441
+
442
+ function createValidWorkspace(): string {
443
+ const ws = path.join(tempDir, 'workspace');
444
+ fs.mkdirSync(ws, { recursive: true });
445
+
446
+ fs.writeFileSync(path.join(ws, 'SYSTEM.md'), '# Test\n\n## Folder Map\n\n- `01-input/`\n- `02-output/`\n');
447
+ fs.writeFileSync(path.join(ws, 'CONTEXT.md'), '# Router\n\n## Routing Table\n\n- `01-input/`\n');
448
+ fs.mkdirSync(path.join(ws, '01-input'), { recursive: true });
449
+ fs.writeFileSync(path.join(ws, '01-input', 'CONTEXT.md'), '# 01-input\n\nContent here.\n');
450
+ fs.mkdirSync(path.join(ws, '02-output'), { recursive: true });
451
+ fs.writeFileSync(path.join(ws, '02-output', 'CONTEXT.md'), '# 02-output\n\nContent here.\n');
452
+ fs.mkdirSync(path.join(ws, '00-meta'), { recursive: true });
453
+ fs.writeFileSync(path.join(ws, '00-meta', 'tools.md'), '# Tools\n\n| Tool | Version |\n|------|---------|\n');
454
+
455
+ return ws;
456
+ }
457
+
458
+ describe('validateWorkspace', () => {
459
+ it('passes for a valid workspace', () => {
460
+ const ws = createValidWorkspace();
461
+ const result = validateWorkspace(ws);
462
+
463
+ expect(result.passed).toBe(true);
464
+ expect(result.checks.every((c) => c.passed)).toBe(true);
465
+ });
466
+
467
+ it('fails if SYSTEM.md is missing', () => {
468
+ const ws = createValidWorkspace();
469
+ fs.unlinkSync(path.join(ws, 'SYSTEM.md'));
470
+
471
+ const result = validateWorkspace(ws);
472
+
473
+ expect(result.passed).toBe(false);
474
+ const systemCheck = result.checks.find((c) => c.name === 'SYSTEM.md exists');
475
+ expect(systemCheck?.passed).toBe(false);
476
+ });
477
+
478
+ it('fails if SYSTEM.md has no folder map', () => {
479
+ const ws = createValidWorkspace();
480
+ fs.writeFileSync(path.join(ws, 'SYSTEM.md'), '# Test\n\nNo folder map here.\n');
481
+
482
+ const result = validateWorkspace(ws);
483
+
484
+ expect(result.passed).toBe(false);
485
+ const check = result.checks.find((c) => c.name.includes('folder map'));
486
+ expect(check?.passed).toBe(false);
487
+ });
488
+
489
+ it('fails if CONTEXT.md is missing at root', () => {
490
+ const ws = createValidWorkspace();
491
+ fs.unlinkSync(path.join(ws, 'CONTEXT.md'));
492
+
493
+ const result = validateWorkspace(ws);
494
+
495
+ expect(result.passed).toBe(false);
496
+ const check = result.checks.find((c) => c.name.includes('CONTEXT.md exists'));
497
+ expect(check?.passed).toBe(false);
498
+ });
499
+
500
+ it('fails if a numbered folder is missing CONTEXT.md', () => {
501
+ const ws = createValidWorkspace();
502
+ fs.unlinkSync(path.join(ws, '01-input', 'CONTEXT.md'));
503
+
504
+ const result = validateWorkspace(ws);
505
+
506
+ expect(result.passed).toBe(false);
507
+ const check = result.checks.find((c) => c.name.includes('CONTEXT.md'));
508
+ expect(check?.passed).toBe(false);
509
+ });
510
+
511
+ it('fails if a CONTEXT.md is empty', () => {
512
+ const ws = createValidWorkspace();
513
+ fs.writeFileSync(path.join(ws, '01-input', 'CONTEXT.md'), '');
514
+
515
+ const result = validateWorkspace(ws);
516
+
517
+ expect(result.passed).toBe(false);
518
+ const check = result.checks.find((c) => c.name.includes('empty'));
519
+ expect(check?.passed).toBe(false);
520
+ });
521
+
522
+ it('fails if duplicate content exists across files', () => {
523
+ const ws = createValidWorkspace();
524
+ const duplicateText = 'This is a long duplicate text block that appears in multiple files and should be flagged as a potential duplicate content issue for testing purposes.';
525
+ fs.appendFileSync(path.join(ws, '01-input', 'CONTEXT.md'), duplicateText);
526
+ fs.appendFileSync(path.join(ws, '02-output', 'CONTEXT.md'), duplicateText);
527
+
528
+ const result = validateWorkspace(ws);
529
+
530
+ expect(result.passed).toBe(false);
531
+ const check = result.checks.find((c) => c.name.includes('duplicate'));
532
+ expect(check?.passed).toBe(false);
533
+ });
534
+
535
+ it('returns structured output with all check names', () => {
536
+ const ws = createValidWorkspace();
537
+ const result = validateWorkspace(ws);
538
+
539
+ const checkNames = result.checks.map((c) => c.name);
540
+ expect(checkNames).toContain('SYSTEM.md exists');
541
+ expect(checkNames).toContain('CONTEXT.md exists at root');
542
+ });
543
+ });
544
+ });
545
+ ```
546
+
547
+ - [ ] **Step 2: Run tests to verify they fail**
548
+
549
+ ```bash
550
+ npm test -- tests/validate.test.ts
551
+ ```
552
+
553
+ Expected: FAIL — module not found
554
+
555
+ ---
556
+
557
+ ### Task 4: Validate Script — Implementation
558
+
559
+ **Files:**
560
+ - Create: `src/scripts/validate.ts`
561
+
562
+ - [ ] **Step 1: Implement validate script**
563
+
564
+ ```typescript
565
+ // src/scripts/validate.ts
566
+ import * as fs from 'fs';
567
+ import * as path from 'path';
568
+
569
+ export interface CheckResult {
570
+ name: string;
571
+ passed: boolean;
572
+ message: string;
573
+ }
574
+
575
+ export interface ValidationResult {
576
+ passed: boolean;
577
+ checks: CheckResult[];
578
+ }
579
+
580
+ export function validateWorkspace(workspacePath: string): ValidationResult {
581
+ const ws = path.resolve(workspacePath);
582
+ const checks: CheckResult[] = [];
583
+
584
+ // Check 1: SYSTEM.md exists
585
+ const systemMdPath = path.join(ws, 'SYSTEM.md');
586
+ const systemExists = fs.existsSync(systemMdPath);
587
+ checks.push({
588
+ name: 'SYSTEM.md exists',
589
+ passed: systemExists,
590
+ message: systemExists ? 'Found' : 'Missing',
591
+ });
592
+
593
+ // Check 2: SYSTEM.md contains folder map
594
+ if (systemExists) {
595
+ const systemContent = fs.readFileSync(systemMdPath, 'utf-8');
596
+ const hasFolderMap = systemContent.toLowerCase().includes('folder map');
597
+ checks.push({
598
+ name: 'SYSTEM.md contains folder map',
599
+ passed: hasFolderMap,
600
+ message: hasFolderMap ? 'Found' : 'Missing "folder map" reference',
601
+ });
602
+ } else {
603
+ checks.push({
604
+ name: 'SYSTEM.md contains folder map',
605
+ passed: false,
606
+ message: 'Cannot check — SYSTEM.md missing',
607
+ });
608
+ }
609
+
610
+ // Check 3: CONTEXT.md exists at root
611
+ const contextMdPath = path.join(ws, 'CONTEXT.md');
612
+ const contextExists = fs.existsSync(contextMdPath);
613
+ checks.push({
614
+ name: 'CONTEXT.md exists at root',
615
+ passed: contextExists,
616
+ message: contextExists ? 'Found' : 'Missing',
617
+ });
618
+
619
+ // Check 4: Every numbered folder has CONTEXT.md
620
+ const entries = fs.readdirSync(ws, { withFileTypes: true });
621
+ const numberedFolders = entries
622
+ .filter((e) => e.isDirectory() && /^\d/.test(e.name))
623
+ .map((e) => e.name);
624
+
625
+ for (const folder of numberedFolders) {
626
+ const contextPath = path.join(ws, folder, 'CONTEXT.md');
627
+ const exists = fs.existsSync(contextPath);
628
+ checks.push({
629
+ name: `${folder}/CONTEXT.md exists`,
630
+ passed: exists,
631
+ message: exists ? 'Found' : 'Missing',
632
+ });
633
+
634
+ // Check 5: No empty CONTEXT.md files
635
+ if (exists) {
636
+ const content = fs.readFileSync(contextPath, 'utf-8');
637
+ const notEmpty = content.trim().length > 0;
638
+ checks.push({
639
+ name: `${folder}/CONTEXT.md is not empty`,
640
+ passed: notEmpty,
641
+ message: notEmpty ? `${content.trim().length} chars` : 'File is empty',
642
+ });
643
+ }
644
+ }
645
+
646
+ // Check 6: No duplicate content across files
647
+ const allFiles = getAllMarkdownFiles(ws);
648
+ const duplicateCheck = checkDuplicateContent(allFiles);
649
+ checks.push(duplicateCheck);
650
+
651
+ const passed = checks.every((c) => c.passed);
652
+
653
+ // Print results
654
+ console.log(`\nValidation: ${ws}`);
655
+ console.log('='.repeat(50));
656
+ for (const check of checks) {
657
+ const icon = check.passed ? '✓' : '✗';
658
+ console.log(` ${icon} ${check.name}: ${check.message}`);
659
+ }
660
+ console.log('='.repeat(50));
661
+ console.log(passed ? '✓ All checks passed' : '✗ Some checks failed');
662
+
663
+ return { passed, checks };
664
+ }
665
+
666
+ function getAllMarkdownFiles(dir: string): string[] {
667
+ const results: string[] = [];
668
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
669
+
670
+ for (const entry of entries) {
671
+ const fullPath = path.join(dir, entry.name);
672
+ if (entry.isDirectory()) {
673
+ results.push(...getAllMarkdownFiles(fullPath));
674
+ } else if (entry.name.endsWith('.md')) {
675
+ results.push(fullPath);
676
+ }
677
+ }
678
+
679
+ return results;
680
+ }
681
+
682
+ function checkDuplicateContent(files: string[]): CheckResult {
683
+ const MIN_DUPLICATE_LENGTH = 50;
684
+ const duplicates: string[] = [];
685
+
686
+ for (let i = 0; i < files.length; i++) {
687
+ for (let j = i + 1; j < files.length; j++) {
688
+ const contentA = fs.readFileSync(files[i], 'utf-8');
689
+ const contentB = fs.readFileSync(files[j], 'utf-8');
690
+
691
+ // Check for identical text blocks > 50 characters
692
+ const linesA = contentA.split('\n');
693
+ const linesB = contentB.split('\n');
694
+
695
+ for (const lineA of linesA) {
696
+ const trimmed = lineA.trim();
697
+ if (trimmed.length > MIN_DUPLICATE_LENGTH) {
698
+ for (const lineB of linesB) {
699
+ if (lineB.trim() === trimmed) {
700
+ duplicates.push(trimmed.substring(0, 60) + '...');
701
+ break;
702
+ }
703
+ }
704
+ }
705
+ }
706
+ }
707
+ }
708
+
709
+ if (duplicates.length > 0) {
710
+ return {
711
+ name: 'No duplicate content across files',
712
+ passed: false,
713
+ message: `Found ${duplicates.length} duplicate text block(s)`,
714
+ };
715
+ }
716
+
717
+ return {
718
+ name: 'No duplicate content across files',
719
+ passed: true,
720
+ message: 'No duplicates found',
721
+ };
722
+ }
723
+
724
+ // CLI entry point
725
+ if (require.main === module) {
726
+ const args = process.argv.slice(2);
727
+ const workspaceIdx = args.indexOf('--workspace');
728
+ const workspace = workspaceIdx !== -1 ? args[workspaceIdx + 1] : undefined;
729
+
730
+ if (!workspace) {
731
+ console.error('Usage: node validate.ts --workspace <path>');
732
+ process.exit(1);
733
+ }
734
+
735
+ const result = validateWorkspace(workspace);
736
+ process.exit(result.passed ? 0 : 1);
737
+ }
738
+ ```
739
+
740
+ - [ ] **Step 2: Run tests to verify they pass**
741
+
742
+ ```bash
743
+ npm test -- tests/validate.test.ts
744
+ ```
745
+
746
+ Expected: All 8 tests PASS
747
+
748
+ - [ ] **Step 3: Commit**
749
+
750
+ ```bash
751
+ git add src/scripts/validate.ts tests/validate.test.ts
752
+ git commit -m "feat: add validate script with tests"
753
+ ```
754
+
755
+ ---
756
+
757
+ ### Task 5: Install-Tool Script — Tests
758
+
759
+ **Files:**
760
+ - Test: `tests/install-tool.test.ts`
761
+
762
+ - [ ] **Step 1: Write install-tool tests**
763
+
764
+ ```typescript
765
+ // tests/install-tool.test.ts
766
+ import * as fs from 'fs';
767
+ import * as path from 'path';
768
+ import * as os from 'os';
769
+ import { installTool, InstallToolOptions } from '../src/scripts/install-tool';
770
+
771
+ jest.mock('child_process', () => ({
772
+ execSync: jest.fn(),
773
+ }));
774
+
775
+ const { execSync } = require('child_process');
776
+
777
+ describe('install-tool', () => {
778
+ let tempDir: string;
779
+
780
+ beforeEach(() => {
781
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'install-tool-test-'));
782
+ // Create minimal workspace
783
+ const metaDir = path.join(tempDir, '00-meta');
784
+ fs.mkdirSync(metaDir, { recursive: true });
785
+ fs.writeFileSync(
786
+ path.join(metaDir, 'tools.md'),
787
+ '# Tool Inventory\n\n## Installed Tools\n\n| Tool | Version | Manager | Installed |\n|------|---------|---------|-----------|\n| — | — | — | — |\n',
788
+ );
789
+ });
790
+
791
+ afterEach(() => {
792
+ fs.rmSync(tempDir, { recursive: true, force: true });
793
+ jest.clearAllMocks();
794
+ });
795
+
796
+ describe('installTool', () => {
797
+ it('runs correct npm install command', () => {
798
+ (execSync as jest.Mock).mockReturnValue('');
799
+
800
+ const options: InstallToolOptions = {
801
+ tool: 'pdf-lib',
802
+ manager: 'npm',
803
+ workspace: tempDir,
804
+ };
805
+
806
+ installTool(options);
807
+
808
+ expect(execSync).toHaveBeenCalledWith(
809
+ 'npm install pdf-lib',
810
+ expect.objectContaining({ cwd: tempDir, stdio: 'inherit' }),
811
+ );
812
+ });
813
+
814
+ it('runs correct pip install command', () => {
815
+ (execSync as jest.Mock).mockReturnValue('');
816
+
817
+ const options: InstallToolOptions = {
818
+ tool: 'requests',
819
+ manager: 'pip',
820
+ workspace: tempDir,
821
+ };
822
+
823
+ installTool(options);
824
+
825
+ expect(execSync).toHaveBeenCalledWith(
826
+ 'pip install requests',
827
+ expect.objectContaining({ cwd: tempDir, stdio: 'inherit' }),
828
+ );
829
+ });
830
+
831
+ it('runs correct npx install command', () => {
832
+ (execSync as jest.Mock).mockReturnValue('');
833
+
834
+ const options: InstallToolOptions = {
835
+ tool: 'create-next-app',
836
+ manager: 'npx',
837
+ workspace: tempDir,
838
+ };
839
+
840
+ installTool(options);
841
+
842
+ expect(execSync).toHaveBeenCalledWith(
843
+ 'npx create-next-app',
844
+ expect.objectContaining({ cwd: tempDir, stdio: 'inherit' }),
845
+ );
846
+ });
847
+
848
+ it('runs correct brew install command', () => {
849
+ (execSync as jest.Mock).mockReturnValue('');
850
+
851
+ const options: InstallToolOptions = {
852
+ tool: 'ffmpeg',
853
+ manager: 'brew',
854
+ workspace: tempDir,
855
+ };
856
+
857
+ installTool(options);
858
+
859
+ expect(execSync).toHaveBeenCalledWith(
860
+ 'brew install ffmpeg',
861
+ expect.objectContaining({ cwd: tempDir, stdio: 'inherit' }),
862
+ );
863
+ });
864
+
865
+ it('updates tools.md with installed tool', () => {
866
+ (execSync as jest.Mock).mockReturnValue('');
867
+
868
+ const options: InstallToolOptions = {
869
+ tool: 'pdf-lib',
870
+ manager: 'npm',
871
+ workspace: tempDir,
872
+ };
873
+
874
+ installTool(options);
875
+
876
+ const toolsMd = fs.readFileSync(path.join(tempDir, '00-meta', 'tools.md'), 'utf-8');
877
+ expect(toolsMd).toContain('pdf-lib');
878
+ expect(toolsMd).toContain('npm');
879
+ });
880
+
881
+ it('throws if install command fails', () => {
882
+ (execSync as jest.Mock).mockImplementation(() => {
883
+ const err = new Error('Command failed');
884
+ (err as any).status = 1;
885
+ throw err;
886
+ });
887
+
888
+ const options: InstallToolOptions = {
889
+ tool: 'nonexistent-package',
890
+ manager: 'npm',
891
+ workspace: tempDir,
892
+ };
893
+
894
+ expect(() => installTool(options)).toThrow('Command failed');
895
+ });
896
+
897
+ it('throws for unsupported manager', () => {
898
+ const options: InstallToolOptions = {
899
+ tool: 'something',
900
+ manager: 'unknown' as any,
901
+ workspace: tempDir,
902
+ };
903
+
904
+ expect(() => installTool(options)).toThrow('Unsupported package manager');
905
+ });
906
+ });
907
+ });
908
+ ```
909
+
910
+ - [ ] **Step 2: Run tests to verify they fail**
911
+
912
+ ```bash
913
+ npm test -- tests/install-tool.test.ts
914
+ ```
915
+
916
+ Expected: FAIL — module not found
917
+
918
+ ---
919
+
920
+ ### Task 6: Install-Tool Script — Implementation
921
+
922
+ **Files:**
923
+ - Create: `src/scripts/install-tool.ts`
924
+
925
+ - [ ] **Step 1: Implement install-tool script**
926
+
927
+ ```typescript
928
+ // src/scripts/install-tool.ts
929
+ import * as fs from 'fs';
930
+ import * as path from 'path';
931
+ import { execSync } from 'child_process';
932
+
933
+ export type PackageManager = 'npm' | 'pip' | 'npx' | 'brew';
934
+
935
+ export interface InstallToolOptions {
936
+ tool: string;
937
+ manager: PackageManager;
938
+ workspace: string;
939
+ }
940
+
941
+ export function installTool(options: InstallToolOptions): void {
942
+ const { tool, manager, workspace } = options;
943
+ const ws = path.resolve(workspace);
944
+
945
+ // Validate package manager
946
+ const validManagers: PackageManager[] = ['npm', 'pip', 'npx', 'brew'];
947
+ if (!validManagers.includes(manager)) {
948
+ throw new Error(`Unsupported package manager: ${manager}. Supported: ${validManagers.join(', ')}`);
949
+ }
950
+
951
+ // Build install command
952
+ const command = `${manager} install ${tool}`;
953
+
954
+ console.log(`Installing ${tool} via ${manager}...`);
955
+ try {
956
+ execSync(command, { cwd: ws, stdio: 'inherit' });
957
+ } catch (error) {
958
+ const message = error instanceof Error ? error.message : String(error);
959
+ throw new Error(`Failed to install ${tool}: ${message}`);
960
+ }
961
+
962
+ // Update tools.md
963
+ updateToolsMd(ws, tool, manager);
964
+
965
+ console.log(`✓ ${tool} installed and added to tool inventory`);
966
+ }
967
+
968
+ function updateToolsMd(workspace: string, tool: string, manager: string): void {
969
+ const toolsMdPath = path.join(workspace, '00-meta', 'tools.md');
970
+
971
+ if (!fs.existsSync(toolsMdPath)) {
972
+ throw new Error(`tools.md not found at: ${toolsMdPath}`);
973
+ }
974
+
975
+ const content = fs.readFileSync(toolsMdPath, 'utf-8');
976
+ const now = new Date().toISOString().split('T')[0];
977
+
978
+ // Replace the placeholder row or add new row
979
+ const placeholderRow = '| — | — | — | — |';
980
+ const newRow = `| ${tool} | latest | ${manager} | ${now} |`;
981
+
982
+ let updated: string;
983
+ if (content.includes(placeholderRow)) {
984
+ updated = content.replace(placeholderRow, `${placeholderRow}\n${newRow}`);
985
+ } else {
986
+ // Insert before the "## Pending Tools" section or at end
987
+ const pendingIdx = content.indexOf('## Pending Tools');
988
+ if (pendingIdx !== -1) {
989
+ updated = content.slice(0, pendingIdx) + newRow + '\n\n' + content.slice(pendingIdx);
990
+ } else {
991
+ updated = content + '\n' + newRow + '\n';
992
+ }
993
+ }
994
+
995
+ fs.writeFileSync(toolsMdPath, updated);
996
+ }
997
+
998
+ // CLI entry point
999
+ if (require.main === module) {
1000
+ const args = process.argv.slice(2);
1001
+ const parseArg = (flag: string): string | undefined => {
1002
+ const idx = args.indexOf(flag);
1003
+ return idx !== -1 ? args[idx + 1] : undefined;
1004
+ };
1005
+
1006
+ const tool = parseArg('--tool');
1007
+ const manager = parseArg('--manager') as PackageManager;
1008
+ const workspace = parseArg('--workspace');
1009
+
1010
+ if (!tool || !manager || !workspace) {
1011
+ console.error('Usage: node install-tool.ts --tool <name> --manager <npm|pip|npx|brew> --workspace <path>');
1012
+ process.exit(1);
1013
+ }
1014
+
1015
+ installTool({ tool, manager, workspace });
1016
+ }
1017
+ ```
1018
+
1019
+ - [ ] **Step 2: Run tests to verify they pass**
1020
+
1021
+ ```bash
1022
+ npm test -- tests/install-tool.test.ts
1023
+ ```
1024
+
1025
+ Expected: All 7 tests PASS
1026
+
1027
+ - [ ] **Step 3: Commit**
1028
+
1029
+ ```bash
1030
+ git add src/scripts/install-tool.ts tests/install-tool.test.ts
1031
+ git commit -m "feat: add install-tool script with tests"
1032
+ ```
1033
+
1034
+ ---
1035
+
1036
+ ### Task 7: Update Installer to Copy Scripts
1037
+
1038
+ **Files:**
1039
+ - Modify: `src/install.ts`
1040
+
1041
+ - [ ] **Step 1: Modify install.ts to also copy scripts directory**
1042
+
1043
+ The current `installSkill` function copies SKILL.md and `.workspace-templates/`. We need to ensure the scripts directory inside `.workspace-templates/` is also copied. Since the existing code already does a recursive copy of `.workspace-templates/` via `copyDirSync`, scripts will be included automatically once we add them to `templates/.workspace-templates/scripts/`.
1044
+
1045
+ However, we also need to copy the scripts to the skill directory root so agents can invoke them directly. Add a new copy operation:
1046
+
1047
+ ```typescript
1048
+ // src/install.ts — add after the .workspace-templates copy block (before the return statement)
1049
+
1050
+ // Copy scripts to skill root for direct invocation
1051
+ const scriptsSrc = path.join(templatesDir, '.workspace-templates', 'scripts');
1052
+ if (fs.existsSync(scriptsSrc)) {
1053
+ const scriptsDest = path.join(skillDir, 'scripts');
1054
+ copyDirSync(scriptsSrc, scriptsDest);
1055
+ }
1056
+ ```
1057
+
1058
+ Full modified file:
1059
+
1060
+ ```typescript
1061
+ import * as fs from 'fs';
1062
+ import * as path from 'path';
1063
+
1064
+ export interface InstallResult {
1065
+ success: boolean;
1066
+ skillPath: string;
1067
+ error?: string;
1068
+ }
1069
+
1070
+ /**
1071
+ * Walk up from startDir looking for package.json or .git directory.
1072
+ * Returns the first parent containing a marker, or startDir if none found.
1073
+ */
1074
+ export function detectProjectRoot(startDir: string): string {
1075
+ let current = path.resolve(startDir);
1076
+ const root = path.parse(current).root;
1077
+
1078
+ while (current !== root) {
1079
+ const hasPackageJson = fs.existsSync(path.join(current, 'package.json'));
1080
+ const hasGit = fs.existsSync(path.join(current, '.git'));
1081
+
1082
+ if (hasPackageJson || hasGit) {
1083
+ return current;
1084
+ }
1085
+
1086
+ current = path.dirname(current);
1087
+ }
1088
+
1089
+ return startDir;
1090
+ }
1091
+
1092
+ /**
1093
+ * Recursively copy a directory, overwriting existing files.
1094
+ */
1095
+ function copyDirSync(src: string, dest: string): void {
1096
+ if (!fs.existsSync(src)) {
1097
+ throw new Error(`Source directory not found: ${src}`);
1098
+ }
1099
+
1100
+ fs.mkdirSync(dest, { recursive: true });
1101
+
1102
+ const entries = fs.readdirSync(src, { withFileTypes: true });
1103
+
1104
+ for (const entry of entries) {
1105
+ const srcPath = path.join(src, entry.name);
1106
+ const destPath = path.join(dest, entry.name);
1107
+
1108
+ if (entry.isDirectory()) {
1109
+ copyDirSync(srcPath, destPath);
1110
+ } else {
1111
+ fs.copyFileSync(srcPath, destPath);
1112
+ }
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * Install the workspace-maxxing skill into a project.
1118
+ * Copies SKILL.md, .workspace-templates/, and scripts/ to .agents/skills/workspace-maxxing/
1119
+ */
1120
+ export async function installSkill(
1121
+ projectRoot: string,
1122
+ templatesDir: string,
1123
+ ): Promise<InstallResult> {
1124
+ const skillDir = path.join(projectRoot, '.agents', 'skills', 'workspace-maxxing');
1125
+
1126
+ try {
1127
+ // Copy SKILL.md
1128
+ const skillMdSrc = path.join(templatesDir, 'SKILL.md');
1129
+ const skillMdDest = path.join(skillDir, 'SKILL.md');
1130
+ fs.mkdirSync(path.dirname(skillMdDest), { recursive: true });
1131
+ fs.copyFileSync(skillMdSrc, skillMdDest);
1132
+
1133
+ // Copy .workspace-templates/
1134
+ const workspaceTemplatesSrc = path.join(templatesDir, '.workspace-templates');
1135
+ const workspaceTemplatesDest = path.join(skillDir, '.workspace-templates');
1136
+ copyDirSync(workspaceTemplatesSrc, workspaceTemplatesDest);
1137
+
1138
+ // Copy scripts to skill root for direct invocation
1139
+ const scriptsSrc = path.join(templatesDir, '.workspace-templates', 'scripts');
1140
+ if (fs.existsSync(scriptsSrc)) {
1141
+ const scriptsDest = path.join(skillDir, 'scripts');
1142
+ copyDirSync(scriptsSrc, scriptsDest);
1143
+ }
1144
+
1145
+ return { success: true, skillPath: skillDir };
1146
+ } catch (error) {
1147
+ const message = error instanceof Error ? error.message : String(error);
1148
+ return { success: false, skillPath: skillDir, error: message };
1149
+ }
1150
+ }
1151
+ ```
1152
+
1153
+ - [ ] **Step 2: Update integration test to verify scripts are copied**
1154
+
1155
+ Modify `tests/integration.test.ts` — add scripts to the expected files list:
1156
+
1157
+ ```typescript
1158
+ // In the first integration test, add to expectedFiles array:
1159
+ const expectedFiles = [
1160
+ 'SKILL.md',
1161
+ '.workspace-templates/SYSTEM.md',
1162
+ '.workspace-templates/CONTEXT.md',
1163
+ '.workspace-templates/workspace/00-meta/CONTEXT.md',
1164
+ '.workspace-templates/workspace/01-input/CONTEXT.md',
1165
+ '.workspace-templates/workspace/02-process/CONTEXT.md',
1166
+ '.workspace-templates/workspace/03-output/CONTEXT.md',
1167
+ '.workspace-templates/workspace/README.md',
1168
+ 'scripts/scaffold.ts',
1169
+ 'scripts/validate.ts',
1170
+ 'scripts/install-tool.ts',
1171
+ ];
1172
+ ```
1173
+
1174
+ - [ ] **Step 3: Run all tests to verify**
1175
+
1176
+ ```bash
1177
+ npm test
1178
+ ```
1179
+
1180
+ Expected: All tests PASS (existing + new)
1181
+
1182
+ - [ ] **Step 4: Commit**
1183
+
1184
+ ```bash
1185
+ git add src/install.ts tests/integration.test.ts
1186
+ git commit -m "feat: update installer to copy scripts directory"
1187
+ ```
1188
+
1189
+ ---
1190
+
1191
+ ### Task 8: Copy Scripts to Templates Directory
1192
+
1193
+ **Files:**
1194
+ - Create: `templates/.workspace-templates/scripts/scaffold.ts`
1195
+ - Create: `templates/.workspace-templates/scripts/validate.ts`
1196
+ - Create: `templates/.workspace-templates/scripts/install-tool.ts`
1197
+
1198
+ - [ ] **Step 1: Copy compiled script content to templates**
1199
+
1200
+ The templates directory contains the files that get distributed. Copy the content from `src/scripts/` to `templates/.workspace-templates/scripts/`. These should be identical copies — the same TypeScript source that gets compiled and tested.
1201
+
1202
+ Create the scripts directory:
1203
+ ```bash
1204
+ mkdir -p templates/.workspace-templates/scripts
1205
+ ```
1206
+
1207
+ Copy each file:
1208
+ ```bash
1209
+ cp src/scripts/scaffold.ts templates/.workspace-templates/scripts/scaffold.ts
1210
+ cp src/scripts/validate.ts templates/.workspace-templates/scripts/validate.ts
1211
+ cp src/scripts/install-tool.ts templates/.workspace-templates/scripts/install-tool.ts
1212
+ ```
1213
+
1214
+ - [ ] **Step 2: Run all tests to verify**
1215
+
1216
+ ```bash
1217
+ npm test
1218
+ ```
1219
+
1220
+ Expected: All tests PASS (integration test now verifies scripts are copied)
1221
+
1222
+ - [ ] **Step 3: Commit**
1223
+
1224
+ ```bash
1225
+ git add templates/.workspace-templates/scripts/
1226
+ git commit -m "feat: add scripts to workspace templates for distribution"
1227
+ ```
1228
+
1229
+ ---
1230
+
1231
+ ### Task 9: Enhance SKILL.md with Available Scripts Section
1232
+
1233
+ **Files:**
1234
+ - Modify: `templates/SKILL.md`
1235
+
1236
+ - [ ] **Step 1: Update SKILL.md with scripts documentation**
1237
+
1238
+ ```markdown
1239
+ # Workspace-Maxxing Skill
1240
+
1241
+ ## Role
1242
+ You are a workspace architect. You create structured, ICM-compliant workspaces.
1243
+
1244
+ ## Available Scripts
1245
+
1246
+ Use these scripts to programmatically build, validate, and equip workspaces. Invoke them via shell commands from the skill directory.
1247
+
1248
+ ### scaffold.ts — Generate ICM Workspace
1249
+
1250
+ Creates a complete ICM workspace structure from a plan.
1251
+
1252
+ ```bash
1253
+ node scripts/scaffold.ts --name "research" --stages "01-research,02-analysis,03-report" --output ./workspace
1254
+ ```
1255
+
1256
+ Options:
1257
+ - `--name <name>` — Workspace name
1258
+ - `--stages <s1,s2,...>` — Comma-separated stage folder names
1259
+ - `--output <path>` — Where to create the workspace
1260
+ - `--force` — Overwrite if output directory already exists
1261
+
1262
+ ### validate.ts — Check ICM Compliance
1263
+
1264
+ Validates a workspace against ICM rules.
1265
+
1266
+ ```bash
1267
+ node scripts/validate.ts --workspace ./workspace
1268
+ ```
1269
+
1270
+ Checks:
1271
+ - SYSTEM.md exists and contains a folder map
1272
+ - CONTEXT.md exists at root level
1273
+ - Every numbered folder has a CONTEXT.md
1274
+ - No empty CONTEXT.md files
1275
+ - No duplicate content across files
1276
+
1277
+ Exit code: 0 = all pass, 1 = some failed
1278
+
1279
+ ### install-tool.ts — Install Packages
1280
+
1281
+ Installs a tool and updates the workspace inventory.
1282
+
1283
+ ```bash
1284
+ node scripts/install-tool.ts --tool "pdf-lib" --manager npm --workspace ./workspace
1285
+ ```
1286
+
1287
+ Supported managers: `npm`, `pip`, `npx`, `brew`
1288
+
1289
+ ## Process
1290
+
1291
+ 1. CAPTURE INTENT — Ask: "What workflow do you want to automate?"
1292
+ 2. PROPOSE STRUCTURE — Design workspace with numbered folders, CONTEXT.md routing files, canonical sources
1293
+ 3. GET APPROVAL — Present plan. Wait. Do not build until approved.
1294
+ 4. BUILD WORKSPACE — Run: `node scripts/scaffold.ts --name "<name>" --stages "<stages>" --output ./workspace`
1295
+ 5. VALIDATE — Run: `node scripts/validate.ts --workspace ./workspace`. Fix any failures.
1296
+ 6. ASSESS TOOLS — Scan environment. List available tools. Propose missing tools needed. Get approval.
1297
+ 7. INSTALL TOOLS — For each approved tool: `node scripts/install-tool.ts --tool "<name>" --manager <mgr> --workspace ./workspace`
1298
+ 8. FINAL VALIDATE — Run validate.ts one more time to confirm compliance.
1299
+ 9. DELIVER — Output: workspace folder + skill package + usage guide
1300
+
1301
+ ## When to Use Scripts vs Manual
1302
+
1303
+ - **Scripts:** For structure creation, validation, and tool installation
1304
+ - **Manual:** For writing content inside CONTEXT.md files, customizing stage descriptions, adding domain-specific instructions
1305
+
1306
+ ## ICM Rules
1307
+ - Canonical sources: each fact lives in exactly one file
1308
+ - One-way dependencies only: A → B, never B → A
1309
+ - Selective loading: route to sections, not whole files
1310
+ - Numbered folders for workflow stages
1311
+
1312
+ ## Output Format
1313
+ - workspace/ — the built workspace
1314
+ - .agents/skills/<workspace-name>/ — installable skill
1315
+ - USAGE.md — how to use this workspace in future sessions
1316
+ ```
1317
+
1318
+ - [ ] **Step 2: Update integration test for new SKILL.md sections**
1319
+
1320
+ Add assertions to `tests/integration.test.ts` for the new SKILL.md sections:
1321
+
1322
+ ```typescript
1323
+ // Add to the SKILL.md assertions in the first integration test:
1324
+ expect(skillContent).toContain('## Available Scripts');
1325
+ expect(skillContent).toContain('scaffold.ts');
1326
+ expect(skillContent).toContain('validate.ts');
1327
+ expect(skillContent).toContain('install-tool.ts');
1328
+ ```
1329
+
1330
+ - [ ] **Step 3: Run all tests to verify**
1331
+
1332
+ ```bash
1333
+ npm test
1334
+ ```
1335
+
1336
+ Expected: All tests PASS
1337
+
1338
+ - [ ] **Step 4: Commit**
1339
+
1340
+ ```bash
1341
+ git add templates/SKILL.md tests/integration.test.ts
1342
+ git commit -m "feat: enhance SKILL.md with Available Scripts section"
1343
+ ```
1344
+
1345
+ ---
1346
+
1347
+ ### Task 10: Final Verification & All Tests
1348
+
1349
+ **Files:**
1350
+ - All files
1351
+
1352
+ - [ ] **Step 1: Run full test suite**
1353
+
1354
+ ```bash
1355
+ npm test
1356
+ ```
1357
+
1358
+ Expected output: All tests pass (Phase 1: 33 tests + Phase 2: ~24 new tests = ~57 total)
1359
+
1360
+ - [ ] **Step 2: Build and verify no TypeScript errors**
1361
+
1362
+ ```bash
1363
+ npm run build
1364
+ ```
1365
+
1366
+ Expected: No errors, output in `dist/`
1367
+
1368
+ - [ ] **Step 3: Verify dist/scripts/ exists**
1369
+
1370
+ ```bash
1371
+ ls dist/scripts/
1372
+ ```
1373
+
1374
+ Expected: `scaffold.js`, `validate.js`, `install-tool.js`
1375
+
1376
+ - [ ] **Step 4: Commit final state**
1377
+
1378
+ ```bash
1379
+ git add -A
1380
+ git commit -m "feat: workspace builder logic complete — scaffold, validate, install-tool"
1381
+ ```
1382
+
1383
+ ---
1384
+
1385
+ ## Self-Review
1386
+
1387
+ ### 1. Spec Coverage Check
1388
+
1389
+ | Spec Requirement | Task |
1390
+ |---|---|
1391
+ | scaffold.ts generates ICM workspace from JSON plan | Task 1-2 |
1392
+ | scaffold.ts CLI: --name, --stages, --output, --force | Task 2 |
1393
+ | scaffold.ts creates SYSTEM.md, CONTEXT.md, stage folders, 00-meta, README.md | Task 1-2 |
1394
+ | scaffold.ts dependencies: Node.js builtins only | Task 2 |
1395
+ | validate.ts checks SYSTEM.md exists with folder map | Task 3-4 |
1396
+ | validate.ts checks CONTEXT.md at root | Task 3-4 |
1397
+ | validate.ts checks numbered folders have CONTEXT.md | Task 3-4 |
1398
+ | validate.ts checks no empty CONTEXT.md | Task 3-4 |
1399
+ | validate.ts checks no duplicate content (>50 chars) | Task 3-4 |
1400
+ | validate.ts exit code 0/1, structured output | Task 4 |
1401
+ | install-tool.ts runs install command | Task 5-6 |
1402
+ | install-tool.ts updates 00-meta/tools.md | Task 5-6 |
1403
+ | install-tool.ts supports npm, pip, npx, brew | Task 5-6 |
1404
+ | install-tool.ts fails on non-zero exit | Task 5-6 |
1405
+ | Enhanced SKILL.md with Available Scripts | Task 9 |
1406
+ | Installer copies scripts | Task 7-8 |
1407
+ | Tests for all three scripts | Tasks 1, 3, 5 |
1408
+ | Integration: scaffold → validate → pass | Covered by scaffold tests + integration test |
1409
+
1410
+ All requirements covered. ✓
1411
+
1412
+ ### 2. Placeholder Scan
1413
+
1414
+ No TBD, TODO, "add tests for the above", "handle edge cases", or "similar to Task N" patterns found. All steps contain complete code. ✓
1415
+
1416
+ ### 3. Type Consistency
1417
+
1418
+ - `ScaffoldOptions` interface defined in Task 2, used in Task 1 tests ✓
1419
+ - `ValidationResult` and `CheckResult` interfaces defined in Task 4, used in Task 3 tests ✓
1420
+ - `InstallToolOptions` and `PackageManager` type defined in Task 6, used in Task 5 tests ✓
1421
+ - All scripts use `require.main === module` pattern for CLI entry ✓
1422
+ - All exported functions match test imports ✓
1423
+
1424
+ ---
1425
+
1426
+ Plan complete. Ready for execution.