@moda-ai/cli 1.55.93 → 1.55.95

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
@@ -77,6 +77,29 @@ moda harness candidates --path=. --promote=all --out=harness-components.json --y
77
77
 
78
78
  Every enumerated component includes a source citation and `reachable` or `defined_only` status. Production trace evidence can later promote it to `in_prod`. Dynamic MCP allow-all servers are one group; extraction does not connect to those servers. The repository-bound `createAgentScopeResolver(result, ctx)` callback maps `CallSite.enclosing` to emitted agent IDs, including inherited hooks and shared helpers; ambiguous or unknown scopes return `[]`.
79
79
 
80
+ ### Component skeleton and diff
81
+
82
+ ```bash
83
+ moda harness skeleton --path=. --out=/tmp/skeleton.json --json
84
+ moda harness skeleton-diff /tmp/before.json /tmp/after.json --json
85
+ moda harness skeleton-diff --from=/tmp/before.json --to=/tmp/after.json --json
86
+ ```
87
+
88
+ `skeleton` produces `harness-skeleton/v1`, a component map of the harness: a `HarnessGraph` (agents, artifacts and relationships in the usual places, so sync and Ask read it unchanged) plus `skeleton.callSites`, counts, commit, content hash, coverage and a control-flow slot. It runs locally without an LLM or network calls and holds locations and owners, never bodies.
89
+
90
+ - Prompts: a named builder, template or prompt file a model call reaches is one unit, shared by every call that reaches it. Text assembled in the calling function is a `call` unit for that function, with the pieces as `members`. Constants inside a builder are not listed separately.
91
+ - Ids are stable across commits: they hold no line numbers, except the extractor's anonymous `path#L<line>` scopes. Repeated keys get `~2`, `~3` in source order.
92
+ - `liveKey` is the key traces and the traced registry match on, normalised like the backend's trace status: agent class name, model-facing tool name, model id, skill name. Prompts and call sites have none.
93
+ - Map edges live in `skeleton.edges`, a compact table; decode them with `decodeSkeletonEdges(graph.skeleton.edges)` into `{ id: "<type>:<from>-><to>", type, from, to, source, evidence }`. Types: `triggers` (entry point to agent), `invokes_subagent` and `hands_off_to` (agent to agent), `uses_artifact` (agent to prompt, tool or model), `implemented_by` (tool to `code:<path>#<handler>`), `has_control_flow` and `guards` (agent to control-flow item to call site), and `covered_by_eval` (agent or tool to eval suite). `source` is `code` for every extracted edge; consumers merge edges seen in traces as `trace`. `relationships` keeps only the agent-to-agent edges in the usual graph form, so sync and Ask read them unchanged.
94
+ - `skeleton.coverage` says per component type whether detection is `complete` or `partial`, with `skeleton.knownGaps`. Claim completeness only for `complete` types.
95
+ - Entry points: workflows and crons, exported route handlers of route agents (`trigger: "http_route"`) and Durable Object bindings (`trigger: "do_binding"`) of agent classes or of classes that construct one. Each lists the agents it starts in their `entrypointIds` (`triggers` edges); a workflow starts the function agents and sub-agent calls its code reaches.
96
+ - Tools are attributed to agents whose own model calls receive tools. A call that picks its model from a table at runtime has `modelSource: "factory (computed)"` and the table in `modelCandidates`; those candidates are not the call's or the agent's models, and each agent lists such calls in `modelChoices` (call site, location and candidates). A Workers eval suite that drives the deployed Worker covers the agents bound in its default Wrangler config.
97
+ - `covered_by_eval` is a static association (what the suite's own files import, through re-export barrels, and its own model calls), not execution or assertion coverage.
98
+
99
+ The JSON envelope's `data` is the skeleton; `--out` writes the bare skeleton. Incomplete scans return exit code 3 with warnings. `skeleton-diff` accepts either form, lists added, removed, moved and changed components and edges, ignores line-only shifts, and pairs unique same-name components across file renames as `moved`. Its `identical` flag compares content hashes. `harness extract` output is unchanged.
100
+
101
+ Library exports (`src/harness-extract/skeleton/index.ts`): `buildHarnessSkeleton(rootDir, options?)`, `skeletonFromExtract(result, ctx)`, `diffHarnessSkeletons(before, after)` and `registerControlFlowDetector(detector)`. A detector receives `{result, ctx, callSites, agents, callSiteIdOf}` and returns control-flow items; items naming unknown call sites or agents are dropped, a throwing detector leaves `controlFlowStatus: "failed"` with a warning, and without a detector the status is `not_run`.
102
+
80
103
  ## Usage
81
104
 
82
105
  Find things by meaning, then drill in:
@@ -437,3 +460,32 @@ Both commands accept two flags to inline a message window per row:
437
460
  ## License
438
461
 
439
462
  MIT
463
+
464
+ ### Local change impact (experimental)
465
+
466
+ `moda harness impact --path=<checkout> --from=<old-commit> --to=<new-commit> --json`
467
+ follows changed JavaScript/TypeScript symbols through checker-confirmed references to model
468
+ calls, agents, prompt units, and tool definitions. Alternatively pass `--diff=<zero-context-diff>`
469
+ against `--from`. Control-flow additions use the old enclosing function. Single-line edits use
470
+ changed columns to distinguish changed literals from unchanged siblings.
471
+
472
+ The command builds a disposable old source snapshot, preserves working-tree edits, returns only
473
+ the reached slice, and makes no API calls or replays. It excludes assets and source files above
474
+ 1 MiB; diff inputs have a 16 MiB aggregate source budget. Closure is bounded to 5,000 symbols and
475
+ 100,000 references; returned units are bounded to 2,000 units / 2 MiB of literal text. Unchanged
476
+ context reached through a changed symbol is capped at 20 units per call site and 400 in all
477
+ (`contextTruncated`), so it never displaces changed text. Skips, closure budgets and a dropped
478
+ changed-text unit (`unitSliceTruncated`) are reported as warnings and mean the result is incomplete.
479
+ Changed styles, media and fonts are listed in `ignoredFiles`: they carry no prompt or tool text.
480
+
481
+ Optional `--calls=<json>` accepts `{sourceCommit, candidate, sections, tools}` records.
482
+ `sourceCommit` must equal the resolved old commit, and `candidate` uses the frontend change-tests
483
+ `ChangeCase` fields. Sections/tools supply `text` and `sources: [{path, line, endLine}]` from a
484
+ same-agent source join. Optional `agentIds` rejects mismatched owners. Optional `toolDefinitions`
485
+ (`[{name, definition}]`) joins recorded definitions to unique extracted tool names, owners and
486
+ old source text. Ambiguous definitions remain unlinked. Templates require all static fragments
487
+ in order; changed old literals cannot qualify through unchanged sibling text.
488
+
489
+ Keep call inputs and redirected output outside the repository: they can contain customer content.
490
+ Python, unresolved dynamic dispatch and new files without old context remain unmodelled. This
491
+ local result does not establish deployment attribution or production precision/recall.
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  buildDoctorReport
3
- } from "./cli-2ctk8gy8.js";
3
+ } from "./cli-abfcqf4p.js";
4
4
  import"./cli-qwnkv4jy.js";
5
- import"./cli-0qgxw8cx.js";
5
+ import"./cli-92zfpz9f.js";
6
6
  import"./cli-mm48br00.js";
7
+ import"./cli-764m5azg.js";
7
8
  import {
8
9
  readHarnessSyncState
9
10
  } from "./cli-k0vxtzxb.js";
@@ -15,10 +16,10 @@ import {
15
16
  readHarnessReport,
16
17
  validateHarnessReport
17
18
  } from "./cli-krm4w2v9.js";
18
- import"./cli-6ga6kmdp.js";
19
- import"./cli-dxajrq9h.js";
20
19
  import"./cli-13zdcv4w.js";
20
+ import"./cli-6ga6kmdp.js";
21
21
  import"./cli-e0kbye4s.js";
22
+ import"./cli-dxajrq9h.js";
22
23
  import {
23
24
  resolveApiKey,
24
25
  resolveIngestUrl,
@@ -95,7 +96,7 @@ async function buildAgentContext(context) {
95
96
  const graph = readHarnessGraphIfPresent(context.cwd);
96
97
  const reportState = readHarnessReportState(context.cwd);
97
98
  const syncState = readHarnessSyncState(context.cwd);
98
- const harness = graph ? await import("./harness-fjrhp1af.js") : undefined;
99
+ const harness = graph ? await import("./harness-jka07f99.js") : undefined;
99
100
  const topFindings = doctor.checks.filter((check) => check.status === "fail" || check.status === "warning").slice(0, 12).map(checkToFinding);
100
101
  const evidenceRefs = [
101
102
  ...doctor.checks.map(checkToEvidenceRef),