youmd 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/commands/build.d.ts.map +1 -1
  2. package/dist/commands/build.js +27 -5
  3. package/dist/commands/build.js.map +1 -1
  4. package/dist/commands/chat.d.ts.map +1 -1
  5. package/dist/commands/chat.js +47 -7
  6. package/dist/commands/chat.js.map +1 -1
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +30 -0
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/link.js +1 -1
  11. package/dist/commands/link.js.map +1 -1
  12. package/dist/commands/login.d.ts.map +1 -1
  13. package/dist/commands/login.js +150 -24
  14. package/dist/commands/login.js.map +1 -1
  15. package/dist/commands/preview.js +1 -1
  16. package/dist/commands/preview.js.map +1 -1
  17. package/dist/commands/publish.d.ts.map +1 -1
  18. package/dist/commands/publish.js +66 -4
  19. package/dist/commands/publish.js.map +1 -1
  20. package/dist/commands/pull.d.ts.map +1 -1
  21. package/dist/commands/pull.js +68 -4
  22. package/dist/commands/pull.js.map +1 -1
  23. package/dist/commands/push.d.ts +1 -0
  24. package/dist/commands/push.d.ts.map +1 -1
  25. package/dist/commands/push.js +120 -2
  26. package/dist/commands/push.js.map +1 -1
  27. package/dist/commands/register.d.ts.map +1 -1
  28. package/dist/commands/register.js +174 -39
  29. package/dist/commands/register.js.map +1 -1
  30. package/dist/commands/skill.d.ts +8 -0
  31. package/dist/commands/skill.d.ts.map +1 -0
  32. package/dist/commands/skill.js +1150 -0
  33. package/dist/commands/skill.js.map +1 -0
  34. package/dist/commands/status.d.ts.map +1 -1
  35. package/dist/commands/status.js +118 -2
  36. package/dist/commands/status.js.map +1 -1
  37. package/dist/commands/sync.d.ts.map +1 -1
  38. package/dist/commands/sync.js +12 -0
  39. package/dist/commands/sync.js.map +1 -1
  40. package/dist/commands/whoami.d.ts.map +1 -1
  41. package/dist/commands/whoami.js +16 -11
  42. package/dist/commands/whoami.js.map +1 -1
  43. package/dist/index.js +12 -3
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/api.d.ts +88 -5
  46. package/dist/lib/api.d.ts.map +1 -1
  47. package/dist/lib/api.js +56 -2
  48. package/dist/lib/api.js.map +1 -1
  49. package/dist/lib/ascii.d.ts +14 -0
  50. package/dist/lib/ascii.d.ts.map +1 -0
  51. package/dist/lib/ascii.js +108 -0
  52. package/dist/lib/ascii.js.map +1 -0
  53. package/dist/lib/config.d.ts +26 -0
  54. package/dist/lib/config.d.ts.map +1 -1
  55. package/dist/lib/config.js +40 -0
  56. package/dist/lib/config.js.map +1 -1
  57. package/dist/lib/hash.d.ts +3 -0
  58. package/dist/lib/hash.d.ts.map +1 -0
  59. package/dist/lib/hash.js +31 -0
  60. package/dist/lib/hash.js.map +1 -0
  61. package/dist/lib/onboarding.d.ts +1 -1
  62. package/dist/lib/onboarding.d.ts.map +1 -1
  63. package/dist/lib/onboarding.js +227 -85
  64. package/dist/lib/onboarding.js.map +1 -1
  65. package/dist/lib/render.d.ts +4 -2
  66. package/dist/lib/render.d.ts.map +1 -1
  67. package/dist/lib/render.js +81 -9
  68. package/dist/lib/render.js.map +1 -1
  69. package/dist/lib/skill-catalog.d.ts +57 -0
  70. package/dist/lib/skill-catalog.d.ts.map +1 -0
  71. package/dist/lib/skill-catalog.js +196 -0
  72. package/dist/lib/skill-catalog.js.map +1 -0
  73. package/dist/lib/skill-renderer.d.ts +55 -0
  74. package/dist/lib/skill-renderer.d.ts.map +1 -0
  75. package/dist/lib/skill-renderer.js +364 -0
  76. package/dist/lib/skill-renderer.js.map +1 -0
  77. package/dist/lib/skills.d.ts +125 -0
  78. package/dist/lib/skills.d.ts.map +1 -0
  79. package/dist/lib/skills.js +677 -0
  80. package/dist/lib/skills.js.map +1 -0
  81. package/package.json +8 -4
  82. package/skills/claude-md-generator.md +66 -0
  83. package/skills/meta-improve.md +57 -0
  84. package/skills/project-context-init.md +51 -0
  85. package/skills/voice-sync.md +41 -0
@@ -0,0 +1,1150 @@
1
+ "use strict";
2
+ /**
3
+ * youmd skill — identity-aware agent skill system.
4
+ *
5
+ * Subcommand router following the project.ts pattern.
6
+ * Manages skill lifecycle: install, remove, use, sync, link, init-project.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.skillCommand = skillCommand;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const chalk_1 = __importDefault(require("chalk"));
49
+ const skill_catalog_1 = require("../lib/skill-catalog");
50
+ const skills_1 = require("../lib/skills");
51
+ const skill_renderer_1 = require("../lib/skill-renderer");
52
+ const render_1 = require("../lib/render");
53
+ const config_1 = require("../lib/config");
54
+ const api_1 = require("../lib/api");
55
+ const skills_2 = require("../lib/skills");
56
+ const ACCENT = chalk_1.default.hex("#C46A3A");
57
+ const DIM = chalk_1.default.dim;
58
+ // ─── Personality spinner labels ──────────────────────────────────────
59
+ const SKILL_SPINNERS = {
60
+ install: [
61
+ "downloading your superpowers",
62
+ "wiring identity into skill matrix",
63
+ "importing agent knowledge",
64
+ "bootstrapping skill neurons",
65
+ "fetching your secret sauce",
66
+ ],
67
+ use: [
68
+ "resolving identity against skill template",
69
+ "interpolating your whole vibe",
70
+ "mapping your identity to agent instructions",
71
+ "converting you into context",
72
+ "rendering your digital twin's playbook",
73
+ ],
74
+ sync: [
75
+ "re-interpolating skills against live identity",
76
+ "propagating identity changes across skills",
77
+ "syncing your soul to all agents",
78
+ "reconciling who you are with what agents know",
79
+ ],
80
+ link: [
81
+ "wiring your identity into the agent",
82
+ "bridging identity to agent workspace",
83
+ "establishing the neural handshake",
84
+ ],
85
+ init: [
86
+ "computing your project's main character energy",
87
+ "scaffolding your identity into this repo",
88
+ "making this project know who it's working with",
89
+ "infusing project with identity context",
90
+ ],
91
+ remove: [
92
+ "severing the identity link",
93
+ "cleaning up skill artifacts",
94
+ "removing agent knowledge",
95
+ ],
96
+ };
97
+ function randomSpinner(category) {
98
+ const labels = SKILL_SPINNERS[category];
99
+ return labels[Math.floor(Math.random() * labels.length)];
100
+ }
101
+ // ─── Subcommands ──────────────────────────────────────────────────────
102
+ function listSkills() {
103
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
104
+ console.log("");
105
+ console.log(" " + chalk_1.default.bold("youmd skills") + DIM(` (${catalog.skills.length} in catalog)`));
106
+ console.log("");
107
+ if (catalog.skills.length === 0) {
108
+ console.log(DIM(" no skills registered."));
109
+ console.log(DIM(" run ") + chalk_1.default.cyan("youmd skill add <name> <source>") + DIM(" to add one."));
110
+ console.log("");
111
+ return;
112
+ }
113
+ const maxName = Math.max(...catalog.skills.map((s) => s.name.length));
114
+ for (const skill of catalog.skills) {
115
+ const status = skill.installed
116
+ ? chalk_1.default.green("\u2713")
117
+ : DIM("\u2022");
118
+ const scope = skill.scope === "shared"
119
+ ? DIM("shared")
120
+ : skill.scope === "project"
121
+ ? ACCENT("project")
122
+ : chalk_1.default.yellow("private");
123
+ const fields = skill.identity_fields.length > 0
124
+ ? DIM(` [${skill.identity_fields.join(", ")}]`)
125
+ : "";
126
+ console.log(` ${status} ${chalk_1.default.cyan(skill.name.padEnd(maxName + 2))}` +
127
+ `${DIM(skill.description)}`);
128
+ console.log(` ${DIM("v" + skill.version)} ${scope}${fields}`);
129
+ }
130
+ console.log("");
131
+ const installed = catalog.skills.filter((s) => s.installed).length;
132
+ console.log(DIM(` ${installed}/${catalog.skills.length} installed`));
133
+ console.log("");
134
+ }
135
+ async function installSkillCmd(args) {
136
+ const name = args[0];
137
+ // Batch install all
138
+ if (name === "all" || name === "--all" || name === "-a") {
139
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
140
+ const toInstall = catalog.skills.filter((s) => !s.installed);
141
+ if (toInstall.length === 0) {
142
+ console.log("");
143
+ console.log(DIM(" all skills already installed."));
144
+ console.log("");
145
+ return;
146
+ }
147
+ console.log("");
148
+ const spinner = new render_1.BrailleSpinner(randomSpinner("install"));
149
+ spinner.start();
150
+ await new Promise((r) => setTimeout(r, 300));
151
+ let installed = 0;
152
+ for (const entry of toInstall) {
153
+ let result = (0, skills_1.installSkill)(entry.name);
154
+ if (!result.ok && (entry.source.startsWith("github:") || entry.source.startsWith("https://"))) {
155
+ result = await (0, skills_1.installSkillAsync)(entry.name);
156
+ }
157
+ if (result.ok)
158
+ installed++;
159
+ }
160
+ spinner.stop(`${installed} skills installed`);
161
+ console.log("");
162
+ return;
163
+ }
164
+ if (!name) {
165
+ console.log("");
166
+ console.log(chalk_1.default.yellow(" usage: youmd skill install <name>"));
167
+ console.log(DIM(" or: youmd skill install all"));
168
+ console.log("");
169
+ return;
170
+ }
171
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
172
+ const entry = (0, skill_catalog_1.findSkill)(catalog, name);
173
+ if (!entry) {
174
+ // Not in local catalog — check the registry
175
+ if ((0, config_1.isAuthenticated)()) {
176
+ console.log("");
177
+ const regSpinner = new render_1.BrailleSpinner("checking the skill registry");
178
+ regSpinner.start();
179
+ await new Promise((r) => setTimeout(r, 200));
180
+ try {
181
+ const regRes = await (0, api_1.browseSkills)();
182
+ if (regRes.ok) {
183
+ const regSkill = regRes.data.skills.find((s) => s.name === name);
184
+ if (regSkill) {
185
+ regSpinner.stop(`found "${name}" in registry`);
186
+ // Fetch full skill content from registry
187
+ const contentRes = await (0, api_1.getRegistrySkill)(regSkill.name);
188
+ if (contentRes.ok && contentRes.data.content) {
189
+ // Write SKILL.md directly
190
+ const skillDir = (0, skills_2.getSkillDir)(regSkill.name);
191
+ fs.mkdirSync(skillDir, { recursive: true });
192
+ fs.writeFileSync(path.join(skillDir, "SKILL.md"), contentRes.data.content);
193
+ // Render with identity
194
+ const { renderSkillTemplate, loadIdentityData: loadId } = require("../lib/skill-renderer");
195
+ const rendered = renderSkillTemplate(contentRes.data.content, loadId());
196
+ fs.writeFileSync(path.join(skillDir, "RENDERED.md"), rendered);
197
+ // Add to catalog and mark installed
198
+ (0, skill_catalog_1.addSkillEntry)((0, skill_catalog_1.readSkillCatalog)(), {
199
+ name: regSkill.name,
200
+ description: regSkill.description,
201
+ version: regSkill.version,
202
+ source: `registry:${regSkill.name}`,
203
+ scope: regSkill.scope,
204
+ identity_fields: regSkill.identityFields,
205
+ requires: [],
206
+ installed: true,
207
+ });
208
+ console.log("");
209
+ console.log(chalk_1.default.green(" \u2713") + ` ${chalk_1.default.bold(name)} installed from registry` +
210
+ DIM(` [${regSkill.scope}]`));
211
+ if (regSkill.identityFields.length > 0) {
212
+ console.log(DIM(` identity fields: ${regSkill.identityFields.join(", ")}`));
213
+ }
214
+ }
215
+ else {
216
+ // Content not available, just add to catalog
217
+ (0, skill_catalog_1.addSkillEntry)((0, skill_catalog_1.readSkillCatalog)(), {
218
+ name: regSkill.name,
219
+ description: regSkill.description,
220
+ version: regSkill.version,
221
+ source: `registry:${regSkill.name}`,
222
+ scope: regSkill.scope,
223
+ identity_fields: regSkill.identityFields,
224
+ requires: [],
225
+ });
226
+ console.log("");
227
+ console.log(DIM(` added to catalog but content not available.`));
228
+ }
229
+ console.log("");
230
+ return;
231
+ }
232
+ }
233
+ regSpinner.stop("not found in registry either");
234
+ }
235
+ catch {
236
+ regSpinner.fail("registry check failed");
237
+ }
238
+ }
239
+ console.log("");
240
+ console.log(chalk_1.default.yellow(` skill "${name}" not found in catalog or registry.`));
241
+ console.log(DIM(" run ") + chalk_1.default.cyan("youmd skill list") + DIM(" to see available skills."));
242
+ console.log(DIM(" or: ") + chalk_1.default.cyan("youmd skill browse") + DIM(" to check the registry."));
243
+ console.log("");
244
+ return;
245
+ }
246
+ if (entry.installed) {
247
+ console.log("");
248
+ console.log(DIM(` "${entry.name}" is already installed.`));
249
+ console.log("");
250
+ return;
251
+ }
252
+ console.log("");
253
+ const spinner = new render_1.BrailleSpinner(randomSpinner("install"));
254
+ spinner.start();
255
+ await new Promise((r) => setTimeout(r, 300));
256
+ // Try sync install, fall back to async for remote sources
257
+ let result = (0, skills_1.installSkill)(entry.name);
258
+ if (!result.ok && (entry.source.startsWith("github:") || entry.source.startsWith("https://"))) {
259
+ result = await (0, skills_1.installSkillAsync)(entry.name);
260
+ }
261
+ if (result.ok) {
262
+ spinner.stop(`v${entry.version}`);
263
+ console.log("");
264
+ console.log(" " + chalk_1.default.green("\u2713") + ` ${chalk_1.default.bold(entry.name)} installed` +
265
+ DIM(` [${entry.scope}]`));
266
+ if (entry.identity_fields.length > 0) {
267
+ console.log(DIM(` identity fields: ${entry.identity_fields.join(", ")}`));
268
+ }
269
+ }
270
+ else {
271
+ spinner.fail(result.error);
272
+ }
273
+ console.log("");
274
+ }
275
+ async function removeSkillCmd(args) {
276
+ const name = args[0];
277
+ // Batch remove all
278
+ if (name === "all" || name === "--all" || name === "-a") {
279
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
280
+ const toRemove = catalog.skills.filter((s) => s.installed);
281
+ if (toRemove.length === 0) {
282
+ console.log("");
283
+ console.log(DIM(" no skills installed."));
284
+ console.log("");
285
+ return;
286
+ }
287
+ console.log("");
288
+ const spinner = new render_1.BrailleSpinner(randomSpinner("remove"));
289
+ spinner.start();
290
+ await new Promise((r) => setTimeout(r, 200));
291
+ let removed = 0;
292
+ for (const entry of toRemove) {
293
+ const result = (0, skills_1.removeSkill)(entry.name);
294
+ if (result.ok)
295
+ removed++;
296
+ }
297
+ spinner.stop(`${removed} skills removed`);
298
+ console.log("");
299
+ return;
300
+ }
301
+ if (!name) {
302
+ console.log("");
303
+ console.log(chalk_1.default.yellow(" usage: youmd skill remove <name>"));
304
+ console.log(DIM(" or: youmd skill remove all"));
305
+ console.log("");
306
+ return;
307
+ }
308
+ console.log("");
309
+ const spinner = new render_1.BrailleSpinner(randomSpinner("remove"));
310
+ spinner.start();
311
+ await new Promise((r) => setTimeout(r, 200));
312
+ const result = (0, skills_1.removeSkill)(name);
313
+ if (result.ok) {
314
+ spinner.stop("removed");
315
+ }
316
+ else {
317
+ spinner.fail(result.error);
318
+ }
319
+ console.log("");
320
+ }
321
+ async function useSkillCmd(args) {
322
+ const name = args[0];
323
+ if (!name) {
324
+ console.log("");
325
+ console.log(chalk_1.default.yellow(" usage: youmd skill use <name>"));
326
+ console.log("");
327
+ return;
328
+ }
329
+ console.log("");
330
+ const spinner = new render_1.BrailleSpinner(randomSpinner("use"));
331
+ spinner.start();
332
+ await new Promise((r) => setTimeout(r, 400));
333
+ const result = (0, skills_1.useSkill)(name);
334
+ if (!result.ok) {
335
+ spinner.fail(result.error);
336
+ console.log("");
337
+ return;
338
+ }
339
+ spinner.stop();
340
+ // Show readiness
341
+ if (result.readiness) {
342
+ const { total, filled, missing } = result.readiness;
343
+ if (missing.length > 0) {
344
+ console.log("");
345
+ console.log(ACCENT(` ${filled}/${total} identity fields resolved.`) +
346
+ DIM(` missing: ${missing.join(", ")}`));
347
+ console.log(DIM(" fill these via ") + chalk_1.default.cyan("youmd chat") + DIM(" or edit .youmd/preferences/"));
348
+ }
349
+ else {
350
+ console.log(DIM(` ${total}/${total} identity fields resolved.`));
351
+ }
352
+ }
353
+ // Show rendered output
354
+ if (result.content) {
355
+ console.log("");
356
+ console.log(DIM(" " + "\u2500".repeat(50)));
357
+ for (const line of result.content.split("\n").slice(0, 40)) {
358
+ console.log(` ${line}`);
359
+ }
360
+ const totalLines = result.content.split("\n").length;
361
+ if (totalLines > 40) {
362
+ console.log(DIM(` ... ${totalLines - 40} more lines`));
363
+ }
364
+ console.log(DIM(" " + "\u2500".repeat(50)));
365
+ }
366
+ console.log("");
367
+ }
368
+ async function syncSkillsCmd() {
369
+ console.log("");
370
+ const spinner = new render_1.BrailleSpinner(randomSpinner("sync"));
371
+ spinner.start();
372
+ await new Promise((r) => setTimeout(r, 400));
373
+ const result = (0, skills_1.syncAllSkills)();
374
+ if (result.synced.length > 0) {
375
+ spinner.stop(`${result.synced.length} skills synced`);
376
+ for (const name of result.synced) {
377
+ console.log(` ${chalk_1.default.green("\u2713")} ${DIM(name)}`);
378
+ }
379
+ }
380
+ else {
381
+ spinner.stop("no installed skills to sync");
382
+ }
383
+ if (result.errors.length > 0) {
384
+ console.log("");
385
+ for (const err of result.errors) {
386
+ console.log(` ${ACCENT("\u2717")} ${DIM(err)}`);
387
+ }
388
+ }
389
+ console.log("");
390
+ }
391
+ function addSkillCmd(args) {
392
+ const name = args[0];
393
+ const source = args[1];
394
+ if (!name || !source) {
395
+ console.log("");
396
+ console.log(chalk_1.default.yellow(" usage: youmd skill add <name> <source>"));
397
+ console.log(DIM(" source: local:/path/to/skill.md or github:owner/repo/path"));
398
+ console.log("");
399
+ return;
400
+ }
401
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
402
+ (0, skill_catalog_1.addSkillEntry)(catalog, {
403
+ name,
404
+ description: `Custom skill: ${name}`,
405
+ version: "1.0.0",
406
+ source,
407
+ scope: "shared",
408
+ identity_fields: [],
409
+ requires: [],
410
+ });
411
+ console.log("");
412
+ console.log(chalk_1.default.green(" \u2713") + ` ${chalk_1.default.bold(name)} added to catalog`);
413
+ console.log(DIM(` source: ${source}`));
414
+ console.log(DIM(" run ") + chalk_1.default.cyan(`youmd skill install ${name}`) + DIM(" to install."));
415
+ console.log("");
416
+ }
417
+ async function createSkillCmd(args) {
418
+ const readline = require("readline");
419
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
420
+ const ask = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
421
+ console.log("");
422
+ console.log(" " + chalk_1.default.bold("youmd skill create"));
423
+ console.log(DIM(" scaffold a new identity-aware skill\n"));
424
+ // Name
425
+ let name = args[0] || "";
426
+ if (!name) {
427
+ name = await ask(ACCENT(" skill name: "));
428
+ }
429
+ if (!name) {
430
+ console.log(chalk_1.default.yellow(" name is required."));
431
+ rl.close();
432
+ return;
433
+ }
434
+ const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
435
+ // Description
436
+ const description = await ask(DIM(" description: "));
437
+ // Scope
438
+ const scopeInput = await ask(DIM(" scope (shared/project/private) [shared]: "));
439
+ const scope = (["shared", "project", "private"].includes(scopeInput) ? scopeInput : "shared");
440
+ // Identity fields
441
+ console.log(DIM(" common fields: voice.overall, preferences.agent, directives.agent, profile.about"));
442
+ const fieldsInput = await ask(DIM(" identity fields (comma-separated): "));
443
+ const identityFields = fieldsInput
444
+ ? fieldsInput.split(",").map((f) => f.trim()).filter(Boolean)
445
+ : [];
446
+ rl.close();
447
+ // Generate SKILL.md content
448
+ const skillContent = [
449
+ "---",
450
+ `name: ${slug}`,
451
+ `version: 1.0.0`,
452
+ `scope: ${scope}`,
453
+ `identity_fields: [${identityFields.join(", ")}]`,
454
+ `description: "${description || `Custom skill: ${slug}`}"`,
455
+ "---",
456
+ "",
457
+ `# ${slug}`,
458
+ "",
459
+ description || "(describe what this skill does)",
460
+ "",
461
+ ...(identityFields.length > 0
462
+ ? [
463
+ "## Identity Context",
464
+ "",
465
+ ...identityFields.map((f) => `- **${f}:** {{${f}}}`),
466
+ "",
467
+ ]
468
+ : []),
469
+ "## What This Skill Does",
470
+ "",
471
+ "1. (step 1)",
472
+ "2. (step 2)",
473
+ "3. (step 3)",
474
+ "",
475
+ ].join("\n");
476
+ // Write to ~/.youmd/skills/<name>/SKILL.md
477
+ const os = require("os");
478
+ const skillDir = path.join(os.homedir(), ".youmd", "skills", slug);
479
+ fs.mkdirSync(skillDir, { recursive: true });
480
+ fs.writeFileSync(path.join(skillDir, "SKILL.md"), skillContent);
481
+ // Add to catalog
482
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
483
+ (0, skill_catalog_1.addSkillEntry)(catalog, {
484
+ name: slug,
485
+ description: description || `Custom skill: ${slug}`,
486
+ version: "1.0.0",
487
+ source: `local:${path.join(skillDir, "SKILL.md")}`,
488
+ scope,
489
+ identity_fields: identityFields,
490
+ requires: [],
491
+ installed: true,
492
+ });
493
+ console.log("");
494
+ console.log(chalk_1.default.green(" \u2713") + ` ${chalk_1.default.bold(slug)} created`);
495
+ console.log(DIM(` ${path.join(skillDir, "SKILL.md")}`));
496
+ console.log("");
497
+ console.log(DIM(" edit the SKILL.md, then:"));
498
+ console.log(` ${chalk_1.default.cyan(`youmd skill use ${slug}`)} ${DIM("render with your identity")}`);
499
+ console.log(` ${chalk_1.default.cyan(`youmd skill link claude`)} ${DIM("link to your project")}`);
500
+ console.log("");
501
+ }
502
+ function pushSkillCmd(args) {
503
+ const name = args[0];
504
+ if (!name) {
505
+ console.log("");
506
+ console.log(chalk_1.default.yellow(" usage: youmd skill push <name>"));
507
+ console.log("");
508
+ return;
509
+ }
510
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
511
+ const entry = (0, skill_catalog_1.findSkill)(catalog, name);
512
+ if (!entry) {
513
+ console.log("");
514
+ console.log(chalk_1.default.yellow(` skill "${name}" not found.`));
515
+ console.log("");
516
+ return;
517
+ }
518
+ if (!entry.source.startsWith("local:")) {
519
+ console.log("");
520
+ console.log(DIM(" push is only supported for local: sources right now."));
521
+ console.log(DIM(` source: ${entry.source}`));
522
+ console.log("");
523
+ return;
524
+ }
525
+ const skillPath = path.join(require("os").homedir(), ".youmd", "skills", entry.name, "SKILL.md");
526
+ if (!fs.existsSync(skillPath)) {
527
+ console.log(chalk_1.default.yellow(` SKILL.md not found for "${name}". install first.`));
528
+ return;
529
+ }
530
+ const destPath = entry.source.slice("local:".length);
531
+ const content = fs.readFileSync(skillPath, "utf-8");
532
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
533
+ fs.writeFileSync(destPath, content);
534
+ console.log("");
535
+ console.log(chalk_1.default.green(" \u2713") + ` ${name} pushed to ${destPath}`);
536
+ console.log("");
537
+ }
538
+ async function linkSkillsCmd(args) {
539
+ let target = (args[0] || "").toLowerCase();
540
+ const validTargets = ["claude", "cursor", "codex"];
541
+ // Auto-detect agent if no target specified
542
+ if (!target) {
543
+ const cwd = process.cwd();
544
+ if (fs.existsSync(path.join(cwd, ".claude")) || fs.existsSync(path.join(cwd, "CLAUDE.md"))) {
545
+ target = "claude";
546
+ }
547
+ else if (fs.existsSync(path.join(cwd, ".cursor"))) {
548
+ target = "cursor";
549
+ }
550
+ else {
551
+ target = "claude"; // default
552
+ }
553
+ }
554
+ if (!validTargets.includes(target)) {
555
+ console.log("");
556
+ console.log(chalk_1.default.yellow(` unknown target: ${target}`));
557
+ console.log(DIM(` valid targets: ${validTargets.join(", ")}`));
558
+ console.log("");
559
+ return;
560
+ }
561
+ console.log("");
562
+ const spinner = new render_1.BrailleSpinner(randomSpinner("link"));
563
+ spinner.start();
564
+ await new Promise((r) => setTimeout(r, 300));
565
+ const result = (0, skills_1.linkToAgent)(target);
566
+ if (result.ok) {
567
+ spinner.stop(result.path);
568
+ }
569
+ else {
570
+ spinner.fail(result.error);
571
+ }
572
+ console.log("");
573
+ }
574
+ async function initProjectCmd() {
575
+ console.log("");
576
+ console.log(" " + chalk_1.default.bold("youmd skill init-project"));
577
+ console.log(DIM(" scaffolding identity-aware project structure...\n"));
578
+ const spinner = new render_1.BrailleSpinner(randomSpinner("init"));
579
+ spinner.start();
580
+ await new Promise((r) => setTimeout(r, 500));
581
+ const result = (0, skills_1.initProject)();
582
+ spinner.stop();
583
+ console.log("");
584
+ for (const step of result.steps) {
585
+ const icon = step.ok ? chalk_1.default.green("\u2713") : ACCENT("\u2717");
586
+ const detail = step.detail ? DIM(` ${step.detail}`) : "";
587
+ console.log(` ${icon} ${step.name}${detail}`);
588
+ }
589
+ console.log("");
590
+ if (result.ok) {
591
+ console.log(" " + chalk_1.default.green("project initialized with your identity."));
592
+ console.log(DIM(" every agent that touches this repo now knows who you are."));
593
+ }
594
+ else {
595
+ console.log(ACCENT(" some steps failed. check above for details."));
596
+ }
597
+ console.log("");
598
+ }
599
+ function metricsCmd() {
600
+ const metrics = (0, skills_1.getMetrics)();
601
+ console.log("");
602
+ console.log(" " + chalk_1.default.bold("skill metrics"));
603
+ console.log("");
604
+ const skillEntries = Object.entries(metrics.skills);
605
+ if (skillEntries.length === 0) {
606
+ console.log(DIM(" no usage data yet. use some skills first."));
607
+ console.log("");
608
+ return;
609
+ }
610
+ const maxName = Math.max(...skillEntries.map(([name]) => name.length));
611
+ for (const [name, data] of skillEntries) {
612
+ const lastUsed = data.lastUsed
613
+ ? DIM(` last: ${data.lastUsed.slice(0, 10)}`)
614
+ : "";
615
+ console.log(` ${chalk_1.default.cyan(name.padEnd(maxName + 2))}` +
616
+ `${ACCENT(String(data.uses))} uses ` +
617
+ `${DIM(String(data.installs))} installs${lastUsed}`);
618
+ }
619
+ console.log("");
620
+ const fieldEntries = Object.entries(metrics.identityFields);
621
+ if (fieldEntries.length > 0) {
622
+ console.log(" " + ACCENT("identity field usage:"));
623
+ for (const [field, data] of fieldEntries.sort((a, b) => b[1].references - a[1].references)) {
624
+ console.log(` ${DIM(field.padEnd(25))} ${ACCENT(String(data.references))} refs`);
625
+ }
626
+ console.log("");
627
+ }
628
+ }
629
+ function improveCmd() {
630
+ const metrics = (0, skills_1.getMetrics)();
631
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
632
+ const identity = (0, skill_renderer_1.loadIdentityData)();
633
+ console.log("");
634
+ console.log(" " + chalk_1.default.bold("skill improvement analysis"));
635
+ console.log("");
636
+ const installed = catalog.skills.filter((s) => s.installed);
637
+ const notInstalled = catalog.skills.filter((s) => !s.installed);
638
+ // ─── Activity Analysis ────────────────────────────────────────────
639
+ const totalUses = Object.values(metrics.skills).reduce((sum, s) => sum + s.uses, 0);
640
+ console.log(" " + ACCENT("overview: ") +
641
+ `${installed.length} installed, ${totalUses} total uses, ` +
642
+ `${Object.keys(metrics.identityFields).length} identity fields tracked`);
643
+ console.log("");
644
+ // Most used
645
+ const mostUsed = Object.entries(metrics.skills)
646
+ .filter(([, data]) => data.uses > 0)
647
+ .sort((a, b) => b[1].uses - a[1].uses)
648
+ .slice(0, 5);
649
+ if (mostUsed.length > 0) {
650
+ console.log(" " + ACCENT("most active:"));
651
+ for (const [name, data] of mostUsed) {
652
+ const bar = ACCENT("\u2588".repeat(Math.min(data.uses, 20))) + DIM("\u2591".repeat(Math.max(0, 20 - data.uses)));
653
+ console.log(` ${chalk_1.default.cyan(name.padEnd(24))} ${bar} ${data.uses}`);
654
+ }
655
+ console.log("");
656
+ }
657
+ // Unused installed skills
658
+ const unused = installed.filter((s) => {
659
+ const m = metrics.skills[s.name];
660
+ return !m || m.uses === 0;
661
+ });
662
+ if (unused.length > 0) {
663
+ console.log(" " + chalk_1.default.yellow("installed but never used:"));
664
+ for (const s of unused) {
665
+ console.log(` ${DIM(s.name)} — consider removing or using`);
666
+ }
667
+ console.log("");
668
+ }
669
+ if (notInstalled.length > 0) {
670
+ console.log(" " + DIM("available but not installed:"));
671
+ for (const s of notInstalled) {
672
+ console.log(` ${chalk_1.default.cyan(s.name)} — ${DIM(s.description)}`);
673
+ }
674
+ console.log("");
675
+ }
676
+ // ─── Identity Coverage ────────────────────────────────────────────
677
+ const allFields = new Set();
678
+ for (const s of catalog.skills) {
679
+ for (const f of s.identity_fields)
680
+ allFields.add(f);
681
+ }
682
+ const filled = [];
683
+ const missing = [];
684
+ for (const field of allFields) {
685
+ const val = (0, skill_renderer_1.resolveVariable)(field, identity);
686
+ if (val) {
687
+ filled.push(field);
688
+ }
689
+ else {
690
+ missing.push(field);
691
+ }
692
+ }
693
+ if (allFields.size > 0) {
694
+ const pct = Math.round((filled.length / allFields.size) * 100);
695
+ const barWidth = 30;
696
+ const filledBar = Math.round((filled.length / allFields.size) * barWidth);
697
+ const bar = chalk_1.default.green("\u2588".repeat(filledBar)) + DIM("\u2591".repeat(barWidth - filledBar));
698
+ console.log(" " + ACCENT("identity coverage:"));
699
+ console.log(` ${bar} ${pct}% (${filled.length}/${allFields.size})`);
700
+ console.log("");
701
+ }
702
+ if (missing.length > 0) {
703
+ console.log(" " + ACCENT("identity gaps:"));
704
+ console.log(DIM(" referenced by skills but empty in your identity:"));
705
+ for (const f of missing) {
706
+ console.log(` ${chalk_1.default.yellow(f)}`);
707
+ }
708
+ console.log(DIM("\n fill via ") + chalk_1.default.cyan("youmd chat") + DIM(" or edit .youmd/preferences/"));
709
+ console.log("");
710
+ }
711
+ else if (allFields.size > 0) {
712
+ console.log(" " + chalk_1.default.green("\u2713") + DIM(" all identity fields populated."));
713
+ console.log("");
714
+ }
715
+ // ─── Actionable Proposals ─────────────────────────────────────────
716
+ const proposals = [];
717
+ // Propose installing uninstalled skills if identity data exists for them
718
+ for (const s of notInstalled) {
719
+ const hasData = s.identity_fields.some((f) => (0, skill_renderer_1.resolveVariable)(f, identity));
720
+ if (hasData) {
721
+ proposals.push(`install "${s.name}" — you have identity data it needs`);
722
+ }
723
+ }
724
+ // Propose adding directives if agent preference exists but no directives
725
+ if (identity.preferences.agent && !identity.directives.agent) {
726
+ proposals.push("add directives.agent — you have agent preferences but no directives file");
727
+ }
728
+ // Propose voice sync if voice data exists but voice-sync isn't installed
729
+ if (identity.voice.overall && !(0, skill_catalog_1.findSkill)(catalog, "voice-sync")?.installed) {
730
+ proposals.push("install voice-sync — you have voice data that could propagate to all agents");
731
+ }
732
+ // Propose running sync if skills are installed but metrics show 0 syncs
733
+ const syncCount = Object.values(metrics.skills).reduce((sum, s) => sum + s.uses, 0);
734
+ if (installed.length > 0 && syncCount === 0) {
735
+ proposals.push("run \"youmd skill sync\" — installed skills haven't been synced yet");
736
+ }
737
+ // Propose linking if skills installed but no .claude/skills/youmd exists
738
+ if (installed.length > 0) {
739
+ const claudeSkillsDir = path.join(process.cwd(), ".claude", "skills", "youmd");
740
+ if (!fs.existsSync(claudeSkillsDir)) {
741
+ proposals.push("run \"youmd skill link claude\" — skills aren't linked to this project's agent");
742
+ }
743
+ }
744
+ if (proposals.length > 0) {
745
+ console.log(" " + chalk_1.default.bold("proposals:"));
746
+ for (const p of proposals) {
747
+ console.log(` ${ACCENT("\u203A")} ${p}`);
748
+ }
749
+ console.log("");
750
+ }
751
+ else {
752
+ console.log(" " + chalk_1.default.green("\u2713") + DIM(" no improvements to suggest right now."));
753
+ console.log("");
754
+ }
755
+ }
756
+ function searchCmd(args) {
757
+ const query = args.join(" ");
758
+ if (!query) {
759
+ console.log("");
760
+ console.log(chalk_1.default.yellow(" usage: youmd skill search <query>"));
761
+ console.log("");
762
+ return;
763
+ }
764
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
765
+ const results = (0, skill_catalog_1.searchSkills)(catalog, query);
766
+ console.log("");
767
+ if (results.length === 0) {
768
+ console.log(DIM(` no skills matching "${query}".`));
769
+ }
770
+ else {
771
+ console.log(` ${results.length} result${results.length === 1 ? "" : "s"} for "${query}":`);
772
+ console.log("");
773
+ for (const s of results) {
774
+ const status = s.installed ? chalk_1.default.green("\u2713") : DIM("\u2022");
775
+ console.log(` ${status} ${chalk_1.default.cyan(s.name)} — ${DIM(s.description)}`);
776
+ }
777
+ }
778
+ console.log("");
779
+ }
780
+ async function browseCmd() {
781
+ if (!(0, config_1.isAuthenticated)()) {
782
+ console.log("");
783
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
784
+ console.log("");
785
+ return;
786
+ }
787
+ console.log("");
788
+ const spinner = new render_1.BrailleSpinner("scanning the skill registry");
789
+ spinner.start();
790
+ await new Promise((r) => setTimeout(r, 300));
791
+ try {
792
+ const res = await (0, api_1.browseSkills)();
793
+ if (!res.ok) {
794
+ spinner.fail("could not reach registry");
795
+ console.log("");
796
+ return;
797
+ }
798
+ const skills = res.data.skills;
799
+ spinner.stop(`${skills.length} skill${skills.length === 1 ? "" : "s"} in registry`);
800
+ if (skills.length === 0) {
801
+ console.log(DIM(" no skills published yet."));
802
+ console.log(DIM(" be the first: ") + chalk_1.default.cyan("youmd skill publish <name>"));
803
+ console.log("");
804
+ return;
805
+ }
806
+ console.log("");
807
+ const maxName = Math.max(...skills.map((s) => s.name.length));
808
+ for (const s of skills) {
809
+ const dl = s.downloads > 0 ? ACCENT(` ${s.downloads} dl`) : "";
810
+ console.log(` ${chalk_1.default.cyan(s.name.padEnd(maxName + 2))}` +
811
+ `${DIM(s.description)}${dl}`);
812
+ console.log(` ${DIM("v" + s.version)} ${DIM(s.scope)} ${DIM(`[${s.identityFields.join(", ")}]`)}`);
813
+ }
814
+ console.log("");
815
+ console.log(DIM(` install with: youmd skill add <name> registry:<name>`));
816
+ }
817
+ catch {
818
+ spinner.fail("registry unreachable");
819
+ }
820
+ console.log("");
821
+ }
822
+ async function publishSkillCmd(args) {
823
+ const name = args[0];
824
+ if (!name) {
825
+ console.log("");
826
+ console.log(chalk_1.default.yellow(" usage: youmd skill publish <name>"));
827
+ console.log(DIM(" publishes an installed skill to the you.md registry."));
828
+ console.log("");
829
+ return;
830
+ }
831
+ if (!(0, config_1.isAuthenticated)()) {
832
+ console.log("");
833
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
834
+ console.log("");
835
+ return;
836
+ }
837
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
838
+ const entry = (0, skill_catalog_1.findSkill)(catalog, name);
839
+ if (!entry) {
840
+ console.log("");
841
+ console.log(chalk_1.default.yellow(` skill "${name}" not found in catalog.`));
842
+ console.log("");
843
+ return;
844
+ }
845
+ const skillFile = (0, skills_2.readSkillFile)(entry.name);
846
+ if (!skillFile) {
847
+ console.log("");
848
+ console.log(chalk_1.default.yellow(` SKILL.md not found for "${name}". install first.`));
849
+ console.log("");
850
+ return;
851
+ }
852
+ console.log("");
853
+ const spinner = new render_1.BrailleSpinner("publishing to the skill registry");
854
+ spinner.start();
855
+ await new Promise((r) => setTimeout(r, 400));
856
+ try {
857
+ const res = await (0, api_1.publishSkill)({
858
+ name: entry.name,
859
+ description: entry.description,
860
+ version: entry.version,
861
+ scope: entry.scope,
862
+ identityFields: entry.identity_fields,
863
+ content: skillFile.content,
864
+ });
865
+ if (res.ok) {
866
+ spinner.stop(res.data.updated ? "updated" : "published");
867
+ console.log("");
868
+ console.log(chalk_1.default.green(" \u2713") + ` ${chalk_1.default.bold(entry.name)} is live on the registry`);
869
+ console.log(DIM(` others can install with: youmd skill add ${entry.name} registry:${entry.name}`));
870
+ }
871
+ else {
872
+ spinner.fail(String(res.data?.error || "publish failed"));
873
+ }
874
+ }
875
+ catch (err) {
876
+ spinner.fail(err instanceof Error ? err.message : "failed");
877
+ }
878
+ console.log("");
879
+ }
880
+ async function remoteStatusCmd() {
881
+ if (!(0, config_1.isAuthenticated)()) {
882
+ console.log("");
883
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
884
+ console.log("");
885
+ return;
886
+ }
887
+ console.log("");
888
+ const spinner = new render_1.BrailleSpinner("fetching your skill profile from the cloud");
889
+ spinner.start();
890
+ await new Promise((r) => setTimeout(r, 300));
891
+ try {
892
+ const res = await (0, api_1.getMySkills)();
893
+ if (!res.ok) {
894
+ spinner.fail("could not fetch");
895
+ console.log("");
896
+ return;
897
+ }
898
+ const skills = res.data.skills;
899
+ spinner.stop(`${skills.length} synced to you.md`);
900
+ if (skills.length === 0) {
901
+ console.log(DIM(" no skills synced to your account yet."));
902
+ console.log(DIM(" install skills locally and they'll auto-sync."));
903
+ }
904
+ else {
905
+ console.log("");
906
+ for (const s of skills) {
907
+ const uses = s.useCount > 0 ? ACCENT(` ${s.useCount} uses`) : "";
908
+ console.log(` ${chalk_1.default.green("\u2713")} ${chalk_1.default.cyan(s.skillName)} ${DIM(s.source)}${uses}`);
909
+ }
910
+ }
911
+ }
912
+ catch {
913
+ spinner.fail("unreachable");
914
+ }
915
+ console.log("");
916
+ }
917
+ function infoCmd(args) {
918
+ const name = args[0];
919
+ if (!name) {
920
+ console.log("");
921
+ console.log(chalk_1.default.yellow(" usage: youmd skill info <name>"));
922
+ console.log("");
923
+ return;
924
+ }
925
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
926
+ const entry = (0, skill_catalog_1.findSkill)(catalog, name);
927
+ if (!entry) {
928
+ console.log("");
929
+ console.log(chalk_1.default.yellow(` skill "${name}" not found.`));
930
+ console.log("");
931
+ return;
932
+ }
933
+ const skillFile = (0, skills_2.readSkillFile)(entry.name);
934
+ const metrics = (0, skills_1.getMetrics)();
935
+ const m = metrics.skills[entry.name];
936
+ const identity = (0, skill_renderer_1.loadIdentityData)();
937
+ console.log("");
938
+ console.log(" " + chalk_1.default.bold(entry.name) + DIM(` v${entry.version}`));
939
+ console.log(DIM(` ${entry.description}`));
940
+ console.log("");
941
+ // Status
942
+ console.log(` ${ACCENT("status:")} ${entry.installed ? chalk_1.default.green("installed") : DIM("not installed")}`);
943
+ console.log(` ${ACCENT("scope:")} ${entry.scope}`);
944
+ console.log(` ${ACCENT("source:")} ${DIM(entry.source)}`);
945
+ // Metrics
946
+ if (m) {
947
+ console.log(` ${ACCENT("uses:")} ${m.uses}`);
948
+ console.log(` ${ACCENT("installs:")} ${m.installs}`);
949
+ if (m.lastUsed)
950
+ console.log(` ${ACCENT("last used:")} ${DIM(m.lastUsed.slice(0, 10))}`);
951
+ }
952
+ // Identity fields
953
+ if (entry.identity_fields.length > 0) {
954
+ console.log("");
955
+ console.log(" " + ACCENT("identity fields:"));
956
+ for (const field of entry.identity_fields) {
957
+ const val = (0, skill_renderer_1.resolveVariable)(field, identity);
958
+ const status = val ? chalk_1.default.green("\u2713") : chalk_1.default.yellow("\u2022");
959
+ const preview = val ? DIM(` ${val.slice(0, 50)}${val.length > 50 ? "..." : ""}`) : DIM(" (empty)");
960
+ console.log(` ${status} ${field}${preview}`);
961
+ }
962
+ }
963
+ // Content preview
964
+ if (skillFile) {
965
+ console.log("");
966
+ console.log(" " + ACCENT("content:"));
967
+ const lines = skillFile.content.split("\n").slice(0, 8);
968
+ for (const line of lines) {
969
+ console.log(DIM(` ${line}`));
970
+ }
971
+ const total = skillFile.content.split("\n").length;
972
+ if (total > 8) {
973
+ console.log(DIM(` ... ${total - 8} more lines`));
974
+ }
975
+ }
976
+ console.log("");
977
+ }
978
+ function exportSkillsCmd(args) {
979
+ const outputDir = args[0] || path.join(process.cwd(), "youmd-skills");
980
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
981
+ const installed = catalog.skills.filter((s) => s.installed);
982
+ if (installed.length === 0) {
983
+ console.log("");
984
+ console.log(DIM(" no skills installed to export."));
985
+ console.log("");
986
+ return;
987
+ }
988
+ fs.mkdirSync(outputDir, { recursive: true });
989
+ console.log("");
990
+ let exported = 0;
991
+ for (const entry of installed) {
992
+ const skillFile = (0, skills_2.readSkillFile)(entry.name);
993
+ if (!skillFile)
994
+ continue;
995
+ // Export the raw SKILL.md
996
+ const outPath = path.join(outputDir, `${entry.name}.md`);
997
+ const raw = fs.readFileSync(path.join(require("os").homedir(), ".youmd", "skills", entry.name, "SKILL.md"), "utf-8");
998
+ fs.writeFileSync(outPath, raw);
999
+ // Also export the rendered version
1000
+ const renderedPath = path.join(require("os").homedir(), ".youmd", "skills", entry.name, "RENDERED.md");
1001
+ if (fs.existsSync(renderedPath)) {
1002
+ fs.writeFileSync(path.join(outputDir, `${entry.name}.rendered.md`), fs.readFileSync(renderedPath, "utf-8"));
1003
+ }
1004
+ console.log(` ${chalk_1.default.green("\u2713")} ${entry.name}`);
1005
+ exported++;
1006
+ }
1007
+ // Write catalog.yaml
1008
+ const yaml = require("js-yaml");
1009
+ const exportCatalog = {
1010
+ version: 1,
1011
+ exported: new Date().toISOString(),
1012
+ skills: installed.map((s) => ({
1013
+ name: s.name,
1014
+ description: s.description,
1015
+ version: s.version,
1016
+ scope: s.scope,
1017
+ identity_fields: s.identity_fields,
1018
+ })),
1019
+ };
1020
+ fs.writeFileSync(path.join(outputDir, "catalog.yaml"), yaml.dump(exportCatalog, { lineWidth: 120, noRefs: true }));
1021
+ console.log("");
1022
+ console.log(chalk_1.default.green(` exported ${exported} skills to ${outputDir}/`));
1023
+ console.log(DIM(" share this directory or publish individual skills."));
1024
+ console.log("");
1025
+ }
1026
+ // ─── Main command router ──────────────────────────────────────────────
1027
+ async function skillCommand(subcommand, ...args) {
1028
+ const sub = (subcommand || "").toLowerCase();
1029
+ switch (sub) {
1030
+ case "list":
1031
+ case "ls":
1032
+ listSkills();
1033
+ break;
1034
+ case "install":
1035
+ await installSkillCmd(args);
1036
+ break;
1037
+ case "remove":
1038
+ case "rm":
1039
+ await removeSkillCmd(args);
1040
+ break;
1041
+ case "use":
1042
+ case "run":
1043
+ await useSkillCmd(args);
1044
+ break;
1045
+ case "sync":
1046
+ await syncSkillsCmd();
1047
+ break;
1048
+ case "add":
1049
+ addSkillCmd(args);
1050
+ break;
1051
+ case "create":
1052
+ case "new":
1053
+ await createSkillCmd(args);
1054
+ break;
1055
+ case "push":
1056
+ pushSkillCmd(args);
1057
+ break;
1058
+ case "link":
1059
+ await linkSkillsCmd(args);
1060
+ break;
1061
+ case "init-project":
1062
+ case "init":
1063
+ await initProjectCmd();
1064
+ break;
1065
+ case "improve":
1066
+ improveCmd();
1067
+ break;
1068
+ case "metrics":
1069
+ case "stats":
1070
+ metricsCmd();
1071
+ break;
1072
+ case "search":
1073
+ searchCmd(args);
1074
+ break;
1075
+ case "browse":
1076
+ case "registry":
1077
+ await browseCmd();
1078
+ break;
1079
+ case "publish":
1080
+ await publishSkillCmd(args);
1081
+ break;
1082
+ case "remote":
1083
+ case "cloud":
1084
+ await remoteStatusCmd();
1085
+ break;
1086
+ case "export":
1087
+ exportSkillsCmd(args);
1088
+ break;
1089
+ case "info":
1090
+ case "show":
1091
+ infoCmd(args);
1092
+ break;
1093
+ default: {
1094
+ const catalog = (0, skill_catalog_1.readSkillCatalog)();
1095
+ const installed = catalog.skills.filter((s) => s.installed);
1096
+ console.log("");
1097
+ console.log(" " + chalk_1.default.bold("youmd skill") + DIM(" — identity-aware agent skills"));
1098
+ console.log("");
1099
+ if (installed.length > 0) {
1100
+ // Quick identity coverage
1101
+ const identity = (0, skill_renderer_1.loadIdentityData)();
1102
+ const allFields = new Set();
1103
+ for (const s of catalog.skills) {
1104
+ for (const f of s.identity_fields)
1105
+ allFields.add(f);
1106
+ }
1107
+ let filledCount = 0;
1108
+ for (const field of allFields) {
1109
+ if ((0, skill_renderer_1.resolveVariable)(field, identity))
1110
+ filledCount++;
1111
+ }
1112
+ const pct = allFields.size > 0 ? Math.round((filledCount / allFields.size) * 100) : 0;
1113
+ console.log(" " + ACCENT("installed:") +
1114
+ DIM(` ${installed.length}/${catalog.skills.length}`) +
1115
+ DIM(" identity: ") +
1116
+ (pct === 100 ? chalk_1.default.green(`${pct}%`) : ACCENT(`${pct}%`)));
1117
+ for (const s of installed) {
1118
+ console.log(` ${chalk_1.default.green("\u2713")} ${chalk_1.default.cyan(s.name)} ${DIM("v" + s.version)}`);
1119
+ }
1120
+ console.log("");
1121
+ }
1122
+ console.log(" " + chalk_1.default.cyan("commands:"));
1123
+ console.log("");
1124
+ console.log(` ${chalk_1.default.cyan("list".padEnd(28))} ${DIM("show all skills with install status")}`);
1125
+ console.log(` ${chalk_1.default.cyan("install <name|all>".padEnd(28))} ${DIM("install skill(s) from the catalog")}`);
1126
+ console.log(` ${chalk_1.default.cyan("remove <name|all>".padEnd(28))} ${DIM("remove installed skill(s)")}`);
1127
+ console.log(` ${chalk_1.default.cyan("use <name>".padEnd(28))} ${DIM("run a skill with identity interpolation")}`);
1128
+ console.log(` ${chalk_1.default.cyan("sync".padEnd(28))} ${DIM("re-render all skills against live identity")}`);
1129
+ console.log(` ${chalk_1.default.cyan("create [name]".padEnd(28))} ${DIM("scaffold a new custom skill")}`);
1130
+ console.log(` ${chalk_1.default.cyan("add <name> <source>".padEnd(28))} ${DIM("register a new skill in catalog")}`);
1131
+ console.log(` ${chalk_1.default.cyan("push <name>".padEnd(28))} ${DIM("push local changes back to source")}`);
1132
+ console.log(` ${chalk_1.default.cyan("link <agent>".padEnd(28))} ${DIM("link to claude | cursor | codex")}`);
1133
+ console.log(` ${chalk_1.default.cyan("init-project".padEnd(28))} ${DIM("CLAUDE.md + project-context/ + link")}`);
1134
+ console.log(` ${chalk_1.default.cyan("improve".padEnd(28))} ${DIM("review metrics, find gaps, propose changes")}`);
1135
+ console.log(` ${chalk_1.default.cyan("metrics".padEnd(28))} ${DIM("usage stats and effectiveness scores")}`);
1136
+ console.log(` ${chalk_1.default.cyan("search <query>".padEnd(28))} ${DIM("search skills by name or description")}`);
1137
+ console.log(` ${chalk_1.default.cyan("browse".padEnd(28))} ${DIM("browse the public skill registry")}`);
1138
+ console.log(` ${chalk_1.default.cyan("publish <name>".padEnd(28))} ${DIM("publish a skill to the registry")}`);
1139
+ console.log(` ${chalk_1.default.cyan("remote".padEnd(28))} ${DIM("show skills synced to your you.md account")}`);
1140
+ console.log(` ${chalk_1.default.cyan("export [dir]".padEnd(28))} ${DIM("export all installed skills to a directory")}`);
1141
+ console.log(` ${chalk_1.default.cyan("info <name>".padEnd(28))} ${DIM("detailed info about a skill")}`);
1142
+ console.log("");
1143
+ console.log(DIM(" skills are identity-aware markdown templates."));
1144
+ console.log(DIM(" {{voice.overall}} and {{preferences.agent}} resolve from your bundle."));
1145
+ console.log("");
1146
+ break;
1147
+ }
1148
+ }
1149
+ }
1150
+ //# sourceMappingURL=skill.js.map