@medicine-wheel/app 0.2.3

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 ADDED
@@ -0,0 +1,731 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * mw — Medicine Wheel CLI
5
+ *
6
+ * Talks to the running HTTP server by default (MW_API_URL).
7
+ * Falls back to MCP via JSON-RPC (node mcp/dist/index.js).
8
+ *
9
+ * Usage: mw <command> [args...]
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ const child_process_1 = require("child_process");
46
+ const path = __importStar(require("path"));
47
+ const fs = __importStar(require("fs"));
48
+ // ── Config ────────────────────────────────────────────────────────
49
+ const MW_API_URL = process.env.MW_API_URL ?? 'http://localhost:3940';
50
+ const MW_FORMAT = process.env.MW_FORMAT ?? 'pretty';
51
+ function resolvePackageRoot() {
52
+ try {
53
+ const scriptDir = path.dirname(fs.realpathSync(process.argv[1]));
54
+ return path.resolve(scriptDir, '..', '..');
55
+ }
56
+ catch {
57
+ return process.cwd();
58
+ }
59
+ }
60
+ function resolveMcpPath() {
61
+ if (process.env.MW_MCP_PATH)
62
+ return process.env.MW_MCP_PATH;
63
+ const packageRoot = resolvePackageRoot();
64
+ try {
65
+ const scriptDir = path.dirname(fs.realpathSync(process.argv[1]));
66
+ // dist/cli/mw.js → ../../mcp/dist/index.js
67
+ const fromScript = path.resolve(scriptDir, '..', '..', 'mcp', 'dist', 'index.js');
68
+ if (fs.existsSync(fromScript))
69
+ return fromScript;
70
+ }
71
+ catch { /* ignore */ }
72
+ const fromCwd = path.join(process.cwd(), 'mcp', 'dist', 'index.js');
73
+ if (fs.existsSync(fromCwd))
74
+ return fromCwd;
75
+ // Installed @medicine-wheel/mcp in node_modules
76
+ const fromModules = path.join(packageRoot, 'node_modules', '@medicine-wheel', 'mcp', 'dist', 'index.js');
77
+ if (fs.existsSync(fromModules))
78
+ return fromModules;
79
+ try {
80
+ return require.resolve('@medicine-wheel/mcp/dist/index.js', {
81
+ paths: [packageRoot, process.cwd()],
82
+ });
83
+ }
84
+ catch {
85
+ // Fall through to empty result below.
86
+ }
87
+ return '';
88
+ }
89
+ const MW_MCP_PATH = resolveMcpPath();
90
+ // ── Colors ────────────────────────────────────────────────────────
91
+ const C = {
92
+ east: '\x1b[33m',
93
+ south: '\x1b[31m',
94
+ west: '\x1b[34m',
95
+ north: '\x1b[37m',
96
+ green: '\x1b[32m',
97
+ dim: '\x1b[2m',
98
+ bold: '\x1b[1m',
99
+ reset: '\x1b[0m',
100
+ };
101
+ function parseArgs(argv) {
102
+ const args = argv.slice(2);
103
+ const flags = {};
104
+ const positional = [];
105
+ for (let i = 0; i < args.length; i++) {
106
+ const arg = args[i];
107
+ if (arg === '--') {
108
+ positional.push(...args.slice(i + 1));
109
+ break;
110
+ }
111
+ else if (arg.startsWith('--')) {
112
+ const key = arg.slice(2);
113
+ const next = args[i + 1];
114
+ if (next !== undefined && !next.startsWith('-')) {
115
+ flags[key] = args[++i];
116
+ }
117
+ else {
118
+ flags[key] = true;
119
+ }
120
+ }
121
+ else if (arg.startsWith('-') && arg.length === 2) {
122
+ const key = arg.slice(1);
123
+ const next = args[i + 1];
124
+ if (next !== undefined && !next.startsWith('-')) {
125
+ flags[key] = args[++i];
126
+ }
127
+ else {
128
+ flags[key] = true;
129
+ }
130
+ }
131
+ else {
132
+ positional.push(arg);
133
+ }
134
+ }
135
+ return { flags, positional };
136
+ }
137
+ // ── HTTP API ──────────────────────────────────────────────────────
138
+ async function api(method, urlPath, body) {
139
+ const url = `${MW_API_URL}${urlPath}`;
140
+ const opts = { method, headers: { 'Content-Type': 'application/json' } };
141
+ if (body !== undefined)
142
+ opts.body = JSON.stringify(body);
143
+ const res = await fetch(url, opts);
144
+ if (!res.ok)
145
+ throw new Error(`HTTP ${res.status}: ${url}`);
146
+ return res.json();
147
+ }
148
+ async function checkApi() {
149
+ try {
150
+ const res = await fetch(`${MW_API_URL}/api/directions`, {
151
+ signal: AbortSignal.timeout(2000),
152
+ });
153
+ return res.ok;
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ // ── MCP JSON-RPC ──────────────────────────────────────────────────
160
+ function mcpRaw(rpcMethod, params) {
161
+ if (!MW_MCP_PATH) {
162
+ console.error('Error: MCP server path not found. Set MW_MCP_PATH or ensure @medicine-wheel/mcp is installed.');
163
+ process.exit(1);
164
+ }
165
+ const req = JSON.stringify({ jsonrpc: '2.0', method: rpcMethod, params, id: 1 });
166
+ const result = (0, child_process_1.spawnSync)('node', [MW_MCP_PATH], {
167
+ input: req,
168
+ encoding: 'utf8',
169
+ timeout: 10000,
170
+ });
171
+ if (result.error) {
172
+ console.error('MCP error:', result.error.message);
173
+ return '';
174
+ }
175
+ return result.stdout?.trim() ?? '';
176
+ }
177
+ function mcpCall(toolName, args) {
178
+ const raw = mcpRaw('tools/call', { name: toolName, arguments: args });
179
+ if (!raw)
180
+ return;
181
+ try {
182
+ const data = JSON.parse(raw);
183
+ const text = data?.result?.content?.[0]?.text ?? raw;
184
+ pp(text);
185
+ }
186
+ catch {
187
+ pp(raw);
188
+ }
189
+ }
190
+ // ── Pretty print ──────────────────────────────────────────────────
191
+ function pp(raw) {
192
+ if (MW_FORMAT === 'json') {
193
+ console.log(raw);
194
+ return;
195
+ }
196
+ if (MW_FORMAT === 'quiet')
197
+ return;
198
+ try {
199
+ const data = JSON.parse(raw);
200
+ console.log(JSON.stringify(data, null, 2));
201
+ }
202
+ catch {
203
+ console.log(raw);
204
+ }
205
+ }
206
+ function ppValue(val) {
207
+ pp(JSON.stringify(val));
208
+ }
209
+ // ── Help ──────────────────────────────────────────────────────────
210
+ function cmdHelp() {
211
+ console.log(`
212
+ ${C.bold}🌿 mw — Medicine Wheel CLI${C.reset}
213
+
214
+ CEREMONY LIFECYCLE
215
+ mw ceremony open <intention> Open ceremony (starts in East)
216
+ mw ceremony close <id> [summary] Close ceremony
217
+ mw ceremony list [--direction east] List ceremonies
218
+ mw ceremony get <id> Get ceremony by ID
219
+
220
+ DIRECTIONS
221
+ mw direction [east|south|west|north] Show direction metadata
222
+ mw directions Show all four directions
223
+
224
+ CYCLES
225
+ mw cycle create <research_question> Create research cycle
226
+ mw cycle list List all cycles
227
+ mw cycle advance <id> <direction> Advance cycle direction
228
+
229
+ NODES
230
+ mw node create <name> <type> <desc> Create relational node
231
+ mw node list [--type human] [--direction east]
232
+ mw node get <id> Get node by ID
233
+ mw node search <query> Search nodes
234
+
235
+ NARRATIVE
236
+ mw beat create <dir> <title> <desc> Create narrative beat
237
+ mw beat list [--direction east] List beats
238
+ mw arc <cycle_id> Get narrative arc
239
+
240
+ STRUCTURAL TENSION
241
+ mw chart create <outcome> <reality> <dir> Create STC
242
+ mw chart list [--direction east] List charts
243
+ mw chart progress <id> Get progress
244
+ mw mmot <chart_id> <expected> <actual> <analysis> MMOT review
245
+
246
+ RELATIONS
247
+ mw edge create <from> <to> <type> Create edge
248
+ mw edge list [--node <id>] List edges
249
+ mw web <node_id> [depth] Relational web
250
+
251
+ VALIDATION
252
+ mw validate wilson <description> Wilson paradigm check
253
+ mw validate ocap <data_plan_json> OCAP® compliance check
254
+ mw validate accountability <plan_json> Accountability audit
255
+ mw validate bridge <concept> [dir] Two-Eyed Seeing bridge
256
+
257
+ MEMORY
258
+ mw memory store <key> <value> [dir] Store relational memory
259
+
260
+ SYSTEM
261
+ mw status Show system status
262
+ mw tools List all MCP tools
263
+ mw help This help
264
+
265
+ ENVIRONMENT
266
+ MW_API_URL API base URL (default: http://localhost:3940)
267
+ MW_MCP_PATH MCP server path (auto-detected)
268
+ MW_FORMAT Output format: pretty (default), json, quiet
269
+ `);
270
+ }
271
+ // ── Status ────────────────────────────────────────────────────────
272
+ async function cmdStatus() {
273
+ console.log(`${C.bold}🌿 Medicine Wheel Status${C.reset}\n`);
274
+ const apiOk = await checkApi();
275
+ if (apiOk) {
276
+ console.log(` ${C.green}✓${C.reset} API: ${MW_API_URL}`);
277
+ }
278
+ else {
279
+ console.log(` ${C.south}✗${C.reset} API: ${MW_API_URL} (not reachable)`);
280
+ }
281
+ if (MW_MCP_PATH && fs.existsSync(MW_MCP_PATH)) {
282
+ console.log(` ${C.green}✓${C.reset} MCP: ${MW_MCP_PATH}`);
283
+ }
284
+ else {
285
+ console.log(` ${C.south}✗${C.reset} MCP: not found (set MW_MCP_PATH)`);
286
+ }
287
+ // Locate store relative to cwd
288
+ const storeDir = process.env.MW_DATA_DIR
289
+ ?? path.join(process.cwd(), '.mw', 'store');
290
+ if (fs.existsSync(storeDir)) {
291
+ const count = (file) => {
292
+ try {
293
+ return fs.readFileSync(path.join(storeDir, file), 'utf8')
294
+ .split('\n').filter(Boolean).length;
295
+ }
296
+ catch {
297
+ return 0;
298
+ }
299
+ };
300
+ console.log(` ${C.green}✓${C.reset} Store: ${storeDir}`);
301
+ console.log(` ${C.dim}${count('nodes.jsonl')} nodes · ` +
302
+ `${count('edges.jsonl')} edges · ` +
303
+ `${count('ceremonies.jsonl')} ceremonies · ` +
304
+ `${count('cycles.jsonl')} cycles · ` +
305
+ `${count('beats.jsonl')} beats · ` +
306
+ `${count('charts.jsonl')} charts${C.reset}`);
307
+ }
308
+ else {
309
+ console.log(` ${C.south}✗${C.reset} Store: .mw/store/ not found`);
310
+ }
311
+ console.log('');
312
+ }
313
+ // ── Tools ─────────────────────────────────────────────────────────
314
+ function cmdTools() {
315
+ const raw = mcpRaw('tools/list', {});
316
+ if (!raw)
317
+ return;
318
+ try {
319
+ const data = JSON.parse(raw);
320
+ const tools = data?.result?.tools ?? [];
321
+ console.log(`\n ${C.bold}${tools.length} tools available:${C.reset}\n`);
322
+ for (const t of tools) {
323
+ const desc = t.description.length > 70
324
+ ? t.description.slice(0, 67) + '...'
325
+ : t.description;
326
+ console.log(` ${t.name.padEnd(42)} ${C.dim}${desc}${C.reset}`);
327
+ }
328
+ console.log('');
329
+ }
330
+ catch {
331
+ pp(raw);
332
+ }
333
+ }
334
+ // ── Ceremony ──────────────────────────────────────────────────────
335
+ async function cmdCeremony(positional, flags) {
336
+ const sub = positional[0] ?? 'list';
337
+ switch (sub) {
338
+ case 'open':
339
+ mcpCall('mw_ceremony_open', {
340
+ intention: positional.slice(1).join(' ') || 'Opening ceremony',
341
+ });
342
+ break;
343
+ case 'close':
344
+ mcpCall('mw_ceremony_close', {
345
+ ceremony_id: positional[1] ?? '',
346
+ summary: positional.slice(2).join(' ') || 'Ceremony complete',
347
+ });
348
+ break;
349
+ case 'get':
350
+ mcpCall('get_ceremony', { ceremony_id: positional[1] ?? '' });
351
+ break;
352
+ case 'list': {
353
+ const dir = flags['direction'] ?? flags['d'];
354
+ const apiAvailable = await checkApi();
355
+ if (apiAvailable) {
356
+ const params = dir ? `?direction=${dir}` : '';
357
+ ppValue(await api('GET', `/api/ceremonies${params}`));
358
+ }
359
+ else {
360
+ mcpCall('list_ceremonies', {});
361
+ }
362
+ break;
363
+ }
364
+ default:
365
+ console.error(`Unknown ceremony sub-command: ${sub}`);
366
+ }
367
+ }
368
+ // ── Direction ─────────────────────────────────────────────────────
369
+ async function cmdDirection(positional) {
370
+ const dir = positional[0];
371
+ if (!dir) {
372
+ await cmdDirections();
373
+ return;
374
+ }
375
+ mcpCall('mw_get_direction', { direction: dir });
376
+ }
377
+ async function cmdDirections() {
378
+ const apiAvailable = await checkApi();
379
+ if (apiAvailable) {
380
+ const dirs = await api('GET', '/api/directions');
381
+ const icons = {
382
+ east: '🌅', south: '🔥', west: '🌊', north: '❄️',
383
+ };
384
+ const colors = {
385
+ east: C.east, south: C.south, west: C.west, north: C.north,
386
+ };
387
+ console.log('');
388
+ for (const d of dirs) {
389
+ const icon = icons[d.name] ?? '🌿';
390
+ const color = colors[d.name] ?? C.reset;
391
+ console.log(` ${icon} ${color}${d.name.toUpperCase().padEnd(6)}${C.reset} (${d.ojibwe}) — ${d.season}`);
392
+ if (d.medicine?.length) {
393
+ console.log(` ${C.dim}Medicine: ${d.medicine.join(', ')}${C.reset}`);
394
+ }
395
+ if (d.teachings?.length) {
396
+ console.log(` ${C.dim}${d.teachings.slice(0, 2).join(', ')}${C.reset}`);
397
+ }
398
+ console.log('');
399
+ }
400
+ }
401
+ else {
402
+ for (const d of ['east', 'south', 'west', 'north']) {
403
+ mcpCall('mw_get_direction', { direction: d });
404
+ }
405
+ }
406
+ }
407
+ // ── Cycle ─────────────────────────────────────────────────────────
408
+ async function cmdCycle(positional) {
409
+ const sub = positional[0] ?? 'list';
410
+ switch (sub) {
411
+ case 'create': {
412
+ const question = positional.slice(1).join(' ');
413
+ const apiAvailable = await checkApi();
414
+ if (apiAvailable) {
415
+ ppValue(await api('POST', '/api/narrative/cycles', {
416
+ research_question: question,
417
+ current_direction: 'east',
418
+ }));
419
+ }
420
+ else {
421
+ mcpCall('create_research_cycle', {
422
+ research_question: question,
423
+ current_direction: 'east',
424
+ });
425
+ }
426
+ break;
427
+ }
428
+ case 'list': {
429
+ const apiAvailable = await checkApi();
430
+ if (apiAvailable) {
431
+ const cycles = await api('GET', '/api/narrative/cycles');
432
+ const icons = {
433
+ east: '🌅', south: '🔥', west: '🌊', north: '❄️',
434
+ };
435
+ console.log('');
436
+ for (const c of cycles) {
437
+ const d = c.current_direction ?? 'east';
438
+ const icon = icons[d] ?? '🌿';
439
+ const archived = c.archived ? ' [archived]' : '';
440
+ console.log(` ${icon} ${c.id.slice(0, 30)}`);
441
+ console.log(` "${c.research_question}"`);
442
+ console.log(` Direction: ${d} · Wilson: ${c.wilson_alignment ?? 0} · OCAP: ${c.ocap_compliant ?? false}${archived}`);
443
+ console.log('');
444
+ }
445
+ }
446
+ else {
447
+ mcpCall('list_cycles', {});
448
+ }
449
+ break;
450
+ }
451
+ case 'advance':
452
+ mcpCall('update_cycle_direction', {
453
+ cycle_id: positional[1] ?? '',
454
+ new_direction: positional[2] ?? '',
455
+ });
456
+ break;
457
+ case 'get':
458
+ mcpCall('get_cycle', { cycle_id: positional[1] ?? '' });
459
+ break;
460
+ case 'arc':
461
+ mcpCall('get_narrative_arc', { cycle_id: positional[1] ?? '' });
462
+ break;
463
+ default:
464
+ console.error(`Unknown cycle sub-command: ${sub}`);
465
+ }
466
+ }
467
+ // ── Node ──────────────────────────────────────────────────────────
468
+ async function cmdNode(positional, flags) {
469
+ const sub = positional[0] ?? 'list';
470
+ switch (sub) {
471
+ case 'create': {
472
+ const [, name, type, ...descParts] = positional;
473
+ const desc = descParts.join(' ');
474
+ const apiAvailable = await checkApi();
475
+ if (apiAvailable) {
476
+ ppValue(await api('POST', '/api/nodes', { name, type, description: desc }));
477
+ }
478
+ else {
479
+ mcpCall('create_relational_node', { name, type, description: desc });
480
+ }
481
+ break;
482
+ }
483
+ case 'list': {
484
+ const type = flags['type'] ?? flags['t'];
485
+ const dir = flags['direction'] ?? flags['d'];
486
+ const params = new URLSearchParams();
487
+ if (type && typeof type === 'string')
488
+ params.set('type', type);
489
+ if (dir && typeof dir === 'string')
490
+ params.set('direction', dir);
491
+ const query = params.toString() ? `?${params}` : '';
492
+ const apiAvailable = await checkApi();
493
+ if (apiAvailable) {
494
+ const nodes = await api('GET', `/api/nodes${query}`);
495
+ const icons = {
496
+ human: '👤', land: '🌍', spirit: '✨',
497
+ ancestor: '👴', future: '🌱', knowledge: '📚',
498
+ };
499
+ for (const n of nodes) {
500
+ const icon = icons[n.type ?? ''] ?? '•';
501
+ const dStr = n.direction ? ` [${n.direction}]` : '';
502
+ console.log(` ${icon} ${n.name}${dStr}`);
503
+ console.log(` ${n.id}`);
504
+ }
505
+ console.log(`\n ${nodes.length} nodes`);
506
+ }
507
+ else {
508
+ mcpCall('list_relational_nodes', {});
509
+ }
510
+ break;
511
+ }
512
+ case 'get':
513
+ mcpCall('get_relational_node', { node_id: positional[1] ?? '' });
514
+ break;
515
+ case 'search':
516
+ mcpCall('search_nodes', { query: positional.slice(1).join(' ') });
517
+ break;
518
+ default:
519
+ console.error(`Unknown node sub-command: ${sub}`);
520
+ }
521
+ }
522
+ // ── Beat ──────────────────────────────────────────────────────────
523
+ async function cmdBeat(positional) {
524
+ const sub = positional[0] ?? 'list';
525
+ switch (sub) {
526
+ case 'create':
527
+ mcpCall('create_narrative_beat', {
528
+ direction: positional[1] ?? '',
529
+ title: positional[2] ?? '',
530
+ description: positional.slice(3).join(' '),
531
+ learnings: [],
532
+ });
533
+ break;
534
+ case 'list': {
535
+ const apiAvailable = await checkApi();
536
+ if (apiAvailable) {
537
+ ppValue(await api('GET', '/api/narrative/beats'));
538
+ }
539
+ else {
540
+ mcpCall('list_narrative_beats', {});
541
+ }
542
+ break;
543
+ }
544
+ default:
545
+ console.error(`Unknown beat sub-command: ${sub}`);
546
+ }
547
+ }
548
+ // ── Edge ──────────────────────────────────────────────────────────
549
+ function cmdEdge(positional, flags) {
550
+ const sub = positional[0] ?? 'list';
551
+ switch (sub) {
552
+ case 'create':
553
+ mcpCall('create_relational_edge', {
554
+ from_node_id: positional[1] ?? '',
555
+ to_node_id: positional[2] ?? '',
556
+ relationship_type: positional[3] ?? '',
557
+ });
558
+ break;
559
+ case 'list': {
560
+ const node = flags['node'] ?? flags['n'];
561
+ mcpCall('list_edges', node ? { node_id: node } : {});
562
+ break;
563
+ }
564
+ default:
565
+ console.error(`Unknown edge sub-command: ${sub}`);
566
+ }
567
+ }
568
+ // ── Web ───────────────────────────────────────────────────────────
569
+ function cmdWeb(positional) {
570
+ const nodeId = positional[0] ?? '';
571
+ const depth = parseInt(positional[1] ?? '2', 10);
572
+ mcpCall('get_relational_web', { node_id: nodeId, depth });
573
+ }
574
+ // ── Chart / STC ───────────────────────────────────────────────────
575
+ function cmdChart(positional) {
576
+ const sub = positional[0] ?? 'list';
577
+ switch (sub) {
578
+ case 'create':
579
+ mcpCall('create_structural_tension_chart', {
580
+ desired_outcome: positional[1] ?? '',
581
+ current_reality: positional[2] ?? '',
582
+ direction: positional[3] ?? 'east',
583
+ });
584
+ break;
585
+ case 'list':
586
+ mcpCall('list_structural_tension_charts', {});
587
+ break;
588
+ case 'progress':
589
+ mcpCall('get_chart_progress', { chart_id: positional[1] ?? '' });
590
+ break;
591
+ default:
592
+ console.error(`Unknown chart sub-command: ${sub}`);
593
+ }
594
+ }
595
+ // ── MMOT ──────────────────────────────────────────────────────────
596
+ function cmdMmot(positional) {
597
+ mcpCall('creator_moment_of_truth', {
598
+ chart_id: positional[0] ?? '',
599
+ expected_outcome: positional[1] ?? '',
600
+ actual_outcome: positional[2] ?? '',
601
+ analysis: positional[3] ?? '',
602
+ adjustments: [],
603
+ feedback_system: '',
604
+ });
605
+ }
606
+ // ── Validate ──────────────────────────────────────────────────────
607
+ function cmdValidate(positional) {
608
+ const sub = positional[0] ?? 'wilson';
609
+ switch (sub) {
610
+ case 'wilson':
611
+ mcpCall('wilson_paradigm_checker', {
612
+ research_description: positional.slice(1).join(' '),
613
+ });
614
+ break;
615
+ case 'ocap':
616
+ mcpCall('ocap_compliance_checker', {
617
+ data_plan: JSON.parse(positional[1] ?? '{}'),
618
+ });
619
+ break;
620
+ case 'accountability':
621
+ mcpCall('accountability_validator', {
622
+ research_plan: JSON.parse(positional[1] ?? '{}'),
623
+ });
624
+ break;
625
+ case 'bridge':
626
+ mcpCall('two_eyed_seeing_bridge', {
627
+ concept: positional[1] ?? '',
628
+ direction: positional[2] ?? 'integrate_both',
629
+ });
630
+ break;
631
+ default:
632
+ console.error(`Unknown validate sub-command: ${sub}`);
633
+ }
634
+ }
635
+ // ── Memory ────────────────────────────────────────────────────────
636
+ function cmdMemory(positional) {
637
+ const sub = positional[0] ?? 'store';
638
+ switch (sub) {
639
+ case 'store': {
640
+ const args = {
641
+ key: positional[1] ?? '',
642
+ value: positional[2] ?? '',
643
+ };
644
+ if (positional[3])
645
+ args.direction = positional[3];
646
+ mcpCall('mw_store_memory', args);
647
+ break;
648
+ }
649
+ default:
650
+ console.error(`Unknown memory sub-command: ${sub}`);
651
+ }
652
+ }
653
+ // ── Main dispatch ─────────────────────────────────────────────────
654
+ async function main() {
655
+ const { flags, positional } = parseArgs(process.argv);
656
+ if (flags['help'] || flags['h']) {
657
+ cmdHelp();
658
+ return;
659
+ }
660
+ const cmd = positional[0] ?? 'help';
661
+ const rest = positional.slice(1);
662
+ switch (cmd) {
663
+ case 'help':
664
+ cmdHelp();
665
+ break;
666
+ case 'status':
667
+ await cmdStatus();
668
+ break;
669
+ case 'tools':
670
+ cmdTools();
671
+ break;
672
+ case 'ceremony':
673
+ case 'c':
674
+ await cmdCeremony(rest, flags);
675
+ break;
676
+ case 'direction':
677
+ case 'dir':
678
+ await cmdDirection(rest);
679
+ break;
680
+ case 'directions':
681
+ case 'dirs':
682
+ await cmdDirections();
683
+ break;
684
+ case 'cycle':
685
+ case 'cy':
686
+ await cmdCycle(rest);
687
+ break;
688
+ case 'node':
689
+ case 'n':
690
+ await cmdNode(rest, flags);
691
+ break;
692
+ case 'beat':
693
+ case 'b':
694
+ await cmdBeat(rest);
695
+ break;
696
+ case 'edge':
697
+ case 'e':
698
+ cmdEdge(rest, flags);
699
+ break;
700
+ case 'web':
701
+ case 'w':
702
+ cmdWeb(rest);
703
+ break;
704
+ case 'chart':
705
+ case 'stc':
706
+ cmdChart(rest);
707
+ break;
708
+ case 'mmot':
709
+ cmdMmot(rest);
710
+ break;
711
+ case 'validate':
712
+ case 'v':
713
+ cmdValidate(rest);
714
+ break;
715
+ case 'memory':
716
+ case 'mem':
717
+ cmdMemory(rest);
718
+ break;
719
+ case 'arc':
720
+ mcpCall('get_narrative_arc', { cycle_id: rest[0] ?? '' });
721
+ break;
722
+ default:
723
+ console.error(`${C.south}Unknown command: ${cmd}${C.reset}`);
724
+ console.error("Run 'mw help' for usage.");
725
+ process.exit(1);
726
+ }
727
+ }
728
+ main().catch((err) => {
729
+ console.error('❌ Fatal error:', err instanceof Error ? err.message : String(err));
730
+ process.exit(1);
731
+ });