@moda-ai/cli 1.13.2 → 1.13.4
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/dist/cli.js +5 -3
- package/dist/harness-remote.js +12 -1
- package/dist/harness-report.js +4 -1
- package/dist/harness.d.ts +8 -0
- package/dist/harness.js +7 -1
- package/dist/init/index.d.ts +6 -0
- package/dist/init/index.js +60 -15
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -238,9 +238,11 @@ Init flags:
|
|
|
238
238
|
--yes Accept all defaults (non-interactive)
|
|
239
239
|
--analyst=auto|claude|codex|cursor|local-scan
|
|
240
240
|
Choose harness analyst adapter
|
|
241
|
-
--remote Run harness analysis server-side (Moda-hosted
|
|
242
|
-
upload a safe source snapshot; no
|
|
243
|
-
|
|
241
|
+
--remote Run harness analysis server-side (Moda-hosted,
|
|
242
|
+
the default): upload a safe source snapshot; no
|
|
243
|
+
local coding agent required
|
|
244
|
+
--local Run harness analysis with a LOCAL coding agent
|
|
245
|
+
instead of server-side (opt out of remote)
|
|
244
246
|
--tui Show full-screen analyst dashboard in human TTYs
|
|
245
247
|
--no-tui Show raw external analyst output
|
|
246
248
|
--sdk-integrate Run your coding agent to install the Moda SDK and instrument LLM calls
|
package/dist/harness-remote.js
CHANGED
|
@@ -27,7 +27,18 @@ export function isRemoteHarnessAnalyze(context) {
|
|
|
27
27
|
// The CLI normalizes numeric flag values to numbers, so `--remote=1` arrives
|
|
28
28
|
// as the number 1 rather than the string '1'; accept every truthy spelling.
|
|
29
29
|
const flag = context.flags.remote;
|
|
30
|
-
|
|
30
|
+
// An explicit local opt-out (`--local` / `--no-remote` / `--remote=false|0`)
|
|
31
|
+
// wins over the `MODA_HARNESS_REMOTE` env, so callers that force local — the
|
|
32
|
+
// `moda init` local path and its remote-failure fallback — stay local even
|
|
33
|
+
// when the env is set. `no-remote` may arrive kebab- or snake-cased depending
|
|
34
|
+
// on the caller (real CLI parsing snake-cases; internal callers do not).
|
|
35
|
+
const noRemote = context.flags['no-remote'] ?? context.flags.no_remote;
|
|
36
|
+
const local = context.flags.local;
|
|
37
|
+
const isTruthy = (v) => v === true || v === 1 || v === 'true' || v === '1' || v === 'yes';
|
|
38
|
+
const isFalsy = (v) => v === false || v === 0 || v === 'false' || v === '0' || v === 'no';
|
|
39
|
+
if (isFalsy(flag) || isTruthy(noRemote) || isTruthy(local))
|
|
40
|
+
return false;
|
|
41
|
+
if (isTruthy(flag))
|
|
31
42
|
return true;
|
|
32
43
|
const env = (context.env.MODA_HARNESS_REMOTE ?? '').trim().toLowerCase();
|
|
33
44
|
return env !== '' && env !== '0' && env !== 'false';
|
package/dist/harness-report.js
CHANGED
|
@@ -44,7 +44,10 @@ const HARNESS_REPORT_CITATION_TYPES = new Set([
|
|
|
44
44
|
'command_output',
|
|
45
45
|
'production_evidence',
|
|
46
46
|
]);
|
|
47
|
-
|
|
47
|
+
// Raised from 1.5 MB to 12 MB so artifacts can carry their full usable content
|
|
48
|
+
// (prompt bodies, tool signatures/docstrings, guardrail rules) for downstream
|
|
49
|
+
// prompt/tool optimization — not just location metadata.
|
|
50
|
+
const MAX_HARNESS_REPORT_BYTES = 12_000_000;
|
|
48
51
|
const MAX_HARNESS_REPORT_CLAIMS = 250;
|
|
49
52
|
const MAX_HARNESS_REPORT_CITATIONS = 400;
|
|
50
53
|
const MAX_HARNESS_REPORT_SUMMARY_LENGTH = 1_200;
|
package/dist/harness.d.ts
CHANGED
|
@@ -90,6 +90,14 @@ export interface HarnessArtifact extends HarnessDetection {
|
|
|
90
90
|
contentHash?: string;
|
|
91
91
|
ownedByAgentId?: string;
|
|
92
92
|
usedByAgentIds: string[];
|
|
93
|
+
/**
|
|
94
|
+
* The actual usable content of the artifact, captured so downstream
|
|
95
|
+
* consumers (prompt optimization, tool optimization) can use it without
|
|
96
|
+
* re-reading the repo: the full prompt text for `prompt`, the signature +
|
|
97
|
+
* docstring/description for `tool`, the rule for `guardrail`, the assertion
|
|
98
|
+
* for `eval`, the config values for `model_config`, etc.
|
|
99
|
+
*/
|
|
100
|
+
body?: string;
|
|
93
101
|
}
|
|
94
102
|
export interface HarnessIdentityNamespace extends HarnessDetection {
|
|
95
103
|
id: string;
|
package/dist/harness.js
CHANGED
|
@@ -1374,6 +1374,7 @@ function renderExternalHarnessAnalystPrompt(options) {
|
|
|
1374
1374
|
'Each graph item with evidence must use evidence: [{ path, reason }], not citationIds/citations.',
|
|
1375
1375
|
'Runtime agents must include id, name, kind:"runtime_agent", root, entrypointIds, channelAdapterIds, artifactIds, identityNamespaceIds, deploymentIds, frameworkIds, modelProviderIds, confidence, evidence.',
|
|
1376
1376
|
'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.',
|
|
1377
|
+
'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.',
|
|
1377
1378
|
'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.',
|
|
1378
1379
|
'Agent families must include id, name, runtimeAgentIds, sharedArtifactIds, confidence, evidence.',
|
|
1379
1380
|
'Frameworks/providers must include packageNames and runtimeAgentIds arrays; providers also include envVars and modelHints arrays.',
|
|
@@ -1401,6 +1402,7 @@ function renderExternalAnalystProtocol() {
|
|
|
1401
1402
|
'- 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").',
|
|
1402
1403
|
'- 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.',
|
|
1403
1404
|
'- 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.',
|
|
1405
|
+
'- 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.',
|
|
1404
1406
|
'- 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.',
|
|
1405
1407
|
'- For every runtime agent, read and cite at least one concrete entrypoint or orchestration line, and link its prompts/tools via artifactIds.',
|
|
1406
1408
|
'- 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.',
|
|
@@ -2529,13 +2531,17 @@ function maybeAddPromptArtifact(graph, file) {
|
|
|
2529
2531
|
if (!isPrompt)
|
|
2530
2532
|
return;
|
|
2531
2533
|
const id = stableId('prompt', file.path);
|
|
2534
|
+
const text = file.text ?? readFileSync(file.absPath, 'utf8');
|
|
2532
2535
|
upsertArtifact(graph, {
|
|
2533
2536
|
id,
|
|
2534
2537
|
type: 'prompt',
|
|
2535
2538
|
name: promptNameFromPath(file.path),
|
|
2536
2539
|
scope: 'agent',
|
|
2537
2540
|
sourcePath: file.path,
|
|
2538
|
-
contentHash: hashText(
|
|
2541
|
+
contentHash: hashText(text),
|
|
2542
|
+
// Capture the full prompt text so downstream prompt optimization can use it
|
|
2543
|
+
// directly without re-reading the repo.
|
|
2544
|
+
body: text,
|
|
2539
2545
|
usedByAgentIds: [],
|
|
2540
2546
|
confidence: 0.72,
|
|
2541
2547
|
evidence: [{ path: file.path, reason: 'Prompt-like file found under prompts directory.' }],
|
package/dist/init/index.d.ts
CHANGED
|
@@ -28,4 +28,10 @@ export interface InitRunOptions {
|
|
|
28
28
|
onEvent?: (event: InitProgressEvent) => void;
|
|
29
29
|
}
|
|
30
30
|
export declare function runInit(flags: Record<string, unknown>, options?: InitRunOptions): Promise<InitResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Onboarding runs harness discovery SERVER-SIDE by default so the customer
|
|
33
|
+
* needs no local coding agent. Opt out with `--local`, `--no-remote`, or
|
|
34
|
+
* `--remote=false` to use a local coding-agent analyst instead.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveHarnessRemote(flags: Record<string, unknown>): boolean;
|
|
31
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/init/index.js
CHANGED
|
@@ -97,17 +97,14 @@ export async function runInit(flags, options = {}) {
|
|
|
97
97
|
log(renderInitNote(`Active workspace: ${tenantName}`));
|
|
98
98
|
emit('workspace', `Active workspace: ${tenantName}`, 'completed');
|
|
99
99
|
phases.push({ id: 'workspace', title: 'Workspace & Profile', status: 'completed', details: tenantName });
|
|
100
|
-
// Step 4: Harness analyst report.
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
? 'Running Moda-hosted harness discovery.'
|
|
109
|
-
: 'Running Moda Analyst harness discovery.');
|
|
110
|
-
const harnessResult = await runHarnessCommand(createCommandContext({
|
|
100
|
+
// Step 4: Harness analyst report. Onboarding runs the analyst SERVER-SIDE by
|
|
101
|
+
// default (Moda uploads a safe source snapshot and runs the agent on its own
|
|
102
|
+
// infra) so the customer needs no local coding agent. Opt out with `--local`
|
|
103
|
+
// (or `--remote=false`) to run a local coding-agent analyst instead. If the
|
|
104
|
+
// remote run fails (service/network), we fall back to the local analyst so
|
|
105
|
+
// onboarding never hard-fails.
|
|
106
|
+
const remote = resolveHarnessRemote(flags);
|
|
107
|
+
const runAnalyze = (useRemote) => runHarnessCommand(createCommandContext({
|
|
111
108
|
command: 'harness',
|
|
112
109
|
positional: 'analyze',
|
|
113
110
|
positionals: ['analyze'],
|
|
@@ -122,14 +119,44 @@ export async function runInit(flags, options = {}) {
|
|
|
122
119
|
json: json || agent,
|
|
123
120
|
agent,
|
|
124
121
|
analyst: stringFlag(flags.analyst) ?? 'auto',
|
|
125
|
-
remote
|
|
122
|
+
// Pass the remote/local decision EXPLICITLY. `commandFlags` drops
|
|
123
|
+
// `remote: false`, so a bare `remote: useRemote` would leave the child
|
|
124
|
+
// with no signal and it would re-derive remoteness from
|
|
125
|
+
// `MODA_HARNESS_REMOTE` — silently overriding `--local` and the
|
|
126
|
+
// remote-failure fallback below. `--no-remote` survives (it's `true`)
|
|
127
|
+
// and takes precedence over the env in `isRemoteHarnessAnalyze`.
|
|
128
|
+
remote: useRemote ? true : undefined,
|
|
129
|
+
'no-remote': useRemote ? undefined : true,
|
|
126
130
|
quiet: true,
|
|
127
131
|
progress: true,
|
|
128
132
|
'no-telemetry': true,
|
|
129
133
|
}),
|
|
130
134
|
}));
|
|
135
|
+
log(renderInitStep(4, 7, 'Harness Discovery', remote
|
|
136
|
+
? 'Moda runs the harness analyst server-side and writes cited local artifacts.'
|
|
137
|
+
: 'Moda Analyst investigates the harness and writes cited local artifacts.'));
|
|
138
|
+
emit('harness', remote
|
|
139
|
+
? 'Running Moda-hosted harness discovery.'
|
|
140
|
+
: 'Running Moda Analyst harness discovery.');
|
|
141
|
+
let harnessResult;
|
|
142
|
+
try {
|
|
143
|
+
harnessResult = await runAnalyze(remote);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
// A remote failure (service down, upload error) must not sink onboarding —
|
|
147
|
+
// fall back to the local analyst path. If the caller pinned `--remote`
|
|
148
|
+
// explicitly, respect that and surface the failure.
|
|
149
|
+
if (remote && !isTruthyFlag(flagValue(flags, 'remote'))) {
|
|
150
|
+
warn(renderInitNote(`Server-side harness discovery unavailable (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, 'warning'));
|
|
151
|
+
emit('harness', 'Server-side discovery failed; falling back to local analyst.', 'warning');
|
|
152
|
+
harnessResult = await runAnalyze(false);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
131
158
|
if (harnessResult.exitCode !== 0) {
|
|
132
|
-
throw new CliInputError('Moda Analyst produced a harness report that did not pass validation.', 'Review .moda/harness-report-validation.json, then rerun `moda init --analyst=claude|cursor|codex` or `moda harness analyze --analyst=local-scan --summary`.');
|
|
159
|
+
throw new CliInputError('Moda Analyst produced a harness report that did not pass validation.', 'Review .moda/harness-report-validation.json, then rerun `moda init --local --analyst=claude|cursor|codex` or `moda harness analyze --analyst=local-scan --summary`.');
|
|
133
160
|
}
|
|
134
161
|
log(renderInitNote('Wrote or verified .moda/harness-report.json, .moda/harness.json, and .moda/HARNESS.md'));
|
|
135
162
|
if (human)
|
|
@@ -628,9 +655,12 @@ async function shouldIntegrateSdk(flags, yes) {
|
|
|
628
655
|
function parseOptionalBoolean(value) {
|
|
629
656
|
if (value === undefined)
|
|
630
657
|
return undefined;
|
|
631
|
-
|
|
658
|
+
// The CLI parser normalizes numeric-looking flag values to numbers, so
|
|
659
|
+
// `--remote=1` arrives as number 1 and `--remote=0` as number 0 (not the
|
|
660
|
+
// string spellings); accept both alongside the string/boolean forms.
|
|
661
|
+
if (value === true || value === 1 || value === 'true' || value === '1' || value === 'yes')
|
|
632
662
|
return true;
|
|
633
|
-
if (value === false || value === 'false' || value === '0' || value === 'no')
|
|
663
|
+
if (value === false || value === 0 || value === 'false' || value === '0' || value === 'no')
|
|
634
664
|
return false;
|
|
635
665
|
return undefined;
|
|
636
666
|
}
|
|
@@ -639,6 +669,21 @@ function isTruthyFlag(value) {
|
|
|
639
669
|
// number 1), so accept the numeric `1` alongside the string spellings.
|
|
640
670
|
return value === true || value === 1 || value === 'true' || value === '1' || value === 'yes';
|
|
641
671
|
}
|
|
672
|
+
/**
|
|
673
|
+
* Onboarding runs harness discovery SERVER-SIDE by default so the customer
|
|
674
|
+
* needs no local coding agent. Opt out with `--local`, `--no-remote`, or
|
|
675
|
+
* `--remote=false` to use a local coding-agent analyst instead.
|
|
676
|
+
*/
|
|
677
|
+
export function resolveHarnessRemote(flags) {
|
|
678
|
+
if (isTruthyFlag(flagValue(flags, 'local')))
|
|
679
|
+
return false;
|
|
680
|
+
if (isTruthyFlag(flagValue(flags, 'no-remote', 'no_remote')))
|
|
681
|
+
return false;
|
|
682
|
+
const remote = parseOptionalBoolean(flagValue(flags, 'remote'));
|
|
683
|
+
if (remote !== undefined)
|
|
684
|
+
return remote;
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
642
687
|
function flagValue(flags, ...keys) {
|
|
643
688
|
for (const key of keys) {
|
|
644
689
|
if (flags[key] !== undefined)
|
package/package.json
CHANGED