migraguard 0.9.0 → 0.9.1

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/cli-contract.yaml CHANGED
@@ -900,12 +900,23 @@ commandSets:
900
900
  enum: [warning, error, critical]
901
901
  default: error
902
902
 
903
+ - name: output
904
+ aliases: [o]
905
+ description: Write result to a file instead of stdout.
906
+ valueName: file
907
+ schema:
908
+ type: string
909
+ file:
910
+ mode: write
911
+ mediaType: application/json
912
+ encoding: utf-8
913
+
903
914
  - name: report-format
904
915
  description: Output format for the audit report.
905
916
  valueName: fmt
906
917
  schema:
907
918
  type: string
908
- enum: [text, json]
919
+ enum: [json, text, yaml]
909
920
  default: text
910
921
 
911
922
  exits:
@@ -947,9 +958,13 @@ commandSets:
947
958
  riskLevel: low
948
959
  requiresConfirmation: false
949
960
  idempotent: true
950
- sideEffects:
951
- - network
961
+ sideEffects: [network]
962
+ sideEffectNote: >-
963
+ Network calls to LLM provider when adapter is not mock.
964
+ Filesystem write only when --output is specified.
952
965
  safeDryRunOption: dry-run
966
+ expectedDurationMs: 120000
967
+ retryableExitCodes: [1, 12]
953
968
 
954
969
  # ── propose-expand-contract ───────────────────────
955
970
  propose-expand-contract:
@@ -1005,11 +1020,38 @@ commandSets:
1005
1020
  type: boolean
1006
1021
  default: false
1007
1022
 
1023
+ - name: fail-on
1024
+ description: Minimum severity that causes a non-zero exit.
1025
+ valueName: level
1026
+ schema:
1027
+ type: string
1028
+ enum: [warning, error, critical]
1029
+ default: error
1030
+
1031
+ - name: output
1032
+ aliases: [o]
1033
+ description: Write result to a file instead of stdout.
1034
+ valueName: file
1035
+ schema:
1036
+ type: string
1037
+ file:
1038
+ mode: write
1039
+ mediaType: application/json
1040
+ encoding: utf-8
1041
+
1042
+ - name: report-format
1043
+ description: Output format for the proposal report.
1044
+ valueName: fmt
1045
+ schema:
1046
+ type: string
1047
+ enum: [json, text, yaml]
1048
+ default: json
1049
+
1008
1050
  exits:
1009
1051
  '0':
1010
1052
  description: Proposal generated successfully.
1011
1053
  stdout:
1012
- format: json
1054
+ format: '{options.report-format}'
1013
1055
  schema:
1014
1056
  $ref: '#/components/schemas/ExpandContractProposal'
1015
1057
 
@@ -1023,6 +1065,13 @@ commandSets:
1023
1065
  stderr:
1024
1066
  format: text
1025
1067
 
1068
+ '10':
1069
+ description: Completed with blocking findings.
1070
+ stdout:
1071
+ format: '{options.report-format}'
1072
+ schema:
1073
+ $ref: '#/components/schemas/ExpandContractProposal'
1074
+
1026
1075
  '11':
1027
1076
  description: Runtime dependency missing (agent-contracts-runtime).
1028
1077
  stderr:
@@ -1037,10 +1086,13 @@ commandSets:
1037
1086
  riskLevel: low
1038
1087
  requiresConfirmation: false
1039
1088
  idempotent: true
1040
- sideEffects:
1041
- - network
1042
- - file_write
1089
+ sideEffects: [network, file_write]
1090
+ sideEffectNote: >-
1091
+ Network calls to LLM provider when adapter is not mock.
1092
+ Filesystem write when --output or --output-dir is specified.
1043
1093
  safeDryRunOption: dry-run
1094
+ expectedDurationMs: 120000
1095
+ retryableExitCodes: [1, 12]
1044
1096
 
1045
1097
  # ── explain ───────────────────────────────────────
1046
1098
  explain:
@@ -1084,11 +1136,38 @@ commandSets:
1084
1136
  type: boolean
1085
1137
  default: false
1086
1138
 
1139
+ - name: fail-on
1140
+ description: Minimum severity that causes a non-zero exit.
1141
+ valueName: level
1142
+ schema:
1143
+ type: string
1144
+ enum: [warning, error, critical]
1145
+ default: error
1146
+
1147
+ - name: output
1148
+ aliases: [o]
1149
+ description: Write result to a file instead of stdout.
1150
+ valueName: file
1151
+ schema:
1152
+ type: string
1153
+ file:
1154
+ mode: write
1155
+ mediaType: application/json
1156
+ encoding: utf-8
1157
+
1158
+ - name: report-format
1159
+ description: Output format for the explanation report.
1160
+ valueName: fmt
1161
+ schema:
1162
+ type: string
1163
+ enum: [json, text, yaml]
1164
+ default: json
1165
+
1087
1166
  exits:
1088
1167
  '0':
1089
1168
  description: Explanation generated.
1090
1169
  stdout:
1091
- format: json
1170
+ format: '{options.report-format}'
1092
1171
  schema:
1093
1172
  $ref: '#/components/schemas/ExplainResult'
1094
1173
 
@@ -1102,6 +1181,13 @@ commandSets:
1102
1181
  stderr:
1103
1182
  format: text
1104
1183
 
1184
+ '10':
1185
+ description: Completed with blocking findings.
1186
+ stdout:
1187
+ format: '{options.report-format}'
1188
+ schema:
1189
+ $ref: '#/components/schemas/ExplainResult'
1190
+
1105
1191
  '11':
1106
1192
  description: Runtime dependency missing (agent-contracts-runtime).
1107
1193
  stderr:
@@ -1116,9 +1202,13 @@ commandSets:
1116
1202
  riskLevel: low
1117
1203
  requiresConfirmation: false
1118
1204
  idempotent: true
1119
- sideEffects:
1120
- - network
1205
+ sideEffects: [network]
1206
+ sideEffectNote: >-
1207
+ Network calls to LLM provider when adapter is not mock.
1208
+ Filesystem write only when --output is specified.
1121
1209
  safeDryRunOption: dry-run
1210
+ expectedDurationMs: 120000
1211
+ retryableExitCodes: [1, 12]
1122
1212
 
1123
1213
  components:
1124
1214
  schemas:
package/dist/cli.js CHANGED
@@ -314,10 +314,11 @@ var init_handoffs = __esm({
314
314
  recommendation: z.string().optional(),
315
315
  confidence: z.number().optional(),
316
316
  evidence: z.array(z.object({
317
- kind: z.enum(["file", "command", "schema", "diff", "stdout", "stderr", "text"]),
318
- target: z.string().optional(),
319
- location: z.string().optional(),
320
- excerpt: z.string().optional()
317
+ type: z.string().optional(),
318
+ content: z.string().optional(),
319
+ source: z.string().optional(),
320
+ kind: z.enum(["file", "command", "schema", "diff", "stdout", "stderr", "text"]).optional(),
321
+ location: z.string().optional()
321
322
  })).optional(),
322
323
  details: z.record(z.string(), z.unknown()).optional()
323
324
  })),
@@ -6089,8 +6090,6 @@ async function runAgentTask(userRequest, taskId, auditConfig, options) {
6089
6090
  retriesUsed: result.retries_used
6090
6091
  };
6091
6092
  }
6092
-
6093
- // src/agents/formatter.ts
6094
6093
  function computeExitCode(result, options) {
6095
6094
  if (result.dryRun) return 0;
6096
6095
  if (result.status !== "success" || !result.data) return 1;
@@ -6149,6 +6148,35 @@ function formatResultJson(result) {
6149
6148
  }
6150
6149
  return JSON.stringify(result.data, null, 2);
6151
6150
  }
6151
+ function formatResultYaml(result) {
6152
+ if (result.dryRun) {
6153
+ return `dryRun: true
6154
+ prompt: |
6155
+ ${indent(result.prompt)}`;
6156
+ }
6157
+ if (!result.data) {
6158
+ return `error: ${JSON.stringify(result.errorMessage)}
6159
+ status: ${result.status}`;
6160
+ }
6161
+ return toSimpleYaml(result.data);
6162
+ }
6163
+ function formatResult(result, format) {
6164
+ switch (format) {
6165
+ case "json":
6166
+ return formatResultJson(result);
6167
+ case "yaml":
6168
+ return formatResultYaml(result);
6169
+ case "text":
6170
+ return formatResultText(result);
6171
+ }
6172
+ }
6173
+ async function writeOutput(content, outputPath) {
6174
+ if (outputPath) {
6175
+ await writeFile(outputPath, content + "\n", "utf-8");
6176
+ } else {
6177
+ process.stdout.write(content + "\n");
6178
+ }
6179
+ }
6152
6180
  function severityIcon(severity) {
6153
6181
  switch (severity) {
6154
6182
  case "critical":
@@ -6161,6 +6189,47 @@ function severityIcon(severity) {
6161
6189
  return "\u2139";
6162
6190
  }
6163
6191
  }
6192
+ function indent(text, spaces = 2) {
6193
+ const pad = " ".repeat(spaces);
6194
+ return text.split("\n").map((l) => pad + l).join("\n");
6195
+ }
6196
+ function toSimpleYaml(obj, depth = 0) {
6197
+ const pad = " ".repeat(depth);
6198
+ if (obj === null || obj === void 0) return `${pad}null`;
6199
+ if (typeof obj === "string") {
6200
+ if (obj.includes("\n")) return `|
6201
+ ${indent(obj, (depth + 1) * 2)}`;
6202
+ return JSON.stringify(obj);
6203
+ }
6204
+ if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
6205
+ if (Array.isArray(obj)) {
6206
+ if (obj.length === 0) return "[]";
6207
+ return obj.map((item) => {
6208
+ const val = toSimpleYaml(item, depth + 1);
6209
+ if (typeof item === "object" && item !== null) {
6210
+ return `${pad}- ${val.trimStart()}`;
6211
+ }
6212
+ return `${pad}- ${val}`;
6213
+ }).join("\n");
6214
+ }
6215
+ if (typeof obj === "object") {
6216
+ const entries = Object.entries(obj);
6217
+ if (entries.length === 0) return "{}";
6218
+ return entries.map(([k, v]) => {
6219
+ const val = toSimpleYaml(v, depth + 1);
6220
+ if (typeof v === "object" && v !== null && !Array.isArray(v)) {
6221
+ return `${pad}${k}:
6222
+ ${val}`;
6223
+ }
6224
+ if (Array.isArray(v) && v.length > 0) {
6225
+ return `${pad}${k}:
6226
+ ${val}`;
6227
+ }
6228
+ return `${pad}${k}: ${val}`;
6229
+ }).join("\n");
6230
+ }
6231
+ return String(obj);
6232
+ }
6164
6233
 
6165
6234
  // src/commands/audit.ts
6166
6235
  async function commandAudit(config, target, opts) {
@@ -6180,12 +6249,8 @@ async function commandAudit(config, target, opts) {
6180
6249
  auditConfig,
6181
6250
  auditOpts
6182
6251
  );
6183
- const format = opts.reportFormat ?? "text";
6184
- if (format === "json") {
6185
- process.stdout.write(formatResultJson(result) + "\n");
6186
- } else {
6187
- process.stdout.write(formatResultText(result) + "\n");
6188
- }
6252
+ const content = formatResult(result, opts.reportFormat ?? "text");
6253
+ await writeOutput(content, opts.output);
6189
6254
  const exitCode = computeExitCode(result, auditOpts);
6190
6255
  if (exitCode !== 0) process.exit(exitCode);
6191
6256
  } catch (err) {
@@ -6208,7 +6273,8 @@ async function commandProposeExpandContract(config, file, opts) {
6208
6273
  model: opts.model
6209
6274
  };
6210
6275
  const auditOpts = {
6211
- dryRun: opts.dryRun
6276
+ dryRun: opts.dryRun,
6277
+ failOn: opts.failOn
6212
6278
  };
6213
6279
  try {
6214
6280
  const result = await runAgentTask(
@@ -6217,10 +6283,6 @@ async function commandProposeExpandContract(config, file, opts) {
6217
6283
  auditConfig,
6218
6284
  auditOpts
6219
6285
  );
6220
- if (result.dryRun) {
6221
- process.stdout.write(formatResultText(result) + "\n");
6222
- return;
6223
- }
6224
6286
  if (result.data && opts.outputDir && result.data.recommendedActions) {
6225
6287
  const outDir = resolve(opts.outputDir);
6226
6288
  await mkdir(outDir, { recursive: true });
@@ -6232,7 +6294,8 @@ async function commandProposeExpandContract(config, file, opts) {
6232
6294
  }
6233
6295
  }
6234
6296
  }
6235
- process.stdout.write(formatResultJson(result) + "\n");
6297
+ const content = formatResult(result, opts.reportFormat ?? "json");
6298
+ await writeOutput(content, opts.output);
6236
6299
  const exitCode = computeExitCode(result, auditOpts);
6237
6300
  if (exitCode !== 0) process.exit(exitCode);
6238
6301
  } catch (err) {
@@ -6261,7 +6324,8 @@ async function commandExplain(_config, opts) {
6261
6324
  model: opts.model
6262
6325
  };
6263
6326
  const auditOpts = {
6264
- dryRun: opts.dryRun
6327
+ dryRun: opts.dryRun,
6328
+ failOn: opts.failOn
6265
6329
  };
6266
6330
  try {
6267
6331
  const result = await runAgentTask(
@@ -6270,11 +6334,10 @@ async function commandExplain(_config, opts) {
6270
6334
  auditConfig,
6271
6335
  auditOpts
6272
6336
  );
6273
- if (result.dryRun) {
6274
- process.stdout.write(formatResultText(result) + "\n");
6275
- } else {
6276
- process.stdout.write(formatResultJson(result) + "\n");
6277
- }
6337
+ const content = formatResult(result, opts.reportFormat ?? "json");
6338
+ await writeOutput(content, opts.output);
6339
+ const exitCode = computeExitCode(result, auditOpts);
6340
+ if (exitCode !== 0) process.exit(exitCode);
6278
6341
  } catch (err) {
6279
6342
  const exitCode = err.exitCode;
6280
6343
  if (exitCode === EXIT_RUNTIME_MISSING) {
@@ -6413,23 +6476,32 @@ program.command("deps").description("Analyze and display migration dependency gr
6413
6476
  const result = await commandDeps(config, { html: opts.html });
6414
6477
  if (!result.ok) process.exit(1);
6415
6478
  }));
6416
- program.command("audit [target]").description("Run LLM-based migration safety audit").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").option("--fail-on <level>", "Minimum severity for non-zero exit (warning, error, critical)", "error").option("--report-format <fmt>", "Output format (text, json)", "text").action((target, opts) => run(async () => {
6479
+ program.command("audit [target]").description("Run LLM-based migration safety audit").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").option("--fail-on <level>", "Minimum severity for non-zero exit (warning, error, critical)", "error").option("-o, --output <file>", "Write result to a file instead of stdout").option("--report-format <fmt>", "Output format (json, text, yaml)", "text").action((target, opts) => run(async () => {
6417
6480
  const config = await loadConfig();
6418
6481
  await commandAudit(config, target, {
6419
6482
  adapter: opts.adapter,
6420
6483
  model: opts.model,
6421
6484
  dryRun: opts.dryRun,
6422
6485
  failOn: opts.failOn,
6486
+ output: opts.output,
6423
6487
  reportFormat: opts.reportFormat
6424
6488
  });
6425
6489
  }));
6426
- program.command("propose-expand-contract <file>").description("Propose expand/contract migration group from unsafe DDL").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").option("--output-dir <dir>", "Directory to write proposed phase files").action((file, opts) => run(async () => {
6490
+ program.command("propose-expand-contract <file>").description("Propose expand/contract migration group from unsafe DDL").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").option("--fail-on <level>", "Minimum severity for non-zero exit (warning, error, critical)", "error").option("-o, --output <file>", "Write result to a file instead of stdout").option("--report-format <fmt>", "Output format (json, text, yaml)", "json").option("--output-dir <dir>", "Directory to write proposed phase files").action((file, opts) => run(async () => {
6427
6491
  const config = await loadConfig();
6428
- await commandProposeExpandContract(config, file, opts);
6492
+ await commandProposeExpandContract(config, file, {
6493
+ ...opts,
6494
+ failOn: opts.failOn,
6495
+ reportFormat: opts.reportFormat
6496
+ });
6429
6497
  }));
6430
- program.command("explain").description("Explain command output in human-readable form using LLM").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").action((opts) => run(async () => {
6498
+ program.command("explain").description("Explain command output in human-readable form using LLM").option("-a, --adapter <name>", "SDK adapter (cursor, claude, openai, gemini, mock)").option("--model <name>", "LLM model override").option("-n, --dry-run", "Output prompt without calling LLM").option("--fail-on <level>", "Minimum severity for non-zero exit (warning, error, critical)", "error").option("-o, --output <file>", "Write result to a file instead of stdout").option("--report-format <fmt>", "Output format (json, text, yaml)", "json").action((opts) => run(async () => {
6431
6499
  const config = await loadConfig();
6432
- await commandExplain(config, opts);
6500
+ await commandExplain(config, {
6501
+ ...opts,
6502
+ failOn: opts.failOn,
6503
+ reportFormat: opts.reportFormat
6504
+ });
6433
6505
  }));
6434
6506
  program.parse();
6435
6507
  //# sourceMappingURL=cli.js.map