@moda-ai/cli 1.55.32 → 1.55.33

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  buildDoctorReport
3
- } from "./cli-a03st7e3.js";
3
+ } from "./cli-5fstvkva.js";
4
4
  import"./cli-9bgnvth2.js";
5
- import"./cli-zgj5bakm.js";
5
+ import"./cli-6j8h2ave.js";
6
6
  import"./cli-mm48br00.js";
7
7
  import {
8
8
  readHarnessSyncState
@@ -4,7 +4,7 @@ import {
4
4
  } from "./cli-9bgnvth2.js";
5
5
  import {
6
6
  inspectPromptManifest
7
- } from "./cli-zgj5bakm.js";
7
+ } from "./cli-6j8h2ave.js";
8
8
  import {
9
9
  readHarnessSyncState,
10
10
  renderDoctorHuman,
@@ -1,15 +1,15 @@
1
- import {
2
- DEFAULT_POLL_INTERVAL_MS,
3
- DEFAULT_WAIT_TIMEOUT_MS,
4
- fetchLatestRun
5
- } from "./cli-yz8xcpyk.js";
6
1
  import {
7
2
  assertReplayablePrompt,
8
3
  discoverPrompts,
9
4
  parsePromptFileForAb,
10
5
  readPromptLock,
11
6
  runPromptSync
12
- } from "./cli-zgj5bakm.js";
7
+ } from "./cli-6j8h2ave.js";
8
+ import {
9
+ DEFAULT_POLL_INTERVAL_MS,
10
+ DEFAULT_WAIT_TIMEOUT_MS,
11
+ fetchLatestRun
12
+ } from "./cli-yz8xcpyk.js";
13
13
  import {
14
14
  UUID_RE
15
15
  } from "./cli-n2hng3gv.js";
@@ -5725,9 +5725,9 @@ async function runPromptsCommand(input, legacyFlags, legacyProfileOptions) {
5725
5725
  await promotePrompt(positionals, flags, profileOptions, context);
5726
5726
  break;
5727
5727
  case "ab":
5728
- return (await import("./prompts-ab-qed3ra5e.js")).runPromptAb(flags, profileOptions, context);
5728
+ return (await import("./prompts-ab-t7xptf0c.js")).runPromptAb(flags, profileOptions, context);
5729
5729
  case "propose":
5730
- await (await import("./prompts-propose-y5r68pcw.js")).runPromptPropose(positionals, flags, profileOptions, context);
5730
+ await (await import("./prompts-propose-8feadk2t.js")).runPromptPropose(positionals, flags, profileOptions, context);
5731
5731
  break;
5732
5732
  case "list":
5733
5733
  case "show":
@@ -1,3 +1,9 @@
1
+ // src/init/rules.ts
2
+ import { createHash } from "node:crypto";
3
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { dirname as dirname2, join as join2, sep } from "node:path";
5
+ import { confirm } from "@inquirer/prompts";
6
+
1
7
  // src/package-assets.ts
2
8
  import { dirname, join } from "node:path";
3
9
  import { fileURLToPath } from "node:url";
@@ -6,10 +12,6 @@ function packageAssetPath(...parts) {
6
12
  }
7
13
 
8
14
  // src/init/rules.ts
9
- import { createHash } from "node:crypto";
10
- import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
11
- import { dirname as dirname2, join as join2, sep } from "node:path";
12
- import { confirm } from "@inquirer/prompts";
13
15
  var SKILLS_SOURCE_DIR = packageAssetPath("skills");
14
16
  var SKILLS = [
15
17
  {
@@ -89,6 +91,13 @@ function writeInstallRecord(record) {
89
91
  writeFileSync(path, `${JSON.stringify(record, null, 2)}
90
92
  `);
91
93
  }
94
+ function bundledSkillVersion(skillId) {
95
+ try {
96
+ return skillVersion(loadSkillSource(skillId).content);
97
+ } catch {
98
+ return "unknown";
99
+ }
100
+ }
92
101
  function skillVersion(skillContent) {
93
102
  const frontmatter = FRONTMATTER_RE.exec(skillContent)?.[1] ?? "";
94
103
  return /^version:\s*(\S+)\s*$/m.exec(frontmatter)?.[1] ?? "unknown";
@@ -357,247 +366,4 @@ async function installRules(detectedAgents, options = {}) {
357
366
  return result;
358
367
  }
359
368
 
360
- // src/init/mcp.ts
361
- import { existsSync as existsSync2 } from "node:fs";
362
- import { join as join3 } from "node:path";
363
- import { execSync } from "node:child_process";
364
- function isOnPath(cmd) {
365
- try {
366
- execSync(`which ${cmd}`, { stdio: "pipe" });
367
- return true;
368
- } catch {
369
- return false;
370
- }
371
- }
372
- function detectAgents() {
373
- const cwd = process.cwd();
374
- const agents = [];
375
- if (existsSync2(join3(cwd, ".claude")) || isOnPath("claude")) {
376
- agents.push("Claude Code");
377
- }
378
- if (existsSync2(join3(cwd, ".cursor"))) {
379
- agents.push("Cursor");
380
- }
381
- return agents;
382
- }
383
-
384
- // src/skills-manifest.ts
385
- import {
386
- existsSync as existsSync3,
387
- mkdirSync as mkdirSync2,
388
- readdirSync as readdirSync2,
389
- readFileSync as readFileSync2,
390
- rmdirSync,
391
- unlinkSync as unlinkSync2,
392
- writeFileSync as writeFileSync2
393
- } from "node:fs";
394
- import { dirname as dirname3, isAbsolute, join as join4, normalize, relative, resolve } from "node:path";
395
- var SKILLS_MANIFEST_PATH = join4(".moda", "skills.yml");
396
- var SKILL_SOURCE_HARNESS = "moda";
397
- var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
398
- function readSkillsManifest(cwd) {
399
- const manifestPath = join4(cwd, SKILLS_MANIFEST_PATH);
400
- if (!existsSync3(manifestPath))
401
- return null;
402
- let text = "";
403
- try {
404
- text = readFileSync2(manifestPath, "utf8");
405
- } catch {
406
- return null;
407
- }
408
- return {
409
- tenantId: readYamlScalar(text, "tenant_id"),
410
- generationRunId: readYamlScalar(text, "generation_run_id"),
411
- generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
412
- releaseKey: readYamlScalar(text, "release_key"),
413
- statusFilter: readYamlScalar(text, "status_filter"),
414
- releasedAt: readYamlScalar(text, "released_at"),
415
- managedPaths: readManagedPaths(text),
416
- records: readSkillRecords(text)
417
- };
418
- }
419
- function manifestHeaderFrom(manifest) {
420
- return {
421
- tenantId: manifest?.tenantId ?? "",
422
- releaseKey: manifest?.releaseKey ?? "",
423
- statusFilter: manifest?.statusFilter ?? "",
424
- generationRunId: manifest?.generationRunId ?? "",
425
- generationVersion: manifest?.generationVersion ?? 0,
426
- releasedAt: manifest?.releasedAt ?? ""
427
- };
428
- }
429
- function writeSkillsManifest(cwd, header, records) {
430
- const manifestPath = join4(cwd, SKILLS_MANIFEST_PATH);
431
- const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
432
- const lines = [
433
- "version: 1",
434
- "source: moda",
435
- `tenant_id: ${yamlQuote(header.tenantId)}`,
436
- `release_key: ${yamlQuote(header.releaseKey)}`,
437
- `status_filter: ${yamlQuote(header.statusFilter)}`,
438
- `generation_run_id: ${yamlQuote(header.generationRunId)}`,
439
- `generation_version: ${header.generationVersion}`,
440
- `released_at: ${yamlQuote(header.releasedAt)}`,
441
- `installed_at: ${yamlQuote(new Date().toISOString())}`,
442
- "managed_paths:",
443
- ...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
444
- "skills:",
445
- ...records.flatMap((record) => [
446
- ` - skill_id: ${yamlQuote(record.skillId)}`,
447
- ` content_hash: ${yamlQuote(record.contentHash)}`,
448
- ` source: ${yamlQuote(record.source)}`,
449
- " paths:",
450
- ...record.paths.map((path) => ` - ${yamlQuote(path)}`)
451
- ]),
452
- ""
453
- ];
454
- mkdirSync2(dirname3(manifestPath), { recursive: true });
455
- writeFileSync2(manifestPath, lines.join(`
456
- `));
457
- }
458
- function upsertSkillManifestRecord(cwd, record) {
459
- const manifest = readSkillsManifest(cwd);
460
- const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
461
- records.push(record);
462
- writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
463
- }
464
- function pruneManagedSkillPaths(cwd, managedPaths, log) {
465
- let pruned = 0;
466
- for (const rawPath of [...new Set(managedPaths)]) {
467
- const safePath = safeManagedRelativePath(rawPath);
468
- if (!safePath)
469
- continue;
470
- const absolutePath = absoluteManagedPath(cwd, safePath);
471
- if (!absolutePath || !existsSync3(absolutePath))
472
- continue;
473
- try {
474
- unlinkSync2(absolutePath);
475
- pruned++;
476
- log(` pruned ${safePath}`);
477
- pruneEmptyClaudeSkillDir(cwd, safePath);
478
- } catch {}
479
- }
480
- return pruned;
481
- }
482
- function pruneEmptyClaudeSkillDir(cwd, relativePath) {
483
- if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
484
- return;
485
- const dir = dirname3(join4(cwd, relativePath));
486
- try {
487
- if (existsSync3(dir) && readdirSync2(dir).length === 0) {
488
- rmdirSync(dir);
489
- }
490
- } catch {}
491
- }
492
- function absoluteManagedPath(cwd, relativePath) {
493
- const abs = resolve(cwd, relativePath);
494
- const root = resolve(cwd);
495
- const rel = relative(root, abs);
496
- if (rel.startsWith("..") || isAbsolute(rel))
497
- return null;
498
- return abs;
499
- }
500
- function safeManagedRelativePath(rawPath) {
501
- if (!rawPath || rawPath.includes("\x00") || isAbsolute(rawPath))
502
- return null;
503
- const normalized = normalize(rawPath).replace(/\\/g, "/");
504
- if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
505
- return null;
506
- const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
507
- const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
508
- return isClaudeSkill || isCursorRule ? normalized : null;
509
- }
510
- function readYamlScalar(text, key) {
511
- const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
512
- return match ? parseYamlScalar(match[1]) : "";
513
- }
514
- function readSkillRecords(text) {
515
- const records = [];
516
- let inSkills = false;
517
- let current = null;
518
- let inPaths = false;
519
- for (const line of text.split(/\r?\n/)) {
520
- if (line.trim() === "skills:") {
521
- inSkills = true;
522
- continue;
523
- }
524
- if (!inSkills)
525
- continue;
526
- if (line.length > 0 && !line.startsWith(" "))
527
- break;
528
- const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
529
- if (skillIdMatch) {
530
- if (current)
531
- records.push(current);
532
- current = {
533
- skillId: parseYamlScalar(skillIdMatch[1]),
534
- contentHash: "",
535
- source: SKILL_SOURCE_HARNESS,
536
- paths: []
537
- };
538
- inPaths = false;
539
- continue;
540
- }
541
- if (!current)
542
- continue;
543
- const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
544
- if (hashMatch) {
545
- current.contentHash = parseYamlScalar(hashMatch[1]);
546
- continue;
547
- }
548
- const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
549
- if (sourceMatch) {
550
- current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
551
- continue;
552
- }
553
- if (/^ {4}paths:\s*$/.test(line)) {
554
- inPaths = true;
555
- continue;
556
- }
557
- const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
558
- if (pathMatch) {
559
- const value = parseYamlScalar(pathMatch[1]);
560
- if (value)
561
- current.paths.push(value);
562
- }
563
- }
564
- if (current)
565
- records.push(current);
566
- return records;
567
- }
568
- function readManagedPaths(text) {
569
- const paths = [];
570
- let inManagedPaths = false;
571
- for (const line of text.split(/\r?\n/)) {
572
- if (line.trim() === "managed_paths:") {
573
- inManagedPaths = true;
574
- continue;
575
- }
576
- if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
577
- break;
578
- if (!inManagedPaths)
579
- continue;
580
- const match = line.match(/^\s*-\s*(.*?)\s*$/);
581
- if (match)
582
- paths.push(parseYamlScalar(match[1]));
583
- }
584
- return paths.filter(Boolean);
585
- }
586
- function parseYamlScalar(raw) {
587
- const value = raw.trim();
588
- if (!value)
589
- return "";
590
- if (value.startsWith('"')) {
591
- try {
592
- return String(JSON.parse(value));
593
- } catch {
594
- return value.replace(/^"|"$/g, "");
595
- }
596
- }
597
- return value.replace(/^'|'$/g, "");
598
- }
599
- function yamlQuote(value) {
600
- return JSON.stringify(value);
601
- }
602
-
603
- export { packageAssetPath, claudeSkillToCursorRule, summarizeInstall, installRules, detectAgents, SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
369
+ export { packageAssetPath, claudeSkillToCursorRule, bundledSkillVersion, summarizeInstall, installRules };
@@ -1,12 +1,16 @@
1
1
  import {
2
2
  SKILL_SOURCE_HARNESS,
3
3
  SKILL_SOURCE_INTEGRATION_BUNDLED,
4
- claudeSkillToCursorRule,
5
- detectAgents,
6
4
  pruneManagedSkillPaths,
7
5
  readSkillsManifest,
8
6
  writeSkillsManifest
9
- } from "./cli-8dtnwp9r.js";
7
+ } from "./cli-qm5b1vck.js";
8
+ import {
9
+ detectAgents
10
+ } from "./cli-rjav0vj1.js";
11
+ import {
12
+ claudeSkillToCursorRule
13
+ } from "./cli-hcwdz9sd.js";
10
14
  import {
11
15
  createCommandContext
12
16
  } from "./cli-dr6x0zv7.js";
@@ -901,19 +905,43 @@ function manifestHeaderFor(fetched) {
901
905
  function sha256(input) {
902
906
  return createHash("sha256").update(input).digest("hex");
903
907
  }
908
+ var MODA_CLI_SKILL_ID = "moda-cli";
909
+ async function installModaCliSkill(context) {
910
+ const { detectAgents: detectAgents2 } = await import("./mcp-1qre9f0n.js");
911
+ const { installRules, summarizeInstall } = await import("./rules-qddx5t0j.js");
912
+ const result = await installRules(detectAgents2(), { yes: true, silent: true });
913
+ if (result.status === "skipped") {
914
+ throw new CliInputError("No supported coding agent was detected here, so the moda-cli skill was not installed.", "Run this from a project with a .claude or .cursor directory, or with Claude Code on PATH. Only Claude Code and Cursor are supported.");
915
+ }
916
+ const summary = summarizeInstall(result);
917
+ if (context.outputMode !== "human") {
918
+ context.output.writeData({ skill: MODA_CLI_SKILL_ID, status: result.status, agents: result.agents, counts: result.counts, summary, outcomes: result.outcomes, notes: result.notes }, { ...result.status === "failed" ? { status: "error" } : {}, warnings: result.warnings });
919
+ } else {
920
+ console.log(`moda-cli skill for ${result.agents.join(", ")}: ${summary}`);
921
+ for (const note of result.notes)
922
+ console.log(` ${note}`);
923
+ for (const warning of result.warnings)
924
+ console.error(` ${warning}`);
925
+ }
926
+ return result.status === "failed" ? 1 : 0;
927
+ }
904
928
  async function runSkillsInstall(context, ids) {
905
- const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-dq20spzk.js");
929
+ const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-7jes6hqx.js");
906
930
  const index = readBundledIndex();
907
931
  if (context.flags.list === "true" || ids.length === 0) {
932
+ const { bundledSkillVersion } = await import("./rules-qddx5t0j.js");
908
933
  if (!index) {
909
934
  throw new CliInputError("This CLI build has no bundled integration guides", "Install a published @moda-ai/cli, or run `bun run bundle:skills` in apps/moda-cli.");
910
935
  }
911
- const skills = index.skills.map((skill) => ({
912
- id: skill.id,
913
- version: skill.version,
914
- language: skill.targets.language,
915
- summary: skill.summary
916
- }));
936
+ const skills = [
937
+ { id: MODA_CLI_SKILL_ID, version: bundledSkillVersion(MODA_CLI_SKILL_ID), language: "any", summary: "This CLI's own guide for coding agents; installs or refreshes it for each detected agent (copies edited locally are kept)." },
938
+ ...index.skills.map((skill) => ({
939
+ id: skill.id,
940
+ version: skill.version,
941
+ language: skill.targets.language,
942
+ summary: skill.summary
943
+ }))
944
+ ];
917
945
  if (context.outputMode !== "human") {
918
946
  context.output.writeData({ skills });
919
947
  } else {
@@ -925,6 +953,13 @@ async function runSkillsInstall(context, ids) {
925
953
  }
926
954
  return 0;
927
955
  }
956
+ if (ids.includes(MODA_CLI_SKILL_ID)) {
957
+ const others = ids.filter((id) => id !== MODA_CLI_SKILL_ID);
958
+ if (others.length > 0) {
959
+ throw new CliInputError(`Install ${MODA_CLI_SKILL_ID} on its own, so nothing was written.`, `Run \`moda skills install ${MODA_CLI_SKILL_ID}\`, then \`moda skills install ${others.join(" ")}\`.`);
960
+ }
961
+ return installModaCliSkill(context);
962
+ }
928
963
  const unknown = ids.filter((id) => !/^[a-z0-9-]+$/.test(id) || readBundledSkillMd(id) === null);
929
964
  if (unknown.length > 0) {
930
965
  throw new CliInputError(`Not bundled with this CLI: ${unknown.map((id) => JSON.stringify(id).slice(0, 80)).join(", ")}`, "List the bundled guides with: moda skills install --list");
@@ -1,11 +1,15 @@
1
1
  import {
2
2
  SKILL_SOURCE_INTEGRATION_BUNDLED,
3
- claudeSkillToCursorRule,
4
- detectAgents,
5
- packageAssetPath,
6
3
  readSkillsManifest,
7
4
  upsertSkillManifestRecord
8
- } from "./cli-8dtnwp9r.js";
5
+ } from "./cli-qm5b1vck.js";
6
+ import {
7
+ detectAgents
8
+ } from "./cli-rjav0vj1.js";
9
+ import {
10
+ claudeSkillToCursorRule,
11
+ packageAssetPath
12
+ } from "./cli-hcwdz9sd.js";
9
13
 
10
14
  // src/skills-bundled.ts
11
15
  import { createHash } from "node:crypto";
@@ -0,0 +1,220 @@
1
+ // src/skills-manifest.ts
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readdirSync,
6
+ readFileSync,
7
+ rmdirSync,
8
+ unlinkSync,
9
+ writeFileSync
10
+ } from "node:fs";
11
+ import { dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
12
+ var SKILLS_MANIFEST_PATH = join(".moda", "skills.yml");
13
+ var SKILL_SOURCE_HARNESS = "moda";
14
+ var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
15
+ function readSkillsManifest(cwd) {
16
+ const manifestPath = join(cwd, SKILLS_MANIFEST_PATH);
17
+ if (!existsSync(manifestPath))
18
+ return null;
19
+ let text = "";
20
+ try {
21
+ text = readFileSync(manifestPath, "utf8");
22
+ } catch {
23
+ return null;
24
+ }
25
+ return {
26
+ tenantId: readYamlScalar(text, "tenant_id"),
27
+ generationRunId: readYamlScalar(text, "generation_run_id"),
28
+ generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
29
+ releaseKey: readYamlScalar(text, "release_key"),
30
+ statusFilter: readYamlScalar(text, "status_filter"),
31
+ releasedAt: readYamlScalar(text, "released_at"),
32
+ managedPaths: readManagedPaths(text),
33
+ records: readSkillRecords(text)
34
+ };
35
+ }
36
+ function manifestHeaderFrom(manifest) {
37
+ return {
38
+ tenantId: manifest?.tenantId ?? "",
39
+ releaseKey: manifest?.releaseKey ?? "",
40
+ statusFilter: manifest?.statusFilter ?? "",
41
+ generationRunId: manifest?.generationRunId ?? "",
42
+ generationVersion: manifest?.generationVersion ?? 0,
43
+ releasedAt: manifest?.releasedAt ?? ""
44
+ };
45
+ }
46
+ function writeSkillsManifest(cwd, header, records) {
47
+ const manifestPath = join(cwd, SKILLS_MANIFEST_PATH);
48
+ const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
49
+ const lines = [
50
+ "version: 1",
51
+ "source: moda",
52
+ `tenant_id: ${yamlQuote(header.tenantId)}`,
53
+ `release_key: ${yamlQuote(header.releaseKey)}`,
54
+ `status_filter: ${yamlQuote(header.statusFilter)}`,
55
+ `generation_run_id: ${yamlQuote(header.generationRunId)}`,
56
+ `generation_version: ${header.generationVersion}`,
57
+ `released_at: ${yamlQuote(header.releasedAt)}`,
58
+ `installed_at: ${yamlQuote(new Date().toISOString())}`,
59
+ "managed_paths:",
60
+ ...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
61
+ "skills:",
62
+ ...records.flatMap((record) => [
63
+ ` - skill_id: ${yamlQuote(record.skillId)}`,
64
+ ` content_hash: ${yamlQuote(record.contentHash)}`,
65
+ ` source: ${yamlQuote(record.source)}`,
66
+ " paths:",
67
+ ...record.paths.map((path) => ` - ${yamlQuote(path)}`)
68
+ ]),
69
+ ""
70
+ ];
71
+ mkdirSync(dirname(manifestPath), { recursive: true });
72
+ writeFileSync(manifestPath, lines.join(`
73
+ `));
74
+ }
75
+ function upsertSkillManifestRecord(cwd, record) {
76
+ const manifest = readSkillsManifest(cwd);
77
+ const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
78
+ records.push(record);
79
+ writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
80
+ }
81
+ function pruneManagedSkillPaths(cwd, managedPaths, log) {
82
+ let pruned = 0;
83
+ for (const rawPath of [...new Set(managedPaths)]) {
84
+ const safePath = safeManagedRelativePath(rawPath);
85
+ if (!safePath)
86
+ continue;
87
+ const absolutePath = absoluteManagedPath(cwd, safePath);
88
+ if (!absolutePath || !existsSync(absolutePath))
89
+ continue;
90
+ try {
91
+ unlinkSync(absolutePath);
92
+ pruned++;
93
+ log(` pruned ${safePath}`);
94
+ pruneEmptyClaudeSkillDir(cwd, safePath);
95
+ } catch {}
96
+ }
97
+ return pruned;
98
+ }
99
+ function pruneEmptyClaudeSkillDir(cwd, relativePath) {
100
+ if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
101
+ return;
102
+ const dir = dirname(join(cwd, relativePath));
103
+ try {
104
+ if (existsSync(dir) && readdirSync(dir).length === 0) {
105
+ rmdirSync(dir);
106
+ }
107
+ } catch {}
108
+ }
109
+ function absoluteManagedPath(cwd, relativePath) {
110
+ const abs = resolve(cwd, relativePath);
111
+ const root = resolve(cwd);
112
+ const rel = relative(root, abs);
113
+ if (rel.startsWith("..") || isAbsolute(rel))
114
+ return null;
115
+ return abs;
116
+ }
117
+ function safeManagedRelativePath(rawPath) {
118
+ if (!rawPath || rawPath.includes("\x00") || isAbsolute(rawPath))
119
+ return null;
120
+ const normalized = normalize(rawPath).replace(/\\/g, "/");
121
+ if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
122
+ return null;
123
+ const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
124
+ const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
125
+ return isClaudeSkill || isCursorRule ? normalized : null;
126
+ }
127
+ function readYamlScalar(text, key) {
128
+ const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
129
+ return match ? parseYamlScalar(match[1]) : "";
130
+ }
131
+ function readSkillRecords(text) {
132
+ const records = [];
133
+ let inSkills = false;
134
+ let current = null;
135
+ let inPaths = false;
136
+ for (const line of text.split(/\r?\n/)) {
137
+ if (line.trim() === "skills:") {
138
+ inSkills = true;
139
+ continue;
140
+ }
141
+ if (!inSkills)
142
+ continue;
143
+ if (line.length > 0 && !line.startsWith(" "))
144
+ break;
145
+ const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
146
+ if (skillIdMatch) {
147
+ if (current)
148
+ records.push(current);
149
+ current = {
150
+ skillId: parseYamlScalar(skillIdMatch[1]),
151
+ contentHash: "",
152
+ source: SKILL_SOURCE_HARNESS,
153
+ paths: []
154
+ };
155
+ inPaths = false;
156
+ continue;
157
+ }
158
+ if (!current)
159
+ continue;
160
+ const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
161
+ if (hashMatch) {
162
+ current.contentHash = parseYamlScalar(hashMatch[1]);
163
+ continue;
164
+ }
165
+ const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
166
+ if (sourceMatch) {
167
+ current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
168
+ continue;
169
+ }
170
+ if (/^ {4}paths:\s*$/.test(line)) {
171
+ inPaths = true;
172
+ continue;
173
+ }
174
+ const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
175
+ if (pathMatch) {
176
+ const value = parseYamlScalar(pathMatch[1]);
177
+ if (value)
178
+ current.paths.push(value);
179
+ }
180
+ }
181
+ if (current)
182
+ records.push(current);
183
+ return records;
184
+ }
185
+ function readManagedPaths(text) {
186
+ const paths = [];
187
+ let inManagedPaths = false;
188
+ for (const line of text.split(/\r?\n/)) {
189
+ if (line.trim() === "managed_paths:") {
190
+ inManagedPaths = true;
191
+ continue;
192
+ }
193
+ if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
194
+ break;
195
+ if (!inManagedPaths)
196
+ continue;
197
+ const match = line.match(/^\s*-\s*(.*?)\s*$/);
198
+ if (match)
199
+ paths.push(parseYamlScalar(match[1]));
200
+ }
201
+ return paths.filter(Boolean);
202
+ }
203
+ function parseYamlScalar(raw) {
204
+ const value = raw.trim();
205
+ if (!value)
206
+ return "";
207
+ if (value.startsWith('"')) {
208
+ try {
209
+ return String(JSON.parse(value));
210
+ } catch {
211
+ return value.replace(/^"|"$/g, "");
212
+ }
213
+ }
214
+ return value.replace(/^'|'$/g, "");
215
+ }
216
+ function yamlQuote(value) {
217
+ return JSON.stringify(value);
218
+ }
219
+
220
+ export { SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
@@ -0,0 +1,25 @@
1
+ // src/init/mcp.ts
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { execSync } from "node:child_process";
5
+ function isOnPath(cmd) {
6
+ try {
7
+ execSync(`which ${cmd}`, { stdio: "pipe" });
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ function detectAgents() {
14
+ const cwd = process.cwd();
15
+ const agents = [];
16
+ if (existsSync(join(cwd, ".claude")) || isOnPath("claude")) {
17
+ agents.push("Claude Code");
18
+ }
19
+ if (existsSync(join(cwd, ".cursor"))) {
20
+ agents.push("Cursor");
21
+ }
22
+ return agents;
23
+ }
24
+
25
+ export { detectAgents };
package/dist/cli.js CHANGED
@@ -1084,7 +1084,7 @@ function getUpdateNotice(options = {}) {
1084
1084
  return [
1085
1085
  "",
1086
1086
  `Update available: moda-cli ${VERSION} → ${latest}`,
1087
- "Run `npm install -g @moda-ai/cli@latest` to update."
1087
+ "Run `npm install -g @moda-ai/cli@latest` to update, then `moda skills install moda-cli` to refresh the agent skill."
1088
1088
  ].join(`
1089
1089
  `);
1090
1090
  }
@@ -1911,10 +1911,10 @@ var SKILLS_SUBCOMMAND_HELP = [
1911
1911
  },
1912
1912
  {
1913
1913
  name: "install",
1914
- description: "Install bundled integration guides into .claude/skills (offline; no API key)",
1915
- usage: "moda skills install [--list] <id...>",
1914
+ description: "Install bundled integration guides into .claude/skills, or refresh the moda-cli skill (offline; no API key)",
1915
+ usage: "moda skills install [--list] <id...> | moda skills install moda-cli",
1916
1916
  flags: [{ name: "--list", description: "List the bundled guides." }],
1917
- examples: ["moda skills install --list", "moda skills install integration-node-openai"]
1917
+ examples: ["moda skills install moda-cli", "moda skills install --list", "moda skills install integration-node-openai"]
1918
1918
  }
1919
1919
  ];
1920
1920
  var PROMPTS_REGISTRY_SUBCOMMAND_HELP = [
@@ -2959,10 +2959,10 @@ function leafManifestFields(leaf, commands) {
2959
2959
  import { fileURLToPath } from "url";
2960
2960
  import { realpathSync } from "fs";
2961
2961
  async function runPromptsCommand(...args) {
2962
- return (await import("./prompts-kswdk3ft.js")).runPromptsCommand(...args);
2962
+ return (await import("./prompts-b8qx3mrv.js")).runPromptsCommand(...args);
2963
2963
  }
2964
2964
  async function runSkillsCommand(...args) {
2965
- return (await import("./skills-j8865009.js")).runSkillsCommand(...args);
2965
+ return (await import("./skills-96z55tj4.js")).runSkillsCommand(...args);
2966
2966
  }
2967
2967
  async function runFixCommand(...args) {
2968
2968
  return (await import("./fix-w5z02wt4.js")).runFixCommand(...args);
@@ -2974,16 +2974,16 @@ async function runHarnessCommand(...args) {
2974
2974
  return (await import("./harness-6vwehf47.js")).runHarnessCommand(...args);
2975
2975
  }
2976
2976
  async function runSyncCommand(...args) {
2977
- return (await import("./sync-5e8t28t7.js")).runSyncCommand(...args);
2977
+ return (await import("./sync-drz7req6.js")).runSyncCommand(...args);
2978
2978
  }
2979
2979
  async function runRegistryCommand(...args) {
2980
2980
  return (await import("./registry-ctnn8e26.js")).runRegistryCommand(...args);
2981
2981
  }
2982
2982
  async function runDoctorCommand(...args) {
2983
- return (await import("./doctor-hnzte896.js")).runDoctorCommand(...args);
2983
+ return (await import("./doctor-tvre7zvk.js")).runDoctorCommand(...args);
2984
2984
  }
2985
2985
  async function runStatusCommand(...args) {
2986
- return (await import("./doctor-hnzte896.js")).runStatusCommand(...args);
2986
+ return (await import("./doctor-tvre7zvk.js")).runStatusCommand(...args);
2987
2987
  }
2988
2988
  async function runAuthCommand(...args) {
2989
2989
  return (await import("./profile-commands-h8evz9x0.js")).runAuthCommand(...args);
@@ -2995,7 +2995,7 @@ async function runProfilesCommand(...args) {
2995
2995
  return (await import("./profile-commands-h8evz9x0.js")).runProfilesCommand(...args);
2996
2996
  }
2997
2997
  async function runAgentCommand(...args) {
2998
- return (await import("./agent-e4k17rzy.js")).runAgentCommand(...args);
2998
+ return (await import("./agent-g07tg64w.js")).runAgentCommand(...args);
2999
2999
  }
3000
3000
  async function runProductionCommand(...args) {
3001
3001
  return (await import("./production-intelligence-7wb1djy2.js")).runProductionCommand(...args);
@@ -6143,7 +6143,7 @@ var commandRegistry = createCommandRegistry([
6143
6143
  resetApiRequestCountBeforeRun: true,
6144
6144
  telemetry: "result-and-error",
6145
6145
  handler: async (context) => {
6146
- const { runInit } = await import("./index-6atgddwt.js");
6146
+ const { runInit } = await import("./index-75vcntpe.js");
6147
6147
  if (context.outputMode === "agent-stream") {
6148
6148
  context.output.writeEvent({
6149
6149
  event: "started",
@@ -7074,6 +7074,7 @@ var commandRegistry = createCommandRegistry([
7074
7074
  "moda skills status",
7075
7075
  "moda skills pull",
7076
7076
  "moda skills push",
7077
+ "moda skills install moda-cli",
7077
7078
  "moda skills install --list",
7078
7079
  "moda skills install integration-node-openai"
7079
7080
  ],
@@ -3,9 +3,9 @@ import {
3
3
  promptsDiscoveredCheck,
4
4
  runDoctorCommand,
5
5
  runStatusCommand
6
- } from "./cli-a03st7e3.js";
6
+ } from "./cli-5fstvkva.js";
7
7
  import"./cli-9bgnvth2.js";
8
- import"./cli-zgj5bakm.js";
8
+ import"./cli-6j8h2ave.js";
9
9
  import"./cli-mm48br00.js";
10
10
  import"./cli-xq45etg1.js";
11
11
  import"./cli-dr6x0zv7.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildDoctorReport
3
- } from "./cli-a03st7e3.js";
3
+ } from "./cli-5fstvkva.js";
4
4
  import {
5
5
  selectTenantAndCreateKey
6
6
  } from "./cli-9bgk5jeh.js";
@@ -16,20 +16,23 @@ import {
16
16
  bundledSkillsDir,
17
17
  installIntegrationSkill,
18
18
  readBundledIndex
19
- } from "./cli-frhm2wz7.js";
19
+ } from "./cli-mc3z77bv.js";
20
20
  import {
21
21
  agentsMdProblem,
22
22
  runSkillSync
23
- } from "./cli-mg5zv4s6.js";
23
+ } from "./cli-k007ztbs.js";
24
+ import"./cli-qm5b1vck.js";
25
+ import {
26
+ detectAgents
27
+ } from "./cli-rjav0vj1.js";
24
28
  import {
25
- detectAgents,
26
29
  installRules,
27
30
  summarizeInstall
28
- } from "./cli-8dtnwp9r.js";
31
+ } from "./cli-hcwdz9sd.js";
29
32
  import {
30
33
  initPrompts,
31
34
  runPromptSync
32
- } from "./cli-zgj5bakm.js";
35
+ } from "./cli-6j8h2ave.js";
33
36
  import"./cli-mm48br00.js";
34
37
  import {
35
38
  codingAgentDisplayName,
@@ -0,0 +1,7 @@
1
+ import {
2
+ detectAgents
3
+ } from "./cli-rjav0vj1.js";
4
+ import"./cli-0v6na3yp.js";
5
+ export {
6
+ detectAgents
7
+ };
@@ -23,9 +23,9 @@ import {
23
23
  runPromptAb,
24
24
  selectProblemCases,
25
25
  waitLabel
26
- } from "./cli-n1d52cww.js";
26
+ } from "./cli-602yx84w.js";
27
+ import"./cli-6j8h2ave.js";
27
28
  import"./cli-yz8xcpyk.js";
28
- import"./cli-zgj5bakm.js";
29
29
  import"./cli-mm48br00.js";
30
30
  import"./cli-dr6x0zv7.js";
31
31
  import"./cli-n2hng3gv.js";
@@ -11,7 +11,7 @@ import {
11
11
  runPromptSync,
12
12
  runPromptsCommand,
13
13
  writePromptSyncProgress
14
- } from "./cli-zgj5bakm.js";
14
+ } from "./cli-6j8h2ave.js";
15
15
  import {
16
16
  promptSourceHashPayload
17
17
  } from "./cli-mm48br00.js";
@@ -7,9 +7,9 @@ import {
7
7
  formatVerdict,
8
8
  rerunWithYes,
9
9
  resolvePromptArmSpec
10
- } from "./cli-n1d52cww.js";
10
+ } from "./cli-602yx84w.js";
11
+ import"./cli-6j8h2ave.js";
11
12
  import"./cli-yz8xcpyk.js";
12
- import"./cli-zgj5bakm.js";
13
13
  import"./cli-mm48br00.js";
14
14
  import"./cli-dr6x0zv7.js";
15
15
  import"./cli-n2hng3gv.js";
@@ -0,0 +1,13 @@
1
+ import {
2
+ bundledSkillVersion,
3
+ claudeSkillToCursorRule,
4
+ installRules,
5
+ summarizeInstall
6
+ } from "./cli-hcwdz9sd.js";
7
+ import"./cli-0v6na3yp.js";
8
+ export {
9
+ summarizeInstall,
10
+ installRules,
11
+ claudeSkillToCursorRule,
12
+ bundledSkillVersion
13
+ };
@@ -3,8 +3,10 @@ import {
3
3
  pullSkills,
4
4
  runSkillSync,
5
5
  runSkillsCommand
6
- } from "./cli-mg5zv4s6.js";
7
- import"./cli-8dtnwp9r.js";
6
+ } from "./cli-k007ztbs.js";
7
+ import"./cli-qm5b1vck.js";
8
+ import"./cli-rjav0vj1.js";
9
+ import"./cli-hcwdz9sd.js";
8
10
  import"./cli-dr6x0zv7.js";
9
11
  import"./cli-n2hng3gv.js";
10
12
  import"./cli-e23g9ywa.js";
@@ -3,8 +3,10 @@ import {
3
3
  installIntegrationSkill,
4
4
  readBundledIndex,
5
5
  readBundledSkillMd
6
- } from "./cli-frhm2wz7.js";
7
- import"./cli-8dtnwp9r.js";
6
+ } from "./cli-mc3z77bv.js";
7
+ import"./cli-qm5b1vck.js";
8
+ import"./cli-rjav0vj1.js";
9
+ import"./cli-hcwdz9sd.js";
8
10
  import"./cli-0v6na3yp.js";
9
11
  export {
10
12
  readBundledSkillMd,
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  runSkillsCommand
3
- } from "./cli-mg5zv4s6.js";
4
- import"./cli-8dtnwp9r.js";
3
+ } from "./cli-k007ztbs.js";
4
+ import"./cli-qm5b1vck.js";
5
+ import"./cli-rjav0vj1.js";
6
+ import"./cli-hcwdz9sd.js";
5
7
  import {
6
8
  runPromptsCommand
7
- } from "./cli-zgj5bakm.js";
9
+ } from "./cli-6j8h2ave.js";
8
10
  import"./cli-mm48br00.js";
9
11
  import {
10
12
  runHarnessCommand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.55.32",
3
+ "version": "1.55.33",
4
4
  "description": "CLI for Moda: production monitoring and evals for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-10-17T02:32:13.042Z",
4
- "cli_version": "1.55.32",
3
+ "bundled_at": "2026-10-17T03:41:09.544Z",
4
+ "cli_version": "1.55.33",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: moda-cli
3
- version: 3.0.1
3
+ version: 3.0.2
4
4
  description: Investigate a production AI agent with the Moda CLI (`moda`) - find where something happened in real traces (traces search, then traces get), check agent health, rank what to fix (investigate, problems), and dig into tool failures, user frustration, and hallucinations. Also prompt registry sync/promote, replays, custom signals, and false-positive marking. Use when the user mentions Moda, production traces or conversations of their agent, agent failures, or asks what to fix.
5
5
  ---
6
6
 
@@ -24,7 +24,9 @@ in `references/`; read one only when the task needs it.
24
24
  interactive wizard that writes to the repo.
25
25
  - Not installed: `npx -y -p @moda-ai/cli moda <command>`.
26
26
  - If a response carries `meta.update`, the installed CLI is out of date and
27
- may not match this guide; tell the user how to update.
27
+ may not match this guide; tell the user how to update
28
+ (`npm install -g @moda-ai/cli@latest`, then `moda skills install moda-cli`
29
+ to refresh this guide).
28
30
  - Flags: `moda <noun> <verb> --help` lists every flag the command accepts, with
29
31
  types, ranges, and defaults. Each command rejects flags it does not take and
30
32
  the error lists the valid ones; read that list instead of guessing.