@moda-ai/cli 1.36.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.
package/README.md CHANGED
@@ -66,6 +66,8 @@ moda problem <problem_id> --evidence # ...attribution evidence (ke
66
66
  moda problem <problem_id> --reports # ...investigation reports
67
67
  moda problem <problem_id> --traces # ...affected traces
68
68
  moda problem-feedback <problem_id> --action=mark_fixed # Close the loop from the terminal
69
+ moda problem-linear <problem_id> # The Problem's Linear issue (key, state, URL)
70
+ moda problem-linear-file <problem_id> # File it in Linear (needs Integrations -> Linear)
69
71
  ```
70
72
 
71
73
  Signals:
@@ -7,9 +7,6 @@ import {
7
7
  MAX_SCANNED_FILE_BYTES,
8
8
  SECRET_KEYS,
9
9
  SKIPPED_DIRS,
10
- __commonJS,
11
- __require,
12
- __toESM,
13
10
  createCommandContext,
14
11
  embeddedTenantIdFromApiKey,
15
12
  getActiveProfile,
@@ -27,7 +24,12 @@ import {
27
24
  resolveTenantId,
28
25
  shouldInventoryFile,
29
26
  stringOption
30
- } from "./cli-drabjmaj.js";
27
+ } from "./cli-q79sq80a.js";
28
+ import {
29
+ __commonJS,
30
+ __require,
31
+ __toESM
32
+ } from "./cli-0v6na3yp.js";
31
33
 
32
34
  // ../../node_modules/.bun/yaml@2.9.1/node_modules/yaml/dist/nodes/identity.js
33
35
  var require_identity = __commonJS((exports) => {
@@ -180242,6 +180244,11 @@ function remoteAnalyzeHardFailure(final, runId) {
180242
180244
  ].join(`
180243
180245
  `));
180244
180246
  }
180247
+ function isRecoverableRemoteRun(final) {
180248
+ if (!final.report)
180249
+ return false;
180250
+ return final.status === "completed" || final.status === "failed" && serverFailedReportValidation(final);
180251
+ }
180245
180252
  function serverFailedReportValidation(final) {
180246
180253
  const { validation } = final;
180247
180254
  return typeof validation === "object" && validation !== null && !Array.isArray(validation) && validation.status === "fail";
@@ -183841,6 +183848,14 @@ function renderInitComplete(options) {
183841
183848
  lines.push("");
183842
183849
  if (options.harness?.state === "pending") {
183843
183850
  lines.push(ui.wrap(` ${ui.status("run")} Harness analysis is still running on Moda. Run ${ui.command("moda harness pull --sync")} in a few minutes.`, 4));
183851
+ } else if (options.harness?.state === "skipped") {
183852
+ const what = {
183853
+ unavailable: "Harness analysis did not start and was skipped.",
183854
+ lost: "Lost track of the harness analysis; it may still be running on Moda.",
183855
+ unfetched: "Harness analysis finished on Moda but could not be fetched.",
183856
+ failed: "Harness analysis failed on Moda and was skipped."
183857
+ }[options.harness.reason ?? "unavailable"];
183858
+ lines.push(ui.wrap(` ${ui.status("warning")} ${what} Run ${ui.command(options.harness.next ?? "moda harness pull --sync")} later.`, 4));
183844
183859
  } else if (options.harness) {
183845
183860
  lines.push(ui.wrap(` ${ui.status("pass")} Harness mapped: ${options.harness.agents ?? 0} agent(s), ${options.harness.artifacts ?? 0} artifact(s). View: ${ui.command(dashboardUrl)}`, 4));
183846
183861
  }
@@ -184284,7 +184299,7 @@ async function runHarnessCommand(context) {
184284
184299
  throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
184285
184300
  }
184286
184301
  if (context.flags["github-actions"] === "true") {
184287
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-tk17mnge.js");
184302
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-qz5d22rz.js");
184288
184303
  const result = await runGithubActionsAnalyze(rootDir, {
184289
184304
  writeReport: (report2) => {
184290
184305
  const normalized = normalizeHarnessReport(report2);
@@ -188294,4 +188309,4 @@ function hashText(text) {
188294
188309
  return createHash4("sha256").update(text).digest("hex");
188295
188310
  }
188296
188311
 
188297
- export { ApiError, callDataAPI, callControlAPI, getApiRequestCount, resetApiRequestCount, $parseDocument, $stringify, require_typescript, inspectCodeSelectors, inspectPythonSelectors, resolvePromptSource, shouldColorTerminalOutput, terminalStyles, stripAnsi2 as stripAnsi, renderMarkdownForTerminal, HARNESS_REPORT_PATH, HARNESS_REPORT_APPROVAL_PATH, HARNESS_ANALYZE_CONCERNS, validateHarnessReport, readHarnessReport, hashHarnessReport, hashStableJson, startRemoteAnalyze, pollRemoteAnalyzeUntilDone, checkPromptMapping, safePromptEvidencePath, discoverHarnessPromptSources, renderDoctorHuman, renderStatusHuman, renderInitBanner, renderInitStep, renderInitNote, renderInitSetupPlan, renderInitComplete, runHarnessCommand, writeHarnessAnalyzeResult, emitWrittenArtifacts, analyzeHarnessPrompts, repairExternalAnalystReport, deriveRelationshipsFromGraph, mergeDeterministicArtifacts, describeCodingAgentEvent, analystEventProgress, isRetryableExternalAnalystFailure, sleep, summarizeInvalidAnalystReport, isDeterministicLocalAnalyst, resolveHarnessAnalyzeConcern, detectAvailableCodingAgents, codingAgentDisplayName, runCodingAgent, resolveAnalystModel, renderExternalHarnessAnalystPrompt, CONCERN_CANDIDATE_KINDS, SEED_CANDIDATE_KIND_LABELS, renderAnalystCandidateLines, renderAnalystSeedLines, truncateText, scanHarness, summarizeHarness, buildSourceSnapshot, writeHumanProgress, readHarnessSyncState, describeSyncIdentity, describeSyncTenantLabel, findFresherHarnessReports, writeHarnessArtifacts, assertHarnessGraphSyncQuality, graphForSync, inventoryFiles, numericEnv, numericEnvAllowZero, computeDeterministicFloor, stableId2 as stableId, titleFromId2 as titleFromId, promptNameFromPath };
188312
+ export { ApiError, callDataAPI, callControlAPI, getApiRequestCount, resetApiRequestCount, $parseDocument, $stringify, require_typescript, inspectCodeSelectors, inspectPythonSelectors, resolvePromptSource, shouldColorTerminalOutput, terminalStyles, stripAnsi2 as stripAnsi, renderMarkdownForTerminal, HARNESS_REPORT_PATH, HARNESS_REPORT_APPROVAL_PATH, HARNESS_ANALYZE_CONCERNS, validateHarnessReport, readHarnessReport, hashHarnessReport, hashStableJson, startRemoteAnalyze, pollRemoteAnalyzeUntilDone, isRecoverableRemoteRun, checkPromptMapping, safePromptEvidencePath, discoverHarnessPromptSources, renderDoctorHuman, renderStatusHuman, renderInitBanner, renderInitStep, renderInitNote, renderInitSetupPlan, renderInitComplete, runHarnessCommand, writeHarnessAnalyzeResult, emitWrittenArtifacts, analyzeHarnessPrompts, repairExternalAnalystReport, deriveRelationshipsFromGraph, mergeDeterministicArtifacts, describeCodingAgentEvent, analystEventProgress, isRetryableExternalAnalystFailure, sleep, summarizeInvalidAnalystReport, isDeterministicLocalAnalyst, resolveHarnessAnalyzeConcern, detectAvailableCodingAgents, codingAgentDisplayName, runCodingAgent, resolveAnalystModel, renderExternalHarnessAnalystPrompt, CONCERN_CANDIDATE_KINDS, SEED_CANDIDATE_KIND_LABELS, renderAnalystCandidateLines, renderAnalystSeedLines, truncateText, scanHarness, summarizeHarness, buildSourceSnapshot, writeHumanProgress, readHarnessSyncState, describeSyncIdentity, describeSyncTenantLabel, findFresherHarnessReports, writeHarnessArtifacts, assertHarnessGraphSyncQuality, graphForSync, inventoryFiles, numericEnv, numericEnvAllowZero, computeDeterministicFloor, stableId2 as stableId, titleFromId2 as titleFromId, promptNameFromPath };
@@ -0,0 +1,35 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
+
35
+ export { __toESM, __commonJS, __require };
@@ -0,0 +1,128 @@
1
+ import {
2
+ SKILL_SOURCE_INTEGRATION_BUNDLED,
3
+ claudeSkillToCursorRule,
4
+ detectAgents,
5
+ packageAssetPath,
6
+ readSkillsManifest,
7
+ upsertSkillManifestRecord
8
+ } from "./cli-ssfq84k5.js";
9
+
10
+ // src/skills-bundled.ts
11
+ import { createHash } from "node:crypto";
12
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
13
+ import { dirname, join, relative } from "node:path";
14
+ var BUNDLED_SKILLS_DIR = packageAssetPath("skills", "integration");
15
+ function bundledSkillsDir() {
16
+ return process.env.MODA_BUNDLED_SKILLS_DIR || BUNDLED_SKILLS_DIR;
17
+ }
18
+ function readBundledIndex() {
19
+ const indexPath = join(bundledSkillsDir(), "index.json");
20
+ if (!existsSync(indexPath))
21
+ return null;
22
+ try {
23
+ const parsed = JSON.parse(readFileSync(indexPath, "utf8"));
24
+ if (!Array.isArray(parsed.skills))
25
+ return null;
26
+ return parsed;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function readBundledSkillMd(skillId) {
32
+ const path = join(bundledSkillsDir(), skillId, "SKILL.md");
33
+ if (!existsSync(path))
34
+ return null;
35
+ try {
36
+ return readFileSync(path, "utf8");
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+ function installIntegrationSkill(skillId, cwd = process.cwd()) {
42
+ if (!/^[a-z0-9-]+$/.test(skillId)) {
43
+ throw new Error(`Invalid integration skill id: ${JSON.stringify(skillId).slice(0, 80)}`);
44
+ }
45
+ const sourceDir = join(bundledSkillsDir(), skillId);
46
+ if (!existsSync(join(sourceDir, "SKILL.md"))) {
47
+ const bundled = readBundledIndex()?.skills.map((skill) => skill.id) ?? [];
48
+ throw new Error(`Integration skill '${skillId}' is not bundled with this CLI.${bundled.length > 0 ? ` Bundled skills: ${bundled.join(", ")}` : ""}`);
49
+ }
50
+ const files = listFilesRecursive(sourceDir).sort();
51
+ const plannedWrites = [];
52
+ for (const file of files) {
53
+ const relPath = relative(sourceDir, file).replace(/\\/g, "/");
54
+ plannedWrites.push({
55
+ targetRelPath: `.claude/skills/${skillId}/${relPath}`,
56
+ targetPath: join(cwd, ".claude", "skills", skillId, relPath),
57
+ content: readFileSync(file, "utf8")
58
+ });
59
+ }
60
+ const writeCursor = detectAgents().includes("Cursor") || existsSync(join(cwd, ".cursor"));
61
+ if (writeCursor) {
62
+ const skillMd = readFileSync(join(sourceDir, "SKILL.md"), "utf8");
63
+ plannedWrites.push({
64
+ targetRelPath: `.cursor/rules/${skillId}.mdc`,
65
+ targetPath: join(cwd, ".cursor", "rules", `${skillId}.mdc`),
66
+ content: claudeSkillToCursorRule(skillMd)
67
+ });
68
+ }
69
+ const ownedPaths = new Set((readSkillsManifest(cwd)?.records ?? []).flatMap((record) => record.paths));
70
+ const collisions = plannedWrites.filter((write) => existsSync(write.targetPath) && !ownedPaths.has(write.targetRelPath));
71
+ if (collisions.length > 0) {
72
+ throw new Error(`Refusing to overwrite ${collisions.length} existing file(s) not managed by Moda: ` + `${collisions.map((write) => write.targetRelPath).join(", ")}. ` + "Move or remove them, then re-run to install the bundled integration skill.");
73
+ }
74
+ const paths = [];
75
+ let wroteAny = false;
76
+ for (const write of plannedWrites) {
77
+ if (writeIfChanged(write.targetPath, write.content))
78
+ wroteAny = true;
79
+ paths.push(write.targetRelPath);
80
+ }
81
+ upsertSkillManifestRecord(cwd, {
82
+ skillId,
83
+ contentHash: hashSkillDir(sourceDir, files),
84
+ source: SKILL_SOURCE_INTEGRATION_BUNDLED,
85
+ paths
86
+ });
87
+ return { skillId, status: wroteAny ? "installed" : "unchanged", paths };
88
+ }
89
+ function writeIfChanged(path, content) {
90
+ if (existsSync(path)) {
91
+ try {
92
+ if (readFileSync(path, "utf8") === content)
93
+ return false;
94
+ } catch {}
95
+ }
96
+ mkdirSync(dirname(path), { recursive: true });
97
+ writeFileSync(path, content);
98
+ return true;
99
+ }
100
+ function listFilesRecursive(dir) {
101
+ const files = [];
102
+ const stack = [dir];
103
+ while (stack.length > 0) {
104
+ const current = stack.pop();
105
+ if (!current)
106
+ continue;
107
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
108
+ const path = join(current, entry.name);
109
+ if (entry.isDirectory())
110
+ stack.push(path);
111
+ else if (entry.isFile())
112
+ files.push(path);
113
+ }
114
+ }
115
+ return files;
116
+ }
117
+ function hashSkillDir(sourceDir, files) {
118
+ const hash = createHash("sha256");
119
+ for (const file of files) {
120
+ hash.update(relative(sourceDir, file).replace(/\\/g, "/"));
121
+ hash.update("\x00");
122
+ hash.update(readFileSync(file));
123
+ hash.update("\x00");
124
+ }
125
+ return hash.digest("hex");
126
+ }
127
+
128
+ export { bundledSkillsDir, readBundledIndex, readBundledSkillMd, installIntegrationSkill };