@moda-ai/cli 1.49.0 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -534,9 +534,11 @@ function writeSecretJsonFile(path, value) {
534
534
  // src/errors.ts
535
535
  class CliInputError extends Error {
536
536
  usage;
537
- constructor(message, usage) {
537
+ reason;
538
+ constructor(message, usage, reason) {
538
539
  super(message);
539
540
  this.usage = usage;
541
+ this.reason = reason;
540
542
  this.name = "CliInputError";
541
543
  }
542
544
  }
@@ -986,6 +988,9 @@ function packSnapshot(inputs, opts) {
986
988
  }
987
989
  // src/output.ts
988
990
  import { randomUUID } from "node:crypto";
991
+ import { lstatSync, mkdirSync as mkdirSync2, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
992
+ import { tmpdir } from "node:os";
993
+ import { join as join2 } from "node:path";
989
994
  var AGENT_SCHEMA_VERSION = "moda.agent.v1";
990
995
  var AGENT_EVENT_SCHEMA_VERSION = "moda.agent_event.v1";
991
996
  var FEEDBACK_TIP = 'Help us improve! If a result in this response looks wrong or is missing data, flag it: moda feedback "<describe what looked wrong>" [--category=...] [--conversation-id=... | --cluster-id=... | --tool-name=...]. Only submit genuine observations.';
@@ -1005,7 +1010,8 @@ var AGENT_ERROR_CODES = [
1005
1010
  "command_error",
1006
1011
  "access_denied",
1007
1012
  "unknown_error",
1008
- "cloud_unavailable"
1013
+ "cloud_unavailable",
1014
+ "output_error"
1009
1015
  ];
1010
1016
  var FALLBACK_RUN_ID = randomUUID();
1011
1017
  var fallbackSeq = 0;
@@ -1116,6 +1122,7 @@ function createCliOutput(options) {
1116
1122
  seq: 0,
1117
1123
  started: false,
1118
1124
  terminal: false,
1125
+ outputError: false,
1119
1126
  startedAt: Date.now(),
1120
1127
  lastEventAt: Date.now()
1121
1128
  };
@@ -1177,6 +1184,8 @@ function createCliOutput(options) {
1177
1184
  if (state.terminal)
1178
1185
  return;
1179
1186
  const envelope = writeEnvelope(data, writeOptions);
1187
+ if (envelope.errors.some((error) => error.code === "output_error"))
1188
+ state.outputError = true;
1180
1189
  if (options.mode === "agent-stream") {
1181
1190
  ensureStarted();
1182
1191
  emitEvent({
@@ -1272,6 +1281,9 @@ function createCliOutput(options) {
1272
1281
  hasTerminalOutput() {
1273
1282
  return state.terminal;
1274
1283
  },
1284
+ hasOutputError() {
1285
+ return state.outputError;
1286
+ },
1275
1287
  close() {
1276
1288
  clearHeartbeat(state);
1277
1289
  }
@@ -1281,41 +1293,198 @@ function createCliOutput(options) {
1281
1293
  function createAgentEnvelope(options) {
1282
1294
  const safeData = redactDeep(options.data);
1283
1295
  const maxBytes = agentMaxOutputBytes();
1296
+ let fullOutput;
1297
+ let saveFailed = false;
1298
+ let envelopeTextSaveFailed = false;
1299
+ const writeFullOutput = options.persistFullOutput === false ? undefined : () => {
1300
+ fullOutput ??= { path: writeAgentFullOutput(options.command, safeData) };
1301
+ if (!fullOutput.path)
1302
+ saveFailed = true;
1303
+ return fullOutput.path;
1304
+ };
1305
+ let text = redactDeep({
1306
+ summary: normalizeSummary(options.summary, options.command, options.status, safeData),
1307
+ findings: options.findings ?? [],
1308
+ evidence_refs: options.evidenceRefs ?? [],
1309
+ recommended_actions: options.recommendedActions ?? [],
1310
+ next_commands: normalizeNextCommands(options.nextCommands ?? []),
1311
+ warnings: options.warnings ?? inferWarnings(safeData)
1312
+ });
1313
+ let moved;
1284
1314
  const build = (bounded) => ({
1285
1315
  schema_version: AGENT_SCHEMA_VERSION,
1286
1316
  command: options.command,
1287
1317
  status: options.status,
1288
1318
  generated_at: new Date().toISOString(),
1289
1319
  ...options.profile ? { profile: options.profile } : {},
1290
- summary: normalizeSummary(options.summary, options.command, options.status, safeData),
1291
- findings: options.findings ?? [],
1292
- evidence_refs: options.evidenceRefs ?? [],
1293
- recommended_actions: options.recommendedActions ?? [],
1294
- next_commands: normalizeNextCommands(options.nextCommands ?? []),
1295
- artifacts: options.artifacts ?? [],
1320
+ summary: text.summary,
1321
+ findings: text.findings,
1322
+ evidence_refs: text.evidence_refs,
1323
+ recommended_actions: text.recommended_actions,
1324
+ next_commands: text.next_commands,
1325
+ artifacts: [
1326
+ ...options.artifacts ?? [],
1327
+ ...bounded.fullOutputPath ? [{ kind: "full_output", path: bounded.fullOutputPath, description: "Complete command output (the same JSON --json prints); data in this envelope was bounded." }] : [],
1328
+ ...moved?.path ? [{ kind: "full_envelope_text", path: moved.path, description: "Whole envelope text (summary, findings, evidence_refs, actions, next_commands, warnings) before oversized fields were moved out of this envelope." }] : []
1329
+ ],
1296
1330
  warnings: [
1297
- ...options.warnings ?? inferWarnings(safeData),
1298
- ...bounded.warning ? [bounded.warning] : []
1331
+ ...text.warnings,
1332
+ ...bounded.warning ? [bounded.warning] : [],
1333
+ ...moved ? [movedTextWarning(moved)] : []
1299
1334
  ],
1300
1335
  errors: options.errors ?? [],
1301
1336
  data: bounded.data,
1302
1337
  ...options.meta ? { meta: options.meta } : {},
1303
- ...bounded.truncation ? { truncation: bounded.truncation } : {}
1338
+ ...bounded.truncation || moved ? { truncation: {
1339
+ ...bounded.truncation ?? {},
1340
+ ...moved ? { envelope_text: {
1341
+ reason: "agent_output_byte_limit",
1342
+ moved_fields: moved.fields,
1343
+ complete: false,
1344
+ ...moved.whole ? { whole_text_moved: true } : {},
1345
+ ...moved.path ? { full_envelope_path: moved.path } : {}
1346
+ } } : {}
1347
+ } } : {}
1304
1348
  });
1305
- let envelope = build(boundForAgent(safeData, maxBytes));
1306
- const envelopeBytes = Buffer.byteLength(JSON.stringify(envelope), "utf8");
1307
- if (envelopeBytes > maxBytes) {
1308
- const overhead = envelopeBytes - Buffer.byteLength(JSON.stringify(envelope.data) ?? "", "utf8");
1309
- envelope = build(boundForAgent(safeData, Math.max(1024, maxBytes - overhead - 1024)));
1349
+ const sizeOf = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
1350
+ const rebound = (current) => {
1351
+ const overhead = sizeOf(current) - sizeOf(current.data);
1352
+ return build(boundForAgent(safeData, Math.max(1024, maxBytes - overhead - 1024), { writeFullOutput }));
1353
+ };
1354
+ let envelope = build(boundForAgent(safeData, maxBytes, { writeFullOutput }));
1355
+ if (sizeOf(envelope) > maxBytes)
1356
+ envelope = rebound(envelope);
1357
+ if (sizeOf(envelope) > maxBytes && !saveFailed) {
1358
+ const original = text;
1359
+ const savedPath = options.persistFullOutput === false ? undefined : writeAgentFullOutput(`${options.command}-envelope`, original);
1360
+ if (options.persistFullOutput !== false && !savedPath) {
1361
+ saveFailed = true;
1362
+ envelopeTextSaveFailed = true;
1363
+ } else {
1364
+ const slots = envelopeTextSlots(original).sort((left, right) => right.bytes - left.bytes);
1365
+ text = structuredClone(original);
1366
+ moved = { path: savedPath, fields: [] };
1367
+ for (const slot of slots) {
1368
+ if (sizeOf(envelope) <= maxBytes)
1369
+ break;
1370
+ const pointer = movedTextPointer(slot.field, slot.text, savedPath);
1371
+ const added = Buffer.byteLength(JSON.stringify(pointer), "utf8") + 2 * movedFieldNameBytes(slot.field);
1372
+ if (added >= slot.bytes)
1373
+ continue;
1374
+ slot.set(text, pointer);
1375
+ moved.fields.push(slot.field);
1376
+ envelope = rebound(envelope);
1377
+ }
1378
+ if (moved.fields.length === 0)
1379
+ moved = undefined;
1380
+ envelope = rebound(envelope);
1381
+ if (sizeOf(envelope) > maxBytes) {
1382
+ text = {
1383
+ summary: { text: movedWholeTextPointer(savedPath), confidence: original.summary.confidence },
1384
+ findings: [],
1385
+ evidence_refs: [],
1386
+ recommended_actions: [],
1387
+ next_commands: [],
1388
+ warnings: []
1389
+ };
1390
+ moved = { path: savedPath, fields: ["*"], whole: true };
1391
+ envelope = rebound(envelope);
1392
+ }
1393
+ }
1394
+ }
1395
+ if (saveFailed) {
1396
+ envelope = {
1397
+ ...envelope,
1398
+ status: "error",
1399
+ errors: [...envelope.errors, saveFailedError(maxBytes, envelopeTextSaveFailed, fullOutput?.path)]
1400
+ };
1310
1401
  }
1311
1402
  return redactDeep(envelope);
1312
1403
  }
1404
+ function saveFailedError(maxBytes, envelopeTextSaveFailed, dataPath) {
1405
+ const where = "(MODA_AGENT_OUTPUT_DIR is unwritable or the disk is full)";
1406
+ const remedy = "Point MODA_AGENT_OUTPUT_DIR at a writable directory, rerun with --json (no byte limit), or narrow the query.";
1407
+ if (!envelopeTextSaveFailed) {
1408
+ return {
1409
+ code: "output_error",
1410
+ cause: "full_output_save_failed",
1411
+ message: `Output is over the ${maxBytes}-byte agent limit and the complete output could not be saved to a file ${where}, ` + `so nothing was moved out: this envelope holds the whole result, over the limit. ${remedy}`,
1412
+ blocking: false
1413
+ };
1414
+ }
1415
+ const dataState = dataPath ? `data is NOT the whole result: it was bounded to fit, and the complete data was saved to ${dataPath} ` + "(truncation.data.full_output_path); read it in line ranges or with jq. " : "data is the whole result. ";
1416
+ return {
1417
+ code: "output_error",
1418
+ cause: "envelope_text_save_failed",
1419
+ message: `Output is over the ${maxBytes}-byte agent limit and the oversized envelope text (summary, findings, evidence_refs, ` + `recommended_actions, next_commands, warnings) could not be saved to a file ${where}, so that text was kept whole ` + `in this envelope, over the limit. ${dataState}${remedy}`,
1420
+ blocking: false
1421
+ };
1422
+ }
1423
+ function envelopeTextSlots(text) {
1424
+ const slots = [
1425
+ { field: "summary.text", text: text.summary.text, set: (target, value) => {
1426
+ target.summary.text = value;
1427
+ } }
1428
+ ];
1429
+ text.findings.forEach((finding, index) => {
1430
+ slots.push({ field: `findings[${index}].title`, text: finding.title, set: (target, value) => {
1431
+ target.findings[index].title = value;
1432
+ } });
1433
+ if (finding.description) {
1434
+ slots.push({ field: `findings[${index}].description`, text: finding.description, set: (target, value) => {
1435
+ target.findings[index].description = value;
1436
+ } });
1437
+ }
1438
+ });
1439
+ text.evidence_refs.forEach((ref, index) => {
1440
+ slots.push({ field: `evidence_refs[${index}].label`, text: ref.label, set: (target, value) => {
1441
+ target.evidence_refs[index].label = value;
1442
+ } });
1443
+ });
1444
+ text.recommended_actions.forEach((action, index) => {
1445
+ slots.push({ field: `recommended_actions[${index}].title`, text: action.title, set: (target, value) => {
1446
+ target.recommended_actions[index].title = value;
1447
+ } });
1448
+ if (action.rationale) {
1449
+ slots.push({ field: `recommended_actions[${index}].rationale`, text: action.rationale, set: (target, value) => {
1450
+ target.recommended_actions[index].rationale = value;
1451
+ } });
1452
+ }
1453
+ });
1454
+ text.next_commands.forEach((command, index) => {
1455
+ slots.push({ field: `next_commands[${index}].purpose`, text: command.purpose, set: (target, value) => {
1456
+ target.next_commands[index].purpose = value;
1457
+ } });
1458
+ });
1459
+ text.warnings.forEach((warning, index) => {
1460
+ slots.push({ field: `warnings[${index}]`, text: warning, set: (target, value) => {
1461
+ target.warnings[index] = value;
1462
+ } });
1463
+ });
1464
+ return slots.map((slot) => ({ ...slot, bytes: Buffer.byteLength(JSON.stringify(slot.text), "utf8") }));
1465
+ }
1466
+ function movedTextPointer(field, value, path) {
1467
+ const chars = Array.from(value).length;
1468
+ return path ? `[${chars} characters moved whole to ${path} at ${field}]` : `[${chars} characters over the agent byte limit; not saved. Rerun with --json or raise MODA_AGENT_MAX_OUTPUT_BYTES]`;
1469
+ }
1470
+ function movedFieldNameBytes(field) {
1471
+ return Buffer.byteLength(JSON.stringify(field), "utf8") + 2;
1472
+ }
1473
+ function movedWholeTextPointer(path) {
1474
+ return path ? `[Envelope text (summary, findings, evidence_refs, recommended_actions, next_commands, warnings) was over the agent byte limit and moved whole to ${path}]` : "[Envelope text was over the agent byte limit; not saved. Rerun with --json or raise MODA_AGENT_MAX_OUTPUT_BYTES]";
1475
+ }
1476
+ function movedTextWarning(moved) {
1477
+ if (moved.whole) {
1478
+ return "Envelope text outside data was over the agent byte limit even after moving its largest fields: all of it was moved whole " + (moved.path ? `to ${moved.path} (read it with jq)` : "(the file could not be saved; rerun with --json or raise MODA_AGENT_MAX_OUTPUT_BYTES)") + ".";
1479
+ }
1480
+ return `Envelope text outside data was over the agent byte limit: ${moved.fields.length} field(s) were moved whole ` + (moved.path ? `to ${moved.path} (same field paths; read it with jq)` : "(the file could not be saved; rerun with --json or raise MODA_AGENT_MAX_OUTPUT_BYTES)") + ` and replaced by pointers: ${moved.fields.join(", ")}.`;
1481
+ }
1313
1482
  var AGENT_ENVELOPE_RESERVE_BYTES = 2048;
1314
1483
  function agentEnvelopeFits(command, data, options = {}) {
1315
1484
  const maxBytes = agentMaxOutputBytes() - AGENT_ENVELOPE_RESERVE_BYTES;
1316
1485
  if (Buffer.byteLength(JSON.stringify(data) ?? "", "utf8") > maxBytes)
1317
1486
  return false;
1318
- const envelope = createAgentEnvelope({ command, status: "ok", data, ...options });
1487
+ const envelope = createAgentEnvelope({ command, status: "ok", data, ...options, persistFullOutput: false });
1319
1488
  return !envelope.truncation && Buffer.byteLength(JSON.stringify(envelope), "utf8") <= maxBytes;
1320
1489
  }
1321
1490
  function errorWarnings(error) {
@@ -1559,41 +1728,61 @@ function inferWarnings(data) {
1559
1728
  }
1560
1729
  return [];
1561
1730
  }
1562
- var AGENT_MAX_STRING_CHARS = 2000;
1563
- var PROTECTED_STRING_KEY = /(cursor|token|_id$|^id$|url$|href$|^key$|hash$)/i;
1564
1731
  function agentMaxOutputBytes() {
1565
1732
  return Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200000);
1566
1733
  }
1567
- function boundForAgent(data, maxBytes = agentMaxOutputBytes()) {
1734
+ var AGENT_FULL_OUTPUT_FILE = /-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z-[0-9a-f]{8}\.json$/;
1735
+ function agentOutputRetentionMs() {
1736
+ const hours = Number(process.env.MODA_AGENT_OUTPUT_RETENTION_HOURS ?? 24);
1737
+ return (Number.isFinite(hours) && hours > 0 ? hours : 24) * 3600000;
1738
+ }
1739
+ function pruneAgentFullOutputs(dir, keep, now = Date.now()) {
1740
+ const cutoff = now - agentOutputRetentionMs();
1741
+ let names;
1742
+ try {
1743
+ names = readdirSync(dir);
1744
+ } catch {
1745
+ return;
1746
+ }
1747
+ for (const name of names) {
1748
+ if (!AGENT_FULL_OUTPUT_FILE.test(name))
1749
+ continue;
1750
+ const path = join2(dir, name);
1751
+ if (path === keep)
1752
+ continue;
1753
+ try {
1754
+ const stat = lstatSync(path);
1755
+ if (stat.isFile() && stat.mtimeMs < cutoff)
1756
+ unlinkSync(path);
1757
+ } catch {}
1758
+ }
1759
+ }
1760
+ function writeAgentFullOutput(command, data) {
1761
+ try {
1762
+ const dir = process.env.MODA_AGENT_OUTPUT_DIR || join2(tmpdir(), "moda-agent-output");
1763
+ mkdirSync2(dir, { recursive: true, mode: 448 });
1764
+ const slug = command.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "output";
1765
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
1766
+ const path = join2(dir, `${slug}-${stamp}-${randomUUID().slice(0, 8)}.json`);
1767
+ writeFileSync2(path, `${JSON.stringify(data, null, 2)}
1768
+ `, { encoding: "utf8", mode: 384 });
1769
+ pruneAgentFullOutputs(dir, path);
1770
+ return path;
1771
+ } catch {
1772
+ return;
1773
+ }
1774
+ }
1775
+ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
1568
1776
  const sizeOf = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
1569
1777
  const serialized = JSON.stringify(data) ?? "";
1570
1778
  const originalBytes = Buffer.byteLength(serialized, "utf8");
1571
1779
  if (originalBytes <= maxBytes)
1572
1780
  return { data };
1573
- let shortenedStrings = 0;
1574
- const shorten = (value, limit, key) => {
1575
- if (typeof value === "string") {
1576
- if (value.length <= limit || key !== undefined && PROTECTED_STRING_KEY.test(key))
1577
- return value;
1578
- shortenedStrings += 1;
1579
- return `${value.slice(0, limit)}… [truncated ${value.length - limit} chars]`;
1580
- }
1581
- if (Array.isArray(value))
1582
- return value.map((item) => shorten(item, limit));
1583
- if (value && typeof value === "object") {
1584
- return Object.fromEntries(Object.entries(value).map(([entryKey, item]) => [entryKey, shorten(item, limit, entryKey)]));
1585
- }
1586
- return value;
1587
- };
1588
- let bounded = data;
1781
+ const fullOutputPath = options.writeFullOutput?.();
1782
+ if (options.writeFullOutput && !fullOutputPath)
1783
+ return { data, saveFailed: true };
1784
+ const bounded = JSON.parse(serialized);
1589
1785
  let boundedBytes = originalBytes;
1590
- for (const limit of [AGENT_MAX_STRING_CHARS, 500, 200]) {
1591
- shortenedStrings = 0;
1592
- bounded = shorten(data, limit);
1593
- boundedBytes = sizeOf(bounded);
1594
- if (boundedBytes <= maxBytes)
1595
- break;
1596
- }
1597
1786
  const droppedItems = {};
1598
1787
  const collectArrays = (value, path, out) => {
1599
1788
  if (Array.isArray(value)) {
@@ -1630,39 +1819,35 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes()) {
1630
1819
  boundedBytes = sizeOf(bounded);
1631
1820
  }
1632
1821
  const dropped = Object.entries(droppedItems);
1633
- const parts = [
1634
- shortenedStrings > 0 ? `shortened ${shortenedStrings} long text field(s)` : "",
1635
- ...dropped.map(([path, count]) => `dropped the last ${count} item(s) of data.${path}`)
1636
- ].filter(Boolean);
1637
- const howToGetMore = "For complete results narrow the query (a smaller --limit, with --offset or --cursor paging where the command supports it), " + "rerun with --json (no byte limit), or raise MODA_AGENT_MAX_OUTPUT_BYTES.";
1822
+ const whereIsTheRest = fullOutputPath ? `The complete output (every item, no text shortened) is in ${fullOutputPath}; read it in line ranges or query it with jq. ` : "The complete output could not be saved to a file. ";
1823
+ const howToGetMore = whereIsTheRest + "Alternatively narrow the query (a smaller --limit, with --offset or --cursor paging where the command supports it), " + "rerun with --json (no byte limit), or raise MODA_AGENT_MAX_OUTPUT_BYTES.";
1824
+ const pathField = fullOutputPath ? { full_output_path: fullOutputPath } : {};
1638
1825
  if (boundedBytes > maxBytes) {
1639
- const stub = { truncated: true, original_bytes: originalBytes, max_bytes: maxBytes, preview: serialized.slice(0, Math.max(0, maxBytes - 256)) };
1640
- for (let stubBytes = sizeOf(stub);stubBytes > maxBytes && stub.preview.length > 0; stubBytes = sizeOf(stub)) {
1641
- stub.preview = stub.preview.slice(0, Math.max(0, stub.preview.length - (stubBytes - maxBytes) - 16));
1642
- }
1643
1826
  return {
1644
- data: stub,
1645
- warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit, and could not be reduced while keeping its structure; data is a string preview. ${howToGetMore}`,
1827
+ data: { truncated: true, original_bytes: originalBytes, max_bytes: maxBytes, ...pathField },
1828
+ warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit, and could not be reduced while keeping its structure; data is a pointer stub. ${howToGetMore}`,
1646
1829
  truncation: {
1647
- data: { original_bytes: originalBytes, max_bytes: maxBytes, reason: "agent_output_byte_limit", structure_preserved: false, complete: false }
1648
- }
1830
+ data: { original_bytes: originalBytes, max_bytes: maxBytes, reason: "agent_output_byte_limit", structure_preserved: false, complete: false, ...pathField }
1831
+ },
1832
+ fullOutputPath
1649
1833
  };
1650
1834
  }
1651
1835
  return {
1652
1836
  data: bounded,
1653
- warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit: ${parts.join("; ")}. ${howToGetMore}`,
1837
+ warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit: ${dropped.map(([path, count]) => `dropped the last ${count} item(s) of data.${path}`).join("; ")}. ${howToGetMore}`,
1654
1838
  truncation: {
1655
1839
  data: {
1656
1840
  original_bytes: originalBytes,
1657
1841
  bounded_bytes: boundedBytes,
1658
1842
  max_bytes: maxBytes,
1659
1843
  reason: "agent_output_byte_limit",
1660
- shortened_strings: shortenedStrings,
1661
1844
  dropped_items: droppedItems,
1662
1845
  structure_preserved: true,
1663
- complete: false
1846
+ complete: false,
1847
+ ...pathField
1664
1848
  }
1665
- }
1849
+ },
1850
+ fullOutputPath
1666
1851
  };
1667
1852
  }
1668
1853
  function asRecord(value) {
@@ -13,7 +13,7 @@ import {
13
13
  resolvePromptSource,
14
14
  safePromptEvidencePath,
15
15
  scanHarness
16
- } from "./cli-m3bg9yyh.js";
16
+ } from "./cli-he2wb4hd.js";
17
17
  import {
18
18
  SKILL_SOURCE_HARNESS,
19
19
  SKILL_SOURCE_INTEGRATION_BUNDLED,
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  isAuthSessionValid,
28
28
  loadAuthSession
29
- } from "./cli-c64d2jrw.js";
29
+ } from "./cli-kxwm7y62.js";
30
30
  import {
31
31
  CliInputError,
32
32
  EXIT_ERROR,
@@ -45,7 +45,7 @@ import {
45
45
  resolveTenantId,
46
46
  secretFilePathFromRef,
47
47
  validateConfig
48
- } from "./cli-bqhs6sk7.js";
48
+ } from "./cli-ac3p2byq.js";
49
49
  import {
50
50
  __commonJS,
51
51
  __require,
@@ -6711,7 +6711,7 @@ async function runPromptSyncExclusive(flags, profileOptions, options) {
6711
6711
  const analyzedDefinitions = new Map;
6712
6712
  if (flags["no-analyze"] !== "true") {
6713
6713
  options.onProgress?.("Analyzing prompt sources before sync.");
6714
- const { analyzeHarnessPrompts } = await import("./harness-4ypeg9dh.js");
6714
+ const { analyzeHarnessPrompts } = await import("./harness-s13wvdxt.js");
6715
6715
  const result = await analyzeHarnessPrompts({
6716
6716
  rootDir: process.cwd(),
6717
6717
  existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
@@ -25,7 +25,7 @@ import {
25
25
  resolveTenantId,
26
26
  shouldInventoryFile,
27
27
  stringOption
28
- } from "./cli-bqhs6sk7.js";
28
+ } from "./cli-ac3p2byq.js";
29
29
  import {
30
30
  __commonJS,
31
31
  __require,
@@ -180210,7 +180210,7 @@ function citationExcerpt(rootDir, path, line) {
180210
180210
  const selected = line ? lines[line - 1] : lines.find((candidate) => candidate.trim().length > 0);
180211
180211
  if (!selected)
180212
180212
  return {};
180213
- return { excerpt: selected.trim().slice(0, 240) };
180213
+ return { excerpt: selected.trim() };
180214
180214
  } catch {
180215
180215
  return {};
180216
180216
  }
@@ -181065,7 +181065,6 @@ function enrichScanCaptureMetadata(graph) {
181065
181065
  // src/harness-candidates.ts
181066
181066
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
181067
181067
  import { basename as basename3, dirname as dirname3, extname, resolve as resolve4 } from "node:path";
181068
- var EXCERPT_MAX_CHARS = 240;
181069
181068
  var MAX_CANDIDATES_PER_FILE_PER_SIGNAL = 20;
181070
181069
  var MIN_PROMPT_LITERAL_CHARS = 160;
181071
181070
  var CONFIDENCE_WEIGHT = {
@@ -181311,8 +181310,7 @@ function instructionScore(text) {
181311
181310
  return score;
181312
181311
  }
181313
181312
  function normalizeExcerpt(text) {
181314
- const collapsed = text.replace(/\s+/g, " ").trim();
181315
- return collapsed.length > EXCERPT_MAX_CHARS ? `${collapsed.slice(0, EXCERPT_MAX_CHARS - 1)}…` : collapsed;
181313
+ return text.replace(/\s+/g, " ").trim();
181316
181314
  }
181317
181315
  function buildLineIndex(text) {
181318
181316
  const offsets = [0];
@@ -181423,7 +181421,7 @@ function detectPromptModule(file, out) {
181423
181421
  `).length),
181424
181422
  confidence: "medium",
181425
181423
  signal: "prompt_module.name_hint",
181426
- excerpt: normalizeExcerpt(file.text.slice(0, EXCERPT_MAX_CHARS * 2))
181424
+ excerpt: normalizeExcerpt(file.text)
181427
181425
  });
181428
181426
  }
181429
181427
  function detectPromptLiterals(file, out) {
@@ -181506,7 +181504,7 @@ function detectPromptFile(file, out) {
181506
181504
  `).length),
181507
181505
  confidence,
181508
181506
  signal,
181509
- excerpt: normalizeExcerpt(file.text.slice(0, EXCERPT_MAX_CHARS * 2))
181507
+ excerpt: normalizeExcerpt(file.text)
181510
181508
  });
181511
181509
  }
181512
181510
  function detectPromptLoaderReferences(file, filesByPath, out) {
@@ -184859,7 +184857,7 @@ async function runHarnessCommand(context) {
184859
184857
  throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
184860
184858
  }
184861
184859
  if (context.flags["github-actions"] === "true") {
184862
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-dvq7spqh.js");
184860
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-gbty3k66.js");
184863
184861
  const result = await runGithubActionsAnalyze(rootDir, {
184864
184862
  writeReport: (report2) => {
184865
184863
  const normalized = normalizeHarnessReport(report2);
@@ -186605,6 +186603,7 @@ var CONCERN_CANDIDATE_KINDS = {
186605
186603
  evals: ["model_config"]
186606
186604
  };
186607
186605
  var MAX_SEED_CANDIDATES_PER_KIND = 40;
186606
+ var MAX_INLINE_CANDIDATE_EXCERPT_CHARS = 500;
186608
186607
  var MAX_SEED_READ_PLAN_FILES = 25;
186609
186608
  var SEED_CANDIDATE_KIND_LABELS = {
186610
186609
  llm_call_site: "LLM call sites",
@@ -186644,7 +186643,7 @@ function renderAnalystCandidateLines(report, concern) {
186644
186643
  const shown = bucket.slice(0, MAX_SEED_CANDIDATES_PER_KIND);
186645
186644
  lines.push(`${label} (${bucket.length}${bucket.length > shown.length ? `, showing ${shown.length}` : ""}):`);
186646
186645
  for (const candidate of shown) {
186647
- lines.push(`- ${candidate.path}:${candidate.startLine} [${candidate.signal}] ${candidate.excerpt}`);
186646
+ lines.push(candidate.endLine > candidate.startLine ? `- ${candidate.path}:${candidate.startLine}-${candidate.endLine} [${candidate.signal}] (multi-line span; read lines ${candidate.startLine}-${candidate.endLine} of the file)` : candidate.excerpt.length > MAX_INLINE_CANDIDATE_EXCERPT_CHARS ? `- ${candidate.path}:${candidate.startLine} [${candidate.signal}] (${candidate.excerpt.length}-character match; read line ${candidate.startLine} of the file)` : `- ${candidate.path}:${candidate.startLine} [${candidate.signal}] ${candidate.excerpt}`);
186648
186647
  }
186649
186648
  }
186650
186649
  const readPlan = report.files.slice(0, MAX_SEED_READ_PLAN_FILES);
@@ -7,7 +7,7 @@ import {
7
7
  saveProfileConfig,
8
8
  secretFilePathFromRef,
9
9
  writeSecretJsonFile
10
- } from "./cli-bqhs6sk7.js";
10
+ } from "./cli-ac3p2byq.js";
11
11
  import {
12
12
  __require
13
13
  } from "./cli-0v6na3yp.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  authFetch,
3
3
  getCachedLoginContext
4
- } from "./cli-c64d2jrw.js";
4
+ } from "./cli-kxwm7y62.js";
5
5
  import {
6
6
  CliAccessError,
7
7
  CliInputError,
@@ -15,7 +15,7 @@ import {
15
15
  resolveTenantIdFromActiveProfile,
16
16
  saveCliConfig,
17
17
  writeSecretJsonFile
18
- } from "./cli-bqhs6sk7.js";
18
+ } from "./cli-ac3p2byq.js";
19
19
 
20
20
  // src/init/tenant.ts
21
21
  import { randomUUID } from "node:crypto";