@moda-ai/cli 1.55.18 → 1.55.20

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 (47) hide show
  1. package/dist/{agent-cha3kg9d.js → agent-mj622k8s.js} +9 -9
  2. package/dist/{cli-1exddmc0.js → cli-033n2mpb.js} +2 -2
  3. package/dist/{cli-c5c6f0yw.js → cli-38p87ykc.js} +1 -1
  4. package/dist/{cli-av3dqgj5.js → cli-4j8nffsm.js} +10 -6
  5. package/dist/{cli-5hq827jt.js → cli-569hj0bj.js} +1 -1
  6. package/dist/{cli-3ezzeqcy.js → cli-5ke3xe6y.js} +47 -3
  7. package/dist/{cli-eazeanvt.js → cli-7ea02wca.js} +1 -1
  8. package/dist/{cli-qmjnh4z9.js → cli-9x9adrtj.js} +1 -1
  9. package/dist/{cli-a1mv0ypm.js → cli-a5c9te95.js} +5 -5
  10. package/dist/cli-cehskjde.js +1302 -0
  11. package/dist/{cli-5q1gyvxn.js → cli-pc5cw7mx.js} +5 -5
  12. package/dist/{cli-c8p69n55.js → cli-rqk6f6n6.js} +2 -2
  13. package/dist/{cli-ssfq84k5.js → cli-sf0g0fjg.js} +133 -18
  14. package/dist/{cli-0e8bfvn7.js → cli-wf7gfa1e.js} +8 -8
  15. package/dist/cli.js +331 -930
  16. package/dist/{doctor-kg0yq1wf.js → doctor-mx6jf435.js} +9 -9
  17. package/dist/{fix-q8er8egx.js → fix-9fmy6p2x.js} +7 -7
  18. package/dist/{harness-github-actions-ver6cprq.js → harness-github-actions-c826bfxs.js} +6 -6
  19. package/dist/{harness-v9s0k9s7.js → harness-xvg6ws6x.js} +6 -6
  20. package/dist/{index-7zqa7t5c.js → index-ewqbpnwz.js} +13 -13
  21. package/dist/{production-intelligence-k36jsvqx.js → production-intelligence-dbe9dmc5.js} +3 -3
  22. package/dist/{profile-commands-88sdwzbg.js → profile-commands-kmmd9y86.js} +3 -3
  23. package/dist/prompt-source-mcp.js +6 -6
  24. package/dist/{prompts-2r756vtd.js → prompts-1tkhrxkq.js} +7 -7
  25. package/dist/{provision-n3r7yway.js → provision-wzttap58.js} +4 -4
  26. package/dist/{registry-5v63ga01.js → registry-atqyb5fa.js} +4 -4
  27. package/dist/skills-br82cpt7.js +19 -0
  28. package/dist/{skills-bundled-mdmhzfsr.js → skills-bundled-r9a2vs07.js} +2 -2
  29. package/dist/{sync-r4tncne4.js → sync-25vn78je.js} +9 -9
  30. package/package.json +1 -1
  31. package/skills/integration/index.json +2 -2
  32. package/skills/moda-cli/SKILL.md +114 -1434
  33. package/skills/moda-cli/references/auth.md +176 -0
  34. package/skills/moda-cli/references/commands.md +74 -0
  35. package/skills/moda-cli/references/dashboard-parity.md +29 -0
  36. package/skills/moda-cli/references/detections.md +79 -0
  37. package/skills/moda-cli/references/feedback.md +45 -0
  38. package/skills/moda-cli/references/output-protocol.md +260 -0
  39. package/skills/moda-cli/references/production-intelligence.md +96 -0
  40. package/skills/moda-cli/references/prompts.md +104 -0
  41. package/skills/moda-cli/references/recipes.md +74 -0
  42. package/skills/moda-cli/references/replays-and-registry.md +173 -0
  43. package/skills/moda-cli/references/search-and-traces.md +180 -0
  44. package/skills/moda-cli/references/signals-and-false-positives.md +79 -0
  45. package/skills/moda-cli/references/troubleshooting.md +65 -0
  46. package/dist/cli-kg0kz05e.js +0 -496
  47. package/dist/skills-fed17et3.js +0 -19
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  isAuthSessionValid,
3
3
  loadAuthSession
4
- } from "./cli-qmjnh4z9.js";
4
+ } from "./cli-9x9adrtj.js";
5
5
  import {
6
6
  inspectPromptManifest
7
- } from "./cli-0e8bfvn7.js";
7
+ } from "./cli-wf7gfa1e.js";
8
8
  import {
9
9
  readHarnessSyncState,
10
10
  renderDoctorHuman,
11
11
  renderStatusHuman,
12
12
  scanHarness
13
- } from "./cli-a1mv0ypm.js";
13
+ } from "./cli-a5c9te95.js";
14
14
  import {
15
15
  callDataAPI
16
- } from "./cli-c8p69n55.js";
16
+ } from "./cli-rqk6f6n6.js";
17
17
  import {
18
18
  ensureSecretFilePermissions,
19
19
  getActiveProfile,
@@ -27,7 +27,7 @@ import {
27
27
  resolveSecretReference,
28
28
  resolveTenantId,
29
29
  secretFilePathFromRef
30
- } from "./cli-3ezzeqcy.js";
30
+ } from "./cli-5ke3xe6y.js";
31
31
 
32
32
  // src/doctor.ts
33
33
  import { createHash } from "node:crypto";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  apiKeyHeaders
3
- } from "./cli-eazeanvt.js";
3
+ } from "./cli-7ea02wca.js";
4
4
  import {
5
5
  recordApiRequest,
6
6
  resolveApiKey,
7
7
  resolveModaBaseUrl
8
- } from "./cli-3ezzeqcy.js";
8
+ } from "./cli-5ke3xe6y.js";
9
9
 
10
10
  // src/api-client.ts
11
11
  var REQUEST_TIMEOUT_MS = 30000;
@@ -6,8 +6,9 @@ function packageAssetPath(...parts) {
6
6
  }
7
7
 
8
8
  // src/init/rules.ts
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
- import { join as join2 } from "node:path";
9
+ import { createHash } from "node:crypto";
10
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { dirname as dirname2, join as join2 } from "node:path";
11
12
  import { confirm } from "@inquirer/prompts";
12
13
  var SKILLS_SOURCE_DIR = packageAssetPath("skills");
13
14
  var SKILLS = [
@@ -23,6 +24,7 @@ var ADAPTERS = {
23
24
  dir: join2(".claude", "skills", skillId),
24
25
  filename: "SKILL.md"
25
26
  }),
27
+ referencesDirFor: (skillId) => join2(".claude", "skills", skillId, "references"),
26
28
  transform: (content) => content
27
29
  },
28
30
  Cursor: {
@@ -31,7 +33,8 @@ var ADAPTERS = {
31
33
  dir: join2(".cursor", "rules"),
32
34
  filename: `${skillId}.mdc`
33
35
  }),
34
- transform: claudeSkillToCursorRule
36
+ referencesDirFor: (skillId) => join2(".cursor", "rules", `${skillId}-references`),
37
+ transform: (content, referencesDir) => claudeSkillToCursorRule(content).replace(/`references\//g, `\`${referencesDir}/`)
35
38
  }
36
39
  };
37
40
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
@@ -53,24 +56,108 @@ function claudeSkillToCursorRule(skillContent) {
53
56
  ${body.replace(/^\s+/, "")}`;
54
57
  }
55
58
  function loadSkillSource(skillId) {
56
- const path = join2(SKILLS_SOURCE_DIR, skillId, "SKILL.md");
59
+ const dir = join2(SKILLS_SOURCE_DIR, skillId);
60
+ const path = join2(dir, "SKILL.md");
57
61
  if (!existsSync(path)) {
58
62
  throw new Error(`Skill source missing: ${path}`);
59
63
  }
60
- return readFileSync(path, "utf8");
64
+ const references = new Map;
65
+ const referencesDir = join2(dir, "references");
66
+ if (existsSync(referencesDir)) {
67
+ for (const name of readdirSync(referencesDir).sort()) {
68
+ if (name.endsWith(".md"))
69
+ references.set(name, readFileSync(join2(referencesDir, name), "utf8"));
70
+ }
71
+ }
72
+ return { content: readFileSync(path, "utf8"), references };
73
+ }
74
+ var INSTALL_RECORD_PATH = join2(".moda", "agent-skills.json");
75
+ function sha256(text) {
76
+ return createHash("sha256").update(text).digest("hex");
77
+ }
78
+ function readInstallRecord() {
79
+ try {
80
+ const parsed = JSON.parse(readFileSync(join2(process.cwd(), INSTALL_RECORD_PATH), "utf8"));
81
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
82
+ } catch {
83
+ return {};
84
+ }
85
+ }
86
+ function writeInstallRecord(record) {
87
+ const path = join2(process.cwd(), INSTALL_RECORD_PATH);
88
+ mkdirSync(dirname2(path), { recursive: true });
89
+ writeFileSync(path, `${JSON.stringify(record, null, 2)}
90
+ `);
61
91
  }
62
- function installSkillForAgent(skill, adapter, content) {
92
+ function skillVersion(skillContent) {
93
+ const frontmatter = FRONTMATTER_RE.exec(skillContent)?.[1] ?? "";
94
+ return /^version:\s*(\S+)\s*$/m.exec(frontmatter)?.[1] ?? "unknown";
95
+ }
96
+ function looksLikeShippedSkill(existing, skill) {
97
+ return existing.startsWith(`---
98
+ name: ${skill.id}
99
+ `) || /^# Moda CLI$/m.test(existing);
100
+ }
101
+ function unusedBackupPath(relativePath) {
102
+ let candidate = `${relativePath}.bak`;
103
+ for (let n = 1;existsSync(join2(process.cwd(), candidate)); n++) {
104
+ candidate = `${relativePath}.bak.${n}`;
105
+ }
106
+ return candidate;
107
+ }
108
+ function installSkillForAgent(skill, adapter, source, record) {
63
109
  const { dir, filename } = adapter.targetFor(skill.id);
64
110
  const dirPath = join2(process.cwd(), dir);
65
111
  const filePath = join2(dirPath, filename);
66
112
  const relativePath = join2(dir, filename);
113
+ const referencesDir = adapter.referencesDirFor(skill.id);
114
+ const content = adapter.transform(source.content, referencesDir);
67
115
  try {
68
116
  mkdirSync(dirPath, { recursive: true });
117
+ let status = "wrote";
118
+ let backupPath;
69
119
  if (existsSync(filePath)) {
70
- return { agent: adapter.name, relativePath, status: "exists" };
120
+ const existing = readFileSync(filePath, "utf8");
121
+ const recorded = record[relativePath];
122
+ if (existing === content) {
123
+ status = "current";
124
+ } else if (recorded ? recorded.sha256 === sha256(existing) : looksLikeShippedSkill(existing, skill)) {
125
+ if (!recorded) {
126
+ backupPath = unusedBackupPath(relativePath);
127
+ writeFileSync(join2(process.cwd(), backupPath), existing);
128
+ }
129
+ status = "updated";
130
+ } else {
131
+ return { agent: adapter.name, relativePath, status: "exists" };
132
+ }
133
+ }
134
+ if (status !== "current")
135
+ writeFileSync(filePath, content);
136
+ const version = skillVersion(source.content);
137
+ record[relativePath] = { version, sha256: sha256(content) };
138
+ const referencesPath = join2(process.cwd(), referencesDir);
139
+ mkdirSync(referencesPath, { recursive: true });
140
+ const keptReferences = [];
141
+ for (const [name, text] of source.references) {
142
+ const referencePath = join2(referencesPath, name);
143
+ const referenceKey = join2(referencesDir, name);
144
+ const existing = existsSync(referencePath) ? readFileSync(referencePath, "utf8") : undefined;
145
+ if (existing !== text) {
146
+ if (existing !== undefined && record[referenceKey]?.sha256 !== sha256(existing)) {
147
+ keptReferences.push(referenceKey);
148
+ continue;
149
+ }
150
+ writeFileSync(referencePath, text);
151
+ }
152
+ record[referenceKey] = { version, sha256: sha256(text) };
71
153
  }
72
- writeFileSync(filePath, adapter.transform(content));
73
- return { agent: adapter.name, relativePath, status: "wrote" };
154
+ return {
155
+ agent: adapter.name,
156
+ relativePath,
157
+ status,
158
+ ...backupPath ? { backupPath } : {},
159
+ ...keptReferences.length > 0 ? { keptReferences } : {}
160
+ };
74
161
  } catch (err) {
75
162
  return {
76
163
  agent: adapter.name,
@@ -103,10 +190,13 @@ async function installRules(detectedAgents, options = {}) {
103
190
  let wrote = 0;
104
191
  let skipped = 0;
105
192
  let failed = 0;
193
+ let updated = 0;
194
+ const record = readInstallRecord();
195
+ const recordBefore = JSON.stringify(record);
106
196
  for (const skill of SKILLS) {
107
- let content;
197
+ let source;
108
198
  try {
109
- content = loadSkillSource(skill.id);
199
+ source = loadSkillSource(skill.id);
110
200
  } catch (err) {
111
201
  const message = err instanceof Error ? err.message : String(err);
112
202
  console.error(` Warning: ${message}`);
@@ -114,16 +204,31 @@ async function installRules(detectedAgents, options = {}) {
114
204
  continue;
115
205
  }
116
206
  for (const adapter of adapters) {
117
- const outcome = installSkillForAgent(skill, adapter, content);
207
+ const outcome = installSkillForAgent(skill, adapter, source, record);
208
+ if (!options.silent) {
209
+ for (const kept of outcome.keptReferences ?? []) {
210
+ console.log(` ${outcome.agent}: ${kept} was edited locally — skipping (delete it to reinstall)`);
211
+ }
212
+ }
118
213
  switch (outcome.status) {
119
214
  case "wrote":
120
215
  if (!options.silent)
121
216
  console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
122
217
  wrote++;
123
218
  break;
219
+ case "updated":
220
+ if (!options.silent) {
221
+ const backup = outcome.backupPath ? ` (previous copy saved to ${outcome.backupPath})` : "";
222
+ console.log(` ${outcome.agent}: updated ${outcome.relativePath} to the current skill${backup}`);
223
+ }
224
+ updated++;
225
+ break;
226
+ case "current":
227
+ skipped++;
228
+ break;
124
229
  case "exists":
125
230
  if (!options.silent) {
126
- console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
231
+ console.log(` ${outcome.agent}: ${outcome.relativePath} was edited locally — skipping (delete it to reinstall)`);
127
232
  }
128
233
  skipped++;
129
234
  break;
@@ -136,8 +241,18 @@ async function installRules(detectedAgents, options = {}) {
136
241
  }
137
242
  }
138
243
  }
244
+ if (JSON.stringify(record) !== recordBefore) {
245
+ try {
246
+ writeInstallRecord(record);
247
+ } catch (err) {
248
+ if (!options.silent) {
249
+ console.error(` Could not save ${INSTALL_RECORD_PATH} — ${err instanceof Error ? err.message : String(err)}. ` + "A later moda init may overwrite local edits to these skill files.");
250
+ }
251
+ }
252
+ }
139
253
  const summary = [
140
254
  wrote && `${wrote} written`,
255
+ updated && `${updated} updated`,
141
256
  skipped && `${skipped} already present`,
142
257
  failed && `${failed} failed`
143
258
  ].filter(Boolean).join(", ");
@@ -175,13 +290,13 @@ function detectAgents() {
175
290
  import {
176
291
  existsSync as existsSync3,
177
292
  mkdirSync as mkdirSync2,
178
- readdirSync,
293
+ readdirSync as readdirSync2,
179
294
  readFileSync as readFileSync2,
180
295
  rmdirSync,
181
296
  unlinkSync,
182
297
  writeFileSync as writeFileSync2
183
298
  } from "node:fs";
184
- import { dirname as dirname2, isAbsolute, join as join4, normalize, relative, resolve } from "node:path";
299
+ import { dirname as dirname3, isAbsolute, join as join4, normalize, relative, resolve } from "node:path";
185
300
  var SKILLS_MANIFEST_PATH = join4(".moda", "skills.yml");
186
301
  var SKILL_SOURCE_HARNESS = "moda";
187
302
  var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
@@ -241,7 +356,7 @@ function writeSkillsManifest(cwd, header, records) {
241
356
  ]),
242
357
  ""
243
358
  ];
244
- mkdirSync2(dirname2(manifestPath), { recursive: true });
359
+ mkdirSync2(dirname3(manifestPath), { recursive: true });
245
360
  writeFileSync2(manifestPath, lines.join(`
246
361
  `));
247
362
  }
@@ -272,9 +387,9 @@ function pruneManagedSkillPaths(cwd, managedPaths, log) {
272
387
  function pruneEmptyClaudeSkillDir(cwd, relativePath) {
273
388
  if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
274
389
  return;
275
- const dir = dirname2(join4(cwd, relativePath));
390
+ const dir = dirname3(join4(cwd, relativePath));
276
391
  try {
277
- if (existsSync3(dir) && readdirSync(dir).length === 0) {
392
+ if (existsSync3(dir) && readdirSync2(dir).length === 0) {
278
393
  rmdirSync(dir);
279
394
  }
280
395
  } catch {}
@@ -12,28 +12,28 @@ import {
12
12
  require_typescript,
13
13
  resolvePromptSource,
14
14
  safePromptEvidencePath
15
- } from "./cli-a1mv0ypm.js";
15
+ } from "./cli-a5c9te95.js";
16
16
  import {
17
17
  createCommandContext
18
- } from "./cli-5hq827jt.js";
18
+ } from "./cli-569hj0bj.js";
19
19
  import {
20
20
  assertFlags,
21
21
  assertPositionals
22
- } from "./cli-kg0kz05e.js";
22
+ } from "./cli-cehskjde.js";
23
23
  import {
24
24
  callControlAPI,
25
25
  callDataAPI
26
- } from "./cli-c8p69n55.js";
26
+ } from "./cli-rqk6f6n6.js";
27
27
  import {
28
28
  setPlatformTenantOverride
29
- } from "./cli-eazeanvt.js";
29
+ } from "./cli-7ea02wca.js";
30
30
  import {
31
31
  CliInputError,
32
32
  EXIT_ERROR,
33
33
  EXIT_OK,
34
34
  resolveApiTenantId,
35
35
  validateConfig
36
- } from "./cli-3ezzeqcy.js";
36
+ } from "./cli-5ke3xe6y.js";
37
37
  import {
38
38
  __commonJS,
39
39
  __require,
@@ -6731,7 +6731,7 @@ async function runPromptSyncExclusive(flags, profileOptions, options) {
6731
6731
  const analyzedDefinitions = new Map;
6732
6732
  if (flags["no-analyze"] !== "true") {
6733
6733
  options.onProgress?.("Analyzing prompt sources before sync.");
6734
- const { analyzeHarnessPrompts } = await import("./harness-v9s0k9s7.js");
6734
+ const { analyzeHarnessPrompts } = await import("./harness-xvg6ws6x.js");
6735
6735
  const result = await analyzeHarnessPrompts({
6736
6736
  rootDir: process.cwd(),
6737
6737
  existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
@@ -7061,7 +7061,7 @@ async function migratePromptManifest(options = {}) {
7061
7061
  let addedSources = [];
7062
7062
  let pendingSources = [];
7063
7063
  if (options.discover) {
7064
- const { analyzeHarnessPrompts } = await import("./harness-v9s0k9s7.js");
7064
+ const { analyzeHarnessPrompts } = await import("./harness-xvg6ws6x.js");
7065
7065
  const result = await analyzeHarnessPrompts({
7066
7066
  rootDir: root,
7067
7067
  existing: manifest.sources,