role-os 1.0.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 (71) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.es.md +160 -0
  4. package/README.fr.md +160 -0
  5. package/README.hi.md +160 -0
  6. package/README.it.md +160 -0
  7. package/README.ja.md +160 -0
  8. package/README.md +160 -0
  9. package/README.pt-BR.md +160 -0
  10. package/README.zh.md +160 -0
  11. package/bin/roleos.mjs +90 -0
  12. package/package.json +41 -0
  13. package/src/fs-utils.mjs +60 -0
  14. package/src/init.mjs +36 -0
  15. package/src/packet.mjs +144 -0
  16. package/src/prompts.mjs +76 -0
  17. package/src/review.mjs +94 -0
  18. package/src/route.mjs +169 -0
  19. package/src/status.mjs +352 -0
  20. package/starter-pack/.claude/workflows/full-treatment.md +74 -0
  21. package/starter-pack/README.md +74 -0
  22. package/starter-pack/agents/core/critic-reviewer.md +39 -0
  23. package/starter-pack/agents/core/orchestrator.md +40 -0
  24. package/starter-pack/agents/core/product-strategist.md +40 -0
  25. package/starter-pack/agents/design/brand-guardian.md +41 -0
  26. package/starter-pack/agents/design/ui-designer.md +42 -0
  27. package/starter-pack/agents/engineering/backend-engineer.md +39 -0
  28. package/starter-pack/agents/engineering/dependency-auditor.md +40 -0
  29. package/starter-pack/agents/engineering/frontend-developer.md +40 -0
  30. package/starter-pack/agents/engineering/performance-engineer.md +42 -0
  31. package/starter-pack/agents/engineering/refactor-engineer.md +41 -0
  32. package/starter-pack/agents/engineering/security-reviewer.md +42 -0
  33. package/starter-pack/agents/engineering/test-engineer.md +38 -0
  34. package/starter-pack/agents/growth/community-manager.md +41 -0
  35. package/starter-pack/agents/growth/content-strategist.md +41 -0
  36. package/starter-pack/agents/growth/launch-strategist.md +42 -0
  37. package/starter-pack/agents/growth/support-triage-lead.md +41 -0
  38. package/starter-pack/agents/marketing/launch-copywriter.md +39 -0
  39. package/starter-pack/agents/product/feedback-synthesizer.md +39 -0
  40. package/starter-pack/agents/product/information-architect.md +40 -0
  41. package/starter-pack/agents/product/roadmap-prioritizer.md +41 -0
  42. package/starter-pack/agents/product/spec-writer.md +42 -0
  43. package/starter-pack/agents/research/competitive-analyst.md +40 -0
  44. package/starter-pack/agents/research/trend-researcher.md +40 -0
  45. package/starter-pack/agents/research/user-interview-synthesizer.md +41 -0
  46. package/starter-pack/agents/research/ux-researcher.md +40 -0
  47. package/starter-pack/agents/treatment/coverage-auditor.md +40 -0
  48. package/starter-pack/agents/treatment/deployment-verifier.md +41 -0
  49. package/starter-pack/agents/treatment/docs-architect.md +40 -0
  50. package/starter-pack/agents/treatment/metadata-curator.md +40 -0
  51. package/starter-pack/agents/treatment/release-engineer.md +43 -0
  52. package/starter-pack/agents/treatment/repo-researcher.md +40 -0
  53. package/starter-pack/agents/treatment/repo-translator.md +38 -0
  54. package/starter-pack/context/brand-rules.md +52 -0
  55. package/starter-pack/context/current-priorities.md +33 -0
  56. package/starter-pack/context/product-brief.md +47 -0
  57. package/starter-pack/context/repo-map.md +45 -0
  58. package/starter-pack/examples/feature-packet.md +39 -0
  59. package/starter-pack/examples/identity-packet.md +39 -0
  60. package/starter-pack/examples/integration-packet.md +39 -0
  61. package/starter-pack/handbook.md +67 -0
  62. package/starter-pack/policy/done-definition.md +15 -0
  63. package/starter-pack/policy/escalation-rules.md +20 -0
  64. package/starter-pack/policy/routing-rules.md +199 -0
  65. package/starter-pack/policy/tool-permissions.md +134 -0
  66. package/starter-pack/schemas/handoff.md +52 -0
  67. package/starter-pack/schemas/review-verdict.md +26 -0
  68. package/starter-pack/schemas/task-packet.md +44 -0
  69. package/starter-pack/workflows/fix-bug.md +18 -0
  70. package/starter-pack/workflows/launch-update.md +15 -0
  71. package/starter-pack/workflows/ship-feature.md +22 -0
package/src/review.mjs ADDED
@@ -0,0 +1,94 @@
1
+ import { resolve, dirname, basename } from "node:path";
2
+ import { readFileSafe, writeFileSafe } from "./fs-utils.mjs";
3
+ import { askRequired, askWithDefault, closePrompts } from "./prompts.mjs";
4
+
5
+ const VERDICTS = ["accept", "accept-with-notes", "reject", "blocked"];
6
+
7
+ export async function reviewCommand(args) {
8
+ const packetFile = args[0];
9
+ const verdict = args[1];
10
+
11
+ if (!packetFile || !verdict) {
12
+ const err = new Error(`Usage: roleos review <packet-file> <verdict>\nVerdicts: ${VERDICTS.join(" | ")}`);
13
+ err.exitCode = 1;
14
+ err.hint = "Provide a packet file and a verdict.";
15
+ throw err;
16
+ }
17
+
18
+ if (!VERDICTS.includes(verdict)) {
19
+ const err = new Error(`Unknown verdict: ${verdict}\nVerdicts: ${VERDICTS.join(" | ")}`);
20
+ err.exitCode = 1;
21
+ err.hint = `Valid verdicts: ${VERDICTS.join(", ")}`;
22
+ throw err;
23
+ }
24
+
25
+ const content = readFileSafe(packetFile);
26
+ if (content === null) {
27
+ const err = new Error(`Packet not found: ${packetFile}`);
28
+ err.exitCode = 1;
29
+ err.hint = "Check the file path and try again.";
30
+ throw err;
31
+ }
32
+
33
+ // Extract task ID from the packet
34
+ const taskIdMatch = content.match(/## Task ID\n(.+)/);
35
+ const taskId = taskIdMatch ? taskIdMatch[1].trim() : basename(packetFile, ".md");
36
+
37
+ console.log(`\nroleos review — ${verdict}\n`);
38
+ console.log(`Packet: ${packetFile}`);
39
+ console.log(`Task ID: ${taskId}\n`);
40
+
41
+ const reviewer = await askWithDefault("Reviewer role", "Critic Reviewer");
42
+ const reason = await askRequired("Reason (tied to contract/evidence)");
43
+
44
+ let corrections = "";
45
+ if (verdict === "reject" || verdict === "accept-with-notes") {
46
+ corrections = await askRequired("Required corrections");
47
+ }
48
+
49
+ const nextOwner = await askWithDefault("Next owner", verdict === "accept" ? "None — done" : "");
50
+
51
+ // Contract check
52
+ console.log("\nContract check (y/n for each):");
53
+ const scopeRespected = await askWithDefault(" Scope respected?", "y");
54
+ const outputComplete = await askWithDefault(" Output shape complete?", "y");
55
+ const qualityMet = await askWithDefault(" Quality bar met?", "y");
56
+ const risksSurfaced = await askWithDefault(" Risks surfaced honestly?", "y");
57
+ const readyForNext = await askWithDefault(" Ready for next stage?", verdict === "accept" ? "y" : "n");
58
+
59
+ const verdictContent = `# Review Verdict
60
+
61
+ ## Reviewer
62
+ ${reviewer}
63
+
64
+ ## Task ID
65
+ ${taskId}
66
+
67
+ ## Verdict
68
+ ${verdict}
69
+
70
+ ## Why
71
+ ${reason}
72
+
73
+ ## Contract Check
74
+ - Scope respected? ${scopeRespected}
75
+ - Output shape complete? ${outputComplete}
76
+ - Quality bar met? ${qualityMet}
77
+ - Risks surfaced honestly? ${risksSurfaced}
78
+ - Ready for next stage? ${readyForNext}
79
+
80
+ ${corrections ? `## Required Corrections\n${corrections}\n` : ""}## Next Owner
81
+ ${nextOwner}
82
+ `;
83
+
84
+ closePrompts();
85
+
86
+ // Write verdict as companion file
87
+ const packetBase = basename(packetFile, ".md");
88
+ const verdictPath = resolve(dirname(packetFile), `${packetBase}.verdict.md`);
89
+
90
+ const wrote = writeFileSafe(verdictPath, verdictContent, { force: true });
91
+ if (wrote) {
92
+ console.log(`\nVerdict recorded: ${verdictPath}`);
93
+ }
94
+ }
package/src/route.mjs ADDED
@@ -0,0 +1,169 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { readFileSafe } from "./fs-utils.mjs";
4
+
5
+ const ROLE_KEYWORDS = {
6
+ "Product Strategist": [
7
+ "product", "scope", "intent", "prioritize", "tradeoff", "framing",
8
+ "feature shaping", "user value",
9
+ ],
10
+ "UI Designer": [
11
+ "ui", "screen", "layout", "hierarchy", "interaction", "visual",
12
+ "design", "flow", "component",
13
+ ],
14
+ "Backend Engineer": [
15
+ "api", "server", "data", "persistence", "contract", "model",
16
+ "migration", "bridge", "wiring", "session", "state",
17
+ ],
18
+ "Frontend Developer": [
19
+ "frontend", "render", "component", "client", "tui", "display",
20
+ "view", "screen implementation",
21
+ ],
22
+ "Test Engineer": [
23
+ "test", "verify", "regression", "coverage", "assertion", "edge case",
24
+ ],
25
+ "Launch Copywriter": [
26
+ "release notes", "launch", "messaging", "copy", "positioning",
27
+ "announcement",
28
+ ],
29
+ };
30
+
31
+ const CHAINS = {
32
+ feature: [
33
+ "Orchestrator", "Product Strategist", "UI Designer",
34
+ "Backend Engineer", "Frontend Developer", "Test Engineer",
35
+ "Critic Reviewer",
36
+ ],
37
+ integration: [
38
+ "Orchestrator", "Backend Engineer", "Frontend Developer",
39
+ "Test Engineer", "Critic Reviewer",
40
+ ],
41
+ identity: [
42
+ "Orchestrator", "Product Strategist", "UI Designer",
43
+ "Frontend Developer", "Test Engineer", "Critic Reviewer",
44
+ ],
45
+ };
46
+
47
+ function detectType(content) {
48
+ const lower = content.toLowerCase();
49
+
50
+ const typeMatch = content.match(/## Packet Type\n(\w+)/);
51
+ if (typeMatch && CHAINS[typeMatch[1]]) {
52
+ return typeMatch[1];
53
+ }
54
+
55
+ if (lower.includes("contamination") || lower.includes("residue") || lower.includes("identity") || lower.includes("purge")) {
56
+ return "identity";
57
+ }
58
+ if (lower.includes("wiring") || lower.includes("bridge") || lower.includes("integration") || lower.includes("seam")) {
59
+ return "integration";
60
+ }
61
+ return "feature";
62
+ }
63
+
64
+ function scoreRoles(content) {
65
+ const lower = content.toLowerCase();
66
+ const scores = {};
67
+
68
+ for (const [role, keywords] of Object.entries(ROLE_KEYWORDS)) {
69
+ let score = 0;
70
+ for (const kw of keywords) {
71
+ if (lower.includes(kw)) score++;
72
+ }
73
+ if (score > 0) scores[role] = score;
74
+ }
75
+
76
+ return scores;
77
+ }
78
+
79
+ function recommendChain(type, scores) {
80
+ const base = CHAINS[type] || CHAINS.feature;
81
+ const relevant = new Set(Object.keys(scores));
82
+
83
+ // Always keep Orchestrator and Critic Reviewer
84
+ const chain = base.filter((role) => {
85
+ if (role === "Orchestrator" || role === "Critic Reviewer") return true;
86
+ if (relevant.has(role)) return true;
87
+ // Keep roles in the base chain even without keyword hits —
88
+ // the type-based chain is the proven default
89
+ return true;
90
+ });
91
+
92
+ return chain;
93
+ }
94
+
95
+ function extractFileRefs(content, packetDir) {
96
+ const refs = [];
97
+ const inputsMatch = content.match(/## Inputs\n([\s\S]*?)(?=\n## |\n---)/);
98
+ if (!inputsMatch) return refs;
99
+
100
+ const inputsSection = inputsMatch[1];
101
+ // Match file paths — look for patterns like path/to/file.ext or ./path/to/file
102
+ const pathPattern = /(?:^|\s|`)((?:\.\/|\.\.\/|[a-zA-Z][\w\-]*\/)[^\s`\n,)]+\.\w+)/gm;
103
+ let match;
104
+ while ((match = pathPattern.exec(inputsSection)) !== null) {
105
+ const ref = match[1];
106
+ const resolved = resolve(dirname(packetDir), "..", "..", ref);
107
+ refs.push({
108
+ ref,
109
+ resolved,
110
+ exists: existsSync(resolved),
111
+ });
112
+ }
113
+
114
+ return refs;
115
+ }
116
+
117
+ export async function routeCommand(args) {
118
+ const packetFile = args[0];
119
+
120
+ if (!packetFile) {
121
+ const err = new Error("Usage: roleos route <packet-file>");
122
+ err.exitCode = 1;
123
+ err.hint = "Provide the path to a packet file.";
124
+ throw err;
125
+ }
126
+
127
+ const content = readFileSafe(packetFile);
128
+ if (content === null) {
129
+ const err = new Error(`Packet not found: ${packetFile}`);
130
+ err.exitCode = 1;
131
+ err.hint = "Check the file path and try again.";
132
+ throw err;
133
+ }
134
+
135
+ const type = detectType(content);
136
+ const scores = scoreRoles(content);
137
+ const chain = recommendChain(type, scores);
138
+ const fileRefs = extractFileRefs(content, resolve(packetFile));
139
+
140
+ console.log(`\nroleos route — ${packetFile}\n`);
141
+ console.log(`Detected type: ${type}`);
142
+ console.log(`\nRecommended chain (${chain.length} roles):`);
143
+ chain.forEach((role, i) => {
144
+ console.log(` ${i + 1}. ${role}`);
145
+ });
146
+
147
+ if (Object.keys(scores).length > 0) {
148
+ console.log(`\nRole signals:`);
149
+ for (const [role, score] of Object.entries(scores).sort((a, b) => b[1] - a[1])) {
150
+ console.log(` ${role}: ${score} keyword hit${score > 1 ? "s" : ""}`);
151
+ }
152
+ }
153
+
154
+ if (fileRefs.length > 0) {
155
+ console.log(`\nDependency verification:`);
156
+ let hasIssues = false;
157
+ for (const ref of fileRefs) {
158
+ const status = ref.exists ? "OK" : "MISSING";
159
+ const marker = ref.exists ? "+" : "!";
160
+ console.log(` ${marker} ${ref.ref} — ${status}`);
161
+ if (!ref.exists) hasIssues = true;
162
+ }
163
+ if (hasIssues) {
164
+ console.log(`\n WARNING: Some referenced files are missing. Verify before proceeding.`);
165
+ }
166
+ }
167
+
168
+ console.log(`\nNext: assign roles and begin execution, or adjust the chain.`);
169
+ }
package/src/status.mjs ADDED
@@ -0,0 +1,352 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join, basename } from "node:path";
3
+ import { readFileSafe, writeFileSafe } from "./fs-utils.mjs";
4
+
5
+ // --- Parsers ---
6
+
7
+ function parsePacket(filePath) {
8
+ const content = readFileSafe(filePath);
9
+ if (!content) return null;
10
+
11
+ const get = (heading) => {
12
+ const re = new RegExp(`## ${heading}\\n([\\s\\S]*?)(?=\\n## |\\n---|$)`);
13
+ const m = content.match(re);
14
+ return m ? m[1].trim() : null;
15
+ };
16
+
17
+ const taskId = get("Task ID");
18
+ const title = get("Title");
19
+ const type = get("Packet Type");
20
+ const openQuestions = get("Open Questions");
21
+ const assignedRole = get("Assigned Role");
22
+
23
+ const hasContent = taskId || title;
24
+ if (!hasContent) return { malformed: true, file: basename(filePath) };
25
+
26
+ // Check for companion verdict
27
+ const verdictPath = filePath.replace(/\.md$/, ".verdict.md");
28
+ const verdict = parseVerdict(verdictPath);
29
+
30
+ const hasBlockers = openQuestions &&
31
+ !openQuestions.startsWith("<!--") &&
32
+ openQuestions.length > 0;
33
+
34
+ return {
35
+ file: basename(filePath),
36
+ taskId: taskId || basename(filePath, ".md"),
37
+ title: title || "(untitled)",
38
+ type: type || "unknown",
39
+ assignedRole: assignedRole && !assignedRole.startsWith("<!--") ? assignedRole : null,
40
+ openQuestions: hasBlockers ? openQuestions : null,
41
+ verdict,
42
+ status: verdict ? (verdict.verdict === "accept" || verdict.verdict === "accept-with-notes" ? "completed" : verdict.verdict) : "active",
43
+ };
44
+ }
45
+
46
+ function parseVerdict(filePath) {
47
+ const content = readFileSafe(filePath);
48
+ if (!content) return null;
49
+
50
+ const get = (heading) => {
51
+ const re = new RegExp(`## ${heading}\\n([\\s\\S]*?)(?=\\n## |$)`);
52
+ const m = content.match(re);
53
+ return m ? m[1].trim() : null;
54
+ };
55
+
56
+ const verdict = get("Verdict");
57
+ const reviewer = get("Reviewer");
58
+ const why = get("Why");
59
+ const taskId = get("Task ID");
60
+ const nextOwner = get("Next Owner");
61
+
62
+ if (!verdict) return { malformed: true, file: basename(filePath) };
63
+
64
+ return {
65
+ file: basename(filePath),
66
+ taskId,
67
+ verdict,
68
+ reviewer: reviewer || "unknown",
69
+ why: why || "",
70
+ nextOwner: nextOwner || null,
71
+ };
72
+ }
73
+
74
+ // --- Data collection ---
75
+
76
+ function collectPackets(claudeDir) {
77
+ const packetsDir = join(claudeDir, "packets");
78
+ if (!existsSync(packetsDir)) return [];
79
+
80
+ const files = readdirSync(packetsDir)
81
+ .filter((f) => f.endsWith(".md") && !f.endsWith(".verdict.md"))
82
+ .sort();
83
+
84
+ return files
85
+ .map((f) => parsePacket(join(packetsDir, f)))
86
+ .filter(Boolean);
87
+ }
88
+
89
+ function checkContextHealth(claudeDir) {
90
+ const contextFiles = [
91
+ "context/product-brief.md",
92
+ "context/repo-map.md",
93
+ "context/current-priorities.md",
94
+ "context/brand-rules.md",
95
+ ];
96
+
97
+ return contextFiles.map((f) => ({
98
+ file: f,
99
+ exists: existsSync(join(claudeDir, f)),
100
+ }));
101
+ }
102
+
103
+ function checkSpineHealth(claudeDir) {
104
+ const checks = [
105
+ "agents/core/orchestrator.md",
106
+ "agents/core/critic-reviewer.md",
107
+ "schemas/task-packet.md",
108
+ "policy/done-definition.md",
109
+ "workflows/ship-feature.md",
110
+ ];
111
+
112
+ return checks.map((f) => ({
113
+ file: f,
114
+ exists: existsSync(join(claudeDir, f)),
115
+ }));
116
+ }
117
+
118
+ // --- Health warnings ---
119
+
120
+ function getWarnings(packets, contextHealth, spineHealth) {
121
+ const warnings = [];
122
+
123
+ // Missing context files
124
+ for (const c of contextHealth) {
125
+ if (!c.exists) warnings.push(`Missing context: ${c.file}`);
126
+ }
127
+
128
+ // Missing spine files
129
+ for (const s of spineHealth) {
130
+ if (!s.exists) warnings.push(`Missing spine: ${s.file}`);
131
+ }
132
+
133
+ // Malformed packets
134
+ for (const p of packets) {
135
+ if (p.malformed) warnings.push(`Malformed packet: ${p.file}`);
136
+ }
137
+
138
+ // Active packets with no review
139
+ const active = packets.filter((p) => !p.malformed && p.status === "active");
140
+ if (active.length > 3) {
141
+ warnings.push(`${active.length} active packets — consider reviewing before adding more`);
142
+ }
143
+
144
+ // Verdicts for missing packets (orphaned verdict files)
145
+ const packetsDir = join(".", ".claude", "packets");
146
+ if (existsSync(packetsDir)) {
147
+ const verdictFiles = readdirSync(packetsDir).filter((f) => f.endsWith(".verdict.md"));
148
+ for (const vf of verdictFiles) {
149
+ const packetFile = vf.replace(".verdict.md", ".md");
150
+ if (!existsSync(join(packetsDir, packetFile))) {
151
+ warnings.push(`Orphaned verdict: ${vf} (no matching packet)`);
152
+ }
153
+ }
154
+ }
155
+
156
+ // No work at all
157
+ if (packets.length === 0) {
158
+ warnings.push("No packets found — create one with 'roleos packet new feature'");
159
+ }
160
+
161
+ return warnings;
162
+ }
163
+
164
+ // --- Output formatters ---
165
+
166
+ function formatTerminal(data) {
167
+ const lines = [];
168
+ lines.push("roleos status\n");
169
+
170
+ // Active packets
171
+ const active = data.packets.filter((p) => !p.malformed && p.status === "active");
172
+ lines.push(`Active packets: ${active.length}`);
173
+ if (active.length > 0) {
174
+ for (const p of active) {
175
+ const role = p.assignedRole ? ` [${p.assignedRole}]` : "";
176
+ lines.push(` - ${p.title} (${p.type})${role}`);
177
+ if (p.openQuestions) {
178
+ lines.push(` Open: ${p.openQuestions.split("\n")[0].slice(0, 80)}`);
179
+ }
180
+ }
181
+ }
182
+ lines.push("");
183
+
184
+ // Blocked/rejected
185
+ const blocked = data.packets.filter((p) => !p.malformed && (p.status === "blocked" || p.status === "reject"));
186
+ if (blocked.length > 0) {
187
+ lines.push(`Blocked/rejected: ${blocked.length}`);
188
+ for (const p of blocked) {
189
+ const why = p.verdict?.why ? ` — ${p.verdict.why.split("\n")[0].slice(0, 60)}` : "";
190
+ lines.push(` - ${p.title}${why}`);
191
+ }
192
+ lines.push("");
193
+ }
194
+
195
+ // Recent verdicts
196
+ const withVerdicts = data.packets.filter((p) => !p.malformed && p.verdict && !p.verdict.malformed);
197
+ if (withVerdicts.length > 0) {
198
+ lines.push(`Recent verdicts: ${withVerdicts.length}`);
199
+ const recent = withVerdicts.slice(-5);
200
+ for (const p of recent) {
201
+ const v = p.verdict;
202
+ lines.push(` - ${p.title}: ${v.verdict} (${v.reviewer})`);
203
+ }
204
+ lines.push("");
205
+ }
206
+
207
+ // Completed
208
+ const completed = data.packets.filter((p) => !p.malformed && p.status === "completed");
209
+ if (completed.length > 0) {
210
+ lines.push(`Completed: ${completed.length}`);
211
+ lines.push("");
212
+ }
213
+
214
+ // Context health
215
+ const missingContext = data.contextHealth.filter((c) => !c.exists);
216
+ if (missingContext.length > 0) {
217
+ lines.push(`Context: ${data.contextHealth.length - missingContext.length}/${data.contextHealth.length} present`);
218
+ } else {
219
+ lines.push(`Context: all ${data.contextHealth.length} files present`);
220
+ }
221
+ lines.push("");
222
+
223
+ // Warnings
224
+ if (data.warnings.length > 0) {
225
+ lines.push("Warnings:");
226
+ for (const w of data.warnings) {
227
+ lines.push(` ! ${w}`);
228
+ }
229
+ lines.push("");
230
+ }
231
+
232
+ // Summary
233
+ const total = data.packets.filter((p) => !p.malformed).length;
234
+ lines.push(`Total: ${total} packet(s) — ${active.length} active, ${completed.length} completed, ${blocked.length} blocked/rejected`);
235
+
236
+ return lines.join("\n");
237
+ }
238
+
239
+ function formatMarkdown(data) {
240
+ const lines = [];
241
+ const ts = new Date().toISOString().replace("T", " ").split(".")[0] + " UTC";
242
+ lines.push(`# Status\n`);
243
+ lines.push(`Generated: ${ts}\n`);
244
+
245
+ // Active
246
+ const active = data.packets.filter((p) => !p.malformed && p.status === "active");
247
+ lines.push(`## Active Packets`);
248
+ if (active.length === 0) {
249
+ lines.push("None\n");
250
+ } else {
251
+ for (const p of active) {
252
+ const role = p.assignedRole ? ` — ${p.assignedRole}` : "";
253
+ lines.push(`- **${p.title}** (${p.type})${role}`);
254
+ if (p.openQuestions) {
255
+ lines.push(` - Open: ${p.openQuestions.split("\n")[0]}`);
256
+ }
257
+ }
258
+ lines.push("");
259
+ }
260
+
261
+ // Blockers
262
+ const blocked = data.packets.filter((p) => !p.malformed && (p.status === "blocked" || p.status === "reject"));
263
+ lines.push(`## Blockers`);
264
+ if (blocked.length === 0) {
265
+ lines.push("None\n");
266
+ } else {
267
+ for (const p of blocked) {
268
+ const why = p.verdict?.why ? `: ${p.verdict.why.split("\n")[0]}` : "";
269
+ lines.push(`- **${p.title}**${why}`);
270
+ }
271
+ lines.push("");
272
+ }
273
+
274
+ // Recent verdicts
275
+ const withVerdicts = data.packets.filter((p) => !p.malformed && p.verdict && !p.verdict.malformed);
276
+ lines.push(`## Recent Verdicts`);
277
+ if (withVerdicts.length === 0) {
278
+ lines.push("None\n");
279
+ } else {
280
+ for (const p of withVerdicts.slice(-5)) {
281
+ lines.push(`- **${p.title}**: ${p.verdict.verdict} (${p.verdict.reviewer})`);
282
+ }
283
+ lines.push("");
284
+ }
285
+
286
+ // Completed
287
+ const completed = data.packets.filter((p) => !p.malformed && p.status === "completed");
288
+ lines.push(`## Completed`);
289
+ if (completed.length === 0) {
290
+ lines.push("None\n");
291
+ } else {
292
+ for (const p of completed) {
293
+ lines.push(`- ${p.title} (${p.type})`);
294
+ }
295
+ lines.push("");
296
+ }
297
+
298
+ // Context health
299
+ lines.push(`## Context Health`);
300
+ for (const c of data.contextHealth) {
301
+ lines.push(`- ${c.exists ? "+" : "!"} ${c.file}`);
302
+ }
303
+ lines.push("");
304
+
305
+ // Warnings
306
+ if (data.warnings.length > 0) {
307
+ lines.push(`## Warnings`);
308
+ for (const w of data.warnings) {
309
+ lines.push(`- ${w}`);
310
+ }
311
+ lines.push("");
312
+ }
313
+
314
+ return lines.join("\n");
315
+ }
316
+
317
+ // --- Command ---
318
+
319
+ export async function statusCommand(args) {
320
+ const claudeDir = join(".", ".claude");
321
+
322
+ if (!existsSync(claudeDir)) {
323
+ const err = new Error("No .claude/ directory found. Run 'roleos init' first.");
324
+ err.exitCode = 1;
325
+ err.hint = "Run 'roleos init' to scaffold Role OS.";
326
+ throw err;
327
+ }
328
+
329
+ const packets = collectPackets(claudeDir);
330
+ const contextHealth = checkContextHealth(claudeDir);
331
+ const spineHealth = checkSpineHealth(claudeDir);
332
+ const warnings = getWarnings(packets, contextHealth, spineHealth);
333
+
334
+ const data = { packets, contextHealth, spineHealth, warnings };
335
+
336
+ const isJson = args.includes("--json");
337
+ const isWrite = args.includes("--write");
338
+
339
+ if (isJson) {
340
+ console.log(JSON.stringify(data, null, 2));
341
+ return;
342
+ }
343
+
344
+ console.log(formatTerminal(data));
345
+
346
+ if (isWrite) {
347
+ const statusDir = join(claudeDir, "status");
348
+ const indexPath = join(statusDir, "index.md");
349
+ writeFileSafe(indexPath, formatMarkdown(data), { force: true });
350
+ console.log(`\nWritten: .claude/status/index.md`);
351
+ }
352
+ }
@@ -0,0 +1,74 @@
1
+ # Full Treatment
2
+
3
+ Role OS does not own or redefine the full treatment protocol. The canonical protocol lives in Claude project memory (`memory/full-treatment.md`) and is a 7-phase polish + publish playbook.
4
+
5
+ ## Canonical source
6
+
7
+ The full treatment is defined in Claude project memory as a 7-phase protocol:
8
+
9
+ 1. Pre-flight + finalize README + hand off translations
10
+ 2. Scaffold landing page (Astro site-theme)
11
+ 3. Handbook (Starlight docs)
12
+ 4. Repo metadata + coverage
13
+ 5. Repo Knowledge DB entry
14
+ 6. Commit and deploy
15
+ 7. Post-deploy verification
16
+
17
+ Read `memory/full-treatment.md` and `memory/handbook-playbook.md` before starting.
18
+
19
+ ## Gate: Shipcheck runs first
20
+
21
+ Full treatment does not start until shipcheck passes. Shipcheck is the 31-item quality gate (hard gates A-D block release). The canonical shipcheck reference lives in Claude project memory (`memory/shipcheck.md`).
22
+
23
+ Order: `npx @mcptoolshop/shipcheck audit` → exits 0 → then full treatment.
24
+
25
+ No v1.0.0 bump without passing hard gates A-D.
26
+
27
+ ## Role chain per phase
28
+
29
+ Each treatment phase maps to specific roles:
30
+
31
+ ### Phase 1 — Pre-flight + README + translations
32
+ - **Repo Researcher** — verify repo state, Pages config, package.json
33
+ - **Brand Guardian** — verify logo, README identity, footer
34
+ - **Repo Translator** — hand off and verify translations
35
+
36
+ ### Phase 2 — Scaffold landing page
37
+ - **Docs Architect** — site-theme init, site-config.ts
38
+ - **Frontend Developer** — landing page content and build
39
+ - **Brand Guardian** — verify brand alignment
40
+
41
+ ### Phase 3 — Handbook (Starlight docs)
42
+ - **Docs Architect** — Starlight setup, page structure, content
43
+ - **Repo Translator** — docs translation if applicable
44
+
45
+ ### Phase 4 — Repo metadata + coverage
46
+ - **Metadata Curator** — GitHub metadata, badges, manifest
47
+ - **Coverage Auditor** — test coverage assessment, CI integration
48
+
49
+ ### Phase 5 — Repo Knowledge DB entry
50
+ - **Repo Researcher** — thesis, architecture, relationships
51
+ - **Metadata Curator** — verify entry completeness
52
+
53
+ ### Phase 6 — Commit and deploy
54
+ - **Release Engineer** — staging, version, tag, push
55
+
56
+ ### Phase 7 — Post-deploy verification
57
+ - **Deployment Verifier** — landing page, handbook, package, badges, translations
58
+
59
+ ### Final gate
60
+ - **Critic Reviewer** — accept or reject treatment completeness
61
+
62
+ ## What Role OS adds
63
+
64
+ - Explicit role ownership for each treatment phase
65
+ - Structured handoffs between phases
66
+ - Review gate on treatment completeness
67
+ - Routing and escalation law
68
+
69
+ ## What Role OS does not own
70
+
71
+ - The treatment protocol itself
72
+ - The shipcheck gate
73
+ - Claude project memory
74
+ - Treatment history and repo facts (those live in Claude project memory)