@moda-ai/cli 1.35.0 → 1.37.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.
@@ -11,16 +11,22 @@ import {
11
11
  resolvePromptSource,
12
12
  safePromptEvidencePath,
13
13
  scanHarness
14
- } from "./cli-0p6n391h.js";
14
+ } from "./cli-07tv75te.js";
15
+ import {
16
+ SKILL_SOURCE_HARNESS,
17
+ SKILL_SOURCE_INTEGRATION_BUNDLED,
18
+ claudeSkillToCursorRule,
19
+ detectAgents,
20
+ pruneManagedSkillPaths,
21
+ readSkillsManifest,
22
+ writeSkillsManifest
23
+ } from "./cli-ssfq84k5.js";
15
24
  import {
16
25
  isAuthSessionValid,
17
26
  loadAuthSession
18
- } from "./cli-yz99t7n8.js";
27
+ } from "./cli-strngwn3.js";
19
28
  import {
20
29
  CliInputError,
21
- __commonJS,
22
- __require,
23
- __toESM,
24
30
  createCommandContext,
25
31
  ensureSecretFilePermissions,
26
32
  getActiveProfile,
@@ -34,7 +40,12 @@ import {
34
40
  resolveTenantId,
35
41
  secretFilePathFromRef,
36
42
  validateConfig
37
- } from "./cli-drabjmaj.js";
43
+ } from "./cli-q79sq80a.js";
44
+ import {
45
+ __commonJS,
46
+ __require,
47
+ __toESM
48
+ } from "./cli-0v6na3yp.js";
38
49
 
39
50
  // ../../node_modules/.bun/fast-glob@3.3.3/node_modules/fast-glob/out/utils/array.js
40
51
  var require_array = __commonJS((exports) => {
@@ -6309,7 +6320,7 @@ async function runPromptSyncExclusive(flags, profileOptions, options) {
6309
6320
  const analyzedDefinitions = new Map;
6310
6321
  if (flags["no-analyze"] !== "true") {
6311
6322
  options.onProgress?.("Analyzing prompt sources before sync.");
6312
- const { analyzeHarnessPrompts } = await import("./harness-heasma1f.js");
6323
+ const { analyzeHarnessPrompts } = await import("./harness-n2xgzz31.js");
6313
6324
  const result = await analyzeHarnessPrompts({
6314
6325
  rootDir: process.cwd(),
6315
6326
  existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
@@ -6790,416 +6801,22 @@ function printJson(value) {
6790
6801
  console.log(JSON.stringify(value, null, 2));
6791
6802
  }
6792
6803
 
6793
- // src/init/rules.ts
6794
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
6795
- import { join as join3 } from "node:path";
6796
- import { confirm } from "@inquirer/prompts";
6797
-
6798
- // src/package-assets.ts
6799
- import { dirname as dirname3, join as join2 } from "node:path";
6800
- import { fileURLToPath } from "node:url";
6801
- function packageAssetPath(...parts) {
6802
- return join2(dirname3(fileURLToPath(import.meta.url)), "..", ...parts);
6803
- }
6804
-
6805
- // src/init/rules.ts
6806
- var SKILLS_SOURCE_DIR = packageAssetPath("skills");
6807
- var SKILLS = [
6808
- {
6809
- id: "moda-cli",
6810
- label: "moda-cli — let coding agents query Moda analytics via the CLI"
6811
- }
6812
- ];
6813
- var ADAPTERS = {
6814
- "Claude Code": {
6815
- name: "Claude Code",
6816
- targetFor: (skillId) => ({
6817
- dir: join3(".claude", "skills", skillId),
6818
- filename: "SKILL.md"
6819
- }),
6820
- transform: (content) => content
6821
- },
6822
- Cursor: {
6823
- name: "Cursor",
6824
- targetFor: (skillId) => ({
6825
- dir: join3(".cursor", "rules"),
6826
- filename: `${skillId}.mdc`
6827
- }),
6828
- transform: claudeSkillToCursorRule
6829
- }
6830
- };
6831
- var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
6832
- var DESCRIPTION_RE = /^description:\s*(.+?)\s*$/m;
6833
- function claudeSkillToCursorRule(skillContent) {
6834
- const match = FRONTMATTER_RE.exec(skillContent);
6835
- if (!match) {
6836
- return skillContent;
6837
- }
6838
- const [, frontmatter, body] = match;
6839
- const descMatch = DESCRIPTION_RE.exec(frontmatter);
6840
- const description = descMatch?.[1].trim() ?? "";
6841
- const cursorFrontmatter = `---
6842
- ` + `description: ${JSON.stringify(description)}
6843
- ` + `alwaysApply: false
6844
- ` + `---
6845
- `;
6846
- return `${cursorFrontmatter}
6847
- ${body.replace(/^\s+/, "")}`;
6848
- }
6849
- function loadSkillSource(skillId) {
6850
- const path = join3(SKILLS_SOURCE_DIR, skillId, "SKILL.md");
6851
- if (!existsSync3(path)) {
6852
- throw new Error(`Skill source missing: ${path}`);
6853
- }
6854
- return readFileSync2(path, "utf8");
6855
- }
6856
- function installSkillForAgent(skill, adapter, content) {
6857
- const { dir, filename } = adapter.targetFor(skill.id);
6858
- const dirPath = join3(process.cwd(), dir);
6859
- const filePath = join3(dirPath, filename);
6860
- const relativePath = join3(dir, filename);
6861
- try {
6862
- mkdirSync2(dirPath, { recursive: true });
6863
- if (existsSync3(filePath)) {
6864
- return { agent: adapter.name, relativePath, status: "exists" };
6865
- }
6866
- writeFileSync3(filePath, adapter.transform(content));
6867
- return { agent: adapter.name, relativePath, status: "wrote" };
6868
- } catch (err) {
6869
- return {
6870
- agent: adapter.name,
6871
- relativePath,
6872
- status: "failed",
6873
- error: err instanceof Error ? err.message : String(err)
6874
- };
6875
- }
6876
- }
6877
- async function installRules(detectedAgents, options = {}) {
6878
- const adapters = detectedAgents.map((a) => ADAPTERS[a]).filter((a) => a !== undefined);
6879
- if (adapters.length === 0) {
6880
- if (!options.silent)
6881
- console.log(" No supported coding agents detected");
6882
- return;
6883
- }
6884
- const agentList = adapters.map((a) => a.name).join(", ");
6885
- const skillList = SKILLS.map((s) => `
6886
- • ${s.label}`).join("");
6887
- const shouldInstall = options.yes ? true : await confirm({
6888
- message: `Install Moda skills for ${agentList}?${skillList}
6889
- Continue?`,
6890
- default: true
6891
- });
6892
- if (!shouldInstall) {
6893
- if (!options.silent)
6894
- console.log(" Skipped");
6895
- return;
6896
- }
6897
- let wrote = 0;
6898
- let skipped = 0;
6899
- let failed = 0;
6900
- for (const skill of SKILLS) {
6901
- let content;
6902
- try {
6903
- content = loadSkillSource(skill.id);
6904
- } catch (err) {
6905
- const message = err instanceof Error ? err.message : String(err);
6906
- console.error(` Warning: ${message}`);
6907
- failed += adapters.length;
6908
- continue;
6909
- }
6910
- for (const adapter of adapters) {
6911
- const outcome = installSkillForAgent(skill, adapter, content);
6912
- switch (outcome.status) {
6913
- case "wrote":
6914
- if (!options.silent)
6915
- console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
6916
- wrote++;
6917
- break;
6918
- case "exists":
6919
- if (!options.silent) {
6920
- console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
6921
- }
6922
- skipped++;
6923
- break;
6924
- case "failed":
6925
- if (!options.silent) {
6926
- console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
6927
- }
6928
- failed++;
6929
- break;
6930
- }
6931
- }
6932
- }
6933
- const summary = [
6934
- wrote && `${wrote} written`,
6935
- skipped && `${skipped} already present`,
6936
- failed && `${failed} failed`
6937
- ].filter(Boolean).join(", ");
6938
- if (summary) {
6939
- if (!options.silent)
6940
- console.log(` Done — ${summary}`);
6941
- }
6942
- }
6943
-
6944
- // src/init/mcp.ts
6945
- import { existsSync as existsSync4 } from "node:fs";
6946
- import { join as join4 } from "node:path";
6947
- import { execSync } from "node:child_process";
6948
- function isOnPath(cmd) {
6949
- try {
6950
- execSync(`which ${cmd}`, { stdio: "pipe" });
6951
- return true;
6952
- } catch {
6953
- return false;
6954
- }
6955
- }
6956
- function detectAgents() {
6957
- const cwd = process.cwd();
6958
- const agents = [];
6959
- if (existsSync4(join4(cwd, ".claude")) || isOnPath("claude")) {
6960
- agents.push("Claude Code");
6961
- }
6962
- if (existsSync4(join4(cwd, ".cursor"))) {
6963
- agents.push("Cursor");
6964
- }
6965
- return agents;
6966
- }
6967
-
6968
6804
  // src/skills.ts
6969
6805
  import { execFileSync as execFileSync2 } from "node:child_process";
6970
6806
  import { createHash as createHash2 } from "node:crypto";
6971
6807
  import {
6972
- existsSync as existsSync7,
6808
+ existsSync as existsSync4,
6973
6809
  lstatSync as lstatSync2,
6974
- mkdirSync as mkdirSync5,
6975
- readdirSync as readdirSync2,
6976
- readFileSync as readFileSync5,
6977
- writeFileSync as writeFileSync6
6978
- } from "node:fs";
6979
- import { basename, dirname as dirname6, join as join7, relative as relative3 } from "node:path";
6980
-
6981
- // src/skills-manifest.ts
6982
- import {
6983
- existsSync as existsSync5,
6984
6810
  mkdirSync as mkdirSync3,
6985
6811
  readdirSync,
6986
6812
  readFileSync as readFileSync3,
6987
- rmdirSync,
6988
- unlinkSync,
6989
6813
  writeFileSync as writeFileSync4
6990
6814
  } from "node:fs";
6991
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join5, normalize, relative as relative2, resolve as resolve5 } from "node:path";
6992
- var SKILLS_MANIFEST_PATH = join5(".moda", "skills.yml");
6993
- var SKILL_SOURCE_HARNESS = "moda";
6994
- var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
6995
- function readSkillsManifest(cwd) {
6996
- const manifestPath = join5(cwd, SKILLS_MANIFEST_PATH);
6997
- if (!existsSync5(manifestPath))
6998
- return null;
6999
- let text = "";
7000
- try {
7001
- text = readFileSync3(manifestPath, "utf8");
7002
- } catch {
7003
- return null;
7004
- }
7005
- return {
7006
- tenantId: readYamlScalar(text, "tenant_id"),
7007
- generationRunId: readYamlScalar(text, "generation_run_id"),
7008
- generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
7009
- releaseKey: readYamlScalar(text, "release_key"),
7010
- statusFilter: readYamlScalar(text, "status_filter"),
7011
- releasedAt: readYamlScalar(text, "released_at"),
7012
- managedPaths: readManagedPaths(text),
7013
- records: readSkillRecords(text)
7014
- };
7015
- }
7016
- function manifestHeaderFrom(manifest) {
7017
- return {
7018
- tenantId: manifest?.tenantId ?? "",
7019
- releaseKey: manifest?.releaseKey ?? "",
7020
- statusFilter: manifest?.statusFilter ?? "",
7021
- generationRunId: manifest?.generationRunId ?? "",
7022
- generationVersion: manifest?.generationVersion ?? 0,
7023
- releasedAt: manifest?.releasedAt ?? ""
7024
- };
7025
- }
7026
- function writeSkillsManifest(cwd, header, records) {
7027
- const manifestPath = join5(cwd, SKILLS_MANIFEST_PATH);
7028
- const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
7029
- const lines = [
7030
- "version: 1",
7031
- "source: moda",
7032
- `tenant_id: ${yamlQuote(header.tenantId)}`,
7033
- `release_key: ${yamlQuote(header.releaseKey)}`,
7034
- `status_filter: ${yamlQuote(header.statusFilter)}`,
7035
- `generation_run_id: ${yamlQuote(header.generationRunId)}`,
7036
- `generation_version: ${header.generationVersion}`,
7037
- `released_at: ${yamlQuote(header.releasedAt)}`,
7038
- `installed_at: ${yamlQuote(new Date().toISOString())}`,
7039
- "managed_paths:",
7040
- ...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
7041
- "skills:",
7042
- ...records.flatMap((record) => [
7043
- ` - skill_id: ${yamlQuote(record.skillId)}`,
7044
- ` content_hash: ${yamlQuote(record.contentHash)}`,
7045
- ` source: ${yamlQuote(record.source)}`,
7046
- " paths:",
7047
- ...record.paths.map((path) => ` - ${yamlQuote(path)}`)
7048
- ]),
7049
- ""
7050
- ];
7051
- mkdirSync3(dirname4(manifestPath), { recursive: true });
7052
- writeFileSync4(manifestPath, lines.join(`
7053
- `));
7054
- }
7055
- function upsertSkillManifestRecord(cwd, record) {
7056
- const manifest = readSkillsManifest(cwd);
7057
- const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
7058
- records.push(record);
7059
- writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
7060
- }
7061
- function pruneManagedSkillPaths(cwd, managedPaths, log) {
7062
- let pruned = 0;
7063
- for (const rawPath of [...new Set(managedPaths)]) {
7064
- const safePath = safeManagedRelativePath(rawPath);
7065
- if (!safePath)
7066
- continue;
7067
- const absolutePath = absoluteManagedPath(cwd, safePath);
7068
- if (!absolutePath || !existsSync5(absolutePath))
7069
- continue;
7070
- try {
7071
- unlinkSync(absolutePath);
7072
- pruned++;
7073
- log(` pruned ${safePath}`);
7074
- pruneEmptyClaudeSkillDir(cwd, safePath);
7075
- } catch {}
7076
- }
7077
- return pruned;
7078
- }
7079
- function pruneEmptyClaudeSkillDir(cwd, relativePath) {
7080
- if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
7081
- return;
7082
- const dir = dirname4(join5(cwd, relativePath));
7083
- try {
7084
- if (existsSync5(dir) && readdirSync(dir).length === 0) {
7085
- rmdirSync(dir);
7086
- }
7087
- } catch {}
7088
- }
7089
- function absoluteManagedPath(cwd, relativePath) {
7090
- const abs = resolve5(cwd, relativePath);
7091
- const root = resolve5(cwd);
7092
- const rel = relative2(root, abs);
7093
- if (rel.startsWith("..") || isAbsolute4(rel))
7094
- return null;
7095
- return abs;
7096
- }
7097
- function safeManagedRelativePath(rawPath) {
7098
- if (!rawPath || rawPath.includes("\x00") || isAbsolute4(rawPath))
7099
- return null;
7100
- const normalized = normalize(rawPath).replace(/\\/g, "/");
7101
- if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
7102
- return null;
7103
- const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
7104
- const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
7105
- return isClaudeSkill || isCursorRule ? normalized : null;
7106
- }
7107
- function readYamlScalar(text, key) {
7108
- const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
7109
- return match ? parseYamlScalar(match[1]) : "";
7110
- }
7111
- function readSkillRecords(text) {
7112
- const records = [];
7113
- let inSkills = false;
7114
- let current = null;
7115
- let inPaths = false;
7116
- for (const line of text.split(/\r?\n/)) {
7117
- if (line.trim() === "skills:") {
7118
- inSkills = true;
7119
- continue;
7120
- }
7121
- if (!inSkills)
7122
- continue;
7123
- if (line.length > 0 && !line.startsWith(" "))
7124
- break;
7125
- const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
7126
- if (skillIdMatch) {
7127
- if (current)
7128
- records.push(current);
7129
- current = {
7130
- skillId: parseYamlScalar(skillIdMatch[1]),
7131
- contentHash: "",
7132
- source: SKILL_SOURCE_HARNESS,
7133
- paths: []
7134
- };
7135
- inPaths = false;
7136
- continue;
7137
- }
7138
- if (!current)
7139
- continue;
7140
- const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
7141
- if (hashMatch) {
7142
- current.contentHash = parseYamlScalar(hashMatch[1]);
7143
- continue;
7144
- }
7145
- const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
7146
- if (sourceMatch) {
7147
- current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
7148
- continue;
7149
- }
7150
- if (/^ {4}paths:\s*$/.test(line)) {
7151
- inPaths = true;
7152
- continue;
7153
- }
7154
- const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
7155
- if (pathMatch) {
7156
- const value = parseYamlScalar(pathMatch[1]);
7157
- if (value)
7158
- current.paths.push(value);
7159
- }
7160
- }
7161
- if (current)
7162
- records.push(current);
7163
- return records;
7164
- }
7165
- function readManagedPaths(text) {
7166
- const paths = [];
7167
- let inManagedPaths = false;
7168
- for (const line of text.split(/\r?\n/)) {
7169
- if (line.trim() === "managed_paths:") {
7170
- inManagedPaths = true;
7171
- continue;
7172
- }
7173
- if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
7174
- break;
7175
- if (!inManagedPaths)
7176
- continue;
7177
- const match = line.match(/^\s*-\s*(.*?)\s*$/);
7178
- if (match)
7179
- paths.push(parseYamlScalar(match[1]));
7180
- }
7181
- return paths.filter(Boolean);
7182
- }
7183
- function parseYamlScalar(raw) {
7184
- const value = raw.trim();
7185
- if (!value)
7186
- return "";
7187
- if (value.startsWith('"')) {
7188
- try {
7189
- return String(JSON.parse(value));
7190
- } catch {
7191
- return value.replace(/^"|"$/g, "");
7192
- }
7193
- }
7194
- return value.replace(/^'|'$/g, "");
7195
- }
7196
- function yamlQuote(value) {
7197
- return JSON.stringify(value);
7198
- }
6815
+ import { basename, dirname as dirname4, join as join3, relative as relative2 } from "node:path";
7199
6816
 
7200
6817
  // src/agents-md.ts
7201
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
7202
- import { dirname as dirname5, join as join6 } from "node:path";
6818
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
6819
+ import { dirname as dirname3, join as join2 } from "node:path";
7203
6820
  var AGENTS_MD_FILENAME = "AGENTS.md";
7204
6821
  var FIXES_BLOCK_BEGIN = "<!-- moda:fixes:begin -->";
7205
6822
  var FIXES_BLOCK_END = "<!-- moda:fixes:end -->";
@@ -7245,26 +6862,26 @@ function upsertManagedBlock(existing, block) {
7245
6862
  }
7246
6863
  function writeFileIfChanged(filePath, content) {
7247
6864
  try {
7248
- mkdirSync4(dirname5(filePath), { recursive: true });
7249
- if (existsSync6(filePath)) {
7250
- const current = readFileSync4(filePath, "utf8");
6865
+ mkdirSync2(dirname3(filePath), { recursive: true });
6866
+ if (existsSync3(filePath)) {
6867
+ const current = readFileSync2(filePath, "utf8");
7251
6868
  if (current === content)
7252
6869
  return "unchanged";
7253
- writeFileSync5(filePath, content);
6870
+ writeFileSync3(filePath, content);
7254
6871
  return "updated";
7255
6872
  }
7256
- writeFileSync5(filePath, content);
6873
+ writeFileSync3(filePath, content);
7257
6874
  return "wrote";
7258
6875
  } catch {
7259
6876
  return "failed";
7260
6877
  }
7261
6878
  }
7262
6879
  function upsertFixesAgentsBlock(cwd) {
7263
- const filePath = join6(cwd, AGENTS_MD_FILENAME);
6880
+ const filePath = join2(cwd, AGENTS_MD_FILENAME);
7264
6881
  let existing = null;
7265
- if (existsSync6(filePath)) {
6882
+ if (existsSync3(filePath)) {
7266
6883
  try {
7267
- existing = readFileSync4(filePath, "utf8");
6884
+ existing = readFileSync2(filePath, "utf8");
7268
6885
  } catch {
7269
6886
  return { path: AGENTS_MD_FILENAME, status: "failed" };
7270
6887
  }
@@ -7276,7 +6893,7 @@ function upsertFixesAgentsBlock(cwd) {
7276
6893
  // src/skills.ts
7277
6894
  var SKILLS_REQUEST_TIMEOUT_MS = 15000;
7278
6895
  var VALID_STATUSES = new Set(["approved", "proposed", "all"]);
7279
- var SKILL_PROPOSALS_MANIFEST_PATH = join7(".moda", "skill-proposals.yml");
6896
+ var SKILL_PROPOSALS_MANIFEST_PATH = join3(".moda", "skill-proposals.yml");
7280
6897
  async function fetchSkills(status) {
7281
6898
  const apiKey = resolveApiKey();
7282
6899
  if (!apiKey) {
@@ -7401,15 +7018,15 @@ function formatRunStatusHuman(run, events) {
7401
7018
  }
7402
7019
  function writeIfChanged(filePath, content) {
7403
7020
  try {
7404
- mkdirSync5(dirname6(filePath), { recursive: true });
7405
- if (existsSync7(filePath)) {
7406
- const current = readFileSync5(filePath, "utf8");
7021
+ mkdirSync3(dirname4(filePath), { recursive: true });
7022
+ if (existsSync4(filePath)) {
7023
+ const current = readFileSync3(filePath, "utf8");
7407
7024
  if (current === content)
7408
7025
  return "unchanged";
7409
- writeFileSync6(filePath, content);
7026
+ writeFileSync4(filePath, content);
7410
7027
  return "updated";
7411
7028
  }
7412
- writeFileSync6(filePath, content);
7029
+ writeFileSync4(filePath, content);
7413
7030
  return "wrote";
7414
7031
  } catch {
7415
7032
  return "failed";
@@ -7446,7 +7063,7 @@ async function pullSkills(options = {}) {
7446
7063
  }
7447
7064
  }
7448
7065
  const agents = detectAgents();
7449
- const writeCursor = agents.includes("Cursor") || existsSync7(join7(cwd, ".cursor"));
7066
+ const writeCursor = agents.includes("Cursor") || existsSync4(join3(cwd, ".cursor"));
7450
7067
  const records = [];
7451
7068
  for (const skill of skills) {
7452
7069
  if (!skill.skill_id || !skill.skill_md) {
@@ -7459,7 +7076,7 @@ async function pullSkills(options = {}) {
7459
7076
  log(` skipped invalid skill_id: ${JSON.stringify(skill.skill_id).slice(0, 80)}`);
7460
7077
  continue;
7461
7078
  }
7462
- const claudePath = join7(cwd, ".claude", "skills", safeId, "SKILL.md");
7079
+ const claudePath = join3(cwd, ".claude", "skills", safeId, "SKILL.md");
7463
7080
  const claudeStatus = writeIfChanged(claudePath, skill.skill_md);
7464
7081
  const claudeRelativePath = `.claude/skills/${safeId}/SKILL.md`;
7465
7082
  recordOutcome(summary, claudeStatus, log, claudeRelativePath);
@@ -7467,7 +7084,7 @@ async function pullSkills(options = {}) {
7467
7084
  if (claudeStatus !== "failed")
7468
7085
  paths.push(claudeRelativePath);
7469
7086
  if (writeCursor) {
7470
- const cursorPath = join7(cwd, ".cursor", "rules", `${safeId}.mdc`);
7087
+ const cursorPath = join3(cwd, ".cursor", "rules", `${safeId}.mdc`);
7471
7088
  const cursorStatus = writeIfChanged(cursorPath, claudeSkillToCursorRule(skill.skill_md));
7472
7089
  const cursorRelativePath = `.cursor/rules/${safeId}.mdc`;
7473
7090
  recordOutcome(summary, cursorStatus, log, cursorRelativePath);
@@ -7505,7 +7122,7 @@ function recordOutcome(summary, status, log, relativePath) {
7505
7122
  break;
7506
7123
  }
7507
7124
  }
7508
- var SKILLS_DIR = join7(".claude", "skills");
7125
+ var SKILLS_DIR = join3(".claude", "skills");
7509
7126
  var SKILL_FILE = "SKILL.md";
7510
7127
  var MAX_SKILL_WALK_DEPTH = 4;
7511
7128
  async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
@@ -7536,21 +7153,21 @@ async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
7536
7153
  return { synced: response.synced ?? [], dryRun, ...agentsMd ? { agentsMd } : {} };
7537
7154
  }
7538
7155
  function discoverLocalSkills(cwd) {
7539
- const skillsRoot = join7(cwd, SKILLS_DIR);
7540
- if (!existsSync7(skillsRoot))
7156
+ const skillsRoot = join3(cwd, SKILLS_DIR);
7157
+ if (!existsSync4(skillsRoot))
7541
7158
  return [];
7542
7159
  const managed = new Set(managedSkillPaths(cwd));
7543
7160
  const skills = [];
7544
7161
  for (const absPath of walkSkillFiles(skillsRoot, 0)) {
7545
- const sourcePath = relative3(cwd, absPath).replace(/\\/g, "/");
7162
+ const sourcePath = relative2(cwd, absPath).replace(/\\/g, "/");
7546
7163
  if (managed.has(sourcePath))
7547
7164
  continue;
7548
- const key = basename(dirname6(absPath));
7165
+ const key = basename(dirname4(absPath));
7549
7166
  if (key === "moda-cli" || !/^[A-Za-z0-9_-]+$/.test(key))
7550
7167
  continue;
7551
7168
  let content;
7552
7169
  try {
7553
- content = readFileSync5(absPath, "utf8");
7170
+ content = readFileSync3(absPath, "utf8");
7554
7171
  } catch {
7555
7172
  continue;
7556
7173
  }
@@ -7579,13 +7196,13 @@ function walkSkillFiles(dir, depth) {
7579
7196
  return [];
7580
7197
  let entries;
7581
7198
  try {
7582
- entries = readdirSync2(dir);
7199
+ entries = readdirSync(dir);
7583
7200
  } catch {
7584
7201
  return [];
7585
7202
  }
7586
7203
  const files = [];
7587
7204
  for (const entry of entries) {
7588
- const fullPath = join7(dir, entry);
7205
+ const fullPath = join3(dir, entry);
7589
7206
  let stat;
7590
7207
  try {
7591
7208
  stat = lstatSync2(fullPath);
@@ -7659,7 +7276,7 @@ async function applySkillProposal(proposalId, flags, profileOptions = {}, option
7659
7276
  }
7660
7277
  const safeId = safeSkillId(proposal.skillId);
7661
7278
  const relativePath = `.claude/skills/${safeId}/SKILL.md`;
7662
- const writeStatus = writeIfChanged(join7(cwd, relativePath), proposal.skillMd);
7279
+ const writeStatus = writeIfChanged(join3(cwd, relativePath), proposal.skillMd);
7663
7280
  if (writeStatus === "failed") {
7664
7281
  throw new Error(`Failed to write ${relativePath}`);
7665
7282
  }
@@ -7692,9 +7309,9 @@ function safeSkillId(skillId) {
7692
7309
  return safeId;
7693
7310
  }
7694
7311
  function writeSkillProposalManifest(cwd, proposal, relativePath) {
7695
- const manifestPath = join7(cwd, SKILL_PROPOSALS_MANIFEST_PATH);
7696
- mkdirSync5(dirname6(manifestPath), { recursive: true });
7697
- const existing = existsSync7(manifestPath) ? readFileSync5(manifestPath, "utf8") : `applied_proposals:
7312
+ const manifestPath = join3(cwd, SKILL_PROPOSALS_MANIFEST_PATH);
7313
+ mkdirSync3(dirname4(manifestPath), { recursive: true });
7314
+ const existing = existsSync4(manifestPath) ? readFileSync3(manifestPath, "utf8") : `applied_proposals:
7698
7315
  `;
7699
7316
  const entry = [
7700
7317
  ` - proposal_id: "${escapeYamlString(proposal.proposalId)}"`,
@@ -7713,7 +7330,7 @@ function writeSkillProposalManifest(cwd, proposal, relativePath) {
7713
7330
  const prefix = existing.endsWith(`
7714
7331
  `) ? existing : `${existing}
7715
7332
  `;
7716
- writeFileSync6(manifestPath, `${prefix}${entry}
7333
+ writeFileSync4(manifestPath, `${prefix}${entry}
7717
7334
  `);
7718
7335
  }
7719
7336
  function escapeYamlString(value) {
@@ -7825,8 +7442,11 @@ async function runSkillsCommand(input) {
7825
7442
  });
7826
7443
  return 0;
7827
7444
  }
7445
+ if (sub === "install") {
7446
+ return runSkillsInstall(context, positionals.slice(1));
7447
+ }
7828
7448
  if (sub !== "pull") {
7829
- throw new CliInputError(sub ? `Unknown skills subcommand '${sub}'` : "A skills subcommand is required", "Usage: moda skills gen [--source=all|sdk] [--max-sessions=N] [--start-at=ISO] [--end-at=ISO] [--replay] [--replay-set-id=ID] [--reprocess-segments] [--force-reprocess] [--wait] | moda skills status [run-id] | moda skills proposals list [--status=ready_for_pr] | moda skills proposal apply <proposal-id> | moda skills pull [--status=approved|proposed|all] | moda skills sync [--dry-run]");
7449
+ throw new CliInputError(sub ? `Unknown skills subcommand '${sub}'` : "A skills subcommand is required", "Usage: moda skills gen [--source=all|sdk] [--max-sessions=N] [--start-at=ISO] [--end-at=ISO] [--replay] [--replay-set-id=ID] [--reprocess-segments] [--force-reprocess] [--wait] | moda skills status [run-id] | moda skills proposals list [--status=ready_for_pr] | moda skills proposal apply <proposal-id> | moda skills pull [--status=approved|proposed|all] | moda skills sync [--dry-run] | moda skills install [--list] <id...>");
7830
7450
  }
7831
7451
  const status = (flags.status ?? "approved").toLowerCase();
7832
7452
  if (!VALID_STATUSES.has(status)) {
@@ -7949,11 +7569,59 @@ function manifestHeaderFor(fetched) {
7949
7569
  function sha2562(input) {
7950
7570
  return createHash2("sha256").update(input).digest("hex");
7951
7571
  }
7572
+ async function runSkillsInstall(context, ids) {
7573
+ const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-mdmhzfsr.js");
7574
+ const index = readBundledIndex();
7575
+ if (context.flags.list === "true" || ids.length === 0) {
7576
+ if (!index) {
7577
+ 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.");
7578
+ }
7579
+ const skills = index.skills.map((skill) => ({
7580
+ id: skill.id,
7581
+ version: skill.version,
7582
+ language: skill.targets.language,
7583
+ summary: skill.summary
7584
+ }));
7585
+ if (context.outputMode !== "human") {
7586
+ context.output.writeData({ skills });
7587
+ } else {
7588
+ for (const skill of skills)
7589
+ console.log(`${skill.id} ${skill.version} ${skill.summary}`);
7590
+ if (ids.length === 0 && context.flags.list !== "true") {
7591
+ console.error("Install one or more with: moda skills install <id...>");
7592
+ }
7593
+ }
7594
+ return 0;
7595
+ }
7596
+ const unknown = ids.filter((id) => !/^[a-z0-9-]+$/.test(id) || readBundledSkillMd(id) === null);
7597
+ if (unknown.length > 0) {
7598
+ 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");
7599
+ }
7600
+ const results = [];
7601
+ for (const id of ids) {
7602
+ try {
7603
+ results.push(installIntegrationSkill(id));
7604
+ } catch (error) {
7605
+ const done = results.map((result) => result.skillId);
7606
+ throw new CliInputError(error instanceof Error ? error.message : String(error), done.length > 0 ? `Already installed before the failure: ${done.join(", ")}. Fix the error and rerun; installed guides are left as they are.` : "List the bundled guides with: moda skills install --list");
7607
+ }
7608
+ }
7609
+ if (context.outputMode !== "human") {
7610
+ context.output.writeData({ installed: results });
7611
+ } else {
7612
+ for (const result of results) {
7613
+ console.log(`${result.status} ${result.skillId}`);
7614
+ for (const path of result.paths)
7615
+ console.log(` ${path}`);
7616
+ }
7617
+ }
7618
+ return 0;
7619
+ }
7952
7620
 
7953
7621
  // src/doctor.ts
7954
7622
  import { createHash as createHash3 } from "node:crypto";
7955
- import { existsSync as existsSync8, readFileSync as readFileSync6, statSync } from "node:fs";
7956
- import { join as join8 } from "node:path";
7623
+ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync } from "node:fs";
7624
+ import { join as join4 } from "node:path";
7957
7625
  async function runDoctorCommand(context) {
7958
7626
  const report = await buildDoctorReport(context, {
7959
7627
  offline: context.flags.offline === "true"
@@ -8076,8 +7744,8 @@ async function buildDoctorReport(context, options) {
8076
7744
  checks.push(urlCheck("base_url_valid", "config", "Base URL is valid", baseUrl));
8077
7745
  checks.push(urlCheck("ingest_url_valid", "config", "Ingest URL is valid", ingestUrl));
8078
7746
  checks.push(localBaseUrlCheck(activeProfile.environment, baseUrl));
8079
- const manifestPath = join8(context.cwd, ".moda", "harness.json");
8080
- const manifestExists = existsSync8(manifestPath);
7747
+ const manifestPath = join4(context.cwd, ".moda", "harness.json");
7748
+ const manifestExists = existsSync5(manifestPath);
8081
7749
  checks.push({
8082
7750
  id: "harness_manifest_exists",
8083
7751
  group: "harness",
@@ -8161,7 +7829,7 @@ function authTokenCheck(context, hasApiKey) {
8161
7829
  };
8162
7830
  }
8163
7831
  function configPermissionCheck(path) {
8164
- if (!existsSync8(path)) {
7832
+ if (!existsSync5(path)) {
8165
7833
  return {
8166
7834
  id: "config_file_permissions",
8167
7835
  group: "config",
@@ -8304,7 +7972,7 @@ function harnessCurrentCheck(cwd, manifestPath, manifestExists, tenantId) {
8304
7972
  tenantId,
8305
7973
  tenantSource: tenantId ? "env_or_config" : undefined
8306
7974
  });
8307
- const saved = JSON.parse(readFileSync6(manifestPath, "utf8"));
7975
+ const saved = JSON.parse(readFileSync4(manifestPath, "utf8"));
8308
7976
  const savedHash = stableGraphHash(saved);
8309
7977
  const currentHash = stableGraphHash(current);
8310
7978
  const matches = savedHash === currentHash;
@@ -8364,7 +8032,7 @@ function harnessSyncStateCheck(cwd) {
8364
8032
  };
8365
8033
  }
8366
8034
  function discoveryCheck(id, group, title, cwd, relativePaths, nextCommand) {
8367
- const found = relativePaths.filter((path) => existsSync8(join8(cwd, path)));
8035
+ const found = relativePaths.filter((path) => existsSync5(join4(cwd, path)));
8368
8036
  return {
8369
8037
  id,
8370
8038
  group,
@@ -8681,4 +8349,4 @@ function isRecord(value) {
8681
8349
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8682
8350
  }
8683
8351
 
8684
- export { DEFAULT_POLL_INTERVAL_MS, DEFAULT_WAIT_TIMEOUT_MS, fetchLatestRun, runPromptsCommand, initPrompts, runPromptSync, packageAssetPath, claudeSkillToCursorRule, installRules, detectAgents, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, upsertSkillManifestRecord, runSkillSync, runSkillsCommand, runDoctorCommand, runStatusCommand, buildDoctorReport };
8352
+ export { DEFAULT_POLL_INTERVAL_MS, DEFAULT_WAIT_TIMEOUT_MS, fetchLatestRun, runPromptsCommand, initPrompts, runPromptSync, runSkillSync, runSkillsCommand, runDoctorCommand, runStatusCommand, buildDoctorReport };