@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/Dockerfile +69 -0
- package/LICENSE +205 -0
- package/README.md +201 -0
- package/app/accountability/page.tsx +95 -0
- package/app/api/ceremonies/route.ts +52 -0
- package/app/api/directions/route.ts +6 -0
- package/app/api/edges/route.ts +27 -0
- package/app/api/health/route.ts +37 -0
- package/app/api/narrative/beats/route.ts +29 -0
- package/app/api/narrative/cycles/route.ts +23 -0
- package/app/api/nodes/route.ts +52 -0
- package/app/api/resources/route.ts +48 -0
- package/app/ceremonies/page.tsx +161 -0
- package/app/globals.css +68 -0
- package/app/graph/page.tsx +200 -0
- package/app/layout.tsx +24 -0
- package/app/narrative/beats/page.tsx +145 -0
- package/app/narrative/cycles/page.tsx +143 -0
- package/app/narrative/page.tsx +113 -0
- package/app/nodes/page.tsx +199 -0
- package/app/page.tsx +148 -0
- package/app/relations/page.tsx +191 -0
- package/components/direction-panel.tsx +96 -0
- package/components/navigation.tsx +105 -0
- package/components/theme-provider.tsx +11 -0
- package/components/workspaces-panel.tsx +110 -0
- package/dist/cli/mw.js +731 -0
- package/dist/cli/mwsrv.js +267 -0
- package/docker-build-push.sh +15 -0
- package/docker-entrypoint.sh +26 -0
- package/lib/jsonl-store.ts +586 -0
- package/lib/store.ts +226 -0
- package/lib/types.ts +23 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +5 -0
- package/package.json +112 -0
- package/postcss.config.mjs +6 -0
- package/public/fonts/Stereohead.otf +0 -0
- package/tsconfig.json +21 -0
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
|
+
});
|