@moda-ai/cli 1.55.26 → 1.55.27

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.
Files changed (30) hide show
  1. package/dist/{agent-nqmha8ck.js → agent-vp9prdma.js} +8 -8
  2. package/dist/{cli-k6q8dbpk.js → cli-0pkc8jhg.js} +3 -3
  3. package/dist/{cli-5g1tv80h.js → cli-4h0ave0f.js} +5 -5
  4. package/dist/{cli-8tfk8qeb.js → cli-6et4z504.js} +7 -6
  5. package/dist/{cli-1mbb0ct0.js → cli-ccjfwzqx.js} +1 -1
  6. package/dist/{cli-q6f6vrrr.js → cli-hmjdgxrn.js} +1 -1
  7. package/dist/{cli-jtyvbd8d.js → cli-pg5qfmqc.js} +1 -1
  8. package/dist/{cli-b4740tnv.js → cli-q9nsvqqg.js} +14 -14
  9. package/dist/{cli-d577kq6h.js → cli-qwgct6x7.js} +7 -6
  10. package/dist/{cli-sqsjd08w.js → cli-r5pp7qny.js} +64 -438
  11. package/dist/{cli-cdrvjva5.js → cli-v8q7sd7g.js} +28 -6
  12. package/dist/cli-vdkwkkp5.js +567 -0
  13. package/dist/{cli-jgfm7w1r.js → cli-wzds257x.js} +5 -4
  14. package/dist/cli.js +299 -151
  15. package/dist/{doctor-w913fj4g.js → doctor-wmre3mr7.js} +8 -8
  16. package/dist/{fix-crkwdkt4.js → fix-75s9s0d6.js} +45 -47
  17. package/dist/{harness-x3e7d68b.js → harness-a7tb9a23.js} +5 -5
  18. package/dist/{harness-github-actions-fjhvhgpj.js → harness-github-actions-6v4wydb8.js} +5 -5
  19. package/dist/{index-2kf3fnnb.js → index-91bpc8g8.js} +15 -14
  20. package/dist/{production-intelligence-7n83c701.js → production-intelligence-7nqddd4f.js} +15 -15
  21. package/dist/{profile-commands-ct2t0bfr.js → profile-commands-49yn02yg.js} +5 -4
  22. package/dist/prompt-source-mcp.js +5 -5
  23. package/dist/{prompts-92aqqb2k.js → prompts-17wyrst6.js} +6 -6
  24. package/dist/{provision-gn8raqpt.js → provision-bc3h68e1.js} +5 -5
  25. package/dist/{registry-ex162j5y.js → registry-fpzhjznj.js} +3 -3
  26. package/dist/{skills-2mbsj9bt.js → skills-ccdbqghj.js} +5 -5
  27. package/dist/{sync-kghmmmk5.js → sync-hkxce5zh.js} +7 -7
  28. package/package.json +1 -1
  29. package/skills/integration/index.json +2 -2
  30. package/dist/cli-bb7133m7.js +0 -144
@@ -449,6 +449,14 @@ function resolveBaseUrl(options = {}) {
449
449
  return PROD_BASE_URL;
450
450
  }
451
451
  }
452
+ function resolveBaseUrlStrict(options = {}) {
453
+ try {
454
+ getActiveProfile(options);
455
+ } catch {
456
+ return;
457
+ }
458
+ return resolveBaseUrl(options);
459
+ }
452
460
  function resolveIngestUrl(options = {}) {
453
461
  const environment = activeProfileEnvironment(options);
454
462
  const envIngest = optionalString(getEnv(options).MODA_INGEST_URL);
@@ -668,6 +676,9 @@ var MODA_INGEST_URL = process.env.MODA_INGEST_URL || DEFAULT_INGEST_URL;
668
676
  function resolveModaBaseUrl(options = {}) {
669
677
  return resolveBaseUrl(options) || PROD_BASE_URL;
670
678
  }
679
+ function resolveModaBaseUrlStrict(options = {}) {
680
+ return resolveBaseUrlStrict(options);
681
+ }
671
682
  function validateConfig() {
672
683
  if (!resolveApiKey()) {
673
684
  throw new CliAuthError('No Moda API key found. Set MODA_API_KEY=moda_sk_... in the environment, save one with "moda auth login --api-key", or run "moda init" for interactive setup.', {
@@ -1029,425 +1040,13 @@ function packSnapshot(inputs, opts) {
1029
1040
  skips
1030
1041
  };
1031
1042
  }
1032
- // src/batch-reads.ts
1033
- import { readFile } from "node:fs/promises";
1034
- var BATCH_MAX_TARGETS = 25;
1035
- var BATCH_CONCURRENCY = 4;
1036
- var ANCHOR_SUFFIX = /^(.+)@(\d+)$/;
1037
- function parseBatchTarget(text, anchors, usage) {
1038
- const raw = text.trim();
1039
- if (raw.startsWith("{")) {
1040
- let parsed;
1041
- try {
1042
- parsed = JSON.parse(raw);
1043
- } catch {
1044
- throw new CliInputError(`Could not parse the JSON target ${JSON.stringify(raw)}.`, usage, "bad_batch_target");
1045
- }
1046
- const record = parsed;
1047
- const id = record?.id ?? record?.conversation_id ?? record?.trace_id;
1048
- if (typeof id !== "string" || id.trim() === "") {
1049
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has no "id" string.`, usage, "bad_batch_target");
1050
- }
1051
- const index = record.msg_index ?? record.message_index;
1052
- if (index !== undefined && index !== null) {
1053
- if (!anchors) {
1054
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has a msg_index, which this command does not take.`, usage, "bad_batch_target");
1055
- }
1056
- if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
1057
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has an invalid msg_index (want an integer >= 0).`, usage, "bad_batch_target");
1058
- }
1059
- return { raw, id, msgIndex: index };
1060
- }
1061
- return { raw, id };
1062
- }
1063
- if (anchors) {
1064
- const match = ANCHOR_SUFFIX.exec(raw);
1065
- if (match) {
1066
- const msgIndex = Number(match[2]);
1067
- if (!Number.isSafeInteger(msgIndex)) {
1068
- throw new CliInputError(`Target ${JSON.stringify(raw)} has an @msg_index past ${Number.MAX_SAFE_INTEGER}. ` + 'If the id itself ends in @<digits>, pass it as {"id": "..."}.', usage, "bad_batch_target");
1069
- }
1070
- return { raw, id: match[1], msgIndex };
1071
- }
1072
- }
1073
- return { raw, id: raw };
1074
- }
1075
- function parseTargetLines(text, anchors, usage) {
1076
- return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "").map((line) => parseBatchTarget(line, anchors, usage));
1077
- }
1078
- async function readStdinText() {
1079
- if (process.stdin.isTTY) {
1080
- throw new CliInputError("--ids-from=- reads targets from stdin, but stdin is a terminal. Pipe ids in (one per line) or pass a file path.", undefined, "ids_from_tty");
1081
- }
1082
- const chunks = [];
1083
- for await (const chunk of process.stdin)
1084
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
1085
- return Buffer.concat(chunks).toString("utf8");
1086
- }
1087
- async function readIdsFrom(source, usage) {
1088
- if (source === "" || source === "true") {
1089
- throw new CliInputError("--ids-from needs a value: - (stdin) or a file path.", usage, "missing_flag_value");
1090
- }
1091
- if (source === "-")
1092
- return readStdinText();
1093
- try {
1094
- return await readFile(source, "utf8");
1095
- } catch (error) {
1096
- const code = error.code;
1097
- throw new CliInputError(`Could not read --ids-from file ${JSON.stringify(source)}${typeof code === "string" ? ` (${code})` : ""}.`, usage, "ids_from_unreadable");
1098
- }
1099
- }
1100
- async function collectBatchTargets(positionals, idsFrom, options) {
1101
- const targets = positionals.map((value) => parseBatchTarget(value, options.anchors, options.usage));
1102
- if (idsFrom !== undefined) {
1103
- targets.push(...parseTargetLines(await readIdsFrom(idsFrom, options.usage), options.anchors, options.usage));
1104
- }
1105
- const seen = new Set;
1106
- const unique = targets.filter((target) => {
1107
- const key = `${target.id}\x00${target.msgIndex ?? ""}`;
1108
- if (seen.has(key))
1109
- return false;
1110
- seen.add(key);
1111
- return true;
1112
- });
1113
- if (unique.length === 0) {
1114
- throw new CliInputError(`No targets: --ids-from gave no ids (one per line).`, options.usage, "missing_positional");
1115
- }
1116
- if (unique.length > BATCH_MAX_TARGETS) {
1117
- throw new CliInputError(`Got ${unique.length} targets; 'moda ${options.command}' reads at most ${BATCH_MAX_TARGETS} per call, so nothing was read. ` + `Split the ids into calls of ${BATCH_MAX_TARGETS} or fewer.`, options.usage, "batch_too_large");
1118
- }
1119
- return unique;
1120
- }
1121
- async function mapWithConcurrency(items, limit, fn, options = {}) {
1122
- const results = new Array(items.length);
1123
- let next = 0;
1124
- let failure;
1125
- const worker = async () => {
1126
- while (!failure && next < items.length && (options.shouldStart?.(next) ?? true)) {
1127
- const index = next++;
1128
- try {
1129
- results[index] = await fn(items[index], index);
1130
- } catch (error) {
1131
- failure ??= { error };
1132
- }
1133
- }
1134
- };
1135
- await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
1136
- if (failure)
1137
- throw failure.error;
1138
- return next < items.length ? results.slice(0, next) : results;
1139
- }
1140
- function idNeedsJsonTarget(id) {
1141
- return ANCHOR_SUFFIX.test(id);
1142
- }
1143
- function contextTarget(id) {
1144
- const verbatim = id === id.trim() && !id.startsWith("{") && !idNeedsJsonTarget(id);
1145
- return verbatim ? id : JSON.stringify({ id });
1146
- }
1147
-
1148
- // src/transforms.ts
1149
- function asRecord(value) {
1150
- if (value && typeof value === "object" && !Array.isArray(value)) {
1043
+ // src/shell-quote.ts
1044
+ function shellArg(value) {
1045
+ if (/^[A-Za-z0-9_.,:@%+/=-]+$/.test(value))
1151
1046
  return value;
1152
- }
1153
- return null;
1154
- }
1155
- function asString(value) {
1156
- return typeof value === "string" ? value : null;
1157
- }
1158
- function asNumber(value) {
1159
- return typeof value === "number" && Number.isFinite(value) ? value : null;
1160
- }
1161
- function frustrationAnchorIndex(row) {
1162
- if (!row)
1163
- return null;
1164
- const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
1165
- const firstQuote = asRecord(userQuotes[0]);
1166
- const resolved = asNumber(firstQuote?.msg_index) ?? asNumber(row.anchor_msg_index);
1167
- if (resolved !== null)
1168
- return resolved;
1169
- const keyTurns = Array.isArray(row.key_turns) ? row.key_turns : [];
1170
- const firstTurn = keyTurns.find((t) => typeof t === "number");
1171
- if (firstTurn !== undefined)
1172
- return firstTurn;
1173
- return asNumber(firstQuote?.turn);
1174
- }
1175
- function buildFrustrationAnchor(row) {
1176
- const conversation_id = asString(row.conversation_id) ?? "";
1177
- const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
1178
- const firstQuote = asRecord(userQuotes[0]);
1179
- const keyTurnsRaw = Array.isArray(row.key_turns) ? row.key_turns : [];
1180
- const all_turns = keyTurnsRaw.filter((t) => typeof t === "number");
1181
- const msg_index = frustrationAnchorIndex(row);
1182
- let signal = null;
1183
- let quote_preview = null;
1184
- if (firstQuote) {
1185
- signal = asString(firstQuote.signal);
1186
- const quote = asString(firstQuote.quote);
1187
- if (quote !== null) {
1188
- let end = 0;
1189
- let points = 0;
1190
- for (const ch of quote) {
1191
- if (points++ === 200)
1192
- break;
1193
- end += ch.length;
1194
- }
1195
- quote_preview = quote.slice(0, end);
1196
- }
1197
- }
1198
- return {
1199
- kind: "frustration",
1200
- conversation_id,
1201
- msg_index,
1202
- signal,
1203
- quote_preview,
1204
- all_turns,
1205
- no_anchor: msg_index === null
1206
- };
1207
- }
1208
- function buildToolFailureAnchor(row, toolName) {
1209
- const conversation_id = asString(row.conversation_id) ?? "";
1210
- const msg_index = asNumber(row.msg_index);
1211
- const tool_use_id = asString(row.tool_use_id);
1212
- const error_subtype = asString(row.error_subtype);
1213
- return {
1214
- kind: "tool_failure",
1215
- conversation_id,
1216
- msg_index,
1217
- tool_name: toolName,
1218
- tool_use_id,
1219
- error_subtype,
1220
- no_anchor: msg_index === null
1221
- };
1222
- }
1223
- function frustrationsFromEmotions(response) {
1224
- if (!Array.isArray(response.detections) || Array.isArray(response.frustrations))
1225
- return response;
1226
- const summary = asRecord(response.summary) ?? {};
1227
- const pagination = asRecord(response.pagination) ?? {};
1228
- const byFamily = asRecord(summary.by_family) ?? {};
1229
- const totalAnalyzed = asNumber(summary.conversations_analyzed) ?? 0;
1230
- const byFamilyConversations = asRecord(summary.conversations_by_family);
1231
- const frustrated = asNumber(byFamilyConversations?.frustration) ?? asNumber(byFamily.frustration) ?? 0;
1232
- const frustrations = response.detections.map((item) => asRecord(item)).filter((row) => row !== null && (row.family === undefined || row.family === "frustration")).map(({ conversation_context, score, is_detected, ...row }) => ({
1233
- ...row,
1234
- is_frustrated: is_detected === true,
1235
- frustration_score: score,
1236
- target: row.elicitor === "bot" || row.elicitor === "external" ? row.elicitor : "ambiguous",
1237
- conversation: conversation_context ?? null
1238
- }));
1239
- const atRisk = asNumber(pagination.at_risk) ?? Math.max(0, (asNumber(pagination.total) ?? 0) - frustrated);
1240
- const signalBreakdown = {};
1241
- for (const item of Array.isArray(response.signal_breakdown) ? response.signal_breakdown : []) {
1242
- const row = asRecord(item);
1243
- if (row?.family === "frustration" && typeof row.signal === "string") {
1244
- signalBreakdown[row.signal] = asNumber(row.count) ?? 0;
1245
- }
1246
- }
1247
- const { detections: _detections, ...rest } = response;
1248
- return {
1249
- ...rest,
1250
- summary: {
1251
- total_analyzed: totalAnalyzed,
1252
- frustrated_count: frustrated,
1253
- at_risk_count: atRisk,
1254
- frustration_rate_pct: totalAnalyzed > 0 ? Math.round(frustrated / totalAnalyzed * 1000) / 10 : 0
1255
- },
1256
- signal_breakdown: signalBreakdown,
1257
- frustrations,
1258
- source: "emotion_detections"
1259
- };
1260
- }
1261
- function withFrustrationAnchors(response) {
1262
- if (!Array.isArray(response.frustrations))
1263
- return response;
1264
- const frustrations = response.frustrations.map((item) => {
1265
- const row = asRecord(item);
1266
- if (!row)
1267
- return item;
1268
- const anchor = buildFrustrationAnchor(row);
1269
- return { ...row, anchor };
1270
- });
1271
- return { ...response, frustrations };
1272
- }
1273
- var TOOL_FAILURE_CONTEXT_RADIUS = 2;
1274
- function shellWord(value) {
1275
- return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
1276
- }
1277
- function trimFailureWindow(value, radius, fallbackCenter) {
1278
- const conv = asRecord(value);
1279
- if (!conv)
1280
- return value ?? null;
1281
- const { coverage: _coverage, context, ...convRest } = conv;
1282
- const ctx = asRecord(context);
1283
- if (!ctx)
1284
- return convRest;
1285
- const center = asNumber(ctx.center_index) ?? fallbackCenter;
1286
- const messages = (Array.isArray(ctx.messages) ? ctx.messages : []).map((message) => asRecord(message)).filter((message) => {
1287
- const index = asNumber(message?.index);
1288
- return message !== null && (center === null || index === null || Math.abs(index - center) <= radius);
1289
- }).map(({ content_blocks, ...message }) => {
1290
- const covered = typeof message.content === "string" && message.content.length > 0 || Array.isArray(message.tool_calls) && message.tool_calls.length > 0 || Array.isArray(message.tool_results) && message.tool_results.length > 0;
1291
- return covered || content_blocks === undefined ? message : { ...message, content_blocks };
1292
- });
1293
- const indexes = messages.map((message) => asNumber(message.index)).filter((index) => index !== null);
1294
- return {
1295
- ...convRest,
1296
- context: {
1297
- ...ctx,
1298
- ...indexes.length > 0 ? { from_index: Math.min(...indexes), to_index: Math.max(...indexes) } : {},
1299
- messages
1300
- }
1301
- };
1302
- }
1303
- function trimToolFailureConversations(response, opts) {
1304
- if (!Array.isArray(response.examples))
1305
- return response;
1306
- const examples = response.examples.map((item) => {
1307
- const row = asRecord(item);
1308
- if (!row)
1309
- return item;
1310
- const { conversation, window, ...rest } = row;
1311
- const conversationId = asString(row.conversation_id);
1312
- const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? asNumber(row.msg_index);
1313
- return {
1314
- ...rest,
1315
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${TOOL_FAILURE_CONTEXT_RADIUS}` } : {},
1316
- ...opts.drop ? {} : { conversation: trimFailureWindow(conversation, TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) },
1317
- ..."window" in row ? { window: trimFailureWindow(window, opts.windowRadius ?? TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) } : {}
1318
- };
1319
- });
1320
- return { ...response, examples };
1321
- }
1322
- function withToolFailureAnchors(response) {
1323
- if (!Array.isArray(response.examples))
1324
- return response;
1325
- const toolName = asString(response.tool_name) ?? "";
1326
- const examples = response.examples.map((item) => {
1327
- const row = asRecord(item);
1328
- if (!row)
1329
- return item;
1330
- const anchor = buildToolFailureAnchor(row, toolName);
1331
- return { ...row, anchor };
1332
- });
1333
- return { ...response, examples };
1334
- }
1335
- function embeddedWindowSize(conversation) {
1336
- if (!conversation)
1337
- return null;
1338
- const ctx = asRecord(conversation.context);
1339
- if (!ctx)
1340
- return null;
1341
- const center = asNumber(ctx.center_index);
1342
- const from = asNumber(ctx.from_index);
1343
- const to = asNumber(ctx.to_index);
1344
- if (center === null || from === null || to === null)
1345
- return null;
1346
- return Math.min(center - from, to - center);
1347
- }
1348
- var DETECTION_CONTEXT_RADIUS = 2;
1349
- function withoutDetectionWindows(response, listKey, windowKey) {
1350
- const list = response[listKey];
1351
- if (!Array.isArray(list))
1352
- return response;
1353
- const rows = list.map((item) => {
1354
- const row = asRecord(item);
1355
- if (!row)
1356
- return item;
1357
- const { [windowKey]: _window, ...rest } = row;
1358
- const conversationId = asString(row.conversation_id);
1359
- const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? frustrationAnchorIndex(row);
1360
- return {
1361
- ...rest,
1362
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${DETECTION_CONTEXT_RADIUS}` } : {}
1363
- };
1364
- });
1365
- return { ...response, [listKey]: rows };
1366
- }
1367
- var PROBLEM_COMPACT_EVIDENCE = 5;
1368
- var PROBLEM_COMPACT_OMITTED_BLOCKS = [
1369
- "rubric",
1370
- "trend",
1371
- "sub_problems",
1372
- "affected_conversations",
1373
- "remainder",
1374
- "confidence",
1375
- "change_history",
1376
- "stories"
1377
- ];
1378
- function readCommand(command, purpose) {
1379
- return { command, purpose, mutability: "read", requires_approval: false };
1380
- }
1381
- function evidenceMsgIndex(row) {
1382
- return asNumber(row.anchor_msg_index) ?? asNumber(asRecord(asRecord(row.snippet_parsed)?.anchor)?.msg_index);
1383
- }
1384
- function compactProblemDossier(response, problemId) {
1385
- const id = shellWord(asString(response.problem_id) ?? problemId);
1386
- const evidence = (Array.isArray(response.evidence) ? response.evidence : []).map((item) => asRecord(item)).filter((row) => row !== null);
1387
- const topEvidence = evidence.slice(0, PROBLEM_COMPACT_EVIDENCE).map((row) => {
1388
- const conversationId = asString(row.conversation_id);
1389
- const msgIndex = evidenceMsgIndex(row);
1390
- return {
1391
- attribution_id: row.attribution_id ?? null,
1392
- conversation_id: conversationId,
1393
- msg_index: msgIndex,
1394
- signal_family: row.signal_family ?? null,
1395
- signal_type: row.signal_type ?? null,
1396
- confidence: row.confidence ?? null,
1397
- causal_rationale: row.causal_rationale ?? null,
1398
- door: row.door ?? null,
1399
- replay_verified: row.replay_verified ?? null,
1400
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex}` } : {}
1401
- };
1402
- });
1403
- const reportsBlock = asRecord(response.investigation_reports);
1404
- const reports = (Array.isArray(reportsBlock?.items) ? reportsBlock.items : []).map((item) => asRecord(item)).filter((row) => row !== null).map((row) => ({
1405
- report_id: row.report_id ?? null,
1406
- conversation_id: row.conversation_id ?? null,
1407
- mechanism: row.mechanism ?? null,
1408
- suspected_surface: row.suspected_surface ?? null,
1409
- severity: row.severity ?? null,
1410
- problem_found: row.problem_found ?? null
1411
- }));
1412
- const verification = asRecord(response.verification);
1413
- const { samples: _samples, ...verificationCounts } = verification ?? {};
1414
- const dossier = asRecord(response.dossier);
1415
- const { representative_story_keys: _storyKeys, ...dossierSummary } = dossier ?? {};
1416
- const trend = Array.isArray(response.trend) ? response.trend : [];
1417
- const data = {
1418
- found: response.found ?? true,
1419
- problem_id: response.problem_id ?? problemId,
1420
- requested_problem_id: response.requested_problem_id ?? problemId,
1421
- alias_resolved: response.alias_resolved ?? false,
1422
- view: "compact",
1423
- header: response.header ?? null,
1424
- dossier: dossier ? dossierSummary : null,
1425
- verification: verification ? verificationCounts : null,
1426
- ...response.impact_identity !== undefined ? { impact_identity: response.impact_identity } : {},
1427
- ...response.hierarchy !== undefined ? { hierarchy: response.hierarchy } : {},
1428
- ...trend.length > 0 ? { trend_latest: trend[trend.length - 1], trend_points: trend.length } : {},
1429
- top_evidence: topEvidence,
1430
- evidence_in_dossier: evidence.length,
1431
- reports,
1432
- omitted: {
1433
- evidence_rows: Math.max(0, evidence.length - topEvidence.length),
1434
- report_narratives: reports.length,
1435
- reports_has_more: reportsBlock?.has_more === true,
1436
- blocks: PROBLEM_COMPACT_OMITTED_BLOCKS.filter((key) => (key in response))
1437
- },
1438
- full_command: `moda problems get ${id} --full`
1439
- };
1440
- const nextCommands = [
1441
- readCommand(`moda problems get ${id} --evidence --limit=20`, "Page every evidence row with its anchor (follow next_cursor)."),
1442
- readCommand(`moda problems get ${id} --reports`, "Page investigation reports with narratives and citations."),
1443
- readCommand(`moda problems get ${id} --traces`, "Page affected traces with their latest anchor."),
1444
- readCommand(`moda problems get ${id} --full`, "The complete dossier, every block (large).")
1445
- ];
1446
- const first = topEvidence.find((row) => typeof row.context_command === "string");
1447
- if (first?.context_command) {
1448
- nextCommands.push(readCommand(first.context_command, "Read the trace around the top evidence anchor."));
1449
- }
1450
- return { data, nextCommands };
1047
+ if (!/["$`\\!]/.test(value))
1048
+ return `"${value}"`;
1049
+ return `'${value.replace(/'/g, `'\\''`)}'`;
1451
1050
  }
1452
1051
 
1453
1052
  // src/legacy-names.ts
@@ -1618,8 +1217,8 @@ function canonicalCommandText(text) {
1618
1217
 
1619
1218
  // src/output.ts
1620
1219
  import { randomUUID } from "node:crypto";
1621
- import { lstatSync, mkdirSync as mkdirSync2, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
1622
- import { tmpdir } from "node:os";
1220
+ import { chmodSync as chmodSync2, lstatSync, mkdirSync as mkdirSync2, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
1221
+ import { tmpdir, userInfo } from "node:os";
1623
1222
  import { join as join2 } from "node:path";
1624
1223
  var SUGGESTION_KEY = /(_commands?|^hints?|^suggestions?)$/i;
1625
1224
  function canonicalDeep(value, rewriteStrings) {
@@ -1713,7 +1312,7 @@ var CUSTOMER_CONTENT_KEYS = new Set([
1713
1312
  "response"
1714
1313
  ]);
1715
1314
  function withProfileOnCommands(value, profile) {
1716
- const flag = `--profile=${shellWord(profile)}`;
1315
+ const flag = `--profile=${shellArg(profile)}`;
1717
1316
  const visit = (node, key, inCommandList) => {
1718
1317
  const isCommandKey = key !== undefined && (GENERATED_COMMAND_KEY.test(key) || inCommandList && key === "command");
1719
1318
  if (typeof node === "string")
@@ -1722,7 +1321,7 @@ function withProfileOnCommands(value, profile) {
1722
1321
  const list = key !== undefined && GENERATED_COMMAND_LIST_KEY.test(key);
1723
1322
  return node.map((item) => typeof item === "string" ? visit(item, isCommandKey ? key : undefined, false) : visit(item, undefined, list));
1724
1323
  }
1725
- const record = asRecord2(node);
1324
+ const record = asRecord(node);
1726
1325
  if (!record)
1727
1326
  return node;
1728
1327
  return Object.fromEntries(Object.entries(record).map(([entryKey, entry]) => [
@@ -2493,11 +2092,11 @@ function normalizeSummary(summary, command, status, data) {
2493
2092
  return inferSummary(command, status, data);
2494
2093
  }
2495
2094
  function inferSummary(command, status, data) {
2496
- const record = asRecord2(data);
2095
+ const record = asRecord(data);
2497
2096
  if (status === "error") {
2498
2097
  return { text: `${command} failed.`, confidence: "high" };
2499
2098
  }
2500
- const summary = asRecord2(record?.summary);
2099
+ const summary = asRecord(record?.summary);
2501
2100
  if (summary) {
2502
2101
  const statusValue = typeof summary.status === "string" ? summary.status : undefined;
2503
2102
  const fail = typeof summary.fail === "number" ? summary.fail : undefined;
@@ -2549,11 +2148,11 @@ function inferMutability(command) {
2549
2148
  return "read";
2550
2149
  }
2551
2150
  function inferWarnings(data) {
2552
- const record = asRecord2(data);
2151
+ const record = asRecord(data);
2553
2152
  const direct = record?.warnings;
2554
2153
  if (Array.isArray(direct))
2555
2154
  return direct.filter((item) => typeof item === "string");
2556
- const validation = asRecord2(record?.validation);
2155
+ const validation = asRecord(record?.validation);
2557
2156
  if (Array.isArray(validation?.warnings)) {
2558
2157
  return validation.warnings.filter((item) => typeof item === "string");
2559
2158
  }
@@ -2591,10 +2190,37 @@ function pruneAgentFullOutputs(dir, keep, now = Date.now()) {
2591
2190
  } catch {}
2592
2191
  }
2593
2192
  }
2193
+ function currentUserTag() {
2194
+ if (typeof process.getuid === "function")
2195
+ return String(process.getuid());
2196
+ try {
2197
+ return userInfo().username.replace(/[^A-Za-z0-9_.-]+/g, "_") || "user";
2198
+ } catch {
2199
+ return "user";
2200
+ }
2201
+ }
2202
+ function defaultAgentOutputDir() {
2203
+ return join2(tmpdir(), `moda-agent-output-${currentUserTag()}`);
2204
+ }
2205
+ function ensurePrivateDir(dir) {
2206
+ mkdirSync2(dir, { recursive: true, mode: 448 });
2207
+ const stat = lstatSync(dir);
2208
+ if (!stat.isDirectory())
2209
+ throw new Error(`${dir} is not a directory.`);
2210
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid()) {
2211
+ throw new Error(`${dir} is owned by another user.`);
2212
+ }
2213
+ if (process.platform !== "win32" && (stat.mode & 63) !== 0)
2214
+ chmodSync2(dir, 448);
2215
+ }
2594
2216
  function writeAgentFullOutput(command, data) {
2595
2217
  try {
2596
- const dir = process.env.MODA_AGENT_OUTPUT_DIR || join2(tmpdir(), "moda-agent-output");
2597
- mkdirSync2(dir, { recursive: true, mode: 448 });
2218
+ const configured = process.env.MODA_AGENT_OUTPUT_DIR;
2219
+ const dir = configured || defaultAgentOutputDir();
2220
+ if (configured)
2221
+ mkdirSync2(dir, { recursive: true, mode: 448 });
2222
+ else
2223
+ ensurePrivateDir(dir);
2598
2224
  const slug = command.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "output";
2599
2225
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
2600
2226
  const path = join2(dir, `${slug}-${stamp}-${randomUUID().slice(0, 8)}.json`);
@@ -2615,7 +2241,7 @@ function contentBlocksRedundant(message) {
2615
2241
  const hasToolResults = Array.isArray(message.tool_results) && message.tool_results.length > 0;
2616
2242
  const texts = [];
2617
2243
  for (const item of blocks) {
2618
- const block = asRecord2(item);
2244
+ const block = asRecord(item);
2619
2245
  if (!block)
2620
2246
  return false;
2621
2247
  if (block.type === "text") {
@@ -2647,7 +2273,7 @@ function shedRedundantContentBlocks(value) {
2647
2273
  visit(item);
2648
2274
  return;
2649
2275
  }
2650
- const record = asRecord2(node);
2276
+ const record = asRecord(node);
2651
2277
  if (!record)
2652
2278
  return;
2653
2279
  if ("content_blocks" in record && contentBlocksRedundant(record)) {
@@ -2669,17 +2295,17 @@ function continuationCommand(invocation, overrides) {
2669
2295
  const dashAt = invocation.positionals.findIndex((value) => value.startsWith("-"));
2670
2296
  const leading = dashAt === -1 ? invocation.positionals : invocation.positionals.slice(0, dashAt);
2671
2297
  const trailing = dashAt === -1 ? [] : invocation.positionals.slice(dashAt);
2672
- const parts = ["moda", invocation.command, ...leading.map((value) => shellWord(value))];
2298
+ const parts = ["moda", invocation.command, ...leading.map((value) => shellArg(value))];
2673
2299
  for (const [key, value] of Object.entries(invocation.flags)) {
2674
2300
  const flag = normalize2(key);
2675
2301
  if (overridden.has(flag) || CONTINUATION_SECRET_FLAGS.has(flag))
2676
2302
  continue;
2677
- parts.push(bare.has(flag) && value === "true" ? `--${flag}` : `--${flag}=${shellWord(String(value))}`);
2303
+ parts.push(bare.has(flag) && value === "true" ? `--${flag}` : `--${flag}=${shellArg(String(value))}`);
2678
2304
  }
2679
2305
  for (const [key, value] of Object.entries(overrides))
2680
- parts.push(`--${normalize2(key)}=${shellWord(String(value))}`);
2306
+ parts.push(`--${normalize2(key)}=${shellArg(String(value))}`);
2681
2307
  if (trailing.length > 0)
2682
- parts.push("--", ...trailing.map((value) => shellWord(value)));
2308
+ parts.push("--", ...trailing.map((value) => shellArg(value)));
2683
2309
  return canonicalCommandText(parts.join(" "));
2684
2310
  }
2685
2311
  var PAGED_LIST_BY_COMMAND = {
@@ -2700,7 +2326,7 @@ function rewritePagination(list, isTopLevel, invocation) {
2700
2326
  const { parent, array, originalLength } = list;
2701
2327
  if (!parent)
2702
2328
  return;
2703
- const nested = asRecord2(parent.pagination);
2329
+ const nested = asRecord(parent.pagination);
2704
2330
  const inline = !nested && !isTopLevel && (("has_more" in parent) || ("next_cursor" in parent)) ? parent : undefined;
2705
2331
  const pagination = nested ?? inline;
2706
2332
  if (!pagination)
@@ -2840,7 +2466,7 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
2840
2466
  fullOutputPath
2841
2467
  };
2842
2468
  }
2843
- const root = asRecord2(bounded);
2469
+ const root = asRecord(bounded);
2844
2470
  const notes = [];
2845
2471
  const nextCommands = [];
2846
2472
  for (const list of droppedLists.values()) {
@@ -2878,7 +2504,7 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
2878
2504
  ...nextCommands.length > 0 ? { nextCommands } : {}
2879
2505
  };
2880
2506
  }
2881
- function asRecord2(value) {
2507
+ function asRecord(value) {
2882
2508
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
2883
2509
  }
2884
2510
  function extractSuggestedCommands(text) {
@@ -2911,4 +2537,4 @@ function apiKeyHeaders(key, options = {}, url) {
2911
2537
  return headers;
2912
2538
  }
2913
2539
 
2914
- export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, attachInputDetails, inputDetailsOf, CliAccessError, CliInputRequiredError, CliAuthError, CliNotFoundError, CliCancellationError, isNetworkLikeError, isApiLikeError, apiErrorStatus, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, isPlatformApiKey, setPlatformTenantOverride, apiKeyHeaders, recordApiRequest, getApiRequestCount, resetApiRequestCount, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, BATCH_MAX_TARGETS, BATCH_CONCURRENCY, parseBatchTarget, collectBatchTargets, mapWithConcurrency, contextTarget, asRecord, asString, asNumber, frustrationAnchorIndex, frustrationsFromEmotions, withFrustrationAnchors, shellWord, trimToolFailureConversations, withToolFailureAnchors, embeddedWindowSize, withoutDetectionWindows, compactProblemDossier, LEGACY_NAMES, canonicalCommandText, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes, continuationCommand };
2540
+ export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, attachInputDetails, inputDetailsOf, CliAccessError, CliInputRequiredError, CliAuthError, CliNotFoundError, CliCancellationError, isNetworkLikeError, isApiLikeError, apiErrorStatus, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, resolveModaBaseUrlStrict, validateConfig, isPlatformApiKey, setPlatformTenantOverride, apiKeyHeaders, recordApiRequest, getApiRequestCount, resetApiRequestCount, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, shellArg, LEGACY_NAMES, canonicalCommandText, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes, continuationCommand };
@@ -1,12 +1,9 @@
1
1
  import {
2
2
  createCliOutput,
3
3
  outputModeFromFlags
4
- } from "./cli-sqsjd08w.js";
4
+ } from "./cli-r5pp7qny.js";
5
5
 
6
- // src/command-context.ts
7
- function flagEnabled(flags, key) {
8
- return flags[key] === "true";
9
- }
6
+ // src/boolean-words.ts
10
7
  var BOOLEAN_TRUE_WORDS = new Set(["true", "yes", "1", "on"]);
11
8
  var BOOLEAN_FALSE_WORDS = new Set(["false", "no", "0", "off"]);
12
9
  function parseBooleanFlagValue(value) {
@@ -23,6 +20,31 @@ function parseBooleanFlagValue(value) {
23
20
  return false;
24
21
  return;
25
22
  }
23
+ function flagValue(flags, name) {
24
+ return flags[name] ?? flags[name.replace(/-/g, "_")];
25
+ }
26
+ function withoutFlag(flags, name) {
27
+ const snake = name.replace(/-/g, "_");
28
+ return Object.fromEntries(Object.entries(flags).filter(([key]) => key !== name && key !== snake));
29
+ }
30
+ function optOutFlagSet(flags, name, strict = true) {
31
+ const raw = flagValue(flags, name);
32
+ if (raw === undefined)
33
+ return false;
34
+ const value = parseBooleanFlagValue(raw);
35
+ return strict ? value === true : value !== false;
36
+ }
37
+ function envSwitchedOff(value) {
38
+ return parseBooleanFlagValue(value) === false;
39
+ }
40
+ function envOptOutSet(value) {
41
+ return value !== undefined && value.trim() !== "" && parseBooleanFlagValue(value) !== false;
42
+ }
43
+
44
+ // src/command-context.ts
45
+ function flagEnabled(flags, key) {
46
+ return flags[key] === "true";
47
+ }
26
48
  function stringOption(value) {
27
49
  if (value === undefined || value === null)
28
50
  return;
@@ -77,4 +99,4 @@ function createCommandContext(input, options = {}) {
77
99
  };
78
100
  }
79
101
 
80
- export { parseBooleanFlagValue, stringOption, createCommandContext };
102
+ export { parseBooleanFlagValue, withoutFlag, optOutFlagSet, envSwitchedOff, envOptOutSet, stringOption, createCommandContext };