@moda-ai/cli 1.30.1 → 1.30.2

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,7 +11,7 @@ import {
11
11
  renderStatusHuman,
12
12
  scanHarness,
13
13
  validateHarnessReport
14
- } from "./cli-js4bmw21.js";
14
+ } from "./cli-ts6nhn51.js";
15
15
  import {
16
16
  isAuthSessionValid,
17
17
  loadAuthSession
@@ -284,6 +284,7 @@ var HARNESS_REPORT_PATH = join(".moda", "harness-report.json");
284
284
  var HARNESS_REPORT_MARKDOWN_PATH = join(".moda", "HARNESS_REPORT.md");
285
285
  var HARNESS_REPORT_VALIDATION_PATH = join(".moda", "harness-report-validation.json");
286
286
  var HARNESS_REPORT_APPROVAL_PATH = join(".moda", "harness-report-approval.json");
287
+ var HARNESS_ANALYZE_CONCERNS = ["topology", "tools", "prompts", "evals"];
287
288
  var HARNESS_GRAPH_ARRAY_FIELDS = [
288
289
  "agentFamilies",
289
290
  "runtimeAgents",
@@ -713,7 +714,7 @@ function validateHarnessReport(report, rootDir, options) {
713
714
  }
714
715
  }
715
716
  }
716
- validateDeterministicFloor(report, options?.deterministicFloor, check, warn);
717
+ validateDeterministicFloor(report, options?.deterministicFloor, check, warn, options?.concern);
717
718
  return {
718
719
  schemaVersion: HARNESS_REPORT_VALIDATION_SCHEMA_VERSION,
719
720
  reportId: report?.reportId ?? "unknown",
@@ -725,24 +726,26 @@ function validateHarnessReport(report, rootDir, options) {
725
726
  warnings
726
727
  };
727
728
  }
728
- function validateDeterministicFloor(report, floor, check, warn) {
729
+ function validateDeterministicFloor(report, floor, check, warn, concern) {
729
730
  if (!floor)
730
731
  return;
731
732
  if (process.env.MODA_HARNESS_FLOOR_CHECK === "0")
732
733
  return;
734
+ const applyToolFloor = concern === undefined || concern === "tools";
735
+ const applyTopologyFloors = concern === undefined || concern === "topology";
733
736
  const graph = isRecord(report?.graph) ? report.graph : {};
734
737
  const toolArtifactCount = Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord(artifact) && artifact.type === "tool").length : 0;
735
738
  const providerCount = safeArrayLength(graph.modelProviders);
736
739
  const runtimeAgentCount = safeArrayLength(graph.runtimeAgents);
737
- if (typeof floor.toolCandidates === "number" && floor.toolCandidates >= 5) {
740
+ if (applyToolFloor && typeof floor.toolCandidates === "number" && floor.toolCandidates >= 5) {
738
741
  const minimumTools = Math.ceil(0.5 * floor.toolCandidates);
739
742
  check("floor_tools", toolArtifactCount >= minimumTools, `Deterministic scan found ${floor.toolCandidates} high-confidence tool definition candidate(s); the report graph must include at least ${minimumTools} tool artifact(s) but has ${toolArtifactCount}.`);
740
743
  }
741
744
  const providerPackages = Array.isArray(floor.providerPackages) ? floor.providerPackages.filter((name) => typeof name === "string" && name.length > 0) : [];
742
- if (providerPackages.length > 0) {
745
+ if (applyTopologyFloors && providerPackages.length > 0) {
743
746
  check("floor_providers", providerCount > 0, `Manifest dependencies include model-provider package(s) ${providerPackages.join(", ")}, but the report graph.modelProviders is empty.`);
744
747
  }
745
- if (typeof floor.llmCallSites === "number" && floor.llmCallSites > 0 && runtimeAgentCount === 0) {
748
+ if (applyTopologyFloors && typeof floor.llmCallSites === "number" && floor.llmCallSites > 0 && runtimeAgentCount === 0) {
746
749
  warn("floor_agents", `Deterministic scan found ${floor.llmCallSites} LLM call site(s) but the report graph.runtimeAgents is empty; sync will be blocked by the zero-agent gate.`);
747
750
  }
748
751
  }
@@ -2973,7 +2976,6 @@ function assertHarnessGraphSyncSize(graph) {
2973
2976
  `));
2974
2977
  }
2975
2978
  }
2976
-
2977
2979
  // src/tui.ts
2978
2980
  var STATUS_LABELS = {
2979
2981
  pass: "ok",
@@ -3524,6 +3526,13 @@ async function runHarnessCommand(context) {
3524
3526
  writeHumanProgress(context, "moda harness analyze is disabled: connect the Moda GitHub App (Settings → Integrations) for automatic analysis, or pass --experimental to run the CLI analyze.");
3525
3527
  return { exitCode: 1 };
3526
3528
  }
3529
+ const concern = resolveHarnessAnalyzeConcern(context.flags, context.env);
3530
+ if (concern) {
3531
+ const unsupported = context.flags["github-actions"] === "true" ? "--github-actions" : isRemoteHarnessAnalyze(context) ? "hosted (--remote) analysis" : await resolveHarnessAnalystAdapter(context) === "local-scan" ? "--analyst=local-scan" : "";
3532
+ if (unsupported) {
3533
+ throw new CliInputError(`--concern=${concern} is not supported by ${unsupported}.`, "Concern scoping narrows the analyst prompt and gates deterministic seeds, promotion, and floors, which only the local coding-agent analyst does today. Drop --concern, or run with --analyst=claude|codex|cursor.");
3534
+ }
3535
+ }
3527
3536
  await approveScanIfNeeded(scanPlan, {
3528
3537
  context,
3529
3538
  message: "Run Moda Analyst to investigate this project and produce a cited harness report?",
@@ -3537,7 +3546,7 @@ async function runHarnessCommand(context) {
3537
3546
  elapsed_ms: Date.now() - context.startedAt
3538
3547
  });
3539
3548
  if (context.flags["github-actions"] === "true") {
3540
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-v98snak8.js");
3549
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-ze17s10y.js");
3541
3550
  const result = await runGithubActionsAnalyze(rootDir, {
3542
3551
  writeReport: (report2) => {
3543
3552
  const normalized = normalizeHarnessReport(report2);
@@ -3565,7 +3574,8 @@ async function runHarnessCommand(context) {
3565
3574
  rootDir,
3566
3575
  scanPlan,
3567
3576
  tenant,
3568
- adapter: analyst
3577
+ adapter: analyst,
3578
+ concern
3569
3579
  });
3570
3580
  }
3571
3581
  context.output.writeEvent({
@@ -3838,7 +3848,8 @@ async function runExternalHarnessAnalyst(options) {
3838
3848
  const prompt = renderExternalHarnessAnalystPrompt({
3839
3849
  adapter: options.adapter,
3840
3850
  rootDir: options.rootDir,
3841
- tenantId: options.tenant?.tenantId
3851
+ tenantId: options.tenant?.tenantId,
3852
+ concern: options.concern
3842
3853
  });
3843
3854
  const streamAnalystProgress = options.context.outputMode === "agent-stream";
3844
3855
  const invocation = resolveExternalHarnessAnalystInvocation({
@@ -3924,7 +3935,7 @@ async function runExternalHarnessAnalyst(options) {
3924
3935
  ${result.stderr}`, 1200);
3925
3936
  throw new CliInputError(`Harness analyst ${options.adapter} exited with status ${result.status ?? "unknown"}.`, output || "Rerun with the selected coding agent CLI fixed, or choose another analyst with `--analyst=claude|codex|cursor`.");
3926
3937
  }
3927
- const report = readExternalAnalystReport(options.rootDir, result, options.adapter);
3938
+ const report = readExternalAnalystReport(options.rootDir, result, options.adapter, options.concern);
3928
3939
  analystTui?.setSummary({
3929
3940
  runtimeAgentCount: report.graph.runtimeAgents.length,
3930
3941
  agentFamilyCount: report.graph.agentFamilies.length,
@@ -3943,7 +3954,8 @@ ${result.stderr}`, 1200);
3943
3954
  });
3944
3955
  analystTui?.setValidation("running");
3945
3956
  const validation = validateHarnessReport(report, options.rootDir, {
3946
- deterministicFloor: computeDeterministicFloor(options.rootDir)
3957
+ deterministicFloor: computeDeterministicFloor(options.rootDir),
3958
+ concern: options.concern
3947
3959
  });
3948
3960
  analystTui?.setValidation(validation.status === "pass" ? "pass" : "fail");
3949
3961
  if (!analystTui)
@@ -3993,10 +4005,10 @@ function buildExternalAnalystEnv(env) {
3993
4005
  }
3994
4006
  return next;
3995
4007
  }
3996
- function readExternalAnalystReport(rootDir, result, adapter) {
4008
+ function readExternalAnalystReport(rootDir, result, adapter, concern) {
3997
4009
  const reportPath = resolve5(rootDir, HARNESS_REPORT_PATH);
3998
4010
  if (existsSync3(reportPath))
3999
- return repairExternalAnalystReport(readHarnessReport(reportPath), rootDir);
4011
+ return repairExternalAnalystReport(readHarnessReport(reportPath), rootDir, { concern });
4000
4012
  const output = [
4001
4013
  result.stdout,
4002
4014
  result.stderr,
@@ -4004,7 +4016,7 @@ function readExternalAnalystReport(rootDir, result, adapter) {
4004
4016
  extractAgentTextFromOutput(result.stderr)
4005
4017
  ].filter(Boolean).join(`
4006
4018
  `);
4007
- const parsed = extractHarnessReportFromText(output, rootDir);
4019
+ const parsed = extractHarnessReportFromText(output, rootDir, concern);
4008
4020
  if (parsed)
4009
4021
  return parsed;
4010
4022
  let debugHint = "";
@@ -4028,11 +4040,11 @@ function readExternalAnalystReport(rootDir, result, adapter) {
4028
4040
  ].filter(Boolean).join(`
4029
4041
  `));
4030
4042
  }
4031
- function extractHarnessReportFromText(output, rootDir) {
4043
+ function extractHarnessReportFromText(output, rootDir, concern) {
4032
4044
  for (const candidate of reportJsonCandidates(output)) {
4033
4045
  const parsed = parseHarnessReportCandidate(candidate);
4034
4046
  if (parsed)
4035
- return repairExternalAnalystReport(parsed, rootDir);
4047
+ return repairExternalAnalystReport(parsed, rootDir, { concern });
4036
4048
  }
4037
4049
  return;
4038
4050
  }
@@ -4066,13 +4078,32 @@ function parseHarnessReportCandidate(candidate) {
4066
4078
  }
4067
4079
  return;
4068
4080
  }
4069
- function repairExternalAnalystReport(report, rootDir) {
4081
+ function repairExternalAnalystReport(report, rootDir, options = {}) {
4070
4082
  const normalized = normalizeHarnessReport(report);
4071
4083
  repairCitationLines(normalized, rootDir);
4072
- mergeDeterministicArtifacts(normalized.graph, rootDir);
4084
+ mergeDeterministicArtifacts(normalized.graph, rootDir, { concern: options.concern });
4073
4085
  hydratePromptAndToolBodies(normalized.graph, rootDir);
4074
4086
  deriveRelationshipsFromGraph(normalized.graph);
4075
4087
  repairReportSummaryCounts(normalized);
4088
+ if (options.concern) {
4089
+ normalized.source = {
4090
+ ...normalized.source ?? {
4091
+ graphSchemaVersion: normalized.graph?.schemaVersion ?? SCHEMA_VERSION,
4092
+ graphHash: "",
4093
+ readPlan: normalized.graph?.scan?.readPlan ?? {
4094
+ scope: "broad",
4095
+ rootPaths: ["."],
4096
+ willRead: [],
4097
+ willSkip: [],
4098
+ skipsSecretFiles: true,
4099
+ followsSymlinks: false,
4100
+ uploadsSourceByDefault: false
4101
+ },
4102
+ deterministicInventory: false
4103
+ },
4104
+ concern: options.concern
4105
+ };
4106
+ }
4076
4107
  if (normalized.source?.graphHash) {
4077
4108
  normalized.source.graphHash = hashStableJson(graphForReport(normalized.graph));
4078
4109
  }
@@ -4114,9 +4145,18 @@ function deriveRelationshipsFromGraph(graph) {
4114
4145
  }
4115
4146
  return { added };
4116
4147
  }
4117
- function mergeDeterministicArtifacts(graph, rootDir) {
4148
+ var CONCERN_PROMOTED_ARTIFACT_TYPES = {
4149
+ topology: new Set,
4150
+ tools: new Set(["tool"]),
4151
+ prompts: new Set(["prompt"]),
4152
+ evals: new Set(["model_config"])
4153
+ };
4154
+ function mergeDeterministicArtifacts(graph, rootDir, options = {}) {
4118
4155
  if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
4119
4156
  return { added: 0 };
4157
+ if (options.concern === "topology")
4158
+ return { added: 0 };
4159
+ const allowedTypes = options.concern ? CONCERN_PROMOTED_ARTIFACT_TYPES[options.concern] : undefined;
4120
4160
  let promoted;
4121
4161
  try {
4122
4162
  promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir);
@@ -4125,6 +4165,8 @@ function mergeDeterministicArtifacts(graph, rootDir) {
4125
4165
  }
4126
4166
  let added = 0;
4127
4167
  for (const artifact of promoted) {
4168
+ if (allowedTypes && !allowedTypes.has(artifact.type))
4169
+ continue;
4128
4170
  if (graphCoversPromotedArtifact(graph, artifact))
4129
4171
  continue;
4130
4172
  graph.artifacts.push(artifact);
@@ -4661,6 +4703,17 @@ function isDeterministicLocalAnalyst(raw) {
4661
4703
  const value = raw?.trim().toLowerCase();
4662
4704
  return value === "local-scan" || value === "local" || value === "scripted" || value === "moda";
4663
4705
  }
4706
+ function resolveHarnessAnalyzeConcern(flags, env = process.env) {
4707
+ const flagValue = flags.concern && flags.concern !== "true" ? flags.concern : undefined;
4708
+ const raw = flagValue ?? env.MODA_HARNESS_CONCERN;
4709
+ const value = raw?.trim().toLowerCase();
4710
+ if (!value)
4711
+ return;
4712
+ if (HARNESS_ANALYZE_CONCERNS.includes(value)) {
4713
+ return value;
4714
+ }
4715
+ throw new CliInputError(`Unknown harness analyze concern '${raw}'. Valid concerns: ${HARNESS_ANALYZE_CONCERNS.join("|")}.`, `Use --concern=${HARNESS_ANALYZE_CONCERNS.join("|")} (or MODA_HARNESS_CONCERN; the flag wins).`);
4716
+ }
4664
4717
  function normalizeAnalystAdapter(raw) {
4665
4718
  const value = (raw && raw !== "true" ? raw : "auto").trim().toLowerCase();
4666
4719
  if (value === "auto")
@@ -4798,9 +4851,100 @@ function resolveAnalystModel(adapter, flags) {
4798
4851
  return raw;
4799
4852
  return adapter === "claude" ? DEFAULT_CLAUDE_ANALYST_MODEL : undefined;
4800
4853
  }
4854
+ var CONCERN_AGENT_STUB_LINE = 'Runtime agents may be represented ONLY as bare low-confidence stubs when linkage needs a target: id, name, kind:"runtime_agent", root, confidence, evidence — nothing more. Do not investigate agent topology beyond that; the topology concern pass owns it.';
4855
+ var CONCERN_LOCATION_PIN_LINE = "Every artifact you author MUST pin an exact source location so downstream optimization can retrieve the item text: carry a numeric `line` on at least one evidence entry (evidence: [{ path, line, reason }]) at the definition line. The only exception is an artifact whose entire dedicated file is the text — set its sourcePath and cite that file, no line required. When a single file holds multiple inline definitions, give each artifact its own distinct line.";
4856
+ var CONCERN_NO_TRANSCRIPTION_LINE = "Do NOT transcribe prompt/tool text into `body` when it exists verbatim in the repo — pin its exact location instead and Moda hydrates `body` locally after validation. Set `body` inline ONLY for content that cannot be recovered from one pinned location (e.g. fragments assembled at runtime). Redact any secret literals.";
4857
+ var CONCERN_CAPS_LINE = "- Keep going until every in-concern item is represented as an artifact. Respect the hard caps (<=250 claims, <=400 citations): artifacts are uncapped, so keep enumerating them as artifacts and reserve claims/citations for the most important ones.";
4858
+ var CONCERN_PROMPT_SPECS = {
4859
+ topology: {
4860
+ mission: "Your single concern for this pass is TOPOLOGY: the agents and the infrastructure they run on. Other concern passes exhaustively enumerate tools, prompts, and evals — you must not.",
4861
+ taskLines: [
4862
+ "1. Map the harness topology: runtime agents, agent families, channel adapters, identity namespaces, environments, agent frameworks, model providers, deployments, the relationships among them, and open questions.",
4863
+ "2. Do NOT exhaustively enumerate tools, prompts, guardrails, or evals as artifacts — separate concern passes produce those inventories. Keep graph.artifacts minimal: entrypoint artifacts only, plus agent artifactIds references you discover naturally along the way."
4864
+ ],
4865
+ protocolLines: [
4866
+ "- Focus EXCLUSIVELY on topology: runtime agents, agent families, channel adapters, identity namespaces, environments, agent frameworks, model providers, deployments, and how they relate.",
4867
+ "- Do NOT enumerate tools, prompts, guardrails, or evals as artifacts. Keep graph.artifacts minimal: entrypoints only.",
4868
+ "- For every runtime agent, read and cite at least one concrete entrypoint or orchestration line, and populate every linkage field (entrypointIds, channelAdapterIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds).",
4869
+ "- Reserve explicit `relationships` entries for what linkage fields cannot express: covered_by_eval, runs_in_environment, invoked_by_channel, deployed_as, shares_artifact_with, resolves_identity_to, emits_telemetry."
4870
+ ],
4871
+ contractLines: [
4872
+ 'Runtime agents must include id, name, kind:"runtime_agent", root, entrypointIds, channelAdapterIds, artifactIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds, confidence, evidence. Agent linkage fields are the product of this pass — empty linkage means a disconnected map.',
4873
+ "Agent families must include id, name, runtimeAgentIds, sharedArtifactIds, confidence, evidence.",
4874
+ "Frameworks/providers must include packageNames and runtimeAgentIds arrays; providers also include envVars and modelHints arrays.",
4875
+ "Keep graph.artifacts minimal (entrypoints only); do not enumerate tools, prompts, guardrails, or evals."
4876
+ ],
4877
+ largeRepoLine: "If the repository is very large, model runtime agents representatively rather than exhaustively, but never drop channel adapters, providers, frameworks, or deployments you have evidence for."
4878
+ },
4879
+ tools: {
4880
+ mission: "Your single concern for this pass is TOOLS: the exhaustive tool inventory. Other concern passes cover agents/topology, prompts, and evals — you must not enumerate those.",
4881
+ taskLines: [
4882
+ "1. Exhaustively enumerate EVERY tool as its own `tool` graph artifact — the complete inventory, not a sample. This report feeds tool optimization, so a missing tool is a real defect.",
4883
+ "2. Do NOT enumerate prompts, guardrails, or evals, and do not map agent topology beyond bare stubs needed for tool linkage."
4884
+ ],
4885
+ protocolLines: [
4886
+ "- Tools: emit a `tool` artifact for EVERY tool definition — each `@function_tool` / `function_tool(...)`-decorated function, every hosted/first-party tool class (e.g. WebSearchTool, FileSearchTool, ComputerTool, CodeInterpreterTool, HostedMCPTool, ImageGenerationTool, LocalShellTool), and every `tool({...})` / `createTool(...)` / `new *Tool(...)` factory. Name each artifact after the tool/function and cite its definition line.",
4887
+ "- Populate usedByAgentIds on every tool artifact whenever the using agent is determinable (and ownedByAgentId when a single agent owns it).",
4888
+ `- ${CONCERN_AGENT_STUB_LINE}`,
4889
+ "- Do NOT emit prompt, guardrail, eval, memory, or retrieval_index artifacts — other concern passes own those.",
4890
+ CONCERN_CAPS_LINE
4891
+ ],
4892
+ contractLines: [
4893
+ 'Artifacts must include id, type:"tool", name, scope, usedByAgentIds, confidence, evidence, and optional sourcePath/contentHash/ownedByAgentId. Emit one artifact PER tool — enumerate them all.',
4894
+ CONCERN_NO_TRANSCRIPTION_LINE,
4895
+ CONCERN_LOCATION_PIN_LINE,
4896
+ CONCERN_AGENT_STUB_LINE
4897
+ ],
4898
+ largeRepoLine: "If the repository is very large, keep enumerating tools as artifacts (uncapped) — do not drop tools to save space."
4899
+ },
4900
+ prompts: {
4901
+ mission: "Your single concern for this pass is PROMPTS: the exhaustive prompt inventory. Other concern passes cover agents/topology, tools, and evals — you must not enumerate those.",
4902
+ taskLines: [
4903
+ "1. Exhaustively enumerate EVERY distinct prompt as its own `prompt` graph artifact — dedicated prompt files AND inline system prompts, the complete inventory, not a sample. This report feeds prompt optimization, so a missing prompt is a real defect.",
4904
+ "2. Do NOT enumerate tools, guardrails, or evals, and do not map agent topology beyond bare stubs needed for prompt linkage."
4905
+ ],
4906
+ protocolLines: [
4907
+ "- Prompts: emit a `prompt` artifact for EVERY distinct prompt — dedicated prompt files AND inline system prompts (`instructions=`, `system=`, `SYSTEM_PROMPT`, prompt-template strings), one per agent/definition, each with its exact file+line location so the prompt text can be retrieved and optimized later.",
4908
+ "- Populate usedByAgentIds on every prompt artifact whenever the using agent is determinable (and ownedByAgentId when a single agent owns it).",
4909
+ `- ${CONCERN_AGENT_STUB_LINE}`,
4910
+ "- Do NOT emit tool, guardrail, eval, memory, or retrieval_index artifacts — other concern passes own those.",
4911
+ CONCERN_CAPS_LINE
4912
+ ],
4913
+ contractLines: [
4914
+ 'Artifacts must include id, type:"prompt", name, scope, usedByAgentIds, confidence, evidence, and optional sourcePath/contentHash/ownedByAgentId. Emit one artifact PER prompt (including each inline system prompt) — enumerate them all.',
4915
+ CONCERN_NO_TRANSCRIPTION_LINE,
4916
+ CONCERN_LOCATION_PIN_LINE,
4917
+ CONCERN_AGENT_STUB_LINE
4918
+ ],
4919
+ largeRepoLine: "If the repository is very large, keep enumerating prompts as artifacts (uncapped) — do not drop prompts to save space."
4920
+ },
4921
+ evals: {
4922
+ mission: "Your single concern for this pass is EVALS & SAFETY CONFIG: evals, guardrails, model configs, and memory/retrieval stores. Other concern passes cover agents/topology, tools, and prompts — you must not enumerate those.",
4923
+ taskLines: [
4924
+ '1. Exhaustively enumerate EVERY eval (type "eval"), guardrail (type "guardrail"), model configuration (type "model_config"), memory/session store (type "memory"), and retrieval index (type "retrieval_index") as its own graph artifact — the complete inventory, not a sample.',
4925
+ "2. Do NOT enumerate tools or prompts, and do not map agent topology beyond bare stubs needed for linkage."
4926
+ ],
4927
+ protocolLines: [
4928
+ '- Emit artifacts for evals/tests-of-agents (type "eval"), guardrails (type "guardrail"), model configuration (type "model_config"), memory/session stores (type "memory"), and retrieval indexes (type "retrieval_index").',
4929
+ "- Short bodies are welcome here: `guardrail` (the rule), `eval` (what it asserts), `model_config` (the config values). Redact any secret literals.",
4930
+ "- Populate usedByAgentIds on every artifact whenever the using agent is determinable.",
4931
+ `- ${CONCERN_AGENT_STUB_LINE}`,
4932
+ "- Do NOT emit tool or prompt artifacts — other concern passes own those.",
4933
+ CONCERN_CAPS_LINE
4934
+ ],
4935
+ contractLines: [
4936
+ "Artifacts must include id, type, name, scope, usedByAgentIds, confidence, evidence, and optional sourcePath/contentHash/ownedByAgentId; artifact types for this pass are eval, guardrail, model_config, memory, retrieval_index. Emit one artifact PER item — enumerate them all.",
4937
+ "Short bodies are welcome: `guardrail` (the rule), `eval` (what it asserts), `model_config` (the config values). Redact any secret literals.",
4938
+ CONCERN_AGENT_STUB_LINE
4939
+ ],
4940
+ largeRepoLine: "If the repository is very large, keep enumerating evals, guardrails, and model configs as artifacts (uncapped) — do not drop them to save space."
4941
+ }
4942
+ };
4801
4943
  function renderExternalHarnessAnalystPrompt(options) {
4944
+ const spec = options.concern ? CONCERN_PROMPT_SPECS[options.concern] : undefined;
4802
4945
  return [
4803
4946
  "You are Moda Analyst, investigating an agent harness for Moda CLI onboarding.",
4947
+ spec?.mission ?? "",
4804
4948
  "",
4805
4949
  "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.",
4806
4950
  "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.",
@@ -4810,13 +4954,15 @@ function renderExternalHarnessAnalystPrompt(options) {
4810
4954
  `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.`,
4811
4955
  "",
4812
4956
  "Your task:",
4813
- renderExternalAnalystProtocol(),
4814
- "1. Identify the runtime agent harnesses, agent families, channel adapters, identity mappings, model providers, frameworks, deployments, and open questions.",
4815
- "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.",
4957
+ renderExternalAnalystProtocol(options.concern),
4958
+ ...spec ? spec.taskLines : [
4959
+ "1. Identify the runtime agent harnesses, agent families, channel adapters, identity mappings, model providers, frameworks, deployments, and open questions.",
4960
+ "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."
4961
+ ],
4816
4962
  "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.",
4817
4963
  `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).`,
4818
4964
  "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.",
4819
- "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.",
4965
+ spec?.largeRepoLine ?? "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.",
4820
4966
  "",
4821
4967
  "Final output format:",
4822
4968
  REPORT_JSON_BEGIN,
@@ -4830,7 +4976,7 @@ function renderExternalHarnessAnalystPrompt(options) {
4830
4976
  "- reportId, harnessId, createdAt",
4831
4977
  `- analyst: { "id": "moda-${options.adapter}-analyst", "adapter": "${options.adapter}", "mode": "agent", "version": "cli" }`,
4832
4978
  '- repo: { root: ".", name, packageManagers, languages }',
4833
- '- source: { graphSchemaVersion: "harness.v0.1", graphHash: "", readPlan, deterministicInventory: false }',
4979
+ options.concern ? `- source: { graphSchemaVersion: "harness.v0.1", graphHash: "", readPlan, deterministicInventory: false, concern: "${options.concern}" }` : '- source: { graphSchemaVersion: "harness.v0.1", graphHash: "", readPlan, deterministicInventory: false }',
4834
4980
  "- summary: numeric fields named runtimeAgentCount, agentFamilyCount, artifactCount, sharedArtifactCount, channelAdapterCount, identityNamespaceCount, environmentCount, agentFrameworkCount, modelProviderCount, deploymentCount, relationshipCount, unknownCount",
4835
4981
  "- graph: an exact sanitized harness.v0.1 graph with arrays for agentFamilies, runtimeAgents, developerAgentAdapters, channelAdapters, artifacts, identityNamespaces, environments, agentFrameworks, modelProviders, deployments, relationships, unknowns",
4836
4982
  "- claims: cited claims about graph items. Each claim must include kind, subjectId, title, summary, confidence, inference, and citationIds; kind should be one of runtime_agent, agent_family, developer_agent_adapter, artifact, channel_adapter, identity_namespace, environment, agent_framework, model_provider, deployment, relationship, unknown. subjectId should be the id of the runtime agent, agent family, developer adapter, artifact, provider, adapter, deployment, relationship, or unknown being claimed.",
@@ -4840,13 +4986,15 @@ function renderExternalHarnessAnalystPrompt(options) {
4840
4986
  "",
4841
4987
  'Use exact field names and numeric confidence values from 0 to 1. Do not write string confidence values like "high". Do not use confidence >= 0.90 unless the claim is direct evidence from the cited line(s).',
4842
4988
  "Each graph item with evidence must use evidence: [{ path, reason }], not citationIds/citations.",
4843
- 'Runtime agents must include id, name, kind:"runtime_agent", root, entrypointIds, channelAdapterIds, artifactIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds, confidence, evidence.',
4844
- "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.",
4845
- "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.",
4846
- "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.",
4847
- "LINKAGE IS MANDATORY, not optional metadata: every artifact you author must carry usedByAgentIds naming each runtime agent that uses it (and ownedByAgentId when a single agent owns it), and every runtime agent's artifactIds must list its prompts, tools, and evals. Moda derives uses_artifact/owns_artifact edges from these fields after your run, so empty linkage means a disconnected map. Reserve explicit `relationships` entries for what linkage fields cannot express: covered_by_eval, runs_in_environment, invoked_by_channel, deployed_as, shares_artifact_with, resolves_identity_to, emits_telemetry.",
4848
- "Agent families must include id, name, runtimeAgentIds, sharedArtifactIds, confidence, evidence.",
4849
- "Frameworks/providers must include packageNames and runtimeAgentIds arrays; providers also include envVars and modelHints arrays.",
4989
+ ...spec ? spec.contractLines : [
4990
+ 'Runtime agents must include id, name, kind:"runtime_agent", root, entrypointIds, channelAdapterIds, artifactIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds, confidence, evidence.',
4991
+ "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.",
4992
+ "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.",
4993
+ "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.",
4994
+ "LINKAGE IS MANDATORY, not optional metadata: every artifact you author must carry usedByAgentIds naming each runtime agent that uses it (and ownedByAgentId when a single agent owns it), and every runtime agent's artifactIds must list its prompts, tools, and evals. Moda derives uses_artifact/owns_artifact edges from these fields after your run, so empty linkage means a disconnected map. Reserve explicit `relationships` entries for what linkage fields cannot express: covered_by_eval, runs_in_environment, invoked_by_channel, deployed_as, shares_artifact_with, resolves_identity_to, emits_telemetry.",
4995
+ "Agent families must include id, name, runtimeAgentIds, sharedArtifactIds, confidence, evidence.",
4996
+ "Frameworks/providers must include packageNames and runtimeAgentIds arrays; providers also include envVars and modelHints arrays."
4997
+ ],
4850
4998
  "Unknowns in graph must include id, question, candidateIds, evidence. Report-level unknowns must include id, question, citationIds.",
4851
4999
  "Do not make every claim about the repo or harness id. Claims should name the specific graph item they support.",
4852
5000
  "",
@@ -4854,16 +5002,24 @@ function renderExternalHarnessAnalystPrompt(options) {
4854
5002
  options.tenantId ? `Tenant ID to include in graph.tenant: ${options.tenantId}` : "No tenant ID was provided for this analysis.",
4855
5003
  `Repository root: ${options.rootDir}`,
4856
5004
  renderAnalystPreflightSection(options.rootDir),
4857
- renderAnalystSeedSection(options.rootDir),
5005
+ renderAnalystSeedSection(options.rootDir, options.concern),
4858
5006
  "",
4859
5007
  "Be conservative. If something is unclear, put it in unknowns instead of inventing an agent."
4860
5008
  ].filter(Boolean).join(`
4861
5009
  `);
4862
5010
  }
4863
- function renderExternalAnalystProtocol() {
5011
+ function renderExternalAnalystProtocol(concern) {
4864
5012
  const protocol = process.env.MODA_HARNESS_ANALYST_PROTOCOL?.trim().toLowerCase();
4865
5013
  if (protocol === "classic" || protocol === "none" || protocol === "0")
4866
5014
  return "";
5015
+ if (concern) {
5016
+ return [
5017
+ "Focused investigation protocol:",
5018
+ "- First build a quick repo map with read-only commands such as pwd, git ls-files, rg, and targeted manifest/doc reads.",
5019
+ ...CONCERN_PROMPT_SPECS[concern].protocolLines
5020
+ ].join(`
5021
+ `);
5022
+ }
4867
5023
  return [
4868
5024
  "Focused investigation protocol:",
4869
5025
  "- First build a quick repo map with read-only commands such as pwd, git ls-files, rg, and targeted manifest/doc reads.",
@@ -4912,23 +5068,31 @@ function renderPreflightGroup(label, paths) {
4912
5068
  return [`${label}:`, ...paths.map((path) => `- ${path}`)];
4913
5069
  }
4914
5070
  var MAX_SEED_ARTIFACT_LINES = 150;
4915
- function renderAnalystSeedSection(rootDir) {
5071
+ function renderAnalystSeedSection(rootDir, concern) {
4916
5072
  if (process.env.MODA_HARNESS_ANALYST_SEEDS === "0")
4917
5073
  return "";
4918
5074
  const sections = [];
5075
+ if (concern === undefined || concern === "topology") {
5076
+ try {
5077
+ const lines = renderAnalystSeedLines(scanHarness({ rootDir, dryRun: true, pathWasExplicit: true }));
5078
+ if (lines)
5079
+ sections.push(lines);
5080
+ } catch {}
5081
+ }
4919
5082
  try {
4920
- const lines = renderAnalystSeedLines(scanHarness({ rootDir, dryRun: true, pathWasExplicit: true }));
4921
- if (lines)
4922
- sections.push(lines);
4923
- } catch {}
4924
- try {
4925
- const lines = renderAnalystCandidateLines(detectHarnessCandidates(rootDir));
5083
+ const lines = renderAnalystCandidateLines(detectHarnessCandidates(rootDir), concern);
4926
5084
  if (lines)
4927
5085
  sections.push(lines);
4928
5086
  } catch {}
4929
5087
  return sections.join(`
4930
5088
  `);
4931
5089
  }
5090
+ var CONCERN_CANDIDATE_KINDS = {
5091
+ topology: ["llm_call_site", "framework_construct"],
5092
+ tools: ["tool_definition", "tool_schema"],
5093
+ prompts: ["prompt_file", "prompt_literal", "message_construction"],
5094
+ evals: ["model_config"]
5095
+ };
4932
5096
  var MAX_SEED_CANDIDATES_PER_KIND = 40;
4933
5097
  var MAX_SEED_READ_PLAN_FILES = 25;
4934
5098
  var SEED_CANDIDATE_KIND_LABELS = {
@@ -4941,11 +5105,13 @@ var SEED_CANDIDATE_KIND_LABELS = {
4941
5105
  prompt_literal: "Inline prompt literals",
4942
5106
  model_config: "Model configuration"
4943
5107
  };
4944
- function renderAnalystCandidateLines(report) {
4945
- if (report.candidates.length === 0)
5108
+ function renderAnalystCandidateLines(report, concern) {
5109
+ const allowedKinds = concern ? new Set(CONCERN_CANDIDATE_KINDS[concern]) : undefined;
5110
+ const candidates = allowedKinds ? report.candidates.filter((candidate) => allowedKinds.has(candidate.kind)) : report.candidates;
5111
+ if (candidates.length === 0)
4946
5112
  return "";
4947
5113
  const byKind = new Map;
4948
- for (const candidate of report.candidates) {
5114
+ for (const candidate of candidates) {
4949
5115
  const bucket = byKind.get(candidate.kind);
4950
5116
  if (bucket)
4951
5117
  bucket.push(candidate);
@@ -4954,9 +5120,12 @@ function renderAnalystCandidateLines(report) {
4954
5120
  }
4955
5121
  const lines = [
4956
5122
  "",
4957
- `Deterministic candidate inventory (${report.candidates.length} candidates across ${report.stats.filesWithCandidates} files, scanned in ${report.stats.durationMs}ms):`,
4958
- "Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file and drop false positives. Verified deterministic candidates are also auto-merged into the graph after your run, so prioritize (1) agents, topology, and relationships, (2) verifying or refuting these candidates (list refuted ones in unknowns), and (3) artifacts the patterns cannot see (runtime agents, prompts assembled at runtime, cross-file wiring) — rather than re-listing every candidate as an artifact. Do not cite this list as evidence; cite the files."
5123
+ concern ? `Deterministic candidate inventory for the ${concern} concern (${candidates.length} candidates, scanned in ${report.stats.durationMs}ms):` : `Deterministic candidate inventory (${candidates.length} candidates across ${report.stats.filesWithCandidates} files, scanned in ${report.stats.durationMs}ms):`,
5124
+ concern ? "Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file, drop false positives (list refuted ones in unknowns), and hunt for in-concern items the patterns cannot see. Do not cite this list as evidence; cite the files." : "Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file and drop false positives. Verified deterministic candidates are also auto-merged into the graph after your run, so prioritize (1) agents, topology, and relationships, (2) verifying or refuting these candidates (list refuted ones in unknowns), and (3) artifacts the patterns cannot see (runtime agents, prompts assembled at runtime, cross-file wiring) — rather than re-listing every candidate as an artifact. Do not cite this list as evidence; cite the files."
4959
5125
  ];
5126
+ if (concern === "evals") {
5127
+ lines.push("Note: evals, guardrails, and memory/retrieval stores have no deterministic detector — only model_config candidates are seeded. Discover the rest by investigation.");
5128
+ }
4960
5129
  for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
4961
5130
  const bucket = byKind.get(kind);
4962
5131
  if (!bucket || bucket.length === 0)
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-mq00fkwt.js";
11
+ } from "./cli-c1vhkg8n.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-js4bmw21.js";
32
+ } from "./cli-ts6nhn51.js";
33
33
  import {
34
34
  authFetch,
35
35
  clearAuthSession,
@@ -5514,6 +5514,11 @@ Init flags:
5514
5514
  --analyst-model=MODEL Model for the claude analyst (default: sonnet;
5515
5515
  "default" = the claude CLI's own default;
5516
5516
  env: MODA_HARNESS_ANALYST_MODEL)
5517
+ --concern=topology|tools|prompts|evals
5518
+ Harness analyze: restrict the pass to one
5519
+ concern (whole repo, one report scope); an
5520
+ orchestrator merges the per-concern reports
5521
+ (env: MODA_HARNESS_CONCERN, flag wins)
5517
5522
  --allow-no-agents Allow harness sync to push a graph with zero
5518
5523
  runtime agents despite detected LLM usage
5519
5524
  --remote Run harness analysis server-side (Moda-hosted,
@@ -6521,7 +6526,7 @@ var commandRegistry = createCommandRegistry([
6521
6526
  resetApiRequestCountBeforeRun: true,
6522
6527
  telemetry: "result",
6523
6528
  handler: async (context) => {
6524
- const { runInit } = await import("./index-14sr487g.js");
6529
+ const { runInit } = await import("./index-v3j2848b.js");
6525
6530
  if (context.outputMode === "agent-stream") {
6526
6531
  context.output.writeEvent({
6527
6532
  event: "started",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-js4bmw21.js";
3
+ } from "./cli-ts6nhn51.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-mq00fkwt.js";
12
+ } from "./cli-c1vhkg8n.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-js4bmw21.js";
30
+ } from "./cli-ts6nhn51.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.30.1",
3
+ "version": "1.30.2",
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-09-02T04:30:52.582Z",
4
- "cli_version": "1.30.1",
3
+ "bundled_at": "2026-09-03T05:01:51.741Z",
4
+ "cli_version": "1.30.2",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-node-anthropic",