role-os 1.3.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Added
6
+
7
+ #### Session Spine (Phase Q)
8
+ - `roleos init claude` — scaffolds Claude Code integration: CLAUDE.md instructions, /roleos-route + /roleos-review + /roleos-status slash commands
9
+ - `roleos doctor` — verifies repo is correctly wired for Role OS sessions (6 checks: .claude/ dir, CLAUDE.md section, /roleos-route command, context files, role contracts, packets)
10
+ - Route card generation — session header artifact proving Role OS was engaged (task type, pack, confidence, composite status, success artifact)
11
+ - CLAUDE.md template instructs Claude to route through Role OS before non-trivial work
12
+ - /roleos-route command produces structured route cards
13
+ - /roleos-review command guides structured verdict production
14
+ - /roleos-status command shows active work and context health
15
+ - Appends to existing CLAUDE.md without overwriting (detects Role OS section)
16
+ - --force flag overwrites existing command files
17
+
18
+ ### Evidence
19
+ - 335 tests, zero failures
20
+
3
21
  ## 1.3.0
4
22
 
5
23
  ### Added
package/README.md CHANGED
@@ -178,6 +178,7 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
178
178
  | **Mixed-task decomposition** | Detects composite work, splits into child packets, assigns packs, preserves dependencies. | ✓ Shipped |
179
179
  | **Composite execution** | Runs child packets in dependency order with artifact passing, branch recovery, and synthesis. | ✓ Shipped |
180
180
  | **Adaptive replanning** | Mid-run scope changes, findings, or new requirements update the plan without restarting. | ✓ Shipped |
181
+ | **Session spine** | `roleos init claude` scaffolds CLAUDE.md, /roleos-route, /roleos-review, /roleos-status. `roleos doctor` verifies wiring. Route cards prove engagement. | ✓ Shipped |
181
182
 
182
183
  ## Status
183
184
 
@@ -186,7 +187,8 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
186
187
  - v1.0.2: Role OS lockdown (bootstrap truth fixes, init --force)
187
188
  - v1.1.0: 31 roles, full routing spine, conflict detection, escalation, evidence, dispatch, 7 proven team packs. 35 execution trials. 212 tests.
188
189
  - v1.2.0: Calibrated packs promoted to default entry. Auto-selection, mismatch detection, alternative suggestion, free-routing fallback. 246 tests.
189
- - **Current**: Outcome calibration, mixed-task decomposition, composite execution, adaptive replanning. 317 tests.
190
+ - v1.3.0: Outcome calibration, mixed-task decomposition, composite execution, adaptive replanning. 317 tests.
191
+ - **v1.4.0**: Session spine — `roleos init claude`, `roleos doctor`, route cards, /roleos-route + /roleos-review + /roleos-status commands. Adoption certainty, not just routing cleverness. 335 tests.
190
192
 
191
193
  ## License
192
194
 
package/bin/roleos.mjs CHANGED
@@ -9,6 +9,7 @@ import { routeCommand } from "../src/route.mjs";
9
9
  import { reviewCommand } from "../src/review.mjs";
10
10
  import { statusCommand } from "../src/status.mjs";
11
11
  import { packsCommand } from "../src/packs-cmd.mjs";
12
+ import { scaffoldClaude, doctor, formatDoctor } from "../src/session.mjs";
12
13
 
13
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
15
  const VERSION = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
@@ -29,6 +30,7 @@ Usage:
29
30
  roleos packs list List all available team packs
30
31
  roleos packs suggest <packet-file> Suggest a pack for a packet
31
32
  roleos packs show <pack-key> Show full detail for a named pack
33
+ roleos doctor Verify repo is wired for Role OS sessions
32
34
  roleos help Show this help
33
35
 
34
36
  Verdicts: accept | accept-with-notes | reject | blocked
@@ -64,8 +66,28 @@ const args = process.argv.slice(3);
64
66
  try {
65
67
  switch (command) {
66
68
  case "init":
67
- await initCommand(args);
69
+ if (args[0] === "claude") {
70
+ const force = args.includes("--force");
71
+ const result = scaffoldClaude(process.cwd(), { force });
72
+ if (result.created.length > 0) {
73
+ console.log(`Created:`);
74
+ result.created.forEach(f => console.log(` + ${f}`));
75
+ }
76
+ if (result.skipped.length > 0) {
77
+ console.log(`Skipped:`);
78
+ result.skipped.forEach(f => console.log(` ~ ${f}`));
79
+ }
80
+ console.log(`\nDone. Claude Code will now use Role OS for routing.\nRun: roleos doctor to verify.`);
81
+ } else {
82
+ await initCommand(args);
83
+ }
68
84
  break;
85
+ case "doctor": {
86
+ const result = doctor(process.cwd());
87
+ console.log(formatDoctor(result));
88
+ if (!result.healthy) process.exit(1);
89
+ break;
90
+ }
69
91
  case "packet":
70
92
  await packetCommand(args);
71
93
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "role-os",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Role OS — a multi-Claude operating system where 31 specialized roles execute work through contracts, conflict detection, escalation, and structured evidence. 7 proven team packs for common task families.",
5
5
  "homepage": "https://mcp-tool-shop-org.github.io/role-os/",
6
6
  "bugs": {
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Session Spine — Phase Q (v1.4.0)
3
+ *
4
+ * Makes Role-OS the session substrate, not just a package.
5
+ * Scaffolds CLAUDE.md instructions, skills, and hooks so that
6
+ * Claude Code enters every session through role-os routing.
7
+ *
8
+ * Extension points used:
9
+ * - CLAUDE.md: project instructions loaded at session start
10
+ * - .claude/commands/: slash commands (skills) invokable by user or auto-matched
11
+ * - Hooks: lifecycle events configured in settings
12
+ */
13
+
14
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { writeFileSafe } from "./fs-utils.mjs";
17
+
18
+ // ── roleos init claude ────────────────────────────────────────────────────────
19
+
20
+ /**
21
+ * Scaffold Claude Code integration files into a repo.
22
+ * Creates: CLAUDE.md addition, /roleos-route command, and session guidance.
23
+ *
24
+ * @param {string} cwd - Working directory
25
+ * @param {object} [options]
26
+ * @param {boolean} [options.force] - Overwrite existing files
27
+ * @returns {{ created: string[], skipped: string[] }}
28
+ */
29
+ export function scaffoldClaude(cwd, options = {}) {
30
+ const created = [];
31
+ const skipped = [];
32
+
33
+ // 1. CLAUDE.md — session entry instructions
34
+ const claudeMd = join(cwd, "CLAUDE.md");
35
+ const claudeContent = generateClaudeMd();
36
+ if (!existsSync(claudeMd) || options.force) {
37
+ writeFileSync(claudeMd, claudeContent);
38
+ created.push("CLAUDE.md");
39
+ } else {
40
+ // Append role-os section if not already present
41
+ const existing = readFileSync(claudeMd, "utf-8");
42
+ if (!existing.includes("## Role OS")) {
43
+ writeFileSync(claudeMd, existing + "\n\n" + claudeContent);
44
+ created.push("CLAUDE.md (appended)");
45
+ } else {
46
+ skipped.push("CLAUDE.md (Role OS section already present)");
47
+ }
48
+ }
49
+
50
+ // 2. Slash command: /roleos-route
51
+ const cmdDir = join(cwd, ".claude", "commands");
52
+ mkdirSync(cmdDir, { recursive: true });
53
+ const routeCmd = join(cmdDir, "roleos-route.md");
54
+ if (!existsSync(routeCmd) || options.force) {
55
+ writeFileSync(routeCmd, generateRouteCommand());
56
+ created.push(".claude/commands/roleos-route.md");
57
+ } else {
58
+ skipped.push(".claude/commands/roleos-route.md");
59
+ }
60
+
61
+ // 3. Slash command: /roleos-review
62
+ const reviewCmd = join(cmdDir, "roleos-review.md");
63
+ if (!existsSync(reviewCmd) || options.force) {
64
+ writeFileSync(reviewCmd, generateReviewCommand());
65
+ created.push(".claude/commands/roleos-review.md");
66
+ } else {
67
+ skipped.push(".claude/commands/roleos-review.md");
68
+ }
69
+
70
+ // 4. Slash command: /roleos-status
71
+ const statusCmd = join(cmdDir, "roleos-status.md");
72
+ if (!existsSync(statusCmd) || options.force) {
73
+ writeFileSync(statusCmd, generateStatusCommand());
74
+ created.push(".claude/commands/roleos-status.md");
75
+ } else {
76
+ skipped.push(".claude/commands/roleos-status.md");
77
+ }
78
+
79
+ return { created, skipped };
80
+ }
81
+
82
+ // ── roleos doctor ─────────────────────────────────────────────────────────────
83
+
84
+ /**
85
+ * @typedef {Object} DoctorCheck
86
+ * @property {string} name
87
+ * @property {"pass"|"fail"|"warn"} status
88
+ * @property {string} detail
89
+ */
90
+
91
+ /**
92
+ * Verify that a repo is correctly wired for Role-OS session integration.
93
+ *
94
+ * @param {string} cwd
95
+ * @returns {{ checks: DoctorCheck[], healthy: boolean }}
96
+ */
97
+ export function doctor(cwd) {
98
+ const checks = [];
99
+
100
+ // Check 1: .claude/ directory exists
101
+ const claudeDir = join(cwd, ".claude");
102
+ checks.push({
103
+ name: ".claude/ directory",
104
+ status: existsSync(claudeDir) ? "pass" : "fail",
105
+ detail: existsSync(claudeDir) ? "exists" : "missing — run roleos init first",
106
+ });
107
+
108
+ // Check 2: CLAUDE.md exists and has Role OS section
109
+ const claudeMd = join(cwd, "CLAUDE.md");
110
+ if (existsSync(claudeMd)) {
111
+ const content = readFileSync(claudeMd, "utf-8");
112
+ if (content.includes("## Role OS")) {
113
+ checks.push({ name: "CLAUDE.md Role OS section", status: "pass", detail: "present" });
114
+ } else {
115
+ checks.push({ name: "CLAUDE.md Role OS section", status: "warn", detail: "CLAUDE.md exists but has no Role OS section — run roleos init claude" });
116
+ }
117
+ } else {
118
+ checks.push({ name: "CLAUDE.md", status: "fail", detail: "missing — run roleos init claude" });
119
+ }
120
+
121
+ // Check 3: /roleos-route command exists
122
+ const routeCmd = join(cwd, ".claude", "commands", "roleos-route.md");
123
+ checks.push({
124
+ name: "/roleos-route command",
125
+ status: existsSync(routeCmd) ? "pass" : "fail",
126
+ detail: existsSync(routeCmd) ? "exists" : "missing — run roleos init claude",
127
+ });
128
+
129
+ // Check 4: Context files exist
130
+ const contextDir = join(cwd, ".claude", "context");
131
+ const contextFiles = ["product-brief.md", "repo-map.md", "brand-rules.md", "current-priorities.md"];
132
+ const filledContext = contextFiles.filter(f => {
133
+ const path = join(contextDir, f);
134
+ if (!existsSync(path)) return false;
135
+ const content = readFileSync(path, "utf-8");
136
+ // Check if it's still a template (all comments, no real content)
137
+ const lines = content.split("\n").filter(l => l.trim() && !l.trim().startsWith("#") && !l.trim().startsWith("<!--") && !l.trim().startsWith("//"));
138
+ return lines.length > 2;
139
+ });
140
+
141
+ if (filledContext.length === 4) {
142
+ checks.push({ name: "context files", status: "pass", detail: "all 4 filled" });
143
+ } else if (filledContext.length > 0) {
144
+ checks.push({ name: "context files", status: "warn", detail: `${filledContext.length}/4 filled — empty context reduces routing quality` });
145
+ } else {
146
+ checks.push({ name: "context files", status: "fail", detail: "no context files filled — routing will be low-confidence" });
147
+ }
148
+
149
+ // Check 5: Role contracts exist
150
+ const agentsDir = join(cwd, ".claude", "agents");
151
+ if (existsSync(agentsDir)) {
152
+ checks.push({ name: "role contracts", status: "pass", detail: "agents/ directory exists" });
153
+ } else {
154
+ checks.push({ name: "role contracts", status: "fail", detail: "no agents/ directory — run roleos init first" });
155
+ }
156
+
157
+ // Check 6: Packets directory exists
158
+ const packetsDir = join(cwd, ".claude", "packets");
159
+ checks.push({
160
+ name: "packets directory",
161
+ status: existsSync(packetsDir) ? "pass" : "warn",
162
+ detail: existsSync(packetsDir) ? "exists" : "no packets yet — run roleos packet new",
163
+ });
164
+
165
+ const healthy = checks.every(c => c.status !== "fail");
166
+
167
+ return { checks, healthy };
168
+ }
169
+
170
+ // ── Route card ────────────────────────────────────────────────────────────────
171
+
172
+ /**
173
+ * Generate a route card — the session header artifact that proves
174
+ * role-os was engaged.
175
+ *
176
+ * @param {object} routeResult
177
+ * @returns {string} Markdown route card
178
+ */
179
+ export function generateRouteCard(routeResult) {
180
+ const lines = [
181
+ `## Route Card`,
182
+ ``,
183
+ `| Field | Value |`,
184
+ `|-------|-------|`,
185
+ `| Task type | ${routeResult.type || "unknown"} |`,
186
+ `| Pack | ${routeResult.pack || "free routing"} |`,
187
+ `| Pack confidence | ${routeResult.packConfidence || "n/a"} |`,
188
+ `| Composite | ${routeResult.isComposite ? "yes — " + routeResult.compositeReason : "no"} |`,
189
+ `| Chain | ${routeResult.chain || "pending"} |`,
190
+ `| Confidence | ${routeResult.confidence || "unknown"} |`,
191
+ `| Routed at | ${new Date().toISOString()} |`,
192
+ ];
193
+
194
+ if (routeResult.mismatch) {
195
+ lines.push(`| Mismatch | ${routeResult.mismatch} |`);
196
+ }
197
+
198
+ lines.push(``);
199
+ lines.push(`**Why:** ${routeResult.reason || "Scored against packet content."}`);
200
+
201
+ if (routeResult.successArtifact) {
202
+ lines.push(``);
203
+ lines.push(`**Success artifact:** ${routeResult.successArtifact}`);
204
+ }
205
+
206
+ return lines.join("\n");
207
+ }
208
+
209
+ // ── Format doctor output ──────────────────────────────────────────────────────
210
+
211
+ /**
212
+ * @param {{ checks: DoctorCheck[], healthy: boolean }} result
213
+ * @returns {string}
214
+ */
215
+ export function formatDoctor(result) {
216
+ const lines = [
217
+ `\nRole OS Doctor`,
218
+ `──────────────`,
219
+ ];
220
+
221
+ for (const check of result.checks) {
222
+ const icon = check.status === "pass" ? "✓" : check.status === "warn" ? "!" : "✗";
223
+ lines.push(` ${icon} ${check.name}: ${check.detail}`);
224
+ }
225
+
226
+ lines.push(``);
227
+ lines.push(result.healthy
228
+ ? `Healthy — Role OS is wired correctly.`
229
+ : `Unhealthy — run roleos init claude to fix.`
230
+ );
231
+
232
+ return lines.join("\n");
233
+ }
234
+
235
+ // ── Generators ────────────────────────────────────────────────────────────────
236
+
237
+ function generateClaudeMd() {
238
+ return `## Role OS
239
+
240
+ Before starting non-trivial work in this repo, route the task through Role OS:
241
+
242
+ 1. Run \`/roleos-route\` or \`roleos route <packet-file>\` to classify the task
243
+ 2. Follow the recommended pack or role chain
244
+ 3. Use structured handoffs between roles
245
+ 4. Review with evidence-based verdicts
246
+
247
+ Role OS provides 31 specialized roles across 8 packs (engineering, design, product, research, growth, treatment, marketing, core). It detects broken chains, auto-routes recovery, and requires structured evidence in every verdict.
248
+
249
+ If the task is composite (feature + docs + launch), Role OS will recommend splitting into child packets with dependency ordering.
250
+
251
+ **Route card required:** Every substantial task should produce a route card at the start showing task type, chosen pack/role, confidence, and expected success artifact. If no route card exists, Role OS was not engaged.
252
+ `;
253
+ }
254
+
255
+ function generateRouteCommand() {
256
+ return `# Route Task Through Role OS
257
+
258
+ Classify the current task and recommend the right team.
259
+
260
+ ## Steps
261
+
262
+ 1. Read the task description or packet file
263
+ 2. Run \`roleos route\` against it (or reason through the routing logic)
264
+ 3. Emit a **route card** with:
265
+ - Task type (feature / bugfix / docs / research / security / launch / treatment)
266
+ - Recommended pack or free-routing chain
267
+ - Why this pack/chain was chosen
268
+ - Whether the task is composite (needs splitting)
269
+ - Expected success artifact
270
+ - Confidence level
271
+
272
+ ## Route card format
273
+
274
+ \`\`\`
275
+ ## Route Card
276
+
277
+ | Field | Value |
278
+ |-------|-------|
279
+ | Task type | feature |
280
+ | Pack | feature (high confidence) |
281
+ | Composite | no |
282
+ | Chain | Product Strategist → Spec Writer → Backend Engineer → Test Engineer → Critic Reviewer |
283
+ | Confidence | high |
284
+
285
+ **Why:** Packet contains implementation scope with clear deliverable type.
286
+ **Success artifact:** Working implementation with tests and Critic verdict.
287
+ \`\`\`
288
+
289
+ ## Rules
290
+ - If confidence is low, say so — do not force a pack
291
+ - If composite, recommend splitting before execution
292
+ - If the task is trivial (< 3 steps), skip routing and just do it
293
+ - The route card is the proof Role OS was engaged
294
+ `;
295
+ }
296
+
297
+ function generateReviewCommand() {
298
+ return `# Review Work Through Role OS
299
+
300
+ Review the completed work using Role OS structured verdict.
301
+
302
+ ## Steps
303
+
304
+ 1. Identify which role should review (usually Critic Reviewer)
305
+ 2. Check the work against the original packet's done definition
306
+ 3. Produce a structured verdict:
307
+ - **Verdict:** accept / accept-with-notes / reject / blocked
308
+ - **Evidence:** specific items supporting the verdict
309
+ - **Gaps:** what's missing, if anything
310
+ - **Next owner:** who receives this next
311
+
312
+ ## Rules
313
+ - Tie every verdict to specific evidence, not impressions
314
+ - If evidence is insufficient to judge, say so
315
+ - Reject honestly — do not approve weak work to be nice
316
+ - For reject/blocked: state what must change and who should do it
317
+ `;
318
+ }
319
+
320
+ function generateStatusCommand() {
321
+ return `# Role OS Status
322
+
323
+ Check the current state of Role OS work in this repo.
324
+
325
+ ## Steps
326
+
327
+ 1. Run \`roleos status\` to see active packets, verdicts, and context health
328
+ 2. Report:
329
+ - Active/blocked/completed packets
330
+ - Recent verdicts
331
+ - Context file health
332
+ - Any conflicts or escalations
333
+
334
+ ## If no packets exist
335
+ Role OS has not been engaged in this session. Consider running \`/roleos-route\` first.
336
+ `;
337
+ }