@medicine-wheel/app 0.5.5 → 0.5.7

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/dist/cli/mw.js CHANGED
@@ -101,6 +101,25 @@ const C = {
101
101
  bold: '\x1b[1m',
102
102
  reset: '\x1b[0m',
103
103
  };
104
+ // ── Exit codes ────────────────────────────────────────────────────
105
+ // A command that did not do what it was asked must never exit 0.
106
+ // 0 the command did what it said
107
+ // 1 the command ran and failed
108
+ // 2 usage error — unknown command / sub-command, or a missing argument
109
+ // 3 the sub-command is recognised but has no implementation yet
110
+ const EXIT_USAGE = 2;
111
+ const EXIT_UNIMPLEMENTED = 3;
112
+ /**
113
+ * Report an unknown sub-command and mark the process as failed.
114
+ *
115
+ * Uses `process.exitCode` rather than `process.exit()` so buffered stderr is
116
+ * flushed before the process leaves.
117
+ */
118
+ function unknownSub(group, sub, available) {
119
+ console.error(`${C.south}Unknown ${group} sub-command: ${sub}${C.reset}`);
120
+ console.error(` Available: ${available.join(', ')}`);
121
+ process.exitCode = EXIT_USAGE;
122
+ }
104
123
  function parseArgs(argv) {
105
124
  const args = argv.slice(2);
106
125
  const flags = {};
@@ -276,6 +295,8 @@ ${C.bold}🌿 mw — Medicine Wheel CLI${C.reset}
276
295
  SKILLS
277
296
  mw skill view List available CLI skills
278
297
  mw skill install [name] Install a skill (or all)
298
+ mw skill run <name> NOT AVAILABLE — skills are definitions,
299
+ not programs. Exits ${EXIT_UNIMPLEMENTED}.
279
300
 
280
301
  MEMORY
281
302
  mw memory store <key> <value> [dir] Store relational memory
@@ -385,7 +406,7 @@ async function cmdCeremony(positional, flags) {
385
406
  break;
386
407
  }
387
408
  default:
388
- console.error(`Unknown ceremony sub-command: ${sub}`);
409
+ unknownSub('ceremony', sub, ['open', 'close', 'get', 'list']);
389
410
  }
390
411
  }
391
412
  // ── Direction ─────────────────────────────────────────────────────
@@ -484,7 +505,7 @@ async function cmdCycle(positional) {
484
505
  mcpCall('get_narrative_arc', { cycle_id: positional[1] ?? '' });
485
506
  break;
486
507
  default:
487
- console.error(`Unknown cycle sub-command: ${sub}`);
508
+ unknownSub('cycle', sub, ['create', 'list', 'advance', 'get', 'arc']);
488
509
  }
489
510
  }
490
511
  // ── Node ──────────────────────────────────────────────────────────
@@ -539,7 +560,7 @@ async function cmdNode(positional, flags) {
539
560
  mcpCall('search_nodes', { query: positional.slice(1).join(' ') });
540
561
  break;
541
562
  default:
542
- console.error(`Unknown node sub-command: ${sub}`);
563
+ unknownSub('node', sub, ['create', 'list', 'get', 'search']);
543
564
  }
544
565
  }
545
566
  // ── Beat ──────────────────────────────────────────────────────────
@@ -605,7 +626,7 @@ async function cmdBeat(positional, flags = {}) {
605
626
  break;
606
627
  }
607
628
  default:
608
- console.error(`Unknown beat sub-command: ${sub}`);
629
+ unknownSub('beat', sub, ['register', 'create', 'list']);
609
630
  }
610
631
  }
611
632
  // ── Edge ──────────────────────────────────────────────────────────
@@ -625,7 +646,7 @@ function cmdEdge(positional, flags) {
625
646
  break;
626
647
  }
627
648
  default:
628
- console.error(`Unknown edge sub-command: ${sub}`);
649
+ unknownSub('edge', sub, ['create', 'list']);
629
650
  }
630
651
  }
631
652
  // ── Web ───────────────────────────────────────────────────────────
@@ -652,7 +673,7 @@ function cmdChart(positional) {
652
673
  mcpCall('get_chart_progress', { chart_id: positional[1] ?? '' });
653
674
  break;
654
675
  default:
655
- console.error(`Unknown chart sub-command: ${sub}`);
676
+ unknownSub('chart', sub, ['create', 'list', 'progress']);
656
677
  }
657
678
  }
658
679
  // ── MMOT ──────────────────────────────────────────────────────────
@@ -692,7 +713,7 @@ function cmdValidate(positional) {
692
713
  });
693
714
  break;
694
715
  default:
695
- console.error(`Unknown validate sub-command: ${sub}`);
716
+ unknownSub('validate', sub, ['wilson', 'ocap', 'accountability', 'bridge']);
696
717
  }
697
718
  }
698
719
  // ── Skill ─────────────────────────────────────────────────────────
@@ -705,12 +726,18 @@ function cmdSkill(positional) {
705
726
  break;
706
727
  case 'install': {
707
728
  const name = positional[1]; // undefined means install all
708
- (0, skills_1.installSkill)('cli', name, C);
729
+ // installSkill returns -1 when `name` matched no skill for this target.
730
+ if ((0, skills_1.installSkill)('cli', name, C) < 0)
731
+ process.exitCode = EXIT_USAGE;
709
732
  break;
710
733
  }
734
+ case 'run':
735
+ // Recognised, deliberately unimplemented. See explainSkillRunUnavailable.
736
+ (0, skills_1.explainSkillRunUnavailable)('mw', C);
737
+ process.exitCode = EXIT_UNIMPLEMENTED;
738
+ break;
711
739
  default:
712
- console.error(`Unknown skill sub-command: ${sub}`);
713
- console.error("Available: view, install");
740
+ unknownSub('skill', sub, ['view', 'list', 'install']);
714
741
  }
715
742
  }
716
743
  // ── Memory ────────────────────────────────────────────────────────
@@ -728,7 +755,7 @@ function cmdMemory(positional) {
728
755
  break;
729
756
  }
730
757
  default:
731
- console.error(`Unknown memory sub-command: ${sub}`);
758
+ unknownSub('memory', sub, ['store']);
732
759
  }
733
760
  }
734
761
  // ── Main dispatch ─────────────────────────────────────────────────
@@ -811,7 +838,7 @@ async function main() {
811
838
  default:
812
839
  console.error(`${C.south}Unknown command: ${cmd}${C.reset}`);
813
840
  console.error("Run 'mw help' for usage.");
814
- process.exit(1);
841
+ process.exitCode = EXIT_USAGE;
815
842
  }
816
843
  }
817
844
  main().catch((err) => {
package/dist/cli/mwsrv.js CHANGED
@@ -65,6 +65,14 @@ const C = {
65
65
  };
66
66
  const DEFAULT_PORT = 8040;
67
67
  const CONTAINER_PORT = 8040;
68
+ // ── Exit codes ────────────────────────────────────────────────────
69
+ // A command that did not do what it was asked must never exit 0.
70
+ // 0 the command did what it said
71
+ // 1 the command ran and failed
72
+ // 2 usage error — unknown command / sub-command, or a missing argument
73
+ // 3 the sub-command is recognised but has no implementation yet
74
+ const EXIT_USAGE = 2;
75
+ const EXIT_UNIMPLEMENTED = 3;
68
76
  function parseArgs(argv) {
69
77
  const args = argv.slice(2);
70
78
  const flags = {};
@@ -213,12 +221,20 @@ function cmdSkill(positional) {
213
221
  break;
214
222
  case 'install': {
215
223
  const name = positional[1]; // undefined means install all
216
- (0, skills_1.installSkill)('srv', name, C);
224
+ // installSkill returns -1 when `name` matched no skill for this target.
225
+ if ((0, skills_1.installSkill)('srv', name, C) < 0)
226
+ process.exitCode = EXIT_USAGE;
217
227
  break;
218
228
  }
229
+ case 'run':
230
+ // Recognised, deliberately unimplemented. See explainSkillRunUnavailable.
231
+ (0, skills_1.explainSkillRunUnavailable)('mwsrv', C);
232
+ process.exitCode = EXIT_UNIMPLEMENTED;
233
+ break;
219
234
  default:
220
- console.error(`Unknown skill sub-command: ${sub}`);
221
- console.error("Available: view, install");
235
+ console.error(`${C.south}Unknown skill sub-command: ${sub}${C.reset}`);
236
+ console.error(' Available: view, list, install');
237
+ process.exitCode = EXIT_USAGE;
222
238
  }
223
239
  }
224
240
  // ── Help ──────────────────────────────────────────────────────────
@@ -244,6 +260,8 @@ OPTIONS
244
260
  SKILLS
245
261
  mwsrv skill view List available server skills
246
262
  mwsrv skill install [name] Install a skill (or all)
263
+ mwsrv skill run <name> NOT AVAILABLE — skills are definitions, not
264
+ programs. Exits ${EXIT_UNIMPLEMENTED}.
247
265
 
248
266
  EXAMPLES
249
267
  # Start locally (uses current directory's .mw/store)
@@ -278,6 +296,15 @@ async function main() {
278
296
  cmdSkill(positional.slice(1));
279
297
  return;
280
298
  }
299
+ // Anything else positional is a typo, not a server invocation. Starting the
300
+ // server anyway reported success for a command that was never understood.
301
+ if (positional.length > 0) {
302
+ console.error(`${C.south}Unknown command: ${positional[0]}${C.reset}`);
303
+ console.error(" The only sub-command is 'skill'. Run 'mwsrv --help' for usage.");
304
+ console.error(" To start the server, run 'mwsrv' with no positional arguments.");
305
+ process.exitCode = EXIT_USAGE;
306
+ return;
307
+ }
281
308
  const port = Number(flags['port'] ?? flags['p'] ?? DEFAULT_PORT);
282
309
  const directory = String(flags['directory'] ?? flags['D'] ?? process.cwd());
283
310
  const useDocker = Boolean(flags['docker']);
@@ -43,6 +43,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.listSkills = listSkills;
44
44
  exports.getSkill = getSkill;
45
45
  exports.getComplement = getComplement;
46
+ exports.explainSkillRunUnavailable = explainSkillRunUnavailable;
46
47
  exports.viewSkills = viewSkills;
47
48
  exports.installSkill = installSkill;
48
49
  const fs = __importStar(require("fs"));
@@ -73,6 +74,15 @@ Analyze an engineering task using the Four Directions framework.
73
74
  - Ceremony recommendation if balance is poor
74
75
 
75
76
  ## Usage
77
+
78
+ > **Not available yet.** \`mw skill run\` has no implementation.
79
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
80
+ >
81
+ > **What works today:** \`mw skill install direction-inquiry\` writes this
82
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
83
+ > file to an agent.
84
+
85
+ Planned invocation (does not run yet):
76
86
  \`\`\`
77
87
  mw skill run direction-inquiry "Refactor auth module"
78
88
  \`\`\`
@@ -102,6 +112,15 @@ Evaluate a proposed action against permission tiers and relational gates.
102
112
  - Suggested next move
103
113
 
104
114
  ## Usage
115
+
116
+ > **Not available yet.** \`mw skill run\` has no implementation.
117
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
118
+ >
119
+ > **What works today:** \`mw skill install fire-keeper-check\` writes this
120
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
121
+ > file to an agent.
122
+
123
+ Planned invocation (does not run yet):
105
124
  \`\`\`
106
125
  mw skill run fire-keeper-check "Deploy to production"
107
126
  \`\`\`
@@ -129,6 +148,15 @@ Create a proposal-grade wave bundle matching current .pde practice.
129
148
  - artifacts/ checklist
130
149
 
131
150
  ## Usage
151
+
152
+ > **Not available yet.** \`mw skill run\` has no implementation.
153
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
154
+ >
155
+ > **What works today:** \`mw skill install wave-spec-generator\` writes this
156
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
157
+ > file to an agent.
158
+
159
+ Planned invocation (does not run yet):
132
160
  \`\`\`
133
161
  mw skill run wave-spec-generator "Add caching layer"
134
162
  \`\`\`
@@ -156,9 +184,135 @@ Provide step-by-step guidance through the ceremony lifecycle.
156
184
  - Completion criteria
157
185
 
158
186
  ## Usage
187
+
188
+ > **Not available yet.** \`mw skill run\` has no implementation.
189
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
190
+ >
191
+ > **What works today:** \`mw skill install ceremony-guide\` writes this
192
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
193
+ > file to an agent.
194
+
195
+ Planned invocation (does not run yet):
159
196
  \`\`\`
160
197
  mw skill run ceremony-guide "Community data review"
161
198
  \`\`\`
199
+ `,
200
+ },
201
+ {
202
+ name: 'infrastructure-audit',
203
+ title: 'Infrastructure Audit',
204
+ description: 'Audit hosts, services, and port bindings across the Medicine Wheel estate',
205
+ target: 'cli',
206
+ complement: 'infra-monitor',
207
+ body: `# Skill: Infrastructure Audit
208
+
209
+ ## Purpose
210
+ Analyze infrastructure facets (hosts, tenants, services) and surface relationships, conflicts, and métis.
211
+
212
+ ## Input
213
+ - Target host (optional — audit all if omitted)
214
+ - Facet type filter (host / tenant / service, optional)
215
+ - Conflict scope (declared / observed / union)
216
+
217
+ ## Output
218
+ - Graph: hosts → tenants → services with port bindings
219
+ - Port conflicts (distinct services colliding on same host|proto|port)
220
+ - Métis surface (invisible work, exceptions, heldBy accountability)
221
+ - Reachability status (lan / tailnet / cloudflare / ngrok)
222
+
223
+ ## Usage
224
+
225
+ > **Not available yet.** \`mw skill run\` has no implementation.
226
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
227
+ >
228
+ > **What works today:** \`mw skill install infrastructure-audit\` writes this
229
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
230
+ > file to an agent.
231
+
232
+ Planned invocation (does not run yet):
233
+ \`\`\`
234
+ mw skill run infrastructure-audit --host eury
235
+ mw skill run infrastructure-audit --type service --conflicts
236
+ \`\`\`
237
+ `,
238
+ },
239
+ {
240
+ name: 'service-provisioning',
241
+ title: 'Service Provisioning',
242
+ description: 'Propose and gate service deployment through ceremony-aware preconditions',
243
+ target: 'cli',
244
+ complement: 'infra-monitor',
245
+ body: `# Skill: Service Provisioning
246
+
247
+ ## Purpose
248
+ Propose a new service and check preconditions (port availability, consent, linger-state alignment) via ceremony gates.
249
+
250
+ ## Input
251
+ - Service name and unit (e.g., assembly-mux.service)
252
+ - Port binding claims (host, port, proto)
253
+ - Owner (tenant nodeId)
254
+ - Optional: working directory, execStop, métis exceptions
255
+
256
+ ## Output
257
+ - Precondition checks: port conflicts, tenant consent, linger alignment
258
+ - Fire Keeper gate assessment (hold / proceed)
259
+ - Community review recommendation
260
+ - Deployment ceremony phase to enter
261
+ - Diff against observed state
262
+
263
+ ## Usage
264
+
265
+ > **Not available yet.** \`mw skill run\` has no implementation.
266
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
267
+ >
268
+ > **What works today:** \`mw skill install service-provisioning\` writes this
269
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
270
+ > file to an agent.
271
+
272
+ Planned invocation (does not run yet):
273
+ \`\`\`
274
+ mw skill run service-provisioning \
275
+ --unit zulip.service \
276
+ --port 3000:tcp@eury \
277
+ --owner "node:human:ava"
278
+ \`\`\`
279
+ `,
280
+ },
281
+ {
282
+ name: 'drift-reconciliation',
283
+ title: 'Drift Reconciliation',
284
+ description: 'Compare declared vs observed infrastructure state and propose healing steps',
285
+ target: 'cli',
286
+ complement: 'infra-monitor',
287
+ body: `# Skill: Drift Reconciliation
288
+
289
+ ## Purpose
290
+ Detect infrastructure drift (declared state vs systemd observed reality) and recommend ceremony-gated remediation.
291
+
292
+ ## Input
293
+ - Drift scope: specific host, all hosts, or facet type
294
+ - Healing mode: audit-only / propose-fix / execute-with-gates
295
+
296
+ ## Output
297
+ - Drift report: satisfied / diverged / unobserved facets per host
298
+ - Healing candidates: services to restart, ports to release, linger to reconcile
299
+ - Ceremony phase recommendation (emergency / standard)
300
+ - Accountability notes (who holds métis on each remediation)
301
+
302
+ ## Usage
303
+
304
+ > **Not available yet.** \`mw skill run\` has no implementation.
305
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
306
+ >
307
+ > **What works today:** \`mw skill install drift-reconciliation\` writes this
308
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
309
+ > file to an agent.
310
+
311
+ Planned invocation (does not run yet):
312
+ \`\`\`
313
+ mw skill run drift-reconciliation --host gaia --propose
314
+ mw skill run drift-reconciliation --type service --audit-only
315
+ \`\`\`
162
316
  `,
163
317
  },
164
318
  // ── Server skills (mwsrv) ─────────────────────────────────────
@@ -180,6 +334,15 @@ Configure the Docker environment for running the Medicine Wheel server.
180
334
  - Port availability
181
335
 
182
336
  ## Usage
337
+
338
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
339
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
340
+ >
341
+ > **What works today:** \`mwsrv skill install docker-setup\` writes this
342
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
343
+ > file to an agent.
344
+
345
+ Planned invocation (does not run yet):
183
346
  \`\`\`
184
347
  mwsrv skill run docker-setup
185
348
  \`\`\`
@@ -207,6 +370,15 @@ Configure the storage backend for the Medicine Wheel server.
207
370
  - Migration status
208
371
 
209
372
  ## Usage
373
+
374
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
375
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
376
+ >
377
+ > **What works today:** \`mwsrv skill install storage-config\` writes this
378
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
379
+ > file to an agent.
380
+
381
+ Planned invocation (does not run yet):
210
382
  \`\`\`
211
383
  mwsrv skill run storage-config
212
384
  \`\`\`
@@ -230,6 +402,15 @@ Check the health and connectivity of Medicine Wheel API endpoints.
230
402
  - Storage layer connectivity
231
403
 
232
404
  ## Usage
405
+
406
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
407
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
408
+ >
409
+ > **What works today:** \`mwsrv skill install api-health\` writes this
410
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
411
+ > file to an agent.
412
+
413
+ Planned invocation (does not run yet):
233
414
  \`\`\`
234
415
  mwsrv skill run api-health
235
416
  \`\`\`
@@ -253,9 +434,96 @@ Inspect and manage active sessions on the Medicine Wheel server.
253
434
  - Cleanup stale session data
254
435
 
255
436
  ## Usage
437
+
438
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
439
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
440
+ >
441
+ > **What works today:** \`mwsrv skill install session-manager\` writes this
442
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
443
+ > file to an agent.
444
+
445
+ Planned invocation (does not run yet):
256
446
  \`\`\`
257
447
  mwsrv skill run session-manager
258
448
  \`\`\`
449
+ `,
450
+ },
451
+ {
452
+ name: 'infra-monitor',
453
+ title: 'Infrastructure Monitor',
454
+ description: 'Live monitoring and drift detection for infrastructure facets via MCP',
455
+ target: 'srv',
456
+ complement: 'infrastructure-audit',
457
+ body: `# Skill: Infrastructure Monitor
458
+
459
+ ## Purpose
460
+ Monitor live infrastructure state (observed via systemd) and track drift against declared facets.
461
+
462
+ ## Capabilities
463
+ - Poll systemd for active units, ports, linger-state across tenants
464
+ - Maintain observed state cache (RelationalNode facets)
465
+ - Detect port collisions in declared ∪ observed bindings
466
+ - Track métis holders and accountability chains
467
+ - Stream drift events to active CLI sessions
468
+ - Gate reconciliation requests through ceremony protocol
469
+
470
+ ## Integration
471
+ - **Backend:** @medicine-wheel/infra (HostFacet, TenantFacet, ServiceFacet, detectPortConflicts)
472
+ - **MCP tools (planned, none registered yet):** infrastructure-audit, service-preconditions, drift-reconciliation, métis-surface
473
+ - **Data store:** Postgres/JSONL via @medicine-wheel/storage-provider
474
+
475
+ ## Usage
476
+
477
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
478
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
479
+ >
480
+ > **What works today:** \`mwsrv skill install infra-monitor\` writes this
481
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
482
+ > file to an agent.
483
+
484
+ Planned invocation (does not run yet):
485
+ \`\`\`
486
+ mwsrv skill run infra-monitor --poll-interval 30s
487
+ mwsrv skill run infra-monitor --host gaia --linger-report
488
+ \`\`\`
489
+ `,
490
+ },
491
+ {
492
+ name: 'precondition-guard',
493
+ title: 'Precondition Guard',
494
+ description: 'Evaluate infrastructure preconditions (linger, consent, port, reachability)',
495
+ target: 'srv',
496
+ complement: 'service-provisioning',
497
+ body: `# Skill: Precondition Guard
498
+
499
+ ## Purpose
500
+ Enforce precondition gates before service provisioning or relational state changes. (Roadmap: @medicine-wheel/infra@0.2.0)
501
+
502
+ ## Precondition Types
503
+ - **Port availability** — detectPortConflicts over declared + observed
504
+ - **Tenant linger** — required for user.slice services; consent record must authorize root step
505
+ - **Consent accountability** — ConsentRecord.id must be present and active
506
+ - **Reachability** — host must be reachable via declared transport (lan, tailnet, cloudflare, ngrok)
507
+
508
+ ## Output
509
+ - Unsatisfied precondition report
510
+ - Fire Keeper hold / proceed recommendation
511
+ - Next ceremony gate to enter (if held)
512
+ - Accountability chain (who can unlock the hold)
513
+
514
+ ## Usage
515
+
516
+ > **Not available yet.** \`mwsrv skill run\` has no implementation.
517
+ > It prints this explanation and exits 3 — nothing is executed. Planned, not shipped.
518
+ >
519
+ > **What works today:** \`mwsrv skill install precondition-guard\` writes this
520
+ > SKILL.md into \`.mw/skills/\`. Follow the steps above yourself, or hand this
521
+ > file to an agent.
522
+
523
+ Planned invocation (does not run yet):
524
+ \`\`\`
525
+ mwsrv skill run precondition-guard --facet-id "node:knowledge:zulip:..." --intent provision
526
+ \`\`\`
259
527
  `,
260
528
  },
261
529
  ];
@@ -282,6 +550,31 @@ function getSkill(name) {
282
550
  function getComplement(skill) {
283
551
  return skill.complement ? getSkill(skill.complement) : undefined;
284
552
  }
553
+ /**
554
+ * Print why `skill run` does nothing and what to reach for instead.
555
+ *
556
+ * A `SkillDefinition` is a name/title/description/body record — a document, not
557
+ * a program. Nothing in this repository executes one, so `skill run` has never
558
+ * had an implementation. Both CLIs call this and then exit non-zero rather than
559
+ * printing an error and returning 0, which is what they used to do.
560
+ *
561
+ * @param binary Which CLI is speaking ('mw' | 'mwsrv') — shapes the examples.
562
+ * @param colors ANSI colour map.
563
+ */
564
+ function explainSkillRunUnavailable(binary, colors) {
565
+ const C = colors;
566
+ console.error(`${C.south}${binary} skill run is not implemented.${C.reset}`);
567
+ console.error('');
568
+ console.error(' Skills here are definitions, not programs: each one is a SKILL.md');
569
+ console.error(' describing inputs, outputs and steps. There is no runtime that');
570
+ console.error(' executes them, so nothing would have run.');
571
+ console.error('');
572
+ console.error(` ${C.bold}What works today${C.reset}`);
573
+ console.error(` ${binary} skill view list the skills this CLI knows`);
574
+ console.error(` ${binary} skill install <name> write <name>/SKILL.md into .mw/skills/`);
575
+ console.error('');
576
+ console.error(' Then follow the installed SKILL.md yourself, or hand it to an agent.');
577
+ }
285
578
  /**
286
579
  * Print skill catalog for the given target.
287
580
  *
@@ -307,7 +600,10 @@ function viewSkills(target, colors) {
307
600
  /**
308
601
  * Install a skill (or all skills) for the given target.
309
602
  *
310
- * @returns number of newly installed skills
603
+ * @returns number of newly installed skills, or **-1** when `name` matched no
604
+ * skill for this target — callers must treat -1 as a failure and exit
605
+ * non-zero. Returning 0 for both "nothing to do" and "you named a
606
+ * skill that does not exist" is what let a bad name report success.
311
607
  */
312
608
  function installSkill(target, name, colors) {
313
609
  const C = colors ?? { bold: '', dim: '', green: '', south: '', reset: '' };
@@ -325,14 +621,30 @@ function installSkill(target, name, colors) {
325
621
  else {
326
622
  console.error(`${C.south}Unknown skill: ${name}${C.reset}`);
327
623
  }
328
- return 0;
624
+ return -1;
329
625
  }
330
626
  let installed = 0;
627
+ let stale = 0;
331
628
  for (const skill of toInstall) {
332
629
  const skillDir = path.join(dir, skill.name);
333
630
  const skillFile = path.join(skillDir, 'SKILL.md');
334
631
  if (fs.existsSync(skillFile)) {
335
- console.log(` ${C.dim}⊘ ${skill.name} (already installed)${C.reset}`);
632
+ // An existing file is never overwritten. Say so honestly when the copy
633
+ // on disk no longer matches the catalog, otherwise "already installed"
634
+ // reads as "up to date" while the reader follows outdated instructions.
635
+ let onDisk = '';
636
+ try {
637
+ onDisk = fs.readFileSync(skillFile, 'utf8');
638
+ }
639
+ catch { /* unreadable → treat as drifted */ }
640
+ if (onDisk === skill.body) {
641
+ console.log(` ${C.dim}⊘ ${skill.name} (already installed)${C.reset}`);
642
+ }
643
+ else {
644
+ stale++;
645
+ console.log(` ${C.dim}⊘ ${skill.name} (already installed — copy on disk differs from the catalog)${C.reset}`);
646
+ console.log(` ${C.dim}to update: rm ${skillFile} && ${target === 'cli' ? 'mw' : 'mwsrv'} skill install ${skill.name}${C.reset}`);
647
+ }
336
648
  continue;
337
649
  }
338
650
  fs.mkdirSync(skillDir, { recursive: true });
@@ -352,5 +664,8 @@ function installSkill(target, name, colors) {
352
664
  else {
353
665
  console.log(`\n ${C.dim}All skills already installed.${C.reset}\n`);
354
666
  }
667
+ if (stale > 0) {
668
+ console.log(` ${C.south}${stale} installed copy/copies differ from the catalog and were left untouched.${C.reset}\n`);
669
+ }
355
670
  return installed;
356
671
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medicine-wheel/app",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Medicine Wheel — Interactive visual layer for Indigenous relational research with Four Directions, ceremonies, and narrative arcs",
5
5
  "bin": {
6
6
  "mw": "dist/cli/mw.js",
@@ -85,28 +85,29 @@
85
85
  "release:major": "npm run version:major && npm run publish:all && npm run release:commit"
86
86
  },
87
87
  "dependencies": {
88
- "@medicine-wheel/ceremonial-diary": "^0.5.5",
89
- "@medicine-wheel/ceremony-protocol": "^0.5.5",
90
- "@medicine-wheel/community-review": "^0.5.5",
91
- "@medicine-wheel/consent-lifecycle": "^0.5.5",
92
- "@medicine-wheel/data-store": "^0.5.5",
93
- "@medicine-wheel/data-store-postgres": "^0.5.5",
94
- "@medicine-wheel/fire-keeper": "^0.5.5",
95
- "@medicine-wheel/github-ceremony": "^0.5.5",
96
- "@medicine-wheel/graph-viz": "^0.5.5",
97
- "@medicine-wheel/importance-unit": "^0.5.5",
98
- "@medicine-wheel/mcp": "^4.5.5",
99
- "@medicine-wheel/narrative-cluster": "^0.5.5",
100
- "@medicine-wheel/narrative-engine": "^0.5.5",
101
- "@medicine-wheel/ontology-core": "^0.5.5",
102
- "@medicine-wheel/perception-layer": "^0.5.5",
103
- "@medicine-wheel/prompt-decomposition": "^0.5.5",
104
- "@medicine-wheel/relational-index": "^0.5.5",
105
- "@medicine-wheel/relational-query": "^0.5.5",
106
- "@medicine-wheel/session-reader": "^0.5.5",
107
- "@medicine-wheel/storage-provider": "^0.5.5",
108
- "@medicine-wheel/transformation-tracker": "^0.5.5",
109
- "@medicine-wheel/ui-components": "^0.5.5",
88
+ "@medicine-wheel/ceremonial-diary": "^0.5.7",
89
+ "@medicine-wheel/ceremony-protocol": "^0.5.7",
90
+ "@medicine-wheel/community-review": "^0.5.7",
91
+ "@medicine-wheel/consent-lifecycle": "^0.5.7",
92
+ "@medicine-wheel/creative-orientation": "^0.5.7",
93
+ "@medicine-wheel/data-store": "^0.5.7",
94
+ "@medicine-wheel/data-store-postgres": "^0.5.7",
95
+ "@medicine-wheel/fire-keeper": "^0.5.7",
96
+ "@medicine-wheel/github-ceremony": "^0.5.7",
97
+ "@medicine-wheel/graph-viz": "^0.5.7",
98
+ "@medicine-wheel/importance-unit": "^0.5.7",
99
+ "@medicine-wheel/mcp": "^4.5.6",
100
+ "@medicine-wheel/narrative-cluster": "^0.5.7",
101
+ "@medicine-wheel/narrative-engine": "^0.5.7",
102
+ "@medicine-wheel/ontology-core": "^0.5.7",
103
+ "@medicine-wheel/perception-layer": "^0.5.7",
104
+ "@medicine-wheel/prompt-decomposition": "^0.5.7",
105
+ "@medicine-wheel/relational-index": "^0.5.7",
106
+ "@medicine-wheel/relational-query": "^0.5.7",
107
+ "@medicine-wheel/session-reader": "^0.5.7",
108
+ "@medicine-wheel/storage-provider": "^0.5.7",
109
+ "@medicine-wheel/transformation-tracker": "^0.5.7",
110
+ "@medicine-wheel/ui-components": "^0.5.7",
110
111
  "@neondatabase/serverless": "^0.10.0",
111
112
  "@xyflow/react": "^12.3.0",
112
113
  "clsx": "^2.1.1",