role-os 2.2.0 → 2.3.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.
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Swarm CLI — Dogfood Swarm entry point.
3
+ *
4
+ * roleos swarm Run dogfood swarm on current repo
5
+ * roleos swarm manifest Show the swarm manifest
6
+ * roleos swarm manifest --generate Auto-detect domains and generate manifest
7
+ * roleos swarm status Show swarm run progress
8
+ * roleos swarm findings List all findings by severity
9
+ * roleos swarm approve Approve the current feature gate
10
+ * roleos swarm verify Run Phase 9 final verification
11
+ *
12
+ * This is a first-class shortcut into the dogfood-swarm mission.
13
+ * Under the hood it creates a mission run with dynamic domain dispatch.
14
+ */
15
+
16
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import {
19
+ createPersistentRun, listRuns, loadRun, getPosition,
20
+ } from "./run.mjs";
21
+ import {
22
+ generateSwarmManifest, validateSwarmManifest,
23
+ } from "./swarm/domain-detect.mjs";
24
+
25
+ // ── Constants ────────────────────────────────────────────────────────────────
26
+
27
+ const MANIFEST_FILE = "swarm-manifest.json";
28
+
29
+ // ── Main dispatch ────────────────────────────────────────────────────────────
30
+
31
+ /**
32
+ * @param {string[]} args
33
+ */
34
+ export async function swarmCommand(args) {
35
+ const sub = args[0] || "run";
36
+
37
+ switch (sub) {
38
+ case "run":
39
+ case "start":
40
+ return cmdRun(args.slice(1));
41
+ case "manifest":
42
+ return cmdManifest(args.slice(1));
43
+ case "status":
44
+ return cmdStatus();
45
+ case "findings":
46
+ return cmdFindings();
47
+ case "approve":
48
+ return cmdApprove();
49
+ case "verify":
50
+ return cmdVerify();
51
+ case "help":
52
+ return cmdHelp();
53
+ default:
54
+ if (!["run", "start", "manifest", "status", "findings", "approve", "verify", "help"].includes(sub)) {
55
+ return cmdRun(args);
56
+ }
57
+ cmdHelp();
58
+ }
59
+ }
60
+
61
+ // ── roleos swarm [run] ──────────────────────────────────────────────────────
62
+
63
+ function cmdRun(extraArgs) {
64
+ const cwd = process.cwd();
65
+ const manifestPath = join(cwd, MANIFEST_FILE);
66
+
67
+ // Auto-generate manifest if missing
68
+ if (!existsSync(manifestPath)) {
69
+ console.log("\nNo swarm-manifest.json found — generating from repo structure...");
70
+ const manifest = generateSwarmManifest(cwd);
71
+ const validation = validateSwarmManifest(manifest);
72
+
73
+ if (!validation.valid) {
74
+ console.log("\nGenerated manifest has issues:");
75
+ for (const issue of validation.issues) {
76
+ console.log(` - ${issue}`);
77
+ }
78
+ console.log("\nFix and re-run, or run 'roleos swarm manifest --generate' to customize.\n");
79
+ process.exit(1);
80
+ }
81
+
82
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
83
+ console.log(`Generated ${MANIFEST_FILE} (${manifest.domains.length} domains, type: ${manifest.repoType})`);
84
+ }
85
+
86
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
87
+ const validation = validateSwarmManifest(manifest);
88
+
89
+ if (!validation.valid) {
90
+ console.log("\nSwarm manifest has issues:\n");
91
+ for (const issue of validation.issues) {
92
+ console.log(` - ${issue}`);
93
+ }
94
+ console.log("\nFix the manifest and re-run.\n");
95
+ process.exit(1);
96
+ }
97
+
98
+ const taskDesc = extraArgs.length > 0
99
+ ? extraArgs.join(" ")
100
+ : `Dogfood swarm of ${manifest.repo || "current repo"}`;
101
+
102
+ // Create persistent run via the dogfood-swarm mission
103
+ const run = createPersistentRun(taskDesc, cwd, { forceMission: "dogfood-swarm" });
104
+
105
+ const domainCount = manifest.domains?.length || 0;
106
+ const stageCount = manifest.stages?.length || 4;
107
+
108
+ console.log(`\nDogfood Swarm Started`);
109
+ console.log(`─────────────────────`);
110
+ console.log(`Run: ${run.id}`);
111
+ console.log(`Repo: ${manifest.repo || "unknown"}`);
112
+ console.log(`Type: ${manifest.repoType || "unknown"}`);
113
+ console.log(`Domains: ${domainCount}`);
114
+ console.log(`Stages: ${stageCount} (health-a → health-b → health-c → feature)`);
115
+ console.log(`Steps: ${run.steps.length}`);
116
+ console.log(`\nDomain Agents:`);
117
+ for (const d of manifest.domains || []) {
118
+ console.log(` - ${d.id}: ${d.role} (${d.patterns.length} patterns)`);
119
+ }
120
+ console.log(`\nStage Pipeline:`);
121
+ console.log(` 1. Health-A Bug/Security Fix (loop until 0 CRITICAL + 0 HIGH)`);
122
+ console.log(` 2. Health-B Proactive Hardening (user review gate)`);
123
+ console.log(` 3. Health-C Humanization (loop until 0 CRITICAL + 0 HIGH)`);
124
+ console.log(` 4. Feature Capability Audit (user approval gate)`);
125
+ console.log(` 5. Final Synthesis + Verdict`);
126
+ console.log(`\nRun 'roleos next' to begin the first wave.`);
127
+ console.log(`Run 'roleos swarm status' to check progress.\n`);
128
+ }
129
+
130
+ // ── roleos swarm manifest ───────────────────────────────────────────────────
131
+
132
+ function cmdManifest(args) {
133
+ const cwd = process.cwd();
134
+ const manifestPath = join(cwd, MANIFEST_FILE);
135
+
136
+ if (args.includes("--generate") || args.includes("-g")) {
137
+ return generateManifestFile(cwd, manifestPath);
138
+ }
139
+
140
+ if (!existsSync(manifestPath)) {
141
+ console.log("\nNo swarm-manifest.json found.");
142
+ console.log("Run 'roleos swarm manifest --generate' to create one.\n");
143
+ return;
144
+ }
145
+
146
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
147
+ const validation = validateSwarmManifest(manifest);
148
+
149
+ console.log(`\nSwarm Manifest: ${manifest.repo || "unknown"}`);
150
+ console.log(`──────────────────────────────────────────`);
151
+ console.log(`Version: ${manifest.version || "unknown"}`);
152
+ console.log(`Type: ${manifest.repoType || "unknown"}`);
153
+ console.log(`Domains: ${manifest.domains?.length || 0}`);
154
+ console.log(`Stages: ${manifest.stages?.length || 0}`);
155
+
156
+ if (manifest.domains?.length > 0) {
157
+ console.log(`\nDomains:`);
158
+ for (const d of manifest.domains) {
159
+ console.log(` - ${d.id}: ${d.role}`);
160
+ for (const p of d.patterns || []) {
161
+ console.log(` ${p}`);
162
+ }
163
+ }
164
+ }
165
+
166
+ if (!validation.valid) {
167
+ console.log(`\nIssues:`);
168
+ for (const issue of validation.issues) {
169
+ console.log(` ! ${issue}`);
170
+ }
171
+ } else {
172
+ console.log(`\nManifest is valid.`);
173
+ }
174
+
175
+ console.log("");
176
+ }
177
+
178
+ function generateManifestFile(cwd, manifestPath) {
179
+ if (existsSync(manifestPath)) {
180
+ console.log(`\nManifest already exists at ${MANIFEST_FILE}.`);
181
+ console.log("Delete it to regenerate, or edit manually.\n");
182
+ return;
183
+ }
184
+
185
+ const manifest = generateSwarmManifest(cwd);
186
+ const validation = validateSwarmManifest(manifest);
187
+
188
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
189
+ console.log(`\nGenerated ${MANIFEST_FILE}`);
190
+ console.log(` Repo type: ${manifest.repoType}`);
191
+ console.log(` Domains: ${manifest.domains.length}`);
192
+
193
+ for (const d of manifest.domains) {
194
+ console.log(` - ${d.id}: ${d.patterns.length} patterns`);
195
+ }
196
+
197
+ if (!validation.valid) {
198
+ console.log(`\nWarnings:`);
199
+ for (const issue of validation.issues) {
200
+ console.log(` ! ${issue}`);
201
+ }
202
+ }
203
+
204
+ console.log(`\nEdit the manifest to customize domain boundaries.`);
205
+ console.log(`Then run 'roleos swarm' to start.\n`);
206
+ }
207
+
208
+ // ── roleos swarm status ─────────────────────────────────────────────────────
209
+
210
+ function cmdStatus() {
211
+ const cwd = process.cwd();
212
+ const runs = listRuns(cwd);
213
+ const swarmRuns = runs.filter(r =>
214
+ r.task.toLowerCase().includes("swarm") ||
215
+ r.task.toLowerCase().includes("dogfood")
216
+ );
217
+
218
+ if (swarmRuns.length === 0) {
219
+ console.log("\nNo swarm runs found. Start one with: roleos swarm\n");
220
+ return;
221
+ }
222
+
223
+ const latest = swarmRuns[0];
224
+ console.log(`\nLatest Swarm Run`);
225
+ console.log(`────────────────`);
226
+ console.log(`ID: ${latest.id}`);
227
+ console.log(`Task: ${latest.task}`);
228
+ console.log(`Status: ${latest.status.toUpperCase()}`);
229
+ console.log(`Created: ${latest.createdAt}`);
230
+
231
+ const full = loadRun(cwd, latest.id);
232
+ if (full) {
233
+ const pos = getPosition(full);
234
+ console.log(`Progress: ${pos.progress}`);
235
+
236
+ // Group by stage
237
+ const stageStats = {};
238
+ for (const s of full.steps) {
239
+ const stage = s.stage || "unknown";
240
+ if (!stageStats[stage]) stageStats[stage] = { total: 0, completed: 0, active: 0, pending: 0, failed: 0 };
241
+ stageStats[stage].total++;
242
+ stageStats[stage][s.status] = (stageStats[stage][s.status] || 0) + 1;
243
+ }
244
+
245
+ console.log(`\nStages:`);
246
+ for (const [stage, stats] of Object.entries(stageStats)) {
247
+ const icon = stats.failed > 0 ? "[!]" :
248
+ stats.completed === stats.total ? "[x]" :
249
+ stats.active > 0 ? "[>]" : "[ ]";
250
+ console.log(` ${icon} ${stage}: ${stats.completed}/${stats.total} complete`);
251
+ }
252
+ }
253
+
254
+ console.log(`\nRun 'roleos explain ${latest.id}' for full detail.\n`);
255
+ }
256
+
257
+ // ── roleos swarm findings ───────────────────────────────────────────────────
258
+
259
+ function cmdFindings() {
260
+ const cwd = process.cwd();
261
+ const runs = listRuns(cwd);
262
+ const swarmRuns = runs.filter(r =>
263
+ r.task.toLowerCase().includes("swarm") ||
264
+ r.task.toLowerCase().includes("dogfood")
265
+ );
266
+
267
+ if (swarmRuns.length === 0) {
268
+ console.log("\nNo swarm runs found.\n");
269
+ return;
270
+ }
271
+
272
+ const full = loadRun(cwd, swarmRuns[0].id);
273
+ if (!full) {
274
+ console.log("\nCouldn't load run.\n");
275
+ return;
276
+ }
277
+
278
+ // Extract findings from wave-report artifacts
279
+ const findings = [];
280
+ for (const step of full.steps) {
281
+ if (step.produces === "wave-report" && step.artifact) {
282
+ // Try to parse findings from artifact
283
+ const match = step.artifact.match(/## findings\n([\s\S]*?)(?=\n## |$)/i);
284
+ if (match) {
285
+ findings.push({
286
+ domain: step.domain || "unknown",
287
+ stage: step.stage || "unknown",
288
+ content: match[1].trim(),
289
+ });
290
+ }
291
+ }
292
+ }
293
+
294
+ if (findings.length === 0) {
295
+ console.log("\nNo findings captured yet. Run waves first.\n");
296
+ return;
297
+ }
298
+
299
+ console.log(`\nSwarm Findings (${findings.length} domains with findings)`);
300
+ console.log(`──────────────────────────────────────────────────────`);
301
+ for (const f of findings) {
302
+ console.log(`\n[${f.stage}] ${f.domain}:`);
303
+ console.log(f.content);
304
+ }
305
+ console.log("");
306
+ }
307
+
308
+ // ── roleos swarm approve ────────────────────────────────────────────────────
309
+
310
+ function cmdApprove() {
311
+ const cwd = process.cwd();
312
+ const runs = listRuns(cwd);
313
+ const swarmRuns = runs.filter(r =>
314
+ r.task.toLowerCase().includes("swarm") ||
315
+ r.task.toLowerCase().includes("dogfood")
316
+ );
317
+
318
+ if (swarmRuns.length === 0) {
319
+ console.log("\nNo swarm runs found.\n");
320
+ return;
321
+ }
322
+
323
+ const full = loadRun(cwd, swarmRuns[0].id);
324
+ if (!full) {
325
+ console.log("\nCouldn't load run.\n");
326
+ return;
327
+ }
328
+
329
+ // Find the next gate step waiting for approval
330
+ const gateStep = full.steps.find(s =>
331
+ s.isGate && s.userApproval && s.status === "active"
332
+ );
333
+
334
+ if (!gateStep) {
335
+ console.log("\nNo gate currently waiting for approval.");
336
+ console.log("The swarm either hasn't reached a user gate yet, or has already been approved.\n");
337
+ return;
338
+ }
339
+
340
+ console.log(`\nApproved: ${gateStep.stage} gate`);
341
+ console.log(`The swarm will proceed to the next stage.\n`);
342
+ }
343
+
344
+ // ── roleos swarm verify ─────────────────────────────────────────────────────
345
+
346
+ function cmdVerify() {
347
+ const cwd = process.cwd();
348
+ const manifestPath = join(cwd, MANIFEST_FILE);
349
+
350
+ if (!existsSync(manifestPath)) {
351
+ console.log("\nNo swarm-manifest.json found. Nothing to verify.\n");
352
+ process.exit(1);
353
+ }
354
+
355
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
356
+ const validation = validateSwarmManifest(manifest);
357
+
358
+ console.log(`\nSwarm Verification`);
359
+ console.log(`──────────────────`);
360
+
361
+ // 1. Manifest valid
362
+ if (validation.valid) {
363
+ console.log(` [PASS] Manifest is valid`);
364
+ } else {
365
+ console.log(` [FAIL] Manifest has ${validation.issues.length} issue(s)`);
366
+ for (const i of validation.issues) console.log(` - ${i}`);
367
+ }
368
+
369
+ // 2. Domain count
370
+ const domainCount = manifest.domains?.length || 0;
371
+ if (domainCount >= 1 && domainCount <= 10) {
372
+ console.log(` [PASS] ${domainCount} domains (within 1-10 range)`);
373
+ } else {
374
+ console.log(` [FAIL] ${domainCount} domains (must be 1-10)`);
375
+ }
376
+
377
+ // 3. Check for swarm run
378
+ const runs = listRuns(cwd);
379
+ const swarmRuns = runs.filter(r =>
380
+ r.task.toLowerCase().includes("swarm") ||
381
+ r.task.toLowerCase().includes("dogfood")
382
+ );
383
+
384
+ if (swarmRuns.length > 0) {
385
+ const latest = swarmRuns[0];
386
+ const full = loadRun(cwd, latest.id);
387
+ if (full) {
388
+ const completed = full.steps.filter(s => s.status === "completed").length;
389
+ const total = full.steps.length;
390
+ console.log(` [INFO] Active run: ${completed}/${total} steps complete`);
391
+ }
392
+ } else {
393
+ console.log(` [INFO] No swarm runs yet — run 'roleos swarm' to start`);
394
+ }
395
+
396
+ console.log("");
397
+ }
398
+
399
+ // ── Help ────────────────────────────────────────────────────────────────────
400
+
401
+ function cmdHelp() {
402
+ console.log(`
403
+ Dogfood Swarm — Multi-pass convergence mission
404
+
405
+ Usage:
406
+ roleos swarm Start a dogfood swarm on the current repo
407
+ roleos swarm manifest Show the swarm manifest
408
+ roleos swarm manifest --generate Auto-detect domains and generate manifest
409
+ roleos swarm status Show swarm run progress
410
+ roleos swarm findings List all findings by severity
411
+ roleos swarm approve Approve the current feature gate
412
+ roleos swarm verify Verify manifest and run state
413
+ roleos swarm help Show this help
414
+
415
+ The swarm runs 4 stages in sequence:
416
+ 1. Health-A Bug/Security Fix (loops until 0 CRITICAL + 0 HIGH)
417
+ 2. Health-B Proactive Hardening (user review gate)
418
+ 3. Health-C Humanization (loops until 0 CRITICAL + 0 HIGH)
419
+ 4. Feature Capability Audit (user approval before execution)
420
+
421
+ Each stage dispatches parallel domain agents with exclusive file ownership.
422
+ A build gate (lint + typecheck + test) runs after every wave.
423
+ `);
424
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tool Profiles — per-role tool sandboxing.
3
+ *
4
+ * Extracted to a shared module so that both dispatch.mjs and trial.mjs
5
+ * can import it without creating a circular dependency.
6
+ */
7
+
8
+ export const TOOL_PROFILES = {
9
+ // Core
10
+ "Orchestrator": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
11
+ "Product Strategist": ["Read", "Glob", "Grep", "Write"],
12
+ "Critic Reviewer": ["Read", "Glob", "Grep", "Bash"],
13
+
14
+ // Design
15
+ "UI Designer": ["Read", "Glob", "Grep", "Write", "Edit"],
16
+ "Brand Guardian": ["Read", "Glob", "Grep"],
17
+
18
+ // Engineering
19
+ "Backend Engineer": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
20
+ "Frontend Developer": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
21
+ "Test Engineer": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
22
+ "Performance Engineer": ["Read", "Glob", "Grep", "Bash"],
23
+ "Refactor Engineer": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
24
+ "Security Reviewer": ["Read", "Glob", "Grep", "Bash"],
25
+ "Dependency Auditor": ["Read", "Glob", "Grep", "Bash"],
26
+
27
+ // Treatment
28
+ "Repo Researcher": ["Read", "Glob", "Grep", "Bash"],
29
+ "Repo Translator": ["Read", "Glob", "Grep", "Write", "Edit"],
30
+ "Docs Architect": ["Read", "Glob", "Grep", "Write", "Edit"],
31
+ "Metadata Curator": ["Read", "Glob", "Grep", "Write", "Edit"],
32
+ "Coverage Auditor": ["Read", "Glob", "Grep", "Bash"],
33
+ "Deployment Verifier": ["Read", "Glob", "Grep", "Bash"],
34
+ "Release Engineer": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
35
+
36
+ // Growth / Marketing
37
+ "Launch Strategist": ["Read", "Glob", "Grep", "Write"],
38
+ "Content Strategist": ["Read", "Glob", "Grep", "Write"],
39
+ "Community Manager": ["Read", "Glob", "Grep", "Write"],
40
+ "Support Triage Lead": ["Read", "Glob", "Grep", "Write"],
41
+ "Launch Copywriter": ["Read", "Glob", "Grep", "Write", "Edit"],
42
+
43
+ // Product
44
+ "Feedback Synthesizer": ["Read", "Glob", "Grep"],
45
+ "Roadmap Prioritizer": ["Read", "Glob", "Grep", "Write"],
46
+ "Spec Writer": ["Read", "Glob", "Grep", "Write", "Edit"],
47
+
48
+ // Research
49
+ "UX Researcher": ["Read", "Glob", "Grep"],
50
+ "Competitive Analyst": ["Read", "Glob", "Grep"],
51
+ "Trend Researcher": ["Read", "Glob", "Grep"],
52
+ "User Interview Synthesizer": ["Read", "Glob", "Grep"],
53
+
54
+ // Brainstorm
55
+ "Context Scout": ["Read", "Glob", "Grep"],
56
+ "User Value Scout": ["Read", "Glob", "Grep"],
57
+ "Creative Leap Scout": ["Read", "Glob", "Grep"],
58
+ "Normalizer": ["Read", "Glob", "Grep"],
59
+ "Synthesizer": ["Read", "Glob", "Grep", "Write"],
60
+ "Product Expander": ["Read", "Glob", "Grep", "Write"],
61
+ "Judge": ["Read", "Glob", "Grep"],
62
+ "Mechanics Scout": ["Read", "Glob", "Grep"],
63
+ "Market Scout": ["Read", "Glob", "Grep"],
64
+ "Contrarian Scout": ["Read", "Glob", "Grep"],
65
+ "Feasibility Scout": ["Read", "Glob", "Grep"],
66
+ "Quality Bar Scout": ["Read", "Glob", "Grep"],
67
+ "Scenario Expander": ["Read", "Glob", "Grep", "Write"],
68
+ "Moat Expander": ["Read", "Glob", "Grep", "Write"],
69
+
70
+ // Brainstorm v0.3 analysts
71
+ "Context Analyst": ["Read", "Glob", "Grep"],
72
+ "User Value Analyst": ["Read", "Glob", "Grep"],
73
+ "Mechanics Analyst": ["Read", "Glob", "Grep"],
74
+ "Positioning Analyst": ["Read", "Glob", "Grep"],
75
+ "Contrarian Analyst": ["Read", "Glob", "Grep"],
76
+
77
+ // Deep Audit
78
+ "Component Auditor": ["Read", "Glob", "Grep"],
79
+ "Seam Auditor": ["Read", "Glob", "Grep"],
80
+ "Test Truth Auditor": ["Read", "Glob", "Grep"],
81
+ "Audit Synthesizer": ["Read", "Glob", "Grep", "Write"],
82
+
83
+ // Dogfood Swarm
84
+ "Swarm Coordinator": ["Read", "Glob", "Grep", "Bash", "Write"],
85
+ "Swarm Backend Agent": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
86
+ "Swarm Bridge Agent": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
87
+ "Swarm Tests Agent": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
88
+ "Swarm Infra Agent": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
89
+ "Swarm Frontend Agent": ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
90
+ "Swarm Synthesizer": ["Read", "Glob", "Grep", "Bash", "Write"],
91
+ };
package/src/trial.mjs CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { ROLE_CATALOG, scoreRole, MIN_SCORE_THRESHOLD } from "./route.mjs";
13
- import { TOOL_PROFILES } from "./dispatch.mjs";
13
+ import { TOOL_PROFILES } from "./tool-profiles.mjs";
14
14
  import { getRequirements } from "./evidence.mjs";
15
15
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
16
16
  import { join, resolve } from "node:path";