@medicine-wheel/app 0.2.4 → 0.2.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
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
45
45
  const child_process_1 = require("child_process");
46
46
  const path = __importStar(require("path"));
47
47
  const fs = __importStar(require("fs"));
48
+ const skills_1 = require("./skills");
48
49
  // ── Config ────────────────────────────────────────────────────────
49
50
  const MW_API_URL = process.env.MW_API_URL ?? 'http://localhost:3940';
50
51
  const MW_FORMAT = process.env.MW_FORMAT ?? 'pretty';
@@ -254,6 +255,10 @@ ${C.bold}🌿 mw — Medicine Wheel CLI${C.reset}
254
255
  mw validate accountability <plan_json> Accountability audit
255
256
  mw validate bridge <concept> [dir] Two-Eyed Seeing bridge
256
257
 
258
+ SKILLS
259
+ mw skill view List available CLI skills
260
+ mw skill install [name] Install a skill (or all)
261
+
257
262
  MEMORY
258
263
  mw memory store <key> <value> [dir] Store relational memory
259
264
 
@@ -632,6 +637,24 @@ function cmdValidate(positional) {
632
637
  console.error(`Unknown validate sub-command: ${sub}`);
633
638
  }
634
639
  }
640
+ // ── Skill ─────────────────────────────────────────────────────────
641
+ function cmdSkill(positional) {
642
+ const sub = positional[0] ?? 'view';
643
+ switch (sub) {
644
+ case 'view':
645
+ case 'list':
646
+ (0, skills_1.viewSkills)('cli', C);
647
+ break;
648
+ case 'install': {
649
+ const name = positional[1]; // undefined means install all
650
+ (0, skills_1.installSkill)('cli', name, C);
651
+ break;
652
+ }
653
+ default:
654
+ console.error(`Unknown skill sub-command: ${sub}`);
655
+ console.error("Available: view, install");
656
+ }
657
+ }
635
658
  // ── Memory ────────────────────────────────────────────────────────
636
659
  function cmdMemory(positional) {
637
660
  const sub = positional[0] ?? 'store';
@@ -716,6 +739,10 @@ async function main() {
716
739
  case 'mem':
717
740
  cmdMemory(rest);
718
741
  break;
742
+ case 'skill':
743
+ case 'sk':
744
+ cmdSkill(rest);
745
+ break;
719
746
  case 'arc':
720
747
  mcpCall('get_narrative_arc', { cycle_id: rest[0] ?? '' });
721
748
  break;
package/dist/cli/mwsrv.js CHANGED
@@ -54,7 +54,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
54
54
  const child_process_1 = require("child_process");
55
55
  const path = __importStar(require("path"));
56
56
  const fs = __importStar(require("fs"));
57
+ const skills_1 = require("./skills");
57
58
  const DOCKER_IMAGE = 'jgwill/medicine-wheel:app';
59
+ const C = {
60
+ bold: '\x1b[1m',
61
+ dim: '\x1b[2m',
62
+ green: '\x1b[32m',
63
+ south: '\x1b[31m',
64
+ reset: '\x1b[0m',
65
+ };
58
66
  const DEFAULT_PORT = 3940;
59
67
  const CONTAINER_PORT = 3940;
60
68
  function parseArgs(argv) {
@@ -195,6 +203,24 @@ function runLocal(opts) {
195
203
  process.on('SIGTERM', shutdown);
196
204
  proc.on('exit', (code) => process.exit(code ?? 0));
197
205
  }
206
+ // ── Skill ─────────────────────────────────────────────────────────
207
+ function cmdSkill(positional) {
208
+ const sub = positional[0] ?? 'view';
209
+ switch (sub) {
210
+ case 'view':
211
+ case 'list':
212
+ (0, skills_1.viewSkills)('srv', C);
213
+ break;
214
+ case 'install': {
215
+ const name = positional[1]; // undefined means install all
216
+ (0, skills_1.installSkill)('srv', name, C);
217
+ break;
218
+ }
219
+ default:
220
+ console.error(`Unknown skill sub-command: ${sub}`);
221
+ console.error("Available: view, install");
222
+ }
223
+ }
198
224
  // ── Help ──────────────────────────────────────────────────────────
199
225
  function showHelp() {
200
226
  console.log(`
@@ -215,6 +241,10 @@ OPTIONS
215
241
  --no-open Do not auto-open browser
216
242
  --help, -h Show this help
217
243
 
244
+ SKILLS
245
+ mwsrv skill view List available server skills
246
+ mwsrv skill install [name] Install a skill (or all)
247
+
218
248
  EXAMPLES
219
249
  # Start locally (uses current directory's .mw/store)
220
250
  mwsrv
@@ -239,11 +269,15 @@ ENVIRONMENT
239
269
  }
240
270
  // ── Main ──────────────────────────────────────────────────────────
241
271
  async function main() {
242
- const { flags } = parseArgs(process.argv);
272
+ const { flags, positional } = parseArgs(process.argv);
243
273
  if (flags['help'] || flags['h']) {
244
274
  showHelp();
245
275
  return;
246
276
  }
277
+ if (positional[0] === 'skill' || positional[0] === 'sk') {
278
+ cmdSkill(positional.slice(1));
279
+ return;
280
+ }
247
281
  const port = Number(flags['port'] ?? flags['p'] ?? DEFAULT_PORT);
248
282
  const directory = String(flags['directory'] ?? flags['D'] ?? process.cwd());
249
283
  const useDocker = Boolean(flags['docker']);
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ /**
3
+ * Shared skill registry and management for Medicine Wheel CLIs.
4
+ *
5
+ * Skills are curated capabilities that can be installed into a local
6
+ * `.mw/skills/` directory. Each CLI (mw, mwsrv) exposes its own
7
+ * relevant subset via `skill view` and `skill install`.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.listSkills = listSkills;
44
+ exports.getSkill = getSkill;
45
+ exports.getComplement = getComplement;
46
+ exports.viewSkills = viewSkills;
47
+ exports.installSkill = installSkill;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ // ── Built-in skill catalog ───────────────────────────────────────
51
+ const SKILLS = [
52
+ // ── CLI skills (mw) ────────────────────────────────────────────
53
+ {
54
+ name: 'direction-inquiry',
55
+ title: 'Direction Inquiry',
56
+ description: 'Analyze tasks through the Four Directions (East / South / West / North)',
57
+ target: 'cli',
58
+ complement: 'api-health',
59
+ body: `# Skill: Direction Inquiry
60
+
61
+ ## Purpose
62
+ Analyze an engineering task using the Four Directions framework.
63
+
64
+ ## Input
65
+ - Engineering task description
66
+ - Optional: constraints, repo paths
67
+
68
+ ## Output
69
+ - **East** — vision statement
70
+ - **South** — analysis questions
71
+ - **West** — validation checks
72
+ - **North** — action stack
73
+ - Ceremony recommendation if balance is poor
74
+
75
+ ## Usage
76
+ \`\`\`
77
+ mw skill run direction-inquiry "Refactor auth module"
78
+ \`\`\`
79
+ `,
80
+ },
81
+ {
82
+ name: 'fire-keeper-check',
83
+ title: 'Fire Keeper Check',
84
+ description: 'Run gating and stewardship checks on proposed actions',
85
+ target: 'cli',
86
+ complement: 'session-manager',
87
+ body: `# Skill: Fire Keeper Check
88
+
89
+ ## Purpose
90
+ Evaluate a proposed action against permission tiers and relational gates.
91
+
92
+ ## Input
93
+ - Proposed action description
94
+ - Current permission tier
95
+ - Current ceremony phase
96
+ - Optional: Wilson / OCAP metadata
97
+
98
+ ## Output
99
+ - accept / hold / human-needed assessment
100
+ - Unsatisfied gates
101
+ - Check-back step results
102
+ - Suggested next move
103
+
104
+ ## Usage
105
+ \`\`\`
106
+ mw skill run fire-keeper-check "Deploy to production"
107
+ \`\`\`
108
+ `,
109
+ },
110
+ {
111
+ name: 'wave-spec-generator',
112
+ title: 'Wave Spec Generator',
113
+ description: 'Generate .pde wave specifications for development cycles',
114
+ target: 'cli',
115
+ complement: 'storage-config',
116
+ body: `# Skill: Wave Spec Generator
117
+
118
+ ## Purpose
119
+ Create a proposal-grade wave bundle matching current .pde practice.
120
+
121
+ ## Input
122
+ - Goal description
123
+ - Relevant specs
124
+ - Target paths and constraints
125
+
126
+ ## Output
127
+ - ORCHESTRATION.md
128
+ - PROMPT.txt
129
+ - artifacts/ checklist
130
+
131
+ ## Usage
132
+ \`\`\`
133
+ mw skill run wave-spec-generator "Add caching layer"
134
+ \`\`\`
135
+ `,
136
+ },
137
+ {
138
+ name: 'ceremony-guide',
139
+ title: 'Ceremony Guide',
140
+ description: 'Guide through ceremony lifecycle phases with protocol awareness',
141
+ target: 'cli',
142
+ complement: 'docker-setup',
143
+ body: `# Skill: Ceremony Guide
144
+
145
+ ## Purpose
146
+ Provide step-by-step guidance through the ceremony lifecycle.
147
+
148
+ ## Input
149
+ - Current ceremony state (or new ceremony intention)
150
+ - Phase: opening / council / integration / closure
151
+
152
+ ## Output
153
+ - Current phase assessment
154
+ - Protocol requirements for next transition
155
+ - Relational checks and community review prompts
156
+ - Completion criteria
157
+
158
+ ## Usage
159
+ \`\`\`
160
+ mw skill run ceremony-guide "Community data review"
161
+ \`\`\`
162
+ `,
163
+ },
164
+ // ── Server skills (mwsrv) ─────────────────────────────────────
165
+ {
166
+ name: 'docker-setup',
167
+ title: 'Docker Setup',
168
+ description: 'Configure and validate Docker environment for Medicine Wheel server',
169
+ target: 'srv',
170
+ complement: 'ceremony-guide',
171
+ body: `# Skill: Docker Setup
172
+
173
+ ## Purpose
174
+ Configure the Docker environment for running the Medicine Wheel server.
175
+
176
+ ## Checks
177
+ - Docker daemon availability
178
+ - Image pull status (jgwill/medicine-wheel:app)
179
+ - Volume mount configuration
180
+ - Port availability
181
+
182
+ ## Usage
183
+ \`\`\`
184
+ mwsrv skill run docker-setup
185
+ \`\`\`
186
+ `,
187
+ },
188
+ {
189
+ name: 'storage-config',
190
+ title: 'Storage Configuration',
191
+ description: 'Configure and validate storage providers (JSONL / PostgreSQL)',
192
+ target: 'srv',
193
+ complement: 'wave-spec-generator',
194
+ body: `# Skill: Storage Configuration
195
+
196
+ ## Purpose
197
+ Configure the storage backend for the Medicine Wheel server.
198
+
199
+ ## Supported Providers
200
+ - **jsonl** — Local file-based storage (.mw/store/)
201
+ - **postgres** — PostgreSQL via DATABASE_URL
202
+
203
+ ## Checks
204
+ - Current MW_STORAGE_PROVIDER value
205
+ - Data directory existence and permissions
206
+ - PostgreSQL connectivity (if applicable)
207
+ - Migration status
208
+
209
+ ## Usage
210
+ \`\`\`
211
+ mwsrv skill run storage-config
212
+ \`\`\`
213
+ `,
214
+ },
215
+ {
216
+ name: 'api-health',
217
+ title: 'API Health Monitor',
218
+ description: 'Monitor and diagnose API endpoint health and connectivity',
219
+ target: 'srv',
220
+ complement: 'direction-inquiry',
221
+ body: `# Skill: API Health Monitor
222
+
223
+ ## Purpose
224
+ Check the health and connectivity of Medicine Wheel API endpoints.
225
+
226
+ ## Checks
227
+ - Server reachability (MW_API_URL)
228
+ - Core endpoint status (/api/directions, /api/ceremonies, /api/nodes)
229
+ - Response time measurements
230
+ - Storage layer connectivity
231
+
232
+ ## Usage
233
+ \`\`\`
234
+ mwsrv skill run api-health
235
+ \`\`\`
236
+ `,
237
+ },
238
+ {
239
+ name: 'session-manager',
240
+ title: 'Session Manager',
241
+ description: 'Manage and inspect active server sessions and connections',
242
+ target: 'srv',
243
+ complement: 'fire-keeper-check',
244
+ body: `# Skill: Session Manager
245
+
246
+ ## Purpose
247
+ Inspect and manage active sessions on the Medicine Wheel server.
248
+
249
+ ## Capabilities
250
+ - List active sessions
251
+ - Inspect session state and ceremony context
252
+ - View session data directory contents
253
+ - Cleanup stale session data
254
+
255
+ ## Usage
256
+ \`\`\`
257
+ mwsrv skill run session-manager
258
+ \`\`\`
259
+ `,
260
+ },
261
+ ];
262
+ // ── Helpers ───────────────────────────────────────────────────────
263
+ function getSkillsDir() {
264
+ const dataDir = process.env.MW_DATA_DIR
265
+ ?? path.join(process.cwd(), '.mw');
266
+ return path.join(dataDir, 'skills');
267
+ }
268
+ function isInstalled(skill) {
269
+ const dir = getSkillsDir();
270
+ return fs.existsSync(path.join(dir, skill.name, 'SKILL.md'));
271
+ }
272
+ // ── Public API ────────────────────────────────────────────────────
273
+ /** Return all skills for the given target binary. */
274
+ function listSkills(target) {
275
+ return SKILLS.filter((s) => s.target === target);
276
+ }
277
+ /** Return a single skill by name. */
278
+ function getSkill(name) {
279
+ return SKILLS.find((s) => s.name === name);
280
+ }
281
+ /** Return the complement skill for a given skill. */
282
+ function getComplement(skill) {
283
+ return skill.complement ? getSkill(skill.complement) : undefined;
284
+ }
285
+ /**
286
+ * Print skill catalog for the given target.
287
+ *
288
+ * @param target Which binary's skills to show ('cli' | 'srv')
289
+ * @param colors ANSI colour map (must include bold, dim, green, reset)
290
+ */
291
+ function viewSkills(target, colors) {
292
+ const skills = listSkills(target);
293
+ const label = target === 'cli' ? 'mw' : 'mwsrv';
294
+ console.log(`\n ${colors.bold}🌿 ${label} skills (${skills.length} available)${colors.reset}\n`);
295
+ for (const s of skills) {
296
+ const installed = isInstalled(s);
297
+ const marker = installed
298
+ ? `${colors.green}✓${colors.reset}`
299
+ : `${colors.dim}○${colors.reset}`;
300
+ const comp = s.complement
301
+ ? `${colors.dim} ↔ ${s.complement}${colors.reset}`
302
+ : '';
303
+ console.log(` ${marker} ${s.name.padEnd(24)} ${s.description}${comp}`);
304
+ }
305
+ console.log('');
306
+ }
307
+ /**
308
+ * Install a skill (or all skills) for the given target.
309
+ *
310
+ * @returns number of newly installed skills
311
+ */
312
+ function installSkill(target, name, colors) {
313
+ const C = colors ?? { bold: '', dim: '', green: '', south: '', reset: '' };
314
+ const dir = getSkillsDir();
315
+ const toInstall = name
316
+ ? SKILLS.filter((s) => s.name === name && s.target === target)
317
+ : listSkills(target);
318
+ if (name && toInstall.length === 0) {
319
+ // Check if the skill exists for the other target
320
+ const other = SKILLS.find((s) => s.name === name);
321
+ if (other) {
322
+ const otherLabel = other.target === 'cli' ? 'mw' : 'mwsrv';
323
+ console.error(`${C.south}Skill "${name}" belongs to ${otherLabel}, not this CLI.${C.reset}`);
324
+ }
325
+ else {
326
+ console.error(`${C.south}Unknown skill: ${name}${C.reset}`);
327
+ }
328
+ return 0;
329
+ }
330
+ let installed = 0;
331
+ for (const skill of toInstall) {
332
+ const skillDir = path.join(dir, skill.name);
333
+ const skillFile = path.join(skillDir, 'SKILL.md');
334
+ if (fs.existsSync(skillFile)) {
335
+ console.log(` ${C.dim}⊘ ${skill.name} (already installed)${C.reset}`);
336
+ continue;
337
+ }
338
+ fs.mkdirSync(skillDir, { recursive: true });
339
+ fs.writeFileSync(skillFile, skill.body, 'utf8');
340
+ installed++;
341
+ console.log(` ${C.green}✓${C.reset} Installed ${skill.name}`);
342
+ // Show complement relationship
343
+ const comp = getComplement(skill);
344
+ if (comp) {
345
+ const compLabel = comp.target === 'cli' ? 'mw' : 'mwsrv';
346
+ console.log(` ${C.dim}↔ complement: ${comp.name} (${compLabel} skill install ${comp.name})${C.reset}`);
347
+ }
348
+ }
349
+ if (installed > 0) {
350
+ console.log(`\n ${C.bold}${installed} skill(s) installed to ${dir}${C.reset}\n`);
351
+ }
352
+ else {
353
+ console.log(`\n ${C.dim}All skills already installed.${C.reset}\n`);
354
+ }
355
+ return installed;
356
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medicine-wheel/app",
3
- "version": "0.2.4",
3
+ "version": "0.2.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",
@@ -81,23 +81,23 @@
81
81
  "clsx": "^2.1.1",
82
82
  "tailwind-merge": "^3.0.2",
83
83
  "recharts": "^2.15.4",
84
- "@medicine-wheel/ontology-core": "^0.2.4",
85
- "@medicine-wheel/ceremony-protocol": "^0.2.4",
86
- "@medicine-wheel/narrative-engine": "^0.2.4",
87
- "@medicine-wheel/graph-viz": "^0.2.4",
88
- "@medicine-wheel/relational-query": "^0.2.4",
89
- "@medicine-wheel/prompt-decomposition": "^0.2.4",
90
- "@medicine-wheel/ui-components": "^0.2.4",
91
- "@medicine-wheel/data-store": "^0.2.4",
92
- "@medicine-wheel/session-reader": "^0.2.4",
93
- "@medicine-wheel/fire-keeper": "^0.2.4",
94
- "@medicine-wheel/importance-unit": "^0.2.4",
95
- "@medicine-wheel/relational-index": "^0.2.4",
96
- "@medicine-wheel/transformation-tracker": "^0.2.4",
97
- "@medicine-wheel/storage-provider": "^0.2.4",
98
- "@medicine-wheel/community-review": "^0.2.4",
99
- "@medicine-wheel/consent-lifecycle": "^0.2.4",
100
- "@medicine-wheel/data-store-postgres": "^0.2.4",
84
+ "@medicine-wheel/ontology-core": "^0.2.7",
85
+ "@medicine-wheel/ceremony-protocol": "^0.2.7",
86
+ "@medicine-wheel/narrative-engine": "^0.2.7",
87
+ "@medicine-wheel/graph-viz": "^0.2.7",
88
+ "@medicine-wheel/relational-query": "^0.2.7",
89
+ "@medicine-wheel/prompt-decomposition": "^0.2.7",
90
+ "@medicine-wheel/ui-components": "^0.2.7",
91
+ "@medicine-wheel/data-store": "^0.2.7",
92
+ "@medicine-wheel/session-reader": "^0.2.7",
93
+ "@medicine-wheel/fire-keeper": "^0.2.7",
94
+ "@medicine-wheel/importance-unit": "^0.2.7",
95
+ "@medicine-wheel/relational-index": "^0.2.7",
96
+ "@medicine-wheel/transformation-tracker": "^0.2.7",
97
+ "@medicine-wheel/storage-provider": "^0.2.7",
98
+ "@medicine-wheel/community-review": "^0.2.7",
99
+ "@medicine-wheel/consent-lifecycle": "^0.2.7",
100
+ "@medicine-wheel/data-store-postgres": "^0.2.7",
101
101
  "@neondatabase/serverless": "^0.10.0"
102
102
  },
103
103
  "devDependencies": {