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,473 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { execFileSync } from 'child_process';
4
+
5
+ export interface DispatchReport {
6
+ skill: string;
7
+ status: 'passed' | 'failed' | 'escalated';
8
+ timestamp: string;
9
+ findings: string[];
10
+ recommendations: string[];
11
+ metrics: Record<string, number>;
12
+ nextSkill: string;
13
+ }
14
+
15
+ export interface ParallelInvocation {
16
+ skill: string;
17
+ batchId: number;
18
+ testCaseId: string;
19
+ }
20
+
21
+ export interface ParallelDispatchResult extends DispatchReport {
22
+ batchId: number;
23
+ testCaseId: string;
24
+ }
25
+
26
+ export interface DispatchOptions {
27
+ workspacePath?: string;
28
+ runnerCommand?: string;
29
+ runnerTimeoutSeconds?: number;
30
+ invocation?: ParallelInvocation;
31
+ }
32
+
33
+ export interface ParallelDispatchOptions {
34
+ workspacePath?: string;
35
+ runnerCommand?: string;
36
+ runnerTimeoutSeconds?: number;
37
+ }
38
+
39
+ const SKILL_NEXT_MAP: Record<string, string> = {
40
+ research: 'architecture',
41
+ architecture: 'none',
42
+ validation: 'prompt-engineering',
43
+ 'prompt-engineering': 'testing',
44
+ testing: 'iteration',
45
+ iteration: 'none',
46
+ tooling: 'none',
47
+ worker: 'validation',
48
+ fixer: 'validation',
49
+ };
50
+
51
+ function isValidStatus(value: unknown): value is DispatchReport['status'] {
52
+ return value === 'passed' || value === 'failed' || value === 'escalated';
53
+ }
54
+
55
+ function normalizeStringArray(value: unknown): string[] {
56
+ if (!Array.isArray(value)) {
57
+ return [];
58
+ }
59
+
60
+ return value
61
+ .map((item) => String(item))
62
+ .filter((item) => item.trim().length > 0);
63
+ }
64
+
65
+ function extractJsonPayload(output: string): Record<string, unknown> | null {
66
+ const trimmed = output.trim();
67
+ if (!trimmed) {
68
+ return null;
69
+ }
70
+
71
+ try {
72
+ const parsed = JSON.parse(trimmed);
73
+ return typeof parsed === 'object' && parsed !== null
74
+ ? parsed as Record<string, unknown>
75
+ : null;
76
+ } catch {
77
+ // Continue trying to parse trailing JSON from mixed runner logs.
78
+ }
79
+
80
+ const firstBrace = trimmed.indexOf('{');
81
+ const lastBrace = trimmed.lastIndexOf('}');
82
+ if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
83
+ return null;
84
+ }
85
+
86
+ const candidate = trimmed.slice(firstBrace, lastBrace + 1);
87
+ try {
88
+ const parsed = JSON.parse(candidate);
89
+ return typeof parsed === 'object' && parsed !== null
90
+ ? parsed as Record<string, unknown>
91
+ : null;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ function renderRunnerCommand(
98
+ template: string,
99
+ skillName: string,
100
+ options: DispatchOptions,
101
+ ): string {
102
+ const invocation = options.invocation;
103
+ const replacements: Record<string, string> = {
104
+ '{skill}': skillName,
105
+ '{workspace}': options.workspacePath ?? process.cwd(),
106
+ '{batchId}': invocation ? String(invocation.batchId) : '',
107
+ '{testCaseId}': invocation?.testCaseId ?? '',
108
+ };
109
+
110
+ return template.replace(/\{skill\}|\{workspace\}|\{batchId\}|\{testCaseId\}/g, (token) => {
111
+ return replacements[token] ?? token;
112
+ });
113
+ }
114
+
115
+ function createRunnerFailureReport(
116
+ skillName: string,
117
+ message: string,
118
+ nextSkill: string,
119
+ metrics: Record<string, number> = {},
120
+ ): DispatchReport {
121
+ return {
122
+ skill: skillName,
123
+ status: 'failed',
124
+ timestamp: new Date().toISOString(),
125
+ findings: [message],
126
+ recommendations: ['Inspect runner command and ensure it outputs a valid JSON report'],
127
+ metrics,
128
+ nextSkill,
129
+ };
130
+ }
131
+
132
+ function truncateTelemetryText(value: string, maxLength: number = 2000): string {
133
+ if (value.length <= maxLength) {
134
+ return value;
135
+ }
136
+ return `${value.slice(0, maxLength)}...`;
137
+ }
138
+
139
+ function writeRunnerTelemetry(
140
+ skillName: string,
141
+ options: DispatchOptions,
142
+ details: {
143
+ commandTemplate: string;
144
+ renderedCommand: string;
145
+ status: DispatchReport['status'];
146
+ timestamp: string;
147
+ durationMs: number;
148
+ exitCode: number;
149
+ timedOut: number;
150
+ stdout: string;
151
+ stderr: string;
152
+ parsedPayload: number;
153
+ },
154
+ ): void {
155
+ try {
156
+ const runsDir = path.join(options.workspacePath ?? process.cwd(), '.agents', 'iteration', 'runs');
157
+ fs.mkdirSync(runsDir, { recursive: true });
158
+
159
+ const batchToken = options.invocation ? String(options.invocation.batchId) : 'na';
160
+ const testCaseToken = options.invocation?.testCaseId ? options.invocation.testCaseId.replace(/[^a-zA-Z0-9-_]/g, '_') : 'na';
161
+ const fileName = `${Date.now()}-${skillName}-${batchToken}-${testCaseToken}.json`;
162
+ const filePath = path.join(runsDir, fileName);
163
+
164
+ const telemetry = {
165
+ skill: skillName,
166
+ status: details.status,
167
+ timestamp: details.timestamp,
168
+ batchId: options.invocation?.batchId,
169
+ testCaseId: options.invocation?.testCaseId,
170
+ commandTemplate: details.commandTemplate,
171
+ renderedCommand: details.renderedCommand,
172
+ durationMs: details.durationMs,
173
+ exitCode: details.exitCode,
174
+ timedOut: details.timedOut,
175
+ parsedPayload: details.parsedPayload,
176
+ stdoutLength: details.stdout.length,
177
+ stderrLength: details.stderr.length,
178
+ stdoutPreview: truncateTelemetryText(details.stdout),
179
+ stderrPreview: truncateTelemetryText(details.stderr),
180
+ };
181
+
182
+ fs.writeFileSync(filePath, JSON.stringify(telemetry, null, 2));
183
+ } catch {
184
+ // Best-effort telemetry; dispatch result should not fail due to telemetry write issues.
185
+ }
186
+ }
187
+
188
+ function runExternalRunner(
189
+ skillName: string,
190
+ options: DispatchOptions,
191
+ ): DispatchReport {
192
+ if (!options.runnerCommand) {
193
+ return createRunnerFailureReport(skillName, 'Runner command is required for external dispatch mode', 'none');
194
+ }
195
+
196
+ const command = renderRunnerCommand(options.runnerCommand, skillName, options);
197
+ const startedAt = Date.now();
198
+ const timeoutMs = (options.runnerTimeoutSeconds ?? 300) * 1000;
199
+
200
+ try {
201
+ const stdout = execFileSync(command, {
202
+ cwd: options.workspacePath ?? process.cwd(),
203
+ encoding: 'utf-8',
204
+ shell: true,
205
+ stdio: ['ignore', 'pipe', 'pipe'],
206
+ timeout: timeoutMs,
207
+ });
208
+
209
+ const parsed = extractJsonPayload(stdout);
210
+ const elapsedMs = Date.now() - startedAt;
211
+ const nextSkill = SKILL_NEXT_MAP[skillName] ?? 'none';
212
+
213
+ const commandTemplate = options.runnerCommand ?? '';
214
+
215
+ if (!parsed) {
216
+ const report: DispatchReport = {
217
+ skill: skillName,
218
+ status: 'passed',
219
+ timestamp: new Date().toISOString(),
220
+ findings: ['External runner completed without JSON payload'],
221
+ recommendations: ['Emit JSON report for richer metrics and routing decisions'],
222
+ metrics: {
223
+ executionTimeMs: elapsedMs,
224
+ outputLength: stdout.length,
225
+ },
226
+ nextSkill,
227
+ };
228
+
229
+ writeRunnerTelemetry(skillName, options, {
230
+ commandTemplate,
231
+ renderedCommand: command,
232
+ status: report.status,
233
+ timestamp: report.timestamp,
234
+ durationMs: elapsedMs,
235
+ exitCode: 0,
236
+ timedOut: 0,
237
+ stdout,
238
+ stderr: '',
239
+ parsedPayload: 0,
240
+ });
241
+
242
+ return report;
243
+ }
244
+
245
+ const findings = normalizeStringArray(parsed.findings);
246
+ const recommendations = normalizeStringArray(parsed.recommendations);
247
+ const rawMetrics = parsed.metrics;
248
+ const metrics = typeof rawMetrics === 'object' && rawMetrics !== null
249
+ ? rawMetrics as Record<string, number>
250
+ : {};
251
+
252
+ const report: DispatchReport = {
253
+ skill: typeof parsed.skill === 'string' && parsed.skill.trim()
254
+ ? parsed.skill
255
+ : skillName,
256
+ status: isValidStatus(parsed.status) ? parsed.status : 'passed',
257
+ timestamp: typeof parsed.timestamp === 'string' && parsed.timestamp.trim()
258
+ ? parsed.timestamp
259
+ : new Date().toISOString(),
260
+ findings: findings.length > 0 ? findings : ['External runner completed'],
261
+ recommendations: recommendations.length > 0
262
+ ? recommendations
263
+ : ['Proceed using the returned runner output'],
264
+ metrics: {
265
+ ...metrics,
266
+ executionTimeMs: typeof metrics.executionTimeMs === 'number'
267
+ ? metrics.executionTimeMs
268
+ : elapsedMs,
269
+ },
270
+ nextSkill: typeof parsed.nextSkill === 'string' && parsed.nextSkill.trim()
271
+ ? parsed.nextSkill
272
+ : nextSkill,
273
+ };
274
+
275
+ writeRunnerTelemetry(skillName, options, {
276
+ commandTemplate,
277
+ renderedCommand: command,
278
+ status: report.status,
279
+ timestamp: report.timestamp,
280
+ durationMs: elapsedMs,
281
+ exitCode: 0,
282
+ timedOut: 0,
283
+ stdout,
284
+ stderr: '',
285
+ parsedPayload: 1,
286
+ });
287
+
288
+ return report;
289
+ } catch (error) {
290
+ const err = error as {
291
+ status?: number;
292
+ signal?: string;
293
+ stdout?: string | Buffer;
294
+ stderr?: string | Buffer;
295
+ message?: string;
296
+ killed?: boolean;
297
+ };
298
+
299
+ const stderr = err.stderr ? String(err.stderr).trim() : '';
300
+ const stdout = err.stdout ? String(err.stdout).trim() : '';
301
+ const baseMessage = err.message ?? 'External runner failed';
302
+ const detailMessage = [stderr, stdout].find((value) => value.length > 0) ?? baseMessage;
303
+
304
+ const failureReport = createRunnerFailureReport(
305
+ skillName,
306
+ detailMessage,
307
+ 'none',
308
+ {
309
+ exitCode: typeof err.status === 'number' ? err.status : -1,
310
+ timedOut: err.signal === 'SIGTERM' || err.killed ? 1 : 0,
311
+ },
312
+ );
313
+
314
+ writeRunnerTelemetry(skillName, options, {
315
+ commandTemplate: options.runnerCommand ?? '',
316
+ renderedCommand: command,
317
+ status: failureReport.status,
318
+ timestamp: failureReport.timestamp,
319
+ durationMs: Date.now() - startedAt,
320
+ exitCode: typeof err.status === 'number' ? err.status : -1,
321
+ timedOut: err.signal === 'SIGTERM' || err.killed ? 1 : 0,
322
+ stdout,
323
+ stderr,
324
+ parsedPayload: 0,
325
+ });
326
+
327
+ return failureReport;
328
+ }
329
+ }
330
+
331
+ export function dispatchSkill(skillName: string, skillsDir: string, options: DispatchOptions = {}): DispatchReport {
332
+ const skillPath = path.join(skillsDir, skillName, 'SKILL.md');
333
+
334
+ if (!fs.existsSync(skillPath)) {
335
+ return {
336
+ skill: skillName,
337
+ status: 'failed',
338
+ timestamp: new Date().toISOString(),
339
+ findings: [`Sub-skill SKILL.md not found: ${skillPath}`],
340
+ recommendations: ['Ensure the sub-skill directory and SKILL.md exist'],
341
+ metrics: {},
342
+ nextSkill: 'none',
343
+ };
344
+ }
345
+
346
+ const content = fs.readFileSync(skillPath, 'utf-8');
347
+ const nameMatch = content.match(/^---\nname:\s*(.+)$/m);
348
+ const skill = nameMatch ? nameMatch[1].trim() : skillName;
349
+
350
+ const requiresExternalRunner = skillName === 'worker' || skillName === 'fixer';
351
+ if (requiresExternalRunner && !options.runnerCommand) {
352
+ return createRunnerFailureReport(
353
+ skillName,
354
+ 'External sub-agent runner is required for worker/fixer',
355
+ 'none',
356
+ );
357
+ }
358
+
359
+ const usesExternalRunner = Boolean(options.runnerCommand && (skillName === 'worker' || skillName === 'fixer'));
360
+ if (usesExternalRunner) {
361
+ return runExternalRunner(skillName, options);
362
+ }
363
+
364
+ const fallbackRecommendations = ['Follow the sub-skill instructions to complete the task'];
365
+ if (skillName === 'worker' || skillName === 'fixer') {
366
+ fallbackRecommendations.push('Configure --runner-command or WORKSPACE_MAXXING_SUBAGENT_RUNNER for true sub-agent execution');
367
+ }
368
+
369
+ return {
370
+ skill,
371
+ status: 'passed',
372
+ timestamp: new Date().toISOString(),
373
+ findings: [`Sub-skill "${skill}" loaded successfully`],
374
+ recommendations: fallbackRecommendations,
375
+ metrics: {
376
+ contentLength: content.length,
377
+ simulatedDispatch: skillName === 'worker' || skillName === 'fixer' ? 1 : 0,
378
+ },
379
+ nextSkill: SKILL_NEXT_MAP[skillName] ?? 'none',
380
+ };
381
+ }
382
+
383
+ export function dispatchParallel(
384
+ invocations: ParallelInvocation[],
385
+ skillsDir: string,
386
+ options: ParallelDispatchOptions = {},
387
+ ): ParallelDispatchResult[] {
388
+ return invocations.map((inv) => {
389
+ const report = dispatchSkill(inv.skill, skillsDir, {
390
+ ...options,
391
+ invocation: inv,
392
+ });
393
+ return {
394
+ ...report,
395
+ batchId: inv.batchId,
396
+ testCaseId: inv.testCaseId,
397
+ };
398
+ });
399
+ }
400
+
401
+ if (require.main === module) {
402
+ const args = process.argv.slice(2);
403
+ const parseArg = (flag: string): string | undefined => {
404
+ const idx = args.indexOf(flag);
405
+ return idx !== -1 ? args[idx + 1] : undefined;
406
+ };
407
+
408
+ const skill = parseArg('--skill');
409
+ const workspace = parseArg('--workspace');
410
+ const batchId = parseArg('--batch-id');
411
+ const testCaseId = parseArg('--test-case-id');
412
+ const parallel = args.includes('--parallel');
413
+ const invocationsPath = parseArg('--invocations');
414
+ const runnerCommand = parseArg('--runner-command') ?? process.env.WORKSPACE_MAXXING_SUBAGENT_RUNNER;
415
+ const runnerTimeoutRaw = parseArg('--runner-timeout');
416
+ let runnerTimeoutSeconds: number | undefined;
417
+ if (runnerTimeoutRaw !== undefined) {
418
+ const parsedTimeout = Number(runnerTimeoutRaw);
419
+ if (!Number.isFinite(parsedTimeout) || parsedTimeout <= 0) {
420
+ console.error('--runner-timeout must be a positive number of seconds');
421
+ process.exit(1);
422
+ }
423
+ runnerTimeoutSeconds = parsedTimeout;
424
+ }
425
+
426
+ const skillsDir = workspace
427
+ ? path.join(workspace, '.agents', 'skills', 'workspace-maxxing', 'skills')
428
+ : path.join(process.cwd(), 'skills');
429
+
430
+ const dispatchOptions: ParallelDispatchOptions = {
431
+ workspacePath: workspace,
432
+ runnerCommand,
433
+ runnerTimeoutSeconds,
434
+ };
435
+
436
+ if (parallel) {
437
+ if (!invocationsPath) {
438
+ console.error('--parallel requires --invocations <path>');
439
+ process.exit(1);
440
+ }
441
+
442
+ const parsed = JSON.parse(fs.readFileSync(invocationsPath, 'utf-8'));
443
+ if (!Array.isArray(parsed)) {
444
+ console.error('--invocations must point to a JSON array');
445
+ process.exit(1);
446
+ }
447
+
448
+ const results = dispatchParallel(parsed as ParallelInvocation[], skillsDir, dispatchOptions);
449
+ console.log(JSON.stringify(results, null, 2));
450
+ } else {
451
+ if (!skill) {
452
+ console.error('Usage: node dispatch.ts --skill <name> --workspace <path> [--batch-id <n>] [--parallel --invocations <path>]');
453
+ process.exit(1);
454
+ }
455
+
456
+ const singleInvocation = batchId
457
+ ? {
458
+ skill,
459
+ batchId: parseInt(batchId, 10),
460
+ testCaseId: testCaseId ?? '',
461
+ }
462
+ : undefined;
463
+
464
+ const result = dispatchSkill(skill, skillsDir, {
465
+ ...dispatchOptions,
466
+ invocation: singleInvocation,
467
+ });
468
+ const output = batchId
469
+ ? { ...result, batchId: parseInt(batchId, 10) }
470
+ : result;
471
+ console.log(JSON.stringify(output, null, 2));
472
+ }
473
+ }
@@ -0,0 +1,158 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export interface TestCase {
5
+ stage: string;
6
+ type: 'sample' | 'edge-case' | 'empty';
7
+ input: string;
8
+ expected: string;
9
+ }
10
+
11
+ export interface TestCasesOutput {
12
+ testCases: TestCase[];
13
+ }
14
+
15
+ export function generateTestCases(
16
+ workspacePath: string,
17
+ outputPath?: string,
18
+ ): TestCasesOutput {
19
+ const ws = path.resolve(workspacePath);
20
+ const testCases: TestCase[] = [];
21
+ const workspaceDomain = detectWorkspaceDomain(ws);
22
+
23
+ const stageFolders = getNumberedFolders(ws);
24
+
25
+ if (stageFolders.length === 0) {
26
+ console.warn('Warning: No numbered stage folders found in workspace');
27
+ }
28
+
29
+ for (const stage of stageFolders) {
30
+ const contextPath = path.join(ws, stage, 'CONTEXT.md');
31
+ let purpose = '';
32
+ if (fs.existsSync(contextPath)) {
33
+ const content = fs.readFileSync(contextPath, 'utf-8');
34
+ const purposeMatch = content.match(/## Purpose\n([\s\S]*?)(?=##|$)/i);
35
+ if (purposeMatch) {
36
+ purpose = purposeMatch[1].trim();
37
+ }
38
+ }
39
+
40
+ testCases.push({
41
+ stage,
42
+ type: 'sample',
43
+ input: generateSampleInput(stage, purpose, workspaceDomain),
44
+ expected: `Stage should fulfill its purpose: ${purpose || 'handle stage-specific processing'}`,
45
+ });
46
+
47
+ testCases.push({
48
+ stage,
49
+ type: 'edge-case',
50
+ input: generateEdgeCaseInput(stage, workspaceDomain),
51
+ expected: `Stage should handle edge case gracefully`,
52
+ });
53
+
54
+ testCases.push({
55
+ stage,
56
+ type: 'empty',
57
+ input: '',
58
+ expected: `Stage should handle empty input gracefully`,
59
+ });
60
+ }
61
+
62
+ const result: TestCasesOutput = { testCases };
63
+
64
+ if (outputPath) {
65
+ fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
66
+ console.log(`Test cases written to: ${outputPath}`);
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ function generateSampleInput(stage: string, purpose: string, domain: string): string {
73
+ if (domain === 'sports-prediction') {
74
+ const sportsSamples: Record<string, string> = {
75
+ '01-input': 'Barcelona vs Real Madrid next match prediction request with team form and injury notes',
76
+ '02-process': 'UCL finals winner prediction using recent xG, head-to-head history, and squad availability',
77
+ '03-output': 'Liverpool vs Arsenal game prediction with confidence score and rationale summary',
78
+ };
79
+
80
+ return sportsSamples[stage] || `Sports match outcome prediction scenario for ${stage}`;
81
+ }
82
+
83
+ const samples: Record<string, string> = {
84
+ '01-input': 'A sample input document with valid data for processing',
85
+ '02-process': 'Processed data from the input stage ready for transformation',
86
+ '03-output': 'Final processed data ready for report generation',
87
+ };
88
+ return samples[stage] || `Sample data for ${stage}`;
89
+ }
90
+
91
+ function generateEdgeCaseInput(stage: string, domain: string): string {
92
+ if (domain === 'sports-prediction') {
93
+ const sportsEdgeCases: Record<string, string> = {
94
+ '01-input': 'Barcelona vs Real Madrid request missing kickoff date and missing expected league context',
95
+ '02-process': 'UCL finals winner scenario with conflicting bookmaker odds and incomplete player availability data',
96
+ '03-output': 'Liverpool vs Arsenal prediction where confidence exceeds bounds and explanation is inconsistent',
97
+ };
98
+
99
+ return sportsEdgeCases[stage] || `Sports prediction edge case data for ${stage}`;
100
+ }
101
+
102
+ const edgeCases: Record<string, string> = {
103
+ '01-input': 'Input with special characters: <>&"\' and very long text that exceeds normal length expectations',
104
+ '02-process': 'Data with missing fields and inconsistent formatting',
105
+ '03-output': 'Conflicting output requirements from upstream stages',
106
+ };
107
+ return edgeCases[stage] || `Edge case data for ${stage}`;
108
+ }
109
+
110
+ function detectWorkspaceDomain(workspacePath: string): string {
111
+ const filesToScan = ['SYSTEM.md', 'CONTEXT.md'];
112
+ const textChunks: string[] = [];
113
+
114
+ for (const fileName of filesToScan) {
115
+ const filePath = path.join(workspacePath, fileName);
116
+ if (fs.existsSync(filePath)) {
117
+ textChunks.push(fs.readFileSync(filePath, 'utf-8'));
118
+ }
119
+ }
120
+
121
+ const stageFolders = getNumberedFolders(workspacePath);
122
+ for (const stage of stageFolders) {
123
+ const contextPath = path.join(workspacePath, stage, 'CONTEXT.md');
124
+ if (fs.existsSync(contextPath)) {
125
+ textChunks.push(fs.readFileSync(contextPath, 'utf-8'));
126
+ }
127
+ }
128
+
129
+ const corpus = textChunks.join('\n').toLowerCase();
130
+ const sportsPredictionMatch = /(football|soccer|ucl|champions league|match prediction|sports prediction|game prediction)/i.test(corpus);
131
+
132
+ return sportsPredictionMatch ? 'sports-prediction' : 'generic';
133
+ }
134
+
135
+ function getNumberedFolders(workspacePath: string): string[] {
136
+ const entries = fs.readdirSync(workspacePath, { withFileTypes: true });
137
+ return entries
138
+ .filter((e) => e.isDirectory() && /^\d{2}-/.test(e.name) && e.name !== '00-meta')
139
+ .map((e) => e.name);
140
+ }
141
+
142
+ if (require.main === module) {
143
+ const args = process.argv.slice(2);
144
+ const parseArg = (flag: string): string | undefined => {
145
+ const idx = args.indexOf(flag);
146
+ return idx !== -1 ? args[idx + 1] : undefined;
147
+ };
148
+
149
+ const workspace = parseArg('--workspace');
150
+ const output = parseArg('--output');
151
+
152
+ if (!workspace) {
153
+ console.error('Usage: node generate-tests.ts --workspace <path> [--output <path>]');
154
+ process.exit(1);
155
+ }
156
+
157
+ generateTestCases(workspace, output);
158
+ }
@@ -0,0 +1,82 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { execSync } from 'child_process';
4
+
5
+ export type PackageManager = 'npm' | 'pip' | 'npx' | 'brew';
6
+
7
+ export interface InstallToolOptions {
8
+ tool: string;
9
+ manager: PackageManager;
10
+ workspace: string;
11
+ }
12
+
13
+ export function installTool(options: InstallToolOptions): void {
14
+ const { tool, manager, workspace } = options;
15
+ const ws = path.resolve(workspace);
16
+
17
+ const validManagers: PackageManager[] = ['npm', 'pip', 'npx', 'brew'];
18
+ if (!validManagers.includes(manager)) {
19
+ throw new Error(`Unsupported package manager: ${manager}. Supported: ${validManagers.join(', ')}`);
20
+ }
21
+
22
+ const command = manager === 'npx' ? `npx ${tool}` : `${manager} install ${tool}`;
23
+
24
+ console.log(`Installing ${tool} via ${manager}...`);
25
+ try {
26
+ execSync(command, { cwd: ws, stdio: 'inherit' });
27
+ } catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ throw new Error(`Failed to install ${tool}: ${message}`);
30
+ }
31
+
32
+ updateToolsMd(ws, tool, manager);
33
+
34
+ console.log(`✓ ${tool} installed and added to tool inventory`);
35
+ }
36
+
37
+ function updateToolsMd(workspace: string, tool: string, manager: string): void {
38
+ const toolsMdPath = path.join(workspace, '00-meta', 'tools.md');
39
+
40
+ if (!fs.existsSync(toolsMdPath)) {
41
+ throw new Error(`tools.md not found at: ${toolsMdPath}`);
42
+ }
43
+
44
+ const content = fs.readFileSync(toolsMdPath, 'utf-8');
45
+ const now = new Date().toISOString().split('T')[0];
46
+
47
+ const placeholderRow = '| — | — | — | — |';
48
+ const newRow = `| ${tool} | latest | ${manager} | ${now} |`;
49
+
50
+ let updated: string;
51
+ if (content.includes(placeholderRow)) {
52
+ updated = content.replace(placeholderRow, `${placeholderRow}\n${newRow}`);
53
+ } else {
54
+ const pendingIdx = content.indexOf('## Pending Tools');
55
+ if (pendingIdx !== -1) {
56
+ updated = content.slice(0, pendingIdx) + newRow + '\n\n' + content.slice(pendingIdx);
57
+ } else {
58
+ updated = content + '\n' + newRow + '\n';
59
+ }
60
+ }
61
+
62
+ fs.writeFileSync(toolsMdPath, updated);
63
+ }
64
+
65
+ if (require.main === module) {
66
+ const args = process.argv.slice(2);
67
+ const parseArg = (flag: string): string | undefined => {
68
+ const idx = args.indexOf(flag);
69
+ return idx !== -1 ? args[idx + 1] : undefined;
70
+ };
71
+
72
+ const tool = parseArg('--tool');
73
+ const manager = parseArg('--manager') as PackageManager;
74
+ const workspace = parseArg('--workspace');
75
+
76
+ if (!tool || !manager || !workspace) {
77
+ console.error('Usage: node install-tool.ts --tool <name> --manager <npm|pip|npx|brew> --workspace <path>');
78
+ process.exit(1);
79
+ }
80
+
81
+ installTool({ tool, manager, workspace });
82
+ }