agent-contracts 0.19.3 → 0.19.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/cli-contract.yaml CHANGED
@@ -3,7 +3,7 @@ cliContracts: 0.1.0
3
3
 
4
4
  info:
5
5
  title: agent-contracts CLI
6
- version: 0.19.2
6
+ version: 0.19.4
7
7
  description: >-
8
8
  Declarative YAML DSL toolkit for defining, validating, and rendering
9
9
  multi-agent development workflows. Provides static validation,
@@ -625,44 +625,81 @@ commandSets:
625
625
  schema:
626
626
  type: string
627
627
 
628
+ - name: fail-on
629
+ description: >-
630
+ Minimum finding severity that causes exit 10
631
+ (info|warning|error|critical).
632
+ valueName: level
633
+ schema:
634
+ type: string
635
+ enum: [info, warning, error, critical]
636
+ default: critical
637
+
638
+ - name: output
639
+ aliases: [o]
640
+ description: Write result to a file instead of stdout.
641
+ valueName: file
642
+ schema:
643
+ type: string
644
+ file:
645
+ mode: write
646
+ encoding: utf-8
647
+
648
+ - name: report-format
649
+ description: >-
650
+ Alias for --format. When both are specified, --report-format
651
+ takes precedence.
652
+ valueName: fmt
653
+ schema:
654
+ type: string
655
+ enum: [text, json, markdown]
656
+ default: text
657
+
628
658
  exits:
629
659
  '0':
630
- description: No critical findings detected.
660
+ description: Audit succeeded. No findings at or above --fail-on threshold.
631
661
  stdout:
632
662
  format: '{options.format}'
633
- schemaNote: >-
634
- Audit findings are always emitted on stdout as the primary
635
- output. Format depends on --format option (text, json, or
636
- markdown).
663
+ schema:
664
+ $ref: './dsl_base/components.yaml#/schemas/agent-audit-result'
637
665
 
638
666
  '1':
639
- description: Critical findings detected.
667
+ description: Unexpected error (invalid input, config error, or internal failure).
640
668
  stderr:
641
669
  format: text
642
- schemaNote: >-
643
- Summary diagnostic on stderr. Structured findings are
644
- emitted on stdout in the requested --format.
670
+
671
+ '10':
672
+ description: Findings at or above --fail-on severity threshold detected.
645
673
  stdout:
646
674
  format: '{options.format}'
675
+ schema:
676
+ $ref: './dsl_base/components.yaml#/schemas/agent-audit-result'
677
+ stderr:
678
+ format: text
647
679
  schemaNote: >-
648
- Full findings output. Exit 1 indicates at least one
649
- critical-severity finding exists; the findings themselves
650
- are valid structured data, not error messages.
680
+ Summary diagnostic on stderr. Structured findings are
681
+ emitted on stdout in the requested format.
651
682
 
652
- '2':
653
- description: Invalid input or configuration error.
683
+ '11':
684
+ description: >-
685
+ Runtime dependency missing (agent-contracts-runtime not installed).
654
686
  stderr:
655
687
  format: text
656
688
 
657
- '3':
658
- description: LLM adapter error (API failure, runtime not installed).
689
+ '12':
690
+ description: LLM provider or adapter error (API failure, auth error).
659
691
  stderr:
660
692
  format: text
661
693
 
662
694
  x-agent:
663
695
  riskLevel: medium
664
696
  requiresConfirmation: false
665
- idempotent: true
697
+ idempotent: false
698
+ idempotentNote: >-
699
+ Safe to repeat (no persistent side effects beyond network calls),
700
+ but LLM inference is non-deterministic — identical inputs may
701
+ produce different findings, severity assignments, and
702
+ recommendation text across runs.
666
703
  sideEffects:
667
704
  - network
668
705
  sideEffectNote: >-
@@ -671,7 +708,7 @@ commandSets:
671
708
  cost and sends DSL content to the LLM provider.
672
709
  safeDryRunOption: dry-run
673
710
  expectedDurationMs: 120000
674
- retryableExitCodes: [3]
711
+ retryableExitCodes: [12]
675
712
  recommendedBeforeUse:
676
713
  - Ensure agent-contracts.config.yaml exists with render targets.
677
714
  - Run validate first to confirm DSL is valid.
package/dist/cli.js CHANGED
@@ -55898,6 +55898,7 @@ ${issues}
55898
55898
  );
55899
55899
 
55900
55900
  // src/cli/commands/audit.ts
55901
+ import { writeFileSync as writeFileSync2 } from "fs";
55901
55902
  import { Command as Command8 } from "commander";
55902
55903
 
55903
55904
  // src/auditor/context-builder.ts
@@ -56632,16 +56633,22 @@ function formatAuditResults(results, format2) {
56632
56633
  }
56633
56634
  return results.map((r3) => formatAuditResult(r3, format2)).join("\n\n");
56634
56635
  }
56635
- function computeExitCode(results) {
56636
+ function computeExitCode(results, failOn) {
56636
56637
  for (const r3 of results) {
56637
56638
  if (r3.status === "error") {
56638
- if (r3.errorMessage?.includes("not installed") || r3.errorMessage?.includes("adapter")) return 3;
56639
- return 2;
56639
+ if (r3.errorMessage?.includes("not installed")) return 11;
56640
+ if (r3.errorMessage?.includes("adapter") || r3.errorMessage?.includes("API")) return 12;
56641
+ return 1;
56640
56642
  }
56641
56643
  }
56644
+ const severityRank = { info: 0, warning: 1, error: 2, critical: 3 };
56645
+ const threshold = severityRank[failOn ?? "critical"] ?? 3;
56642
56646
  for (const r3 of results) {
56643
- if (r3.data?.critical_gaps && r3.data.critical_gaps.some((g2) => g2.severity === "critical")) {
56644
- return 1;
56647
+ if (r3.data?.critical_gaps) {
56648
+ for (const gap of r3.data.critical_gaps) {
56649
+ const rank = severityRank[gap.severity ?? ""] ?? 0;
56650
+ if (rank >= threshold) return 10;
56651
+ }
56645
56652
  }
56646
56653
  }
56647
56654
  return 0;
@@ -56659,13 +56666,13 @@ function parseAuditConfig(config) {
56659
56666
  cache_dir: audit.cache_dir
56660
56667
  };
56661
56668
  }
56662
- async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56669
+ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts, failOn) {
56663
56670
  const resolved = await resolve4(dslPath);
56664
56671
  const data = vars ? substituteVars(resolved.data, vars) : resolved.data;
56665
56672
  const schemaResult = validateSchema(data);
56666
56673
  if (!schemaResult.success) {
56667
56674
  process.stderr.write("Schema validation failed. Run 'agent-contracts validate' first.\n");
56668
- return 2;
56675
+ return { exitCode: 1, output: "" };
56669
56676
  }
56670
56677
  const auditConfig = parseAuditConfig(
56671
56678
  configObj.audit
@@ -56678,17 +56685,18 @@ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56678
56685
  scope: opts.scope,
56679
56686
  dryRun: opts.dryRun
56680
56687
  });
56688
+ let output2;
56681
56689
  if (opts.dryRun) {
56682
- for (const r3 of results) {
56683
- process.stdout.write(`
56690
+ output2 = results.map(
56691
+ (r3) => `
56684
56692
  --- Audit prompt: ${r3.auditType} ---
56685
- `);
56686
- process.stdout.write(r3.prompt + "\n");
56687
- }
56693
+ ${r3.prompt}
56694
+ `
56695
+ ).join("");
56688
56696
  } else {
56689
- process.stdout.write(formatAuditResults(results, opts.format) + "\n");
56697
+ output2 = formatAuditResults(results, opts.format) + "\n";
56690
56698
  }
56691
- return computeExitCode(results);
56699
+ return { exitCode: computeExitCode(results, failOn), output: output2 };
56692
56700
  }
56693
56701
  const result = await runAudit(schemaResult.data, configObj, auditConfig, {
56694
56702
  auditType,
@@ -56696,67 +56704,78 @@ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56696
56704
  scope: opts.scope,
56697
56705
  dryRun: opts.dryRun
56698
56706
  });
56707
+ let output;
56699
56708
  if (opts.dryRun) {
56700
- process.stdout.write(result.prompt + "\n");
56709
+ output = result.prompt + "\n";
56701
56710
  } else {
56702
- process.stdout.write(formatAuditResult(result, opts.format) + "\n");
56711
+ output = formatAuditResult(result, opts.format) + "\n";
56703
56712
  }
56704
- return computeExitCode([result]);
56713
+ return { exitCode: computeExitCode([result], failOn), output };
56705
56714
  }
56706
- var auditCommand = new Command8("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs").argument("[type]", `Audit type: ${AUDIT_TYPES.join(", ")}`, "all").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--format <format>", "Output format (text|json|markdown)", "text").option("--scope <entities>", "Limit audit scope (e.g. agents:architect,implementer)").option("--dry-run", "Output the audit prompt without calling LLM", false).option("--adapter <name>", "SDK adapter to use (default from config)").option("--model <name>", "LLM model override").action(
56715
+ var auditCommand = new Command8("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs").argument("[type]", `Audit type: ${AUDIT_TYPES.join(", ")}`, "all").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--format <format>", "Output format (text|json|markdown)", "text").option("--scope <entities>", "Limit audit scope (e.g. agents:architect,implementer)").option("--dry-run", "Output the audit prompt without calling LLM", false).option("--adapter <name>", "SDK adapter to use (default from config)").option("--model <name>", "LLM model override").option("--fail-on <level>", "Minimum severity that causes exit 10 (info|warning|error|critical)", "critical").option("-o, --output <file>", "Write result to a file instead of stdout").option("--report-format <fmt>", "Alias for --format (for cli-contracts standard conformance)").action(
56707
56716
  async (type, opts) => {
56708
56717
  if (!AUDIT_TYPES.includes(type)) {
56709
56718
  process.stderr.write(
56710
56719
  `Unknown audit type: "${type}". Use one of: ${AUDIT_TYPES.join(", ")}
56711
56720
  `
56712
56721
  );
56713
- process.exit(2);
56722
+ process.exit(1);
56714
56723
  }
56715
- const format2 = opts.format;
56724
+ const format2 = opts.reportFormat ?? opts.format;
56716
56725
  try {
56717
56726
  const config = await loadConfig(opts.config);
56718
56727
  if (!config) {
56719
56728
  process.stderr.write(
56720
56729
  "Error: agent-contracts.config.yaml not found. Use --config to specify path.\n"
56721
56730
  );
56722
- process.exit(2);
56731
+ process.exit(1);
56723
56732
  }
56724
56733
  if (isMultiTeamConfig(config)) {
56725
56734
  const teamEntries = getTeamEntries(config, opts.team);
56726
56735
  let maxExit = 0;
56736
+ const allOutput = [];
56727
56737
  for (const [teamId, teamConfig] of teamEntries) {
56728
56738
  process.stderr.write(`
56729
56739
  --- Team: ${teamId} ---
56730
56740
  `);
56731
- const exitCode2 = await runAuditForDsl(
56741
+ const result2 = await runAuditForDsl(
56732
56742
  teamConfig.dsl,
56733
56743
  teamConfig.vars,
56734
56744
  config,
56735
56745
  type,
56736
- { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model }
56746
+ { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model },
56747
+ opts.failOn
56737
56748
  );
56738
- if (exitCode2 > maxExit) maxExit = exitCode2;
56749
+ process.stdout.write(result2.output);
56750
+ allOutput.push(result2.output);
56751
+ if (result2.exitCode > maxExit) maxExit = result2.exitCode;
56739
56752
  }
56753
+ if (opts.output) writeFileSync2(opts.output, allOutput.join(""), "utf8");
56740
56754
  process.exit(maxExit);
56741
56755
  }
56742
- const exitCode = await runAuditForDsl(
56756
+ const result = await runAuditForDsl(
56743
56757
  config.dsl,
56744
56758
  config.vars,
56745
56759
  config,
56746
56760
  type,
56747
- { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model }
56761
+ { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model },
56762
+ opts.failOn
56748
56763
  );
56749
- process.exit(exitCode);
56764
+ process.stdout.write(result.output);
56765
+ if (opts.output) writeFileSync2(opts.output, result.output, "utf8");
56766
+ process.exit(result.exitCode);
56750
56767
  } catch (err) {
56751
56768
  if (err instanceof ConfigLoadError) {
56752
56769
  process.stderr.write(`Config error: ${err.message}
56753
56770
  `);
56754
- process.exit(2);
56771
+ process.exit(1);
56755
56772
  }
56756
56773
  const msg = err instanceof Error ? err.message : String(err);
56757
56774
  process.stderr.write(`Error: ${msg}
56758
56775
  `);
56759
- process.exit(err instanceof Error && msg.includes("not installed") ? 3 : 2);
56776
+ if (msg.includes("not installed")) process.exit(11);
56777
+ if (msg.includes("adapter") || msg.includes("API")) process.exit(12);
56778
+ process.exit(1);
56760
56779
  }
56761
56780
  }
56762
56781
  );