agent-contracts 0.14.0 → 0.15.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.
package/dist/cli.js CHANGED
@@ -50427,7 +50427,7 @@ var require_dist13 = __commonJS({
50427
50427
  // src/cli/cli.ts
50428
50428
  import { readFileSync } from "fs";
50429
50429
  import { fileURLToPath } from "url";
50430
- import { resolve as resolve7, dirname as dirname7 } from "path";
50430
+ import { resolve as resolve7, dirname as dirname8 } from "path";
50431
50431
  import { Command as Command8 } from "commander";
50432
50432
 
50433
50433
  // src/cli/commands/resolve.ts
@@ -50479,6 +50479,12 @@ var AgentSchema = z.object({
50479
50479
  rules: z.array(RuleSchema).optional(),
50480
50480
  anti_patterns: z.array(z.string()).optional(),
50481
50481
  escalation_criteria: z.array(EscalationCriterionSchema).optional(),
50482
+ sections: z.array(
50483
+ z.object({
50484
+ title: z.string(),
50485
+ content: z.string()
50486
+ })
50487
+ ).optional(),
50482
50488
  prerequisites: z.array(PrerequisiteSchema).optional(),
50483
50489
  guardrails: z.array(z.string()).optional()
50484
50490
  }).passthrough();
@@ -50955,13 +50961,51 @@ var RenderTargetSchema = z15.object({
50955
50961
  },
50956
50962
  { message: "include/exclude cannot be used with context: system" }
50957
50963
  );
50964
+ var TeamConfigSchema = z15.object({
50965
+ dsl: z15.string().optional(),
50966
+ bindings: z15.array(z15.string()).default([]),
50967
+ vars: z15.record(z15.string(), z15.string()).optional(),
50968
+ paths: z15.record(z15.string(), z15.string()).optional(),
50969
+ active_guardrail_policy: z15.string().optional(),
50970
+ interface_output: z15.string().optional()
50971
+ });
50958
50972
  var AgentContractsConfigSchema = z15.object({
50959
- dsl: z15.string(),
50973
+ dsl: z15.string().optional(),
50960
50974
  vars: z15.record(z15.string(), z15.string()).optional(),
50961
50975
  renders: z15.array(RenderTargetSchema).default([]),
50962
50976
  bindings: z15.array(z15.string()).default([]),
50963
50977
  active_guardrail_policy: z15.string().optional(),
50964
- paths: z15.record(z15.string(), z15.string()).optional()
50978
+ paths: z15.record(z15.string(), z15.string()).optional(),
50979
+ teams: z15.record(z15.string(), TeamConfigSchema).optional()
50980
+ }).superRefine((data, ctx) => {
50981
+ if (data.dsl !== void 0 && data.teams !== void 0) {
50982
+ ctx.addIssue({
50983
+ code: z15.ZodIssueCode.custom,
50984
+ message: "dsl and teams are mutually exclusive",
50985
+ path: ["teams"]
50986
+ });
50987
+ return;
50988
+ }
50989
+ if (data.dsl === void 0 && data.teams === void 0) {
50990
+ ctx.addIssue({
50991
+ code: z15.ZodIssueCode.custom,
50992
+ message: "Either dsl or teams must be specified",
50993
+ path: []
50994
+ });
50995
+ return;
50996
+ }
50997
+ if (data.teams) {
50998
+ for (const [key, team] of Object.entries(data.teams)) {
50999
+ if (key === "_defaults") continue;
51000
+ if (team.dsl === void 0) {
51001
+ ctx.addIssue({
51002
+ code: z15.ZodIssueCode.custom,
51003
+ message: `Team "${key}" must specify dsl`,
51004
+ path: ["teams", key, "dsl"]
51005
+ });
51006
+ }
51007
+ }
51008
+ }
50965
51009
  });
50966
51010
 
50967
51011
  // src/config/loader.ts
@@ -50982,6 +51026,27 @@ async function fileExists(filePath) {
50982
51026
  return false;
50983
51027
  }
50984
51028
  }
51029
+ function resolveTeamConfigs(teams, configDir) {
51030
+ const defaults = teams._defaults;
51031
+ const result = {};
51032
+ for (const [key, team] of Object.entries(teams)) {
51033
+ if (key === "_defaults") continue;
51034
+ const mergedBindings = [...defaults?.bindings ?? [], ...team.bindings].map(
51035
+ (b3) => resolve(configDir, b3)
51036
+ );
51037
+ const mergedVars = defaults?.vars || team.vars ? { ...defaults?.vars ?? {}, ...team.vars ?? {} } : void 0;
51038
+ const mergedPaths = defaults?.paths || team.paths ? { ...defaults?.paths ?? {}, ...team.paths ?? {} } : void 0;
51039
+ result[key] = {
51040
+ dsl: resolve(configDir, team.dsl),
51041
+ bindings: mergedBindings,
51042
+ vars: mergedVars,
51043
+ activeGuardrailPolicy: team.active_guardrail_policy ?? defaults?.active_guardrail_policy,
51044
+ paths: mergedPaths,
51045
+ interfaceOutput: team.interface_output ? resolve(configDir, team.interface_output) : void 0
51046
+ };
51047
+ }
51048
+ return result;
51049
+ }
50985
51050
  async function loadConfig(configPath) {
50986
51051
  const isExplicit = configPath !== void 0;
50987
51052
  const targetPath = resolve(configPath ?? DEFAULT_CONFIG_NAME);
@@ -51024,14 +51089,27 @@ ${issues}`,
51024
51089
  }
51025
51090
  const configDir = dirname(targetPath);
51026
51091
  const config = result.data;
51092
+ const renders = config.renders.map((r3) => ({
51093
+ ...r3,
51094
+ template: resolve(configDir, r3.template),
51095
+ output: resolve(configDir, r3.output)
51096
+ }));
51097
+ if (config.teams) {
51098
+ return {
51099
+ dsl: "",
51100
+ vars: void 0,
51101
+ renders,
51102
+ configDir,
51103
+ bindings: [],
51104
+ activeGuardrailPolicy: void 0,
51105
+ paths: void 0,
51106
+ teams: resolveTeamConfigs(config.teams, configDir)
51107
+ };
51108
+ }
51027
51109
  return {
51028
51110
  dsl: resolve(configDir, config.dsl),
51029
51111
  vars: config.vars,
51030
- renders: config.renders.map((r3) => ({
51031
- ...r3,
51032
- template: resolve(configDir, r3.template),
51033
- output: resolve(configDir, r3.output)
51034
- })),
51112
+ renders,
51035
51113
  configDir,
51036
51114
  bindings: (config.bindings ?? []).map((b3) => resolve(configDir, b3)),
51037
51115
  activeGuardrailPolicy: config.active_guardrail_policy,
@@ -51853,28 +51931,79 @@ ${issues}`,
51853
51931
 
51854
51932
  // src/cli/commands/resolve.ts
51855
51933
  import { stringify } from "yaml";
51856
- var DIR_DEFAULT = "agent-contracts.yaml";
51857
- var resolveCommand = new Command("resolve").description("Resolve DSL (load + merge extends) and output YAML").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT).option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--format <format>", "Output format (text|json)", "text").option("--expand-defaults", "Expand all Zod default values in output", false).action(async (dir, opts) => {
51858
- try {
51859
- const config = await loadConfig(opts.config);
51860
- const dslPath = resolveDslPath(dir, DIR_DEFAULT, config);
51861
- const result = await resolve4(dslPath);
51862
- let data = config?.vars ? substituteVars(result.data, config.vars) : result.data;
51863
- if (opts.expandDefaults) {
51864
- data = expandDefaults(data);
51865
- }
51866
- if (opts.format === "json") {
51867
- process.stdout.write(JSON.stringify(data, null, 2) + "\n");
51868
- } else {
51869
- process.stdout.write(stringify(data));
51934
+
51935
+ // src/cli/multi-team.ts
51936
+ function isMultiTeamConfig(config) {
51937
+ return config.teams !== void 0 && Object.keys(config.teams).length > 0;
51938
+ }
51939
+ function getTeamEntries(config, teamFilter) {
51940
+ if (!config.teams) return [];
51941
+ const entries = Object.entries(config.teams);
51942
+ if (teamFilter) {
51943
+ const found = entries.filter(([k2]) => k2 === teamFilter);
51944
+ if (found.length === 0) {
51945
+ throw new Error(
51946
+ `Team "${teamFilter}" not found. Available teams: ${entries.map(([k2]) => k2).join(", ")}`
51947
+ );
51870
51948
  }
51871
- } catch (err) {
51872
- const msg = err instanceof Error ? err.message : String(err);
51873
- process.stderr.write(`Error: ${msg}
51949
+ return found;
51950
+ }
51951
+ return entries;
51952
+ }
51953
+
51954
+ // src/cli/commands/resolve.ts
51955
+ var DIR_DEFAULT = "agent-contracts.yaml";
51956
+ var resolveCommand = new Command("resolve").description("Resolve DSL (load + merge extends) and output YAML").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT).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)", "text").option("--expand-defaults", "Expand all Zod default values in output", false).action(
51957
+ async (dir, opts) => {
51958
+ try {
51959
+ const config = await loadConfig(opts.config);
51960
+ if (config !== null && isMultiTeamConfig(config)) {
51961
+ const teamEntries = getTeamEntries(config, opts.team);
51962
+ if (opts.format === "json") {
51963
+ const out = {};
51964
+ for (const [teamId, teamConfig] of teamEntries) {
51965
+ const result2 = await resolve4(teamConfig.dsl);
51966
+ let data2 = teamConfig.vars ? substituteVars(result2.data, teamConfig.vars) : result2.data;
51967
+ if (opts.expandDefaults) {
51968
+ data2 = expandDefaults(data2);
51969
+ }
51970
+ out[teamId] = data2;
51971
+ }
51972
+ process.stdout.write(JSON.stringify(out, null, 2) + "\n");
51973
+ } else {
51974
+ for (const [teamId, teamConfig] of teamEntries) {
51975
+ process.stdout.write(`
51976
+ --- Team: ${teamId} ---
51977
+ `);
51978
+ const result2 = await resolve4(teamConfig.dsl);
51979
+ let data2 = teamConfig.vars ? substituteVars(result2.data, teamConfig.vars) : result2.data;
51980
+ if (opts.expandDefaults) {
51981
+ data2 = expandDefaults(data2);
51982
+ }
51983
+ process.stdout.write(stringify(data2));
51984
+ }
51985
+ }
51986
+ return;
51987
+ }
51988
+ const dslPath = resolveDslPath(dir, DIR_DEFAULT, config);
51989
+ const result = await resolve4(dslPath);
51990
+ let data = config?.vars ? substituteVars(result.data, config.vars) : result.data;
51991
+ if (opts.expandDefaults) {
51992
+ data = expandDefaults(data);
51993
+ }
51994
+ if (opts.format === "json") {
51995
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
51996
+ } else {
51997
+ process.stdout.write(stringify(data));
51998
+ }
51999
+ } catch (err) {
52000
+ const msg = err instanceof Error ? err.message : String(err);
52001
+ process.stderr.write(`Error: ${msg}
51874
52002
  `);
51875
- process.exit(1);
52003
+ process.exit(1);
52004
+ }
51876
52005
  }
51877
- });
52006
+ );
51878
52007
 
51879
52008
  // src/cli/commands/validate.ts
51880
52009
  import { Command as Command2 } from "commander";
@@ -52954,44 +53083,88 @@ function formatDiagnostics(diagnostics, options) {
52954
53083
 
52955
53084
  // src/cli/commands/validate.ts
52956
53085
  var DIR_DEFAULT2 = "agent-contracts.yaml";
52957
- var validateCommand = new Command2("validate").description("Validate DSL against schema and check references").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT2).option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--format <format>", "Output format (text|json)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(async (dir, opts) => {
52958
- try {
52959
- const config = await loadConfig(opts.config);
52960
- const dslPath = resolveDslPath(dir, DIR_DEFAULT2, config);
52961
- const resolved = await resolve4(dslPath);
52962
- const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
52963
- const schemaResult = validateSchema(data);
52964
- const schemaWarnings = schemaResult.diagnostics.filter(
52965
- (d2) => d2.severity === "warning"
52966
- );
52967
- if (!schemaResult.success) {
52968
- const output = formatDiagnostics(schemaResult.diagnostics, opts);
52969
- if (output) process.stderr.write(output + "\n");
52970
- process.exit(1);
52971
- }
52972
- const refDiags = checkReferences(schemaResult.data);
52973
- const handoffDiags = validateHandoffSchemas(schemaResult.data);
52974
- const allDiags = [...refDiags, ...handoffDiags, ...schemaWarnings];
52975
- const hasWarnings = allDiags.some(
52976
- (d2) => "severity" in d2 && d2.severity === "warning"
52977
- );
52978
- if (allDiags.length > 0) {
52979
- const output = formatDiagnostics(allDiags, opts);
52980
- if (output) process.stderr.write(output + "\n");
52981
- if (refDiags.length > 0 || handoffDiags.length > 0 || opts.strict && hasWarnings) {
53086
+ var validateCommand = new Command2("validate").description("Validate DSL against schema and check references").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT2).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)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(
53087
+ async (dir, opts) => {
53088
+ try {
53089
+ const config = await loadConfig(opts.config);
53090
+ if (config !== null && isMultiTeamConfig(config)) {
53091
+ const teamEntries = getTeamEntries(config, opts.team);
53092
+ let hasErrors = false;
53093
+ let allTeamsFullyClean = true;
53094
+ for (const [teamId, teamConfig] of teamEntries) {
53095
+ if (!opts.quiet) process.stdout.write(`
53096
+ --- Team: ${teamId} ---
53097
+ `);
53098
+ const resolved2 = await resolve4(teamConfig.dsl);
53099
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
53100
+ const schemaResult2 = validateSchema(data2);
53101
+ const schemaWarnings2 = schemaResult2.diagnostics.filter(
53102
+ (d2) => d2.severity === "warning"
53103
+ );
53104
+ if (!schemaResult2.success) {
53105
+ const output = formatDiagnostics(schemaResult2.diagnostics, opts);
53106
+ if (output) process.stderr.write(output + "\n");
53107
+ hasErrors = true;
53108
+ allTeamsFullyClean = false;
53109
+ continue;
53110
+ }
53111
+ const refDiags2 = checkReferences(schemaResult2.data);
53112
+ const handoffDiags2 = validateHandoffSchemas(schemaResult2.data);
53113
+ const allDiags2 = [...refDiags2, ...handoffDiags2, ...schemaWarnings2];
53114
+ const hasWarnings2 = allDiags2.some(
53115
+ (d2) => "severity" in d2 && d2.severity === "warning"
53116
+ );
53117
+ if (allDiags2.length > 0) {
53118
+ allTeamsFullyClean = false;
53119
+ const output = formatDiagnostics(allDiags2, opts);
53120
+ if (output) process.stderr.write(output + "\n");
53121
+ if (refDiags2.length > 0 || handoffDiags2.length > 0 || opts.strict && hasWarnings2) {
53122
+ hasErrors = true;
53123
+ }
53124
+ }
53125
+ }
53126
+ if (hasErrors) process.exit(1);
53127
+ if (!opts.quiet && allTeamsFullyClean) {
53128
+ process.stdout.write("Validation passed.\n");
53129
+ }
53130
+ return;
53131
+ }
53132
+ const dslPath = resolveDslPath(dir, DIR_DEFAULT2, config);
53133
+ const resolved = await resolve4(dslPath);
53134
+ const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
53135
+ const schemaResult = validateSchema(data);
53136
+ const schemaWarnings = schemaResult.diagnostics.filter(
53137
+ (d2) => d2.severity === "warning"
53138
+ );
53139
+ if (!schemaResult.success) {
53140
+ const output = formatDiagnostics(schemaResult.diagnostics, opts);
53141
+ if (output) process.stderr.write(output + "\n");
52982
53142
  process.exit(1);
52983
53143
  }
52984
- }
52985
- if (!opts.quiet && allDiags.length === 0) {
52986
- process.stdout.write("Validation passed.\n");
52987
- }
52988
- } catch (err) {
52989
- const msg = err instanceof Error ? err.message : String(err);
52990
- process.stderr.write(`Error: ${msg}
53144
+ const refDiags = checkReferences(schemaResult.data);
53145
+ const handoffDiags = validateHandoffSchemas(schemaResult.data);
53146
+ const allDiags = [...refDiags, ...handoffDiags, ...schemaWarnings];
53147
+ const hasWarnings = allDiags.some(
53148
+ (d2) => "severity" in d2 && d2.severity === "warning"
53149
+ );
53150
+ if (allDiags.length > 0) {
53151
+ const output = formatDiagnostics(allDiags, opts);
53152
+ if (output) process.stderr.write(output + "\n");
53153
+ if (refDiags.length > 0 || handoffDiags.length > 0 || opts.strict && hasWarnings) {
53154
+ process.exit(1);
53155
+ }
53156
+ }
53157
+ if (!opts.quiet && allDiags.length === 0) {
53158
+ process.stdout.write("Validation passed.\n");
53159
+ }
53160
+ } catch (err) {
53161
+ const msg = err instanceof Error ? err.message : String(err);
53162
+ process.stderr.write(`Error: ${msg}
52991
53163
  `);
52992
- process.exit(1);
53164
+ process.exit(1);
53165
+ }
52993
53166
  }
52994
- });
53167
+ );
52995
53168
 
52996
53169
  // src/cli/commands/lint.ts
52997
53170
  import { Command as Command3 } from "commander";
@@ -54389,10 +54562,63 @@ async function spectralLint(dslObject) {
54389
54562
 
54390
54563
  // src/cli/commands/lint.ts
54391
54564
  var DIR_DEFAULT3 = "agent-contracts.yaml";
54392
- var lintCommand = new Command3("lint").description("Run semantic lint rules on resolved DSL").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT3).option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--format <format>", "Output format (text|json)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(
54565
+ var lintCommand = new Command3("lint").description("Run semantic lint rules on resolved DSL").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT3).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)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(
54393
54566
  async (dir, opts) => {
54394
54567
  try {
54395
54568
  const config = await loadConfig(opts.config);
54569
+ if (config !== null && isMultiTeamConfig(config)) {
54570
+ const teamEntries = getTeamEntries(config, opts.team);
54571
+ let hasErrors = false;
54572
+ let allClean = true;
54573
+ for (const [teamId, teamConfig] of teamEntries) {
54574
+ if (!opts.quiet) process.stdout.write(`
54575
+ --- Team: ${teamId} ---
54576
+ `);
54577
+ const resolved2 = await resolve4(teamConfig.dsl);
54578
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
54579
+ const schemaResult2 = validateSchema(data2);
54580
+ const schemaWarnings2 = schemaResult2.diagnostics.filter(
54581
+ (d2) => d2.severity === "warning"
54582
+ );
54583
+ if (!schemaResult2.success) {
54584
+ const output = formatDiagnostics(schemaResult2.diagnostics, {
54585
+ format: opts.format,
54586
+ quiet: opts.quiet
54587
+ });
54588
+ if (output) process.stderr.write(output + "\n");
54589
+ hasErrors = true;
54590
+ allClean = false;
54591
+ continue;
54592
+ }
54593
+ const tsDiagnostics2 = lint(schemaResult2.data);
54594
+ const spectralDiagnostics2 = await spectralLint(
54595
+ schemaResult2.data
54596
+ );
54597
+ const diagnostics2 = [
54598
+ ...tsDiagnostics2,
54599
+ ...spectralDiagnostics2,
54600
+ ...schemaWarnings2
54601
+ ];
54602
+ if (diagnostics2.length > 0) {
54603
+ allClean = false;
54604
+ const output = formatDiagnostics(diagnostics2, {
54605
+ format: opts.format,
54606
+ quiet: opts.quiet
54607
+ });
54608
+ if (output) process.stderr.write(output + "\n");
54609
+ const teamHasErrors = diagnostics2.some((d2) => d2.severity === "error");
54610
+ const teamHasWarnings = diagnostics2.some((d2) => d2.severity === "warning");
54611
+ if (teamHasErrors || opts.strict && teamHasWarnings) {
54612
+ hasErrors = true;
54613
+ }
54614
+ }
54615
+ }
54616
+ if (hasErrors) process.exit(1);
54617
+ if (!opts.quiet && allClean) {
54618
+ process.stdout.write("Lint passed.\n");
54619
+ }
54620
+ return;
54621
+ }
54396
54622
  const dslPath = resolveDslPath(dir, DIR_DEFAULT3, config);
54397
54623
  const resolved = await resolve4(dslPath);
54398
54624
  const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
@@ -55826,7 +56052,7 @@ async function checkDriftFromConfig(dsl, renderTargets, options) {
55826
56052
  }
55827
56053
 
55828
56054
  // src/cli/commands/render.ts
55829
- var renderCommand = new Command4("render").description("Render resolved DSL with Handlebars templates (requires config)").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--check", "Check for drift without writing files", false).option("--quiet", "Suppress output on success", false).action(
56055
+ var renderCommand = new Command4("render").description("Render resolved DSL with Handlebars templates (requires config)").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--check", "Check for drift without writing files", false).option("--quiet", "Suppress output on success", false).action(
55830
56056
  async (opts) => {
55831
56057
  try {
55832
56058
  const config = await loadConfig(opts.config);
@@ -55836,6 +56062,68 @@ var renderCommand = new Command4("render").description("Render resolved DSL with
55836
56062
  );
55837
56063
  process.exit(1);
55838
56064
  }
56065
+ if (isMultiTeamConfig(config)) {
56066
+ const teamEntries = getTeamEntries(config, opts.team);
56067
+ for (const [teamId, teamConfig] of teamEntries) {
56068
+ if (!opts.quiet) process.stdout.write(`
56069
+ --- Team: ${teamId} ---
56070
+ `);
56071
+ const resolved2 = await resolve4(teamConfig.dsl);
56072
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
56073
+ const schemaResult2 = validateSchema(data2);
56074
+ if (!schemaResult2.success) {
56075
+ process.stderr.write(
56076
+ `Schema validation failed for team ${teamId}. Run 'agent-contracts validate' for details.
56077
+ `
56078
+ );
56079
+ process.exit(1);
56080
+ }
56081
+ let renderOptions2;
56082
+ if (teamConfig.bindings.length > 0) {
56083
+ const loadedBindings = await loadBindings(teamConfig.bindings);
56084
+ renderOptions2 = {
56085
+ loadedBindings,
56086
+ activeGuardrailPolicy: teamConfig.activeGuardrailPolicy
56087
+ };
56088
+ }
56089
+ if (opts.check) {
56090
+ const drift = await checkDriftFromConfig(
56091
+ schemaResult2.data,
56092
+ config.renders,
56093
+ renderOptions2
56094
+ );
56095
+ if (drift.hasDrift) {
56096
+ process.stderr.write(
56097
+ `Drift detected for team ${teamId} in the following files:
56098
+ `
56099
+ );
56100
+ for (const f2 of drift.diffs) {
56101
+ process.stderr.write(` ${f2}
56102
+ `);
56103
+ }
56104
+ process.exit(1);
56105
+ }
56106
+ if (!opts.quiet) {
56107
+ process.stdout.write("No drift detected.\n");
56108
+ }
56109
+ } else {
56110
+ const files = await renderFromConfig(
56111
+ schemaResult2.data,
56112
+ config.renders,
56113
+ renderOptions2
56114
+ );
56115
+ if (!opts.quiet) {
56116
+ process.stdout.write(`Rendered ${files.length} file(s):
56117
+ `);
56118
+ for (const f2 of files) {
56119
+ process.stdout.write(` ${f2}
56120
+ `);
56121
+ }
56122
+ }
56123
+ }
56124
+ }
56125
+ return;
56126
+ }
55839
56127
  const resolved = await resolve4(config.dsl);
55840
56128
  const data = config.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
55841
56129
  const schemaResult = validateSchema(data);
@@ -55901,7 +56189,9 @@ var renderCommand = new Command4("render").description("Render resolved DSL with
55901
56189
 
55902
56190
  // src/cli/commands/check.ts
55903
56191
  import { Command as Command5 } from "commander";
55904
- var checkCommand = new Command5("check").description("Run full pipeline: resolve \u2192 validate \u2192 lint \u2192 render --check").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--format <format>", "Output format (text|json)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(
56192
+ import { access as access2 } from "fs/promises";
56193
+ import { resolve as pathResolve, dirname as dirname6 } from "path";
56194
+ var checkCommand = new Command5("check").description("Run full pipeline: resolve \u2192 validate \u2192 lint \u2192 render --check").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)", "text").option("--quiet", "Suppress output on success", false).option("--strict", "Treat warnings as errors", false).action(
55905
56195
  async (opts) => {
55906
56196
  let hasErrors = false;
55907
56197
  try {
@@ -55912,6 +56202,111 @@ var checkCommand = new Command5("check").description("Run full pipeline: resolve
55912
56202
  );
55913
56203
  process.exit(1);
55914
56204
  }
56205
+ if (isMultiTeamConfig(config)) {
56206
+ const teamEntries = getTeamEntries(config, opts.team);
56207
+ for (const [teamId, teamConfig] of teamEntries) {
56208
+ if (!opts.quiet) process.stderr.write(`
56209
+ --- Team: ${teamId} ---
56210
+ `);
56211
+ const resolved2 = await resolve4(teamConfig.dsl);
56212
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
56213
+ const schemaResult2 = validateSchema(data2);
56214
+ const schemaWarnings2 = schemaResult2.diagnostics.filter(
56215
+ (d2) => d2.severity === "warning"
56216
+ );
56217
+ if (!schemaResult2.success) {
56218
+ const output = formatDiagnostics(schemaResult2.diagnostics, {
56219
+ format: opts.format,
56220
+ quiet: opts.quiet
56221
+ });
56222
+ if (output) process.stderr.write(output + "\n");
56223
+ hasErrors = true;
56224
+ continue;
56225
+ }
56226
+ const refDiags2 = checkReferences(schemaResult2.data);
56227
+ const handoffDiags2 = validateHandoffSchemas(schemaResult2.data);
56228
+ const allRefDiags2 = [...refDiags2, ...handoffDiags2, ...schemaWarnings2];
56229
+ if (allRefDiags2.length > 0) {
56230
+ const output = formatDiagnostics(allRefDiags2, {
56231
+ format: opts.format,
56232
+ quiet: opts.quiet
56233
+ });
56234
+ if (output) process.stderr.write(output + "\n");
56235
+ hasErrors = true;
56236
+ }
56237
+ const tsLintDiags2 = lint(schemaResult2.data);
56238
+ const spectralDiags2 = await spectralLint(
56239
+ schemaResult2.data
56240
+ );
56241
+ const lintDiags2 = [...tsLintDiags2, ...spectralDiags2];
56242
+ if (lintDiags2.length > 0) {
56243
+ const output = formatDiagnostics(lintDiags2, {
56244
+ format: opts.format,
56245
+ quiet: opts.quiet
56246
+ });
56247
+ if (output) process.stderr.write(output + "\n");
56248
+ if (lintDiags2.some((d2) => d2.severity === "error")) {
56249
+ hasErrors = true;
56250
+ }
56251
+ if (opts.strict && lintDiags2.some((d2) => d2.severity === "warning")) {
56252
+ hasErrors = true;
56253
+ }
56254
+ }
56255
+ let renderOptions2;
56256
+ if (teamConfig.bindings.length > 0) {
56257
+ const loadedBindings = await loadBindings(teamConfig.bindings);
56258
+ renderOptions2 = {
56259
+ loadedBindings,
56260
+ activeGuardrailPolicy: teamConfig.activeGuardrailPolicy
56261
+ };
56262
+ }
56263
+ const drift2 = await checkDriftFromConfig(
56264
+ schemaResult2.data,
56265
+ config.renders,
56266
+ renderOptions2
56267
+ );
56268
+ if (drift2.hasDrift) {
56269
+ process.stderr.write(`Drift detected for team ${teamId} in:
56270
+ `);
56271
+ for (const f2 of drift2.diffs) {
56272
+ process.stderr.write(` ${f2}
56273
+ `);
56274
+ }
56275
+ hasErrors = true;
56276
+ }
56277
+ }
56278
+ if (!hasErrors) {
56279
+ for (const [teamId, teamConfig] of teamEntries) {
56280
+ const resolved2 = await resolve4(teamConfig.dsl);
56281
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
56282
+ const schemaResult2 = validateSchema(data2);
56283
+ if (!schemaResult2.success || !schemaResult2.data) continue;
56284
+ const dsl = schemaResult2.data;
56285
+ const imports = dsl.imports;
56286
+ if (!imports) continue;
56287
+ for (const [importName, importDef] of Object.entries(imports)) {
56288
+ const interfacePathRel = importDef?.interface;
56289
+ if (typeof interfacePathRel !== "string") continue;
56290
+ const interfacePath = pathResolve(
56291
+ dirname6(teamConfig.dsl),
56292
+ interfacePathRel
56293
+ );
56294
+ try {
56295
+ await access2(interfacePath);
56296
+ } catch {
56297
+ process.stderr.write(
56298
+ `Cross-team error: Team "${teamId}" imports "${importName}" but interface file not found: ${interfacePath}
56299
+ `
56300
+ );
56301
+ hasErrors = true;
56302
+ }
56303
+ }
56304
+ }
56305
+ }
56306
+ if (hasErrors) process.exit(1);
56307
+ if (!opts.quiet) process.stdout.write("All checks passed.\n");
56308
+ return;
56309
+ }
55915
56310
  const resolved = await resolve4(config.dsl);
55916
56311
  const data = config.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
55917
56312
  const schemaResult = validateSchema(data);
@@ -55999,7 +56394,7 @@ import { Command as Command6 } from "commander";
55999
56394
 
56000
56395
  // src/guardrail-generator/generator.ts
56001
56396
  import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2, chmod, unlink as unlink2, copyFile } from "fs/promises";
56002
- import { resolve as resolve6, dirname as dirname6 } from "path";
56397
+ import { resolve as resolve6, dirname as dirname7 } from "path";
56003
56398
  import Handlebars2 from "handlebars";
56004
56399
  import YAML from "yaml";
56005
56400
 
@@ -56226,7 +56621,7 @@ async function generateGuardrails(options) {
56226
56621
  const sourcePath = resolve6(config.configDir, outputDef.source);
56227
56622
  if (!dryRun) {
56228
56623
  try {
56229
- await mkdir2(dirname6(targetPath), { recursive: true });
56624
+ await mkdir2(dirname7(targetPath), { recursive: true });
56230
56625
  await copyFile(sourcePath, targetPath);
56231
56626
  if (outputDef.executable) {
56232
56627
  await chmod(targetPath, 493);
@@ -56310,7 +56705,7 @@ async function generateGuardrails(options) {
56310
56705
  }
56311
56706
  const output = isPatch && !dryRun ? await applyPatch(groupTarget, rendered, outputDef) : rendered;
56312
56707
  if (!dryRun) {
56313
- await mkdir2(dirname6(groupTarget), { recursive: true });
56708
+ await mkdir2(dirname7(groupTarget), { recursive: true });
56314
56709
  await writeFile2(groupTarget, output, "utf8");
56315
56710
  if (outputDef.executable) {
56316
56711
  await chmod(groupTarget, 493);
@@ -56331,7 +56726,7 @@ async function generateGuardrails(options) {
56331
56726
  } else {
56332
56727
  const output = isPatch && !dryRun ? await applyPatch(targetPath, rendered, outputDef) : rendered;
56333
56728
  if (!dryRun) {
56334
- await mkdir2(dirname6(targetPath), { recursive: true });
56729
+ await mkdir2(dirname7(targetPath), { recursive: true });
56335
56730
  await writeFile2(targetPath, output, "utf8");
56336
56731
  if (outputDef.executable) {
56337
56732
  await chmod(targetPath, 493);
@@ -56384,7 +56779,7 @@ async function generateGuardrails(options) {
56384
56779
  continue;
56385
56780
  }
56386
56781
  if (!dryRun) {
56387
- await mkdir2(dirname6(outputPath), { recursive: true });
56782
+ await mkdir2(dirname7(outputPath), { recursive: true });
56388
56783
  await writeFile2(outputPath, rendered, "utf8");
56389
56784
  if (renderTarget.executable) {
56390
56785
  await chmod(outputPath, 493);
@@ -56414,7 +56809,7 @@ async function generateGuardrails(options) {
56414
56809
  continue;
56415
56810
  }
56416
56811
  if (!dryRun) {
56417
- await mkdir2(dirname6(outputPath), { recursive: true });
56812
+ await mkdir2(dirname7(outputPath), { recursive: true });
56418
56813
  await writeFile2(outputPath, rendered, "utf8");
56419
56814
  if (renderTarget.executable) {
56420
56815
  await chmod(outputPath, 493);
@@ -56435,7 +56830,18 @@ function resolveBindingRenderOutputPath(output, paths) {
56435
56830
  }
56436
56831
 
56437
56832
  // src/cli/commands/generate-guardrails.ts
56438
- var generateGuardrailsCommand = new Command6("generate").description("Generate guardrail runtime artifacts from DSL, policies, and bindings").argument("[type]", "Type of artifacts to generate", "guardrails").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--binding <name...>", "Filter to specific software binding(s)").option("--dry-run", "Print what would be generated without writing files", false).option("--quiet", "Suppress output on success", false).action(
56833
+ function resolvedConfigForTeam(workspace, teamConfig) {
56834
+ return {
56835
+ dsl: "",
56836
+ vars: teamConfig.vars,
56837
+ renders: workspace.renders,
56838
+ configDir: workspace.configDir,
56839
+ bindings: teamConfig.bindings,
56840
+ activeGuardrailPolicy: teamConfig.activeGuardrailPolicy,
56841
+ paths: teamConfig.paths
56842
+ };
56843
+ }
56844
+ var generateGuardrailsCommand = new Command6("generate").description("Generate guardrail runtime artifacts from DSL, policies, and bindings").argument("[type]", "Type of artifacts to generate", "guardrails").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--binding <name...>", "Filter to specific software binding(s)").option("--dry-run", "Print what would be generated without writing files", false).option("--quiet", "Suppress output on success", false).action(
56439
56845
  async (type, opts) => {
56440
56846
  if (type !== "guardrails") {
56441
56847
  process.stderr.write(`Unknown generate type: ${type}
@@ -56450,6 +56856,73 @@ var generateGuardrailsCommand = new Command6("generate").description("Generate g
56450
56856
  );
56451
56857
  process.exit(1);
56452
56858
  }
56859
+ if (isMultiTeamConfig(config)) {
56860
+ const teamEntries = getTeamEntries(config, opts.team);
56861
+ let exitWithError = false;
56862
+ for (const [teamId, teamConfig] of teamEntries) {
56863
+ if (!opts.quiet) process.stdout.write(`
56864
+ --- Team: ${teamId} ---
56865
+ `);
56866
+ const resolved2 = await resolve4(teamConfig.dsl);
56867
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
56868
+ const schemaResult2 = validateSchema(data2);
56869
+ if (!schemaResult2.success) {
56870
+ process.stderr.write(
56871
+ `Schema validation failed for team ${teamId}. Run 'agent-contracts validate' for details.
56872
+ `
56873
+ );
56874
+ exitWithError = true;
56875
+ continue;
56876
+ }
56877
+ const loadedBindings2 = await loadBindings(teamConfig.bindings);
56878
+ const result2 = await generateGuardrails({
56879
+ dsl: schemaResult2.data,
56880
+ config: resolvedConfigForTeam(config, teamConfig),
56881
+ loadedBindings: loadedBindings2,
56882
+ filterBindings: opts.binding,
56883
+ dryRun: opts.dryRun
56884
+ });
56885
+ const errors2 = result2.diagnostics.filter((d2) => d2.severity === "error");
56886
+ const warnings2 = result2.diagnostics.filter((d2) => d2.severity === "warning");
56887
+ const infos2 = result2.diagnostics.filter((d2) => d2.severity === "info");
56888
+ if (errors2.length > 0) {
56889
+ process.stderr.write(`Errors (team ${teamId}):
56890
+ `);
56891
+ for (const d2 of errors2) {
56892
+ process.stderr.write(` error [${d2.path}] ${d2.message}
56893
+ `);
56894
+ }
56895
+ exitWithError = true;
56896
+ }
56897
+ if (warnings2.length > 0 && !opts.quiet) {
56898
+ process.stderr.write(`Warnings (team ${teamId}):
56899
+ `);
56900
+ for (const d2 of warnings2) {
56901
+ process.stderr.write(` warning [${d2.path}] ${d2.message}
56902
+ `);
56903
+ }
56904
+ }
56905
+ if (infos2.length > 0 && !opts.quiet) {
56906
+ for (const d2 of infos2) {
56907
+ process.stderr.write(` info [${d2.path}] ${d2.message}
56908
+ `);
56909
+ }
56910
+ }
56911
+ if (!opts.quiet) {
56912
+ const action = opts.dryRun ? "Would generate" : "Generated";
56913
+ process.stdout.write(
56914
+ `${action} ${result2.outputFiles.length} file(s):
56915
+ `
56916
+ );
56917
+ for (const f2 of result2.outputFiles) {
56918
+ process.stdout.write(` ${f2}
56919
+ `);
56920
+ }
56921
+ }
56922
+ }
56923
+ if (exitWithError) process.exit(1);
56924
+ return;
56925
+ }
56453
56926
  const resolved = await resolve4(config.dsl);
56454
56927
  const data = config.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
56455
56928
  const schemaResult = validateSchema(data);
@@ -56855,10 +57328,56 @@ function formatText(result) {
56855
57328
  }
56856
57329
  return lines.join("\n");
56857
57330
  }
56858
- var scoreCommand = new Command7("score").description("Calculate DSL completeness score").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT4).option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--format <format>", "Output format (text|json)", "text").option("--threshold <number>", "Minimum score (exit 1 if below)", void 0).action(
57331
+ var scoreCommand = new Command7("score").description("Calculate DSL completeness score").argument("[dir]", "Path to agent-contracts.yaml", DIR_DEFAULT4).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)", "text").option("--threshold <number>", "Minimum score (exit 1 if below)", void 0).action(
56859
57332
  async (dir, opts) => {
56860
57333
  try {
56861
57334
  const config = await loadConfig(opts.config);
57335
+ if (config !== null && isMultiTeamConfig(config)) {
57336
+ const teamEntries = getTeamEntries(config, opts.team);
57337
+ let thresholdNum;
57338
+ if (opts.threshold !== void 0) {
57339
+ thresholdNum = parseInt(opts.threshold, 10);
57340
+ if (isNaN(thresholdNum)) {
57341
+ process.stderr.write(
57342
+ `Error: --threshold must be a number, got "${opts.threshold}"
57343
+ `
57344
+ );
57345
+ process.exit(1);
57346
+ }
57347
+ }
57348
+ let hasErrors = false;
57349
+ for (const [teamId, teamConfig] of teamEntries) {
57350
+ process.stdout.write(`
57351
+ --- Team: ${teamId} ---
57352
+ `);
57353
+ const resolved2 = await resolve4(teamConfig.dsl);
57354
+ const data2 = teamConfig.vars ? substituteVars(resolved2.data, teamConfig.vars) : resolved2.data;
57355
+ const schemaResult2 = validateSchema(data2);
57356
+ if (!schemaResult2.success) {
57357
+ const issues = schemaResult2.diagnostics.map((d2) => ` ${d2.path}: ${d2.message}`).join("\n");
57358
+ process.stderr.write(`Schema validation failed:
57359
+ ${issues}
57360
+ `);
57361
+ hasErrors = true;
57362
+ continue;
57363
+ }
57364
+ const result2 = score(schemaResult2.data);
57365
+ if (opts.format === "json") {
57366
+ process.stdout.write(JSON.stringify(result2, null, 2) + "\n");
57367
+ } else {
57368
+ process.stdout.write(formatText(result2) + "\n");
57369
+ }
57370
+ if (thresholdNum !== void 0 && result2.overall < thresholdNum) {
57371
+ process.stderr.write(
57372
+ `Score ${result2.overall} is below threshold ${thresholdNum}
57373
+ `
57374
+ );
57375
+ hasErrors = true;
57376
+ }
57377
+ }
57378
+ if (hasErrors) process.exit(1);
57379
+ return;
57380
+ }
56862
57381
  const dslPath = resolveDslPath(dir, DIR_DEFAULT4, config);
56863
57382
  const resolved = await resolve4(dslPath);
56864
57383
  const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
@@ -56903,7 +57422,7 @@ ${issues}
56903
57422
  );
56904
57423
 
56905
57424
  // src/cli/cli.ts
56906
- var __dirname = dirname7(fileURLToPath(import.meta.url));
57425
+ var __dirname = dirname8(fileURLToPath(import.meta.url));
56907
57426
  var pkg = JSON.parse(readFileSync(resolve7(__dirname, "../package.json"), "utf8"));
56908
57427
  var program = new Command8();
56909
57428
  program.name("agent-contracts").description("Agent contracts tooling \u2014 validate, lint, render DSL files").version(pkg.version);