@moda-ai/cli 1.28.5 → 1.28.6

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.
@@ -3153,6 +3153,8 @@ async function runHarnessCommand(context) {
3153
3153
  if (!approved && !context.dryRun) {
3154
3154
  throw new CliInputError("Harness report is not approved; refusing to sync.", "Run `moda harness validate-report`, review the report, then run `moda harness approve --yes` before syncing.");
3155
3155
  }
3156
+ if (!context.dryRun)
3157
+ assertHarnessGraphSyncQuality(report.graph, context.flags);
3156
3158
  const syncGraph2 = graphForReport(report.graph);
3157
3159
  const sourceCommit2 = report.source.sourceCommit ?? getGitCommit(rootDir);
3158
3160
  const localGraphHash2 = hashStableJson(syncGraph2);
@@ -3233,6 +3235,8 @@ async function runHarnessCommand(context) {
3233
3235
  tenantSource: tenant?.source,
3234
3236
  pathWasExplicit: context.flags.path !== undefined
3235
3237
  });
3238
+ if (!context.dryRun)
3239
+ assertHarnessGraphSyncQuality(graph, context.flags);
3236
3240
  const syncGraph = graphForSync(graph);
3237
3241
  const sourceCommit = getGitCommit(rootDir);
3238
3242
  const localGraphHash = hashStableJson2(syncGraph);
@@ -3338,7 +3342,7 @@ async function runHarnessCommand(context) {
3338
3342
  elapsed_ms: Date.now() - context.startedAt
3339
3343
  });
3340
3344
  if (context.flags["github-actions"] === "true") {
3341
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-ct8fwzdr.js");
3345
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-wd1ahhxz.js");
3342
3346
  const result = await runGithubActionsAnalyze(rootDir, {
3343
3347
  writeReport: (report2) => {
3344
3348
  const normalized = normalizeHarnessReport(report2);
@@ -3669,7 +3673,7 @@ async function runExternalHarnessAnalyst(options) {
3669
3673
  reportPath: HARNESS_REPORT_PATH
3670
3674
  }));
3671
3675
  }
3672
- const timeoutMs = numericEnv("MODA_HARNESS_ANALYST_TIMEOUT_MS", 1200000);
3676
+ const timeoutMs = numericEnv("MODA_HARNESS_ANALYST_TIMEOUT_MS", 1800000);
3673
3677
  try {
3674
3678
  const result = await runExternalAnalystWithRetries(invocation, {
3675
3679
  cwd: options.rootDir,
@@ -3799,10 +3803,25 @@ function readExternalAnalystReport(rootDir, result, adapter) {
3799
3803
  const parsed = extractHarnessReportFromText(output, rootDir);
3800
3804
  if (parsed)
3801
3805
  return parsed;
3806
+ let debugHint = "";
3807
+ try {
3808
+ const debugPath = resolve4(rootDir, MODA_DIR, "analyst-debug.log");
3809
+ mkdirSync3(dirname4(debugPath), { recursive: true });
3810
+ writeFileSync3(debugPath, [
3811
+ `# analyst=${adapter} status=${result.status} signal=${result.signal} timedOut=${result.timedOut} maxBufferExceeded=${result.maxBufferExceeded}`,
3812
+ "# ----- stdout -----",
3813
+ result.stdout,
3814
+ "# ----- stderr -----",
3815
+ result.stderr
3816
+ ].join(`
3817
+ `));
3818
+ debugHint = `Raw analyst output saved to ${join2(MODA_DIR, "analyst-debug.log")} for debugging.`;
3819
+ } catch {}
3802
3820
  throw new CliInputError(`Harness analyst ${adapter} did not return a harness report.`, [
3803
3821
  `Expected ${HARNESS_REPORT_PATH} or final JSON between ${REPORT_JSON_BEGIN} and ${REPORT_JSON_END}.`,
3822
+ debugHint,
3804
3823
  "Retry with the selected coding agent CLI fixed, choose another analyst, or run `moda harness analyze --analyst=local-scan --summary` for deterministic fallback."
3805
- ].join(`
3824
+ ].filter(Boolean).join(`
3806
3825
  `));
3807
3826
  }
3808
3827
  function extractHarnessReportFromText(output, rootDir) {
@@ -4112,7 +4131,7 @@ function isRetryableExternalAnalystFailure(result) {
4112
4131
  return false;
4113
4132
  const output = `${result.stdout}
4114
4133
  ${result.stderr}`;
4115
- return /\b529\b|overloaded|server-side issue|temporar(?:y|ily)|try again|rate limit|too many requests|\b429\b|service unavailable/i.test(output);
4134
+ return /\b529\b|\b429\b|\b5(?:00|02|03|04)\b|overloaded|server-side issue|temporar(?:y|ily)|try again|rate limit|too many requests|service unavailable|internal server error|connection (?:closed|reset|error)|mid-response|econnreset|econnrefused|etimedout|socket hang ?up|fetch failed|network error|stream (?:closed|disconnected|error)/i.test(output);
4116
4135
  }
4117
4136
  function externalAnalystMaxAttempts(adapter) {
4118
4137
  const defaultRetries = adapter === "claude" ? 2 : 1;
@@ -4352,12 +4371,14 @@ function resolveExternalHarnessAnalystInvocation(options) {
4352
4371
  }
4353
4372
  if (options.adapter === "claude") {
4354
4373
  const extraArgs = externalAnalystExtraArgs(options.adapter);
4355
- const permission = mode === "write" ? ["--permission-mode", "acceptEdits", "--allowedTools", "Read,Grep,Glob,Edit,Write,Bash"] : ["--permission-mode", "plan", "--allowedTools", "Read,Grep,Glob,Bash(pwd),Bash(ls *),Bash(find *),Bash(rg *),Bash(grep *),Bash(cat *),Bash(git status *),Bash(git ls-files *),Bash(git grep *)"];
4374
+ const model = resolveAnalystModel(options.adapter, options.flags);
4375
+ const permission = mode === "write" ? ["--permission-mode", "acceptEdits", "--allowedTools", "Read,Grep,Glob,Edit,Write,Bash"] : ["--allowedTools", `Read,Grep,Glob,Write(${HARNESS_REPORT_PATH}),Bash(pwd),Bash(ls *),Bash(find *),Bash(rg *),Bash(grep *),Bash(cat *),Bash(git status *),Bash(git ls-files *),Bash(git grep *)`];
4356
4376
  return {
4357
4377
  adapter: options.adapter,
4358
4378
  command,
4359
4379
  args: [
4360
4380
  "--print",
4381
+ ...model ? ["--model", model] : [],
4361
4382
  ...extraArgs,
4362
4383
  ...permission,
4363
4384
  ...options.stream ? ["--verbose"] : [],
@@ -4418,7 +4439,7 @@ async function runCodingAgent(options) {
4418
4439
  stream,
4419
4440
  mode: options.mode
4420
4441
  });
4421
- const timeoutMs = options.timeoutMs ?? numericEnv("MODA_HARNESS_ANALYST_TIMEOUT_MS", 1200000);
4442
+ const timeoutMs = options.timeoutMs ?? numericEnv("MODA_HARNESS_ANALYST_TIMEOUT_MS", 1800000);
4422
4443
  const result = await runExternalAnalystWithRetries(invocation, {
4423
4444
  cwd: options.rootDir,
4424
4445
  env: buildExternalAnalystEnv(options.env ?? process.env),
@@ -4447,24 +4468,34 @@ async function runCodingAgent(options) {
4447
4468
  function externalAnalystExtraArgs(adapter) {
4448
4469
  return splitCommandLine(process.env[`MODA_HARNESS_ANALYST_${adapter.toUpperCase()}_ARGS`] ?? "");
4449
4470
  }
4471
+ var DEFAULT_CLAUDE_ANALYST_MODEL = "sonnet";
4472
+ function resolveAnalystModel(adapter, flags) {
4473
+ const flagValue = flags["analyst-model"] && flags["analyst-model"] !== "true" ? flags["analyst-model"] : undefined;
4474
+ const raw = (flagValue ?? process.env.MODA_HARNESS_ANALYST_MODEL)?.trim();
4475
+ if (raw?.toLowerCase() === "default")
4476
+ return;
4477
+ if (raw)
4478
+ return raw;
4479
+ return adapter === "claude" ? DEFAULT_CLAUDE_ANALYST_MODEL : undefined;
4480
+ }
4450
4481
  function renderExternalHarnessAnalystPrompt(options) {
4451
4482
  return [
4452
4483
  "You are Moda Analyst, investigating an agent harness for Moda CLI onboarding.",
4453
4484
  "",
4454
- "Do real repository investigation. Do not call `moda harness scan`, `moda harness analyze`, or rely on deterministic Moda scanner output.",
4485
+ "Do real repository investigation. Do not call `moda harness scan` or `moda harness analyze` yourself. When Moda provides deterministic scanner seeds below, treat them as navigation candidates to verify against the real files — never as evidence or as a complete inventory.",
4455
4486
  "Only inspect the Repository root named below and files/directories under it. Do not inspect parent directories, sibling projects, the Moda CLI implementation, or this monorepo unless the Repository root itself is that directory.",
4456
4487
  "Inspect package manifests, source files, prompt files, tool definitions, skills/rules, evals, deployment config, and README/AGENTS docs as needed.",
4457
4488
  "Do not read secret-looking files such as .env files, private keys, credential files, or token stores.",
4458
4489
  "You are running as a read-only analyst. Do not edit source files, configuration files, git state, dependency files, or project metadata.",
4459
- "Prefer printing the final report JSON for Moda CLI to validate and write. If your runtime unexpectedly allows file writes, only `.moda/harness-report.json` is permitted.",
4490
+ `The ONLY path you may write is ${HARNESS_REPORT_PATH}. Write the final report JSON there with the Write tool as soon as it is complete — the file is the durable deliverable and survives a dropped connection, unlike a long printed message. If (and only if) the write fails, print the report JSON between the sentinel markers instead.`,
4460
4491
  "",
4461
4492
  "Your task:",
4462
4493
  renderExternalAnalystProtocol(),
4463
4494
  "1. Identify the runtime agent harnesses, agent families, channel adapters, identity mappings, model providers, frameworks, deployments, and open questions.",
4464
4495
  "2. Exhaustively enumerate EVERY prompt, tool, guardrail, and eval as its own graph artifact — the complete inventory, not a sample. This report feeds prompt optimization and tool optimization, so a missing tool or prompt is a real defect. Runtime agents may be modeled representatively on framework/example repos, but prompts and tools must be complete.",
4465
4496
  "3. Cite every narrative claim with local file paths, exact line numbers, and excerpts. File citations without line numbers are invalid for agent-authored reports. Enumerated artifacts only need evidence:[{path, reason}], so completeness of the artifact inventory is not limited by the citation cap.",
4466
- `4. After completing investigation, print the report JSON between ${REPORT_JSON_BEGIN} and ${REPORT_JSON_END}. Moda CLI will validate it and write the local artifacts.`,
4467
- "Your final response must contain the sentinel-wrapped JSON report. Do not end with commentary about being read-only, do not summarize without JSON, and do not omit the sentinel markers.",
4497
+ `4. After completing investigation, WRITE the report JSON to ${HARNESS_REPORT_PATH} with the Write tool, then print a one-line confirmation naming the file. Only if that write fails, print the report JSON between ${REPORT_JSON_BEGIN} and ${REPORT_JSON_END}. Moda CLI validates whichever it finds (the file wins).`,
4498
+ "Do not end with commentary about being read-only and do not summarize without delivering the report: either the written file or the sentinel-wrapped JSON must exist before you finish.",
4468
4499
  "If the repository is very large, keep enumerating prompts/tools/guardrails/evals as artifacts (uncapped) and only summarize runtime AGENTS representatively — do not drop tools or prompts to save space.",
4469
4500
  "",
4470
4501
  "Final output format:",
@@ -4491,7 +4522,7 @@ function renderExternalHarnessAnalystPrompt(options) {
4491
4522
  "Each graph item with evidence must use evidence: [{ path, reason }], not citationIds/citations.",
4492
4523
  'Runtime agents must include id, name, kind:"runtime_agent", root, entrypointIds, channelAdapterIds, artifactIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds, confidence, evidence.',
4493
4524
  "Artifacts must include id, type, name, scope, usedByAgentIds, confidence, evidence, and optional sourcePath/contentHash/ownedByAgentId. Emit one artifact PER tool, PER prompt (including each inline system prompt), PER guardrail, and PER eval — enumerate them all; artifact types are entrypoint, prompt, tool, skill, eval, retrieval_index, memory, guardrail, model_config, unknown.",
4494
- "Also set `body` on each artifact to its actual usable content so it can be consumed downstream WITHOUT re-reading the repo: for `prompt` the COMPLETE prompt text verbatim (full inline `instructions=`/system-prompt string or full prompt file); for `tool` the signature (name + parameters + types) and its docstring/description; for `guardrail` the rule/condition; for `eval` what it asserts; for `model_config` the configuration values. Copy the text in full — do not summarize or truncate `body` (it is the artifact we optimize). Redact any secret literals.",
4525
+ "Do NOT transcribe prompt/tool text into `body` when it exists verbatim in the repo — pin its exact location instead (sourcePath for a dedicated file, or evidence [{ path, line, reason }] at the definition line) and Moda hydrates `body` locally from that location after validation. Set `body` inline ONLY for content that cannot be recovered from one pinned location: dynamically assembled prompts, fragments concatenated at runtime, or values you had to derive. Short bodies for `guardrail` (the rule), `eval` (what it asserts), and `model_config` (the config values) are still welcome. Redact any secret literals.",
4495
4526
  "For every `prompt` and `tool` artifact, at least one evidence entry MUST pin an exact source location so downstream optimization can retrieve the item text: carry a numeric `line` (the definition line, or the first line of the range) — evidence: [{ path, line, reason }]. The only exception is an artifact whose entire dedicated file is the text (e.g. a standalone prompt file); set its sourcePath and cite that same file and no line is required. When a single file holds multiple inline prompts or tool definitions, give each its own distinct line — a bare { path, reason } is a defect there.",
4496
4527
  "Agent families must include id, name, runtimeAgentIds, sharedArtifactIds, confidence, evidence.",
4497
4528
  "Frameworks/providers must include packageNames and runtimeAgentIds arrays; providers also include envVars and modelHints arrays.",
@@ -4502,6 +4533,7 @@ function renderExternalHarnessAnalystPrompt(options) {
4502
4533
  options.tenantId ? `Tenant ID to include in graph.tenant: ${options.tenantId}` : "No tenant ID was provided for this analysis.",
4503
4534
  `Repository root: ${options.rootDir}`,
4504
4535
  renderAnalystPreflightSection(options.rootDir),
4536
+ renderAnalystSeedSection(options.rootDir),
4505
4537
  "",
4506
4538
  "Be conservative. If something is unclear, put it in unknowns instead of inventing an agent."
4507
4539
  ].filter(Boolean).join(`
@@ -4520,7 +4552,7 @@ function renderExternalAnalystProtocol() {
4520
4552
  '- Also emit artifacts for guardrails (type "guardrail"), evals/tests-of-agents (type "eval"), memory/session stores (type "memory"), retrieval indexes (type "retrieval_index"), and model configuration (type "model_config").',
4521
4553
  "- Only runtime AGENTS may be modeled representatively: if the repo is a framework/SDK/examples collection, you need not treat every example as a distinct production agent — but you MUST still enumerate every distinct tool, prompt, guardrail, and eval as its own artifact.",
4522
4554
  "- Each enumerated artifact needs evidence: [{ path, reason }] (file path + short reason); a full line+excerpt citation is required only for the narrative `claims`, NOT for every artifact — so listing hundreds of tools/prompts is cheap and expected.",
4523
- "- Capture the actual usable content in each artifact's `body`: full prompt text for prompts, signature+docstring for tools, the rule for guardrails, the assertion for evals, config values for model configs. This inventory is consumed directly for optimization, so `body` must be the complete text (verbatim, not summarized), with secret literals redacted.",
4555
+ "- Bodies are hydrated locally by Moda from pinned locations — do not copy file text into `body`. Spend your output budget on coverage (every artifact, exact definition locations), not transcription. Inline `body` only for content not recoverable from a single location (e.g. prompts assembled from fragments at runtime), plus short bodies for guardrails/evals/model configs.",
4524
4556
  "- EXCEPTION — `prompt` and `tool` artifacts MUST pin an exact location: put a numeric `line` on at least one evidence entry (evidence: [{ path, line, reason }]) at the definition line. That structured location is what lets downstream optimization retrieve the exact prompt/tool text; when one file has several inline prompts or tools, each artifact gets its own line. The only case that needs no line is a whole dedicated file whose entire content is the artifact (set sourcePath to that file and cite it). Still no excerpt required — just the line.",
4525
4557
  "- For every runtime agent, read and cite at least one concrete entrypoint or orchestration line, and link its prompts/tools via artifactIds.",
4526
4558
  "- Keep going until every prompt, tool, guardrail, and eval in scope is represented as an artifact. Use unknowns only for genuinely ambiguous items — never to skip enumeration. Respect the hard caps (<=250 claims, <=400 citations): artifacts are uncapped, so when there are many items keep enumerating them as artifacts and reserve claims/citations for the most important ones."
@@ -4528,7 +4560,7 @@ function renderExternalAnalystProtocol() {
4528
4560
  `);
4529
4561
  }
4530
4562
  function renderAnalystPreflightSection(rootDir) {
4531
- if (process.env.MODA_HARNESS_ANALYST_PREFLIGHT !== "1")
4563
+ if (process.env.MODA_HARNESS_ANALYST_PREFLIGHT === "0")
4532
4564
  return "";
4533
4565
  const paths = listAnalystPreflightPaths(rootDir);
4534
4566
  if (paths.length === 0)
@@ -4558,6 +4590,91 @@ function renderPreflightGroup(label, paths) {
4558
4590
  return [];
4559
4591
  return [`${label}:`, ...paths.map((path) => `- ${path}`)];
4560
4592
  }
4593
+ var MAX_SEED_ARTIFACT_LINES = 150;
4594
+ function renderAnalystSeedSection(rootDir) {
4595
+ if (process.env.MODA_HARNESS_ANALYST_SEEDS === "0")
4596
+ return "";
4597
+ const sections = [];
4598
+ try {
4599
+ const lines = renderAnalystSeedLines(scanHarness({ rootDir, dryRun: true, pathWasExplicit: true }));
4600
+ if (lines)
4601
+ sections.push(lines);
4602
+ } catch {}
4603
+ try {
4604
+ const lines = renderAnalystCandidateLines(detectHarnessCandidates(rootDir));
4605
+ if (lines)
4606
+ sections.push(lines);
4607
+ } catch {}
4608
+ return sections.join(`
4609
+ `);
4610
+ }
4611
+ var MAX_SEED_CANDIDATES_PER_KIND = 40;
4612
+ var MAX_SEED_READ_PLAN_FILES = 25;
4613
+ var SEED_CANDIDATE_KIND_LABELS = {
4614
+ llm_call_site: "LLM call sites",
4615
+ framework_construct: "Agent-framework constructs",
4616
+ tool_definition: "Tool definitions",
4617
+ tool_schema: "Tool schemas",
4618
+ message_construction: "Message constructions",
4619
+ prompt_file: "Prompt files",
4620
+ prompt_literal: "Inline prompt literals",
4621
+ model_config: "Model configuration"
4622
+ };
4623
+ function renderAnalystCandidateLines(report) {
4624
+ if (report.candidates.length === 0)
4625
+ return "";
4626
+ const byKind = new Map;
4627
+ for (const candidate of report.candidates) {
4628
+ const bucket = byKind.get(candidate.kind);
4629
+ if (bucket)
4630
+ bucket.push(candidate);
4631
+ else
4632
+ byKind.set(candidate.kind, [candidate]);
4633
+ }
4634
+ const lines = [
4635
+ "",
4636
+ `Deterministic candidate inventory (${report.candidates.length} candidates across ${report.stats.filesWithCandidates} files, scanned in ${report.stats.durationMs}ms):`,
4637
+ "Each entry is path:line [detector] excerpt. Verify each candidate against the real file, then represent every confirmed tool/prompt/eval/model-config as a graph artifact pinned to that location. Candidates are pattern matches — drop false positives, and investigate what patterns cannot see (agent topology, relationships, prompts assembled at runtime). Do not cite this list as evidence; cite the files."
4638
+ ];
4639
+ for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
4640
+ const bucket = byKind.get(kind);
4641
+ if (!bucket || bucket.length === 0)
4642
+ continue;
4643
+ const shown = bucket.slice(0, MAX_SEED_CANDIDATES_PER_KIND);
4644
+ lines.push(`${label} (${bucket.length}${bucket.length > shown.length ? `, showing ${shown.length}` : ""}):`);
4645
+ for (const candidate of shown) {
4646
+ lines.push(`- ${candidate.path}:${candidate.startLine} [${candidate.signal}] ${candidate.excerpt}`);
4647
+ }
4648
+ }
4649
+ const readPlan = report.files.slice(0, MAX_SEED_READ_PLAN_FILES);
4650
+ if (readPlan.length > 0) {
4651
+ lines.push(`Highest-signal files to read first (${readPlan.length} of ${report.files.length}):`, ...readPlan.map((file) => `- ${file.path} (score ${Math.round(file.score)}; ${file.kinds.join(", ")})`));
4652
+ }
4653
+ return lines.join(`
4654
+ `);
4655
+ }
4656
+ function renderAnalystSeedLines(graph) {
4657
+ const providers = graph.modelProviders.map((provider) => `- ${provider.name}${provider.packageNames.length ? ` (packages: ${provider.packageNames.join(", ")})` : ""}`);
4658
+ const frameworks = graph.agentFrameworks.map((framework) => `- ${framework.name}${framework.packageNames.length ? ` (packages: ${framework.packageNames.join(", ")})` : ""}`);
4659
+ const artifacts = graph.artifacts.slice(0, MAX_SEED_ARTIFACT_LINES).map((artifact) => `- [${artifact.type}] ${artifact.sourcePath ?? artifact.name}`);
4660
+ if (providers.length === 0 && frameworks.length === 0 && artifacts.length === 0)
4661
+ return "";
4662
+ const lines = [
4663
+ "",
4664
+ "Deterministic scanner seeds (candidates, not ground truth):",
4665
+ "Moda's static scanner pre-detected the items below from package manifests and filename patterns. Verify each against the real files before including it, correct or drop mislabeled entries, and focus your investigation on what pattern matching cannot see: runtime agents, inline prompts, decorated/registered tool definitions, and the relationships between them. Do not cite this list as evidence."
4666
+ ];
4667
+ if (providers.length > 0)
4668
+ lines.push("Detected model providers:", ...providers);
4669
+ if (frameworks.length > 0) {
4670
+ lines.push("Detected agent frameworks (package-name matches; may be false positives):", ...frameworks);
4671
+ }
4672
+ if (artifacts.length > 0) {
4673
+ lines.push(`Detected artifact candidates (${graph.artifacts.length} total${graph.artifacts.length > MAX_SEED_ARTIFACT_LINES ? `, showing first ${MAX_SEED_ARTIFACT_LINES}` : ""}):`, ...artifacts);
4674
+ }
4675
+ return lines.join(`
4676
+ `);
4677
+ }
4561
4678
  function listAnalystPreflightPaths(rootDir) {
4562
4679
  const gitPaths = readGitTrackedPaths(rootDir);
4563
4680
  const paths = gitPaths.length > 0 ? gitPaths : readDirectoryPreflightPaths(rootDir);
@@ -5257,6 +5374,25 @@ function renderFrameworkAndProviderLines(graph) {
5257
5374
  }
5258
5375
  return lines;
5259
5376
  }
5377
+ function assertHarnessGraphSyncQuality(graph, flags) {
5378
+ if (flags["allow-no-agents"] === "true")
5379
+ return;
5380
+ if (graph.runtimeAgents.length > 0)
5381
+ return;
5382
+ const evidence = [];
5383
+ if (graph.modelProviders.length > 0) {
5384
+ evidence.push(`model providers (${graph.modelProviders.map((provider) => provider.name).join(", ")})`);
5385
+ }
5386
+ if (graph.agentFrameworks.length > 0) {
5387
+ evidence.push(`agent frameworks (${graph.agentFrameworks.map((framework) => framework.name).join(", ")})`);
5388
+ }
5389
+ const promptOrToolCount = graph.artifacts.filter((artifact) => artifact.type === "prompt" || artifact.type === "tool").length;
5390
+ if (promptOrToolCount > 0)
5391
+ evidence.push(`${promptOrToolCount} prompt/tool artifacts`);
5392
+ if (evidence.length === 0)
5393
+ return;
5394
+ throw new CliInputError(`Refusing to sync: the harness graph has zero runtime agents but detected ${evidence.join(", ")}.`, "Agent detection likely under-ran (the deterministic scanner only finds agents via @moda annotations). Run `moda harness analyze --analyst=claude` for semantic detection, or pass --allow-no-agents to sync this graph anyway.");
5395
+ }
5260
5396
  function graphForSync(graph) {
5261
5397
  return sortJson2({
5262
5398
  ...graph,
@@ -11,7 +11,7 @@ import {
11
11
  renderStatusHuman,
12
12
  scanHarness,
13
13
  validateHarnessReport
14
- } from "./cli-g931ntph.js";
14
+ } from "./cli-793dhrgb.js";
15
15
  import {
16
16
  isAuthSessionValid,
17
17
  loadAuthSession
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-x1a56ws9.js";
11
+ } from "./cli-n5qz81m0.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -29,7 +29,7 @@ import {
29
29
  summarizeHarness,
30
30
  terminalStyles,
31
31
  validateHarnessReport
32
- } from "./cli-g931ntph.js";
32
+ } from "./cli-793dhrgb.js";
33
33
  import {
34
34
  authFetch,
35
35
  clearAuthSession,
@@ -5499,6 +5499,11 @@ Init flags:
5499
5499
  --yes Accept all defaults (non-interactive)
5500
5500
  --analyst=auto|claude|codex|cursor|local-scan
5501
5501
  Choose harness analyst adapter
5502
+ --analyst-model=MODEL Model for the claude analyst (default: sonnet;
5503
+ "default" = the claude CLI's own default;
5504
+ env: MODA_HARNESS_ANALYST_MODEL)
5505
+ --allow-no-agents Allow harness sync to push a graph with zero
5506
+ runtime agents despite detected LLM usage
5502
5507
  --remote Run harness analysis server-side (Moda-hosted,
5503
5508
  the default): upload a safe source snapshot; no
5504
5509
  local coding agent required
@@ -6491,7 +6496,7 @@ var commandRegistry = createCommandRegistry([
6491
6496
  resetApiRequestCountBeforeRun: true,
6492
6497
  telemetry: "result",
6493
6498
  handler: async (context) => {
6494
- const { runInit } = await import("./index-g3gt62rm.js");
6499
+ const { runInit } = await import("./index-djg29jhz.js");
6495
6500
  if (context.outputMode === "agent-stream") {
6496
6501
  context.output.writeEvent({
6497
6502
  event: "started",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-g931ntph.js";
3
+ } from "./cli-793dhrgb.js";
4
4
  import"./cli-59yacef3.js";
5
5
 
6
6
  // src/harness-github-actions.ts
@@ -9,7 +9,7 @@ import {
9
9
  runPromptSync,
10
10
  runSkillSync,
11
11
  upsertSkillManifestRecord
12
- } from "./cli-x1a56ws9.js";
12
+ } from "./cli-n5qz81m0.js";
13
13
  import {
14
14
  codingAgentDisplayName,
15
15
  describeCodingAgentEvent,
@@ -27,7 +27,7 @@ import {
27
27
  runHarnessCommand,
28
28
  startRemoteAnalyze,
29
29
  stripAnsi
30
- } from "./cli-g931ntph.js";
30
+ } from "./cli-793dhrgb.js";
31
31
  import {
32
32
  selectTenantAndCreateKey
33
33
  } from "./cli-pq6rte0w.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.28.5",
3
+ "version": "1.28.6",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
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-08-27T02:29:51.059Z",
4
- "cli_version": "1.28.5",
3
+ "bundled_at": "2026-08-28T19:57:51.453Z",
4
+ "cli_version": "1.28.6",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-node-anthropic",