agent-contracts 0.11.0 → 0.11.2

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/README.md CHANGED
@@ -1003,6 +1003,76 @@ outputs:
1003
1003
  template: ./templates/cursor-hook-wrapper.sh.hbs
1004
1004
  ````
1005
1005
 
1006
+ ### Binding inheritance
1007
+
1008
+ Binding files support `extends` for inheriting and extending a base binding, using the same mechanism as DSL-level `extends`.
1009
+
1010
+ A base binding defines shared guardrail implementations and outputs:
1011
+
1012
+ ````yaml
1013
+ # skeleton/bindings/cursor.yaml (base)
1014
+ software: cursor
1015
+ version: 1
1016
+
1017
+ guardrail_impl:
1018
+ no-force-push:
1019
+ checks:
1020
+ - hook_event: beforeShellExecution
1021
+ matcher:
1022
+ type: command_regex
1023
+ pattern: "git\\s+push\\s+.*--force"
1024
+ message: "Force push is forbidden"
1025
+
1026
+ outputs:
1027
+ policy-bundle:
1028
+ target: "{cursor_root}/guardrails/policy.json"
1029
+ mode: write
1030
+ inline_template: "{{json resolved_checks}}"
1031
+ ````
1032
+
1033
+ A project binding extends the base and adds project-specific guardrail implementations:
1034
+
1035
+ ````yaml
1036
+ # project/bindings/cursor.yaml
1037
+ extends: ../../skeleton/bindings/cursor.yaml
1038
+ software: cursor
1039
+ version: 1
1040
+
1041
+ guardrail_impl:
1042
+ lint-on-save:
1043
+ checks:
1044
+ - hook_event: afterFileEdit
1045
+ matcher:
1046
+ type: file_glob
1047
+ pattern: "**/*.{ts,tsx}"
1048
+ message: "TS file edited — lint results attached."
1049
+ ````
1050
+
1051
+ The result is a single merged binding with all guardrail implementations from both base and project.
1052
+
1053
+ Merge behavior:
1054
+
1055
+ | Field | Behavior |
1056
+ |-------|----------|
1057
+ | `software` | Project wins |
1058
+ | `guardrail_impl` | Map merge by guardrail ID (new IDs added; same ID deep-merged) |
1059
+ | `outputs` | Map merge by output ID (project overrides base) |
1060
+ | `reporting` | Deep merge (project fields override base) |
1061
+ | passthrough fields | Project wins |
1062
+
1063
+ All merge operators (`$append`, `$prepend`, `$insert_after`, `$replace`, `$remove`) work within binding `extends`, the same as DSL `extends`.
1064
+
1065
+ Chained inheritance (grandparent → parent → child) and both local path (`./`, `../`) and npm package references are supported. Circular extends are detected and rejected.
1066
+
1067
+ When using binding `extends`, the config only needs to list the child binding:
1068
+
1069
+ ````yaml
1070
+ # agent-contracts.config.yaml
1071
+ bindings:
1072
+ - ./bindings/cursor.yaml # extends base internally
1073
+ - ./bindings/git.yaml
1074
+ ````
1075
+
1006
1076
  ### Config
1007
1077
 
1008
1078
  ````yaml
package/dist/cli.js CHANGED
@@ -159,11 +159,11 @@ function __metadata(metadataKey, metadataValue) {
159
159
  }
160
160
  function __awaiter(thisArg, _arguments, P2, generator) {
161
161
  function adopt(value) {
162
- return value instanceof P2 ? value : new P2(function(resolve6) {
163
- resolve6(value);
162
+ return value instanceof P2 ? value : new P2(function(resolve7) {
163
+ resolve7(value);
164
164
  });
165
165
  }
166
- return new (P2 || (P2 = Promise))(function(resolve6, reject) {
166
+ return new (P2 || (P2 = Promise))(function(resolve7, reject) {
167
167
  function fulfilled(value) {
168
168
  try {
169
169
  step(generator.next(value));
@@ -179,7 +179,7 @@ function __awaiter(thisArg, _arguments, P2, generator) {
179
179
  }
180
180
  }
181
181
  function step(result) {
182
- result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
182
+ result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
183
183
  }
184
184
  step((generator = generator.apply(thisArg, _arguments || [])).next());
185
185
  });
@@ -370,14 +370,14 @@ function __asyncValues(o3) {
370
370
  }, i3);
371
371
  function verb(n3) {
372
372
  i3[n3] = o3[n3] && function(v2) {
373
- return new Promise(function(resolve6, reject) {
374
- v2 = o3[n3](v2), settle(resolve6, reject, v2.done, v2.value);
373
+ return new Promise(function(resolve7, reject) {
374
+ v2 = o3[n3](v2), settle(resolve7, reject, v2.done, v2.value);
375
375
  });
376
376
  };
377
377
  }
378
- function settle(resolve6, reject, d2, v2) {
378
+ function settle(resolve7, reject, d2, v2) {
379
379
  Promise.resolve(v2).then(function(v3) {
380
- resolve6({ value: v3, done: d2 });
380
+ resolve7({ value: v3, done: d2 });
381
381
  }, reject);
382
382
  }
383
383
  }
@@ -13033,11 +13033,11 @@ function __metadata2(metadataKey, metadataValue) {
13033
13033
  }
13034
13034
  function __awaiter2(thisArg, _arguments, P2, generator) {
13035
13035
  function adopt(value) {
13036
- return value instanceof P2 ? value : new P2(function(resolve6) {
13037
- resolve6(value);
13036
+ return value instanceof P2 ? value : new P2(function(resolve7) {
13037
+ resolve7(value);
13038
13038
  });
13039
13039
  }
13040
- return new (P2 || (P2 = Promise))(function(resolve6, reject) {
13040
+ return new (P2 || (P2 = Promise))(function(resolve7, reject) {
13041
13041
  function fulfilled(value) {
13042
13042
  try {
13043
13043
  step(generator.next(value));
@@ -13053,7 +13053,7 @@ function __awaiter2(thisArg, _arguments, P2, generator) {
13053
13053
  }
13054
13054
  }
13055
13055
  function step(result) {
13056
- result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
13056
+ result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
13057
13057
  }
13058
13058
  step((generator = generator.apply(thisArg, _arguments || [])).next());
13059
13059
  });
@@ -13231,14 +13231,14 @@ function __asyncValues2(o3) {
13231
13231
  }, i3);
13232
13232
  function verb(n3) {
13233
13233
  i3[n3] = o3[n3] && function(v2) {
13234
- return new Promise(function(resolve6, reject) {
13235
- v2 = o3[n3](v2), settle(resolve6, reject, v2.done, v2.value);
13234
+ return new Promise(function(resolve7, reject) {
13235
+ v2 = o3[n3](v2), settle(resolve7, reject, v2.done, v2.value);
13236
13236
  });
13237
13237
  };
13238
13238
  }
13239
- function settle(resolve6, reject, d2, v2) {
13239
+ function settle(resolve7, reject, d2, v2) {
13240
13240
  Promise.resolve(v2).then(function(v3) {
13241
- resolve6({ value: v3, done: d2 });
13241
+ resolve7({ value: v3, done: d2 });
13242
13242
  }, reject);
13243
13243
  }
13244
13244
  }
@@ -15152,7 +15152,7 @@ function consumeBody() {
15152
15152
  let accum = [];
15153
15153
  let accumBytes = 0;
15154
15154
  let abort = false;
15155
- return new Body.Promise(function(resolve6, reject) {
15155
+ return new Body.Promise(function(resolve7, reject) {
15156
15156
  let resTimeout;
15157
15157
  if (_this4.timeout) {
15158
15158
  resTimeout = setTimeout(function() {
@@ -15186,7 +15186,7 @@ function consumeBody() {
15186
15186
  }
15187
15187
  clearTimeout(resTimeout);
15188
15188
  try {
15189
- resolve6(Buffer.concat(accum, accumBytes));
15189
+ resolve7(Buffer.concat(accum, accumBytes));
15190
15190
  } catch (err) {
15191
15191
  reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
15192
15192
  }
@@ -15449,7 +15449,7 @@ function fetch(url, opts) {
15449
15449
  throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
15450
15450
  }
15451
15451
  Body.Promise = fetch.Promise;
15452
- return new fetch.Promise(function(resolve6, reject) {
15452
+ return new fetch.Promise(function(resolve7, reject) {
15453
15453
  const request = new Request(url, opts);
15454
15454
  const options = getNodeRequestOptions(request);
15455
15455
  const send = (options.protocol === "https:" ? https : http).request;
@@ -15582,7 +15582,7 @@ function fetch(url, opts) {
15582
15582
  requestOpts.body = void 0;
15583
15583
  requestOpts.headers.delete("content-length");
15584
15584
  }
15585
- resolve6(fetch(new Request(locationURL, requestOpts)));
15585
+ resolve7(fetch(new Request(locationURL, requestOpts)));
15586
15586
  finalize();
15587
15587
  return;
15588
15588
  }
@@ -15603,7 +15603,7 @@ function fetch(url, opts) {
15603
15603
  const codings = headers.get("Content-Encoding");
15604
15604
  if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
15605
15605
  response = new Response(body, response_options);
15606
- resolve6(response);
15606
+ resolve7(response);
15607
15607
  return;
15608
15608
  }
15609
15609
  const zlibOptions = {
@@ -15613,7 +15613,7 @@ function fetch(url, opts) {
15613
15613
  if (codings == "gzip" || codings == "x-gzip") {
15614
15614
  body = body.pipe(zlib.createGunzip(zlibOptions));
15615
15615
  response = new Response(body, response_options);
15616
- resolve6(response);
15616
+ resolve7(response);
15617
15617
  return;
15618
15618
  }
15619
15619
  if (codings == "deflate" || codings == "x-deflate") {
@@ -15625,12 +15625,12 @@ function fetch(url, opts) {
15625
15625
  body = body.pipe(zlib.createInflateRaw());
15626
15626
  }
15627
15627
  response = new Response(body, response_options);
15628
- resolve6(response);
15628
+ resolve7(response);
15629
15629
  });
15630
15630
  raw.on("end", function() {
15631
15631
  if (!response) {
15632
15632
  response = new Response(body, response_options);
15633
- resolve6(response);
15633
+ resolve7(response);
15634
15634
  }
15635
15635
  });
15636
15636
  return;
@@ -15638,11 +15638,11 @@ function fetch(url, opts) {
15638
15638
  if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
15639
15639
  body = body.pipe(zlib.createBrotliDecompress());
15640
15640
  response = new Response(body, response_options);
15641
- resolve6(response);
15641
+ resolve7(response);
15642
15642
  return;
15643
15643
  }
15644
15644
  response = new Response(body, response_options);
15645
- resolve6(response);
15645
+ resolve7(response);
15646
15646
  });
15647
15647
  writeToStream(req, request);
15648
15648
  });
@@ -16355,13 +16355,13 @@ var require_file = __commonJS({
16355
16355
  Object.defineProperty(exports, "__esModule", { value: true });
16356
16356
  var fs_1 = __require("fs");
16357
16357
  function resolveFile(ref) {
16358
- return new Promise((resolve6, reject) => {
16358
+ return new Promise((resolve7, reject) => {
16359
16359
  const path = ref.href();
16360
16360
  fs_1.readFile(path, "utf8", (err, data) => {
16361
16361
  if (err) {
16362
16362
  reject(err);
16363
16363
  } else {
16364
- resolve6(data);
16364
+ resolve7(data);
16365
16365
  }
16366
16366
  });
16367
16367
  });
@@ -23964,12 +23964,12 @@ var require_reader = __commonJS({
23964
23964
  }
23965
23965
  } else {
23966
23966
  try {
23967
- return await new Promise((resolve6, reject) => {
23967
+ return await new Promise((resolve7, reject) => {
23968
23968
  fs.readFile(name, opts.encoding, (err, data) => {
23969
23969
  if (err !== null) {
23970
23970
  reject(err);
23971
23971
  } else {
23972
- resolve6(data);
23972
+ resolve7(data);
23973
23973
  }
23974
23974
  });
23975
23975
  });
@@ -40854,14 +40854,14 @@ var require_AsyncFromSyncIteratorContinuation = __commonJS({
40854
40854
  if (!$Promise) {
40855
40855
  throw new $SyntaxError("This environment does not support Promises.");
40856
40856
  }
40857
- return new $Promise(function(resolve6) {
40857
+ return new $Promise(function(resolve7) {
40858
40858
  var done = IteratorComplete(result);
40859
40859
  var value = IteratorValue(result);
40860
40860
  var valueWrapper = PromiseResolve($Promise, value);
40861
40861
  var onFulfilled = function(value2) {
40862
40862
  return CreateIteratorResultObject(value2, done);
40863
40863
  };
40864
- resolve6($then(valueWrapper, onFulfilled));
40864
+ resolve7($then(valueWrapper, onFulfilled));
40865
40865
  });
40866
40866
  };
40867
40867
  }
@@ -41409,7 +41409,7 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41409
41409
  var O2 = this;
41410
41410
  SLOT.assert(O2, "[[SyncIteratorRecord]]");
41411
41411
  var argsLength = arguments.length;
41412
- return new $Promise(function(resolve6) {
41412
+ return new $Promise(function(resolve7) {
41413
41413
  var syncIteratorRecord = SLOT.get(O2, "[[SyncIteratorRecord]]");
41414
41414
  var result;
41415
41415
  if (argsLength > 0) {
@@ -41417,7 +41417,7 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41417
41417
  } else {
41418
41418
  result = IteratorNext(syncIteratorRecord);
41419
41419
  }
41420
- resolve6(AsyncFromSyncIteratorContinuation(result));
41420
+ resolve7(AsyncFromSyncIteratorContinuation(result));
41421
41421
  });
41422
41422
  },
41423
41423
  "return": function() {
@@ -41428,12 +41428,12 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41428
41428
  SLOT.assert(O2, "[[SyncIteratorRecord]]");
41429
41429
  var valueIsPresent = arguments.length > 0;
41430
41430
  var value = valueIsPresent ? arguments[0] : void 0;
41431
- return new $Promise(function(resolve6, reject) {
41431
+ return new $Promise(function(resolve7, reject) {
41432
41432
  var syncIterator = SLOT.get(O2, "[[SyncIteratorRecord]]")["[[Iterator]]"];
41433
41433
  var iteratorReturn = GetMethod(syncIterator, "return");
41434
41434
  if (typeof iteratorReturn === "undefined") {
41435
41435
  var iterResult = CreateIteratorResultObject(value, true);
41436
- Call(resolve6, void 0, [iterResult]);
41436
+ Call(resolve7, void 0, [iterResult]);
41437
41437
  return;
41438
41438
  }
41439
41439
  var result;
@@ -41446,7 +41446,7 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41446
41446
  Call(reject, void 0, [new $TypeError("Iterator `return` method returned a non-object value.")]);
41447
41447
  return;
41448
41448
  }
41449
- resolve6(AsyncFromSyncIteratorContinuation(result));
41449
+ resolve7(AsyncFromSyncIteratorContinuation(result));
41450
41450
  });
41451
41451
  },
41452
41452
  "throw": function() {
@@ -41457,7 +41457,7 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41457
41457
  SLOT.assert(O2, "[[SyncIteratorRecord]]");
41458
41458
  var valueIsPresent = arguments.length > 0;
41459
41459
  var value = valueIsPresent ? arguments[0] : void 0;
41460
- return new $Promise(function(resolve6, reject) {
41460
+ return new $Promise(function(resolve7, reject) {
41461
41461
  var syncIterator = SLOT.get(O2, "[[SyncIteratorRecord]]")["[[Iterator]]"];
41462
41462
  var throwMethod = GetMethod(syncIterator, "throw");
41463
41463
  if (typeof throwMethod === "undefined") {
@@ -41474,7 +41474,7 @@ var require_CreateAsyncFromSyncIterator = __commonJS({
41474
41474
  Call(reject, void 0, [new $TypeError("Iterator `throw` method returned a non-object value.")]);
41475
41475
  return;
41476
41476
  }
41477
- resolve6(AsyncFromSyncIteratorContinuation(
41477
+ resolve7(AsyncFromSyncIteratorContinuation(
41478
41478
  result
41479
41479
  /* , promiseCapability */
41480
41480
  ));
@@ -50431,8 +50431,10 @@ import { Command as Command7 } from "commander";
50431
50431
  import { Command } from "commander";
50432
50432
 
50433
50433
  // src/config/binding-loader.ts
50434
- import { readFile as readFile2 } from "fs/promises";
50435
- import { parse as parseYaml2 } from "yaml";
50434
+ import { readFile as readFile3 } from "fs/promises";
50435
+ import { dirname as dirname4, resolve as resolve5 } from "path";
50436
+ import { stat as stat2 } from "fs/promises";
50437
+ import { parse as parseYaml3 } from "yaml";
50436
50438
 
50437
50439
  // src/schema/agent.ts
50438
50440
  import { z } from "zod";
@@ -50496,17 +50498,18 @@ import { z as z3 } from "zod";
50496
50498
  var CommandRegexMatcherSchema = z3.object({
50497
50499
  type: z3.literal("command_regex"),
50498
50500
  pattern: z3.string()
50499
- });
50501
+ }).passthrough();
50500
50502
  var ContentRegexMatcherSchema = z3.object({
50501
50503
  type: z3.literal("content_regex"),
50502
50504
  pattern: z3.string(),
50503
50505
  file_glob: z3.string().optional(),
50504
50506
  exclude_glob: z3.string().optional()
50505
- });
50507
+ }).passthrough();
50506
50508
  var FileGlobMatcherSchema = z3.object({
50507
50509
  type: z3.literal("file_glob"),
50508
- pattern: z3.string()
50509
- });
50510
+ pattern: z3.string(),
50511
+ exclude_glob: z3.string().optional()
50512
+ }).passthrough();
50510
50513
  var MatcherSchema = z3.discriminatedUnion("type", [
50511
50514
  CommandRegexMatcherSchema,
50512
50515
  ContentRegexMatcherSchema,
@@ -50963,51 +50966,14 @@ function resolveDslPath(dirArg, dirArgDefault, config) {
50963
50966
  return resolve(dirArgDefault);
50964
50967
  }
50965
50968
 
50966
- // src/config/binding-loader.ts
50967
- async function loadBindings(bindingPaths) {
50968
- const results = [];
50969
- for (const filePath of bindingPaths) {
50970
- let content;
50971
- try {
50972
- content = await readFile2(filePath, "utf8");
50973
- } catch {
50974
- throw new ConfigLoadError(
50975
- `Failed to read binding file: ${filePath}`,
50976
- filePath
50977
- );
50978
- }
50979
- let raw;
50980
- try {
50981
- raw = parseYaml2(content);
50982
- } catch (err) {
50983
- const msg = err instanceof Error ? err.message : String(err);
50984
- throw new ConfigLoadError(
50985
- `Invalid YAML syntax in binding file ${filePath}: ${msg}`,
50986
- filePath
50987
- );
50988
- }
50989
- const result = SoftwareBindingSchema.safeParse(raw);
50990
- if (!result.success) {
50991
- const issues = result.error.issues.map((i3) => ` ${i3.path.join(".")}: ${i3.message}`).join("\n");
50992
- throw new ConfigLoadError(
50993
- `Invalid binding schema in ${filePath}:
50994
- ${issues}`,
50995
- filePath
50996
- );
50997
- }
50998
- results.push({ filePath, binding: result.data });
50999
- }
51000
- return results;
51001
- }
51002
-
51003
50969
  // src/resolver/base-resolver.ts
51004
50970
  import { resolve as resolve3, join as join2 } from "path";
51005
50971
  import { stat } from "fs/promises";
51006
50972
 
51007
50973
  // src/loader/loader.ts
51008
- import { readFile as readFile3, readdir, stat as fsStat } from "fs/promises";
50974
+ import { readFile as readFile2, readdir, stat as fsStat } from "fs/promises";
51009
50975
  import { dirname as dirname2, resolve as resolve2, join, extname } from "path";
51010
- import { parse as parseYaml3 } from "yaml";
50976
+ import { parse as parseYaml2 } from "yaml";
51011
50977
  var DslLoadError = class extends Error {
51012
50978
  constructor(message, filePath) {
51013
50979
  super(message);
@@ -51060,7 +51026,7 @@ function hasRefs(value) {
51060
51026
  async function readYaml(filePath) {
51061
51027
  let content;
51062
51028
  try {
51063
- content = await readFile3(filePath, "utf8");
51029
+ content = await readFile2(filePath, "utf8");
51064
51030
  } catch {
51065
51031
  throw new DslLoadError(
51066
51032
  `File not found: ${filePath}`,
@@ -51068,7 +51034,7 @@ async function readYaml(filePath) {
51068
51034
  );
51069
51035
  }
51070
51036
  try {
51071
- return parseYaml3(content);
51037
+ return parseYaml2(content);
51072
51038
  } catch (err) {
51073
51039
  const msg = err instanceof Error ? err.message : String(err);
51074
51040
  throw new DslLoadError(
@@ -51649,6 +51615,143 @@ function substituteVars(data, vars) {
51649
51615
  return walk(data, vars);
51650
51616
  }
51651
51617
 
51618
+ // src/config/binding-merger.ts
51619
+ function isRecord3(v2) {
51620
+ return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
51621
+ }
51622
+ var BINDING_MERGE_SECTIONS = ["guardrail_impl", "outputs"];
51623
+ function mergeBinding(base, project) {
51624
+ const hasExtends = typeof project["extends"] === "string";
51625
+ const result = { ...base, ...project };
51626
+ for (const section of BINDING_MERGE_SECTIONS) {
51627
+ const baseVal = base[section];
51628
+ const projVal = project[section];
51629
+ if (projVal === void 0) {
51630
+ continue;
51631
+ }
51632
+ const baseMap = isRecord3(baseVal) ? baseVal : {};
51633
+ result[section] = mergeEntityMaps(
51634
+ baseMap,
51635
+ projVal,
51636
+ section,
51637
+ hasExtends
51638
+ );
51639
+ }
51640
+ if (project["reporting"] !== void 0 && isRecord3(base["reporting"]) && isRecord3(project["reporting"])) {
51641
+ result["reporting"] = deepMergeEntities(
51642
+ base["reporting"],
51643
+ project["reporting"],
51644
+ "reporting",
51645
+ hasExtends
51646
+ );
51647
+ }
51648
+ delete result["extends"];
51649
+ return result;
51650
+ }
51651
+
51652
+ // src/config/binding-loader.ts
51653
+ async function loadRawBinding(filePath) {
51654
+ let content;
51655
+ try {
51656
+ content = await readFile3(filePath, "utf8");
51657
+ } catch {
51658
+ throw new ConfigLoadError(
51659
+ `Failed to read binding file: ${filePath}`,
51660
+ filePath
51661
+ );
51662
+ }
51663
+ try {
51664
+ return parseYaml3(content);
51665
+ } catch (err) {
51666
+ const msg = err instanceof Error ? err.message : String(err);
51667
+ throw new ConfigLoadError(
51668
+ `Invalid YAML syntax in binding file ${filePath}: ${msg}`,
51669
+ filePath
51670
+ );
51671
+ }
51672
+ }
51673
+ async function resolveBindingExtends(raw, filePath, seen) {
51674
+ const extendsValue = raw["extends"];
51675
+ if (typeof extendsValue !== "string") {
51676
+ return raw;
51677
+ }
51678
+ const bindingDir = dirname4(filePath);
51679
+ let basePath;
51680
+ if (extendsValue.startsWith("./") || extendsValue.startsWith("../")) {
51681
+ basePath = resolve5(bindingDir, extendsValue);
51682
+ } else {
51683
+ try {
51684
+ const resolved = import.meta.resolve(extendsValue);
51685
+ basePath = new URL(resolved).pathname;
51686
+ } catch {
51687
+ throw new ConfigLoadError(
51688
+ `Could not resolve binding extends package: ${extendsValue}`,
51689
+ filePath
51690
+ );
51691
+ }
51692
+ }
51693
+ try {
51694
+ const s2 = await stat2(basePath);
51695
+ if (s2.isDirectory()) {
51696
+ const candidates = ["binding.yaml", "binding.yml"];
51697
+ let found = false;
51698
+ for (const name of candidates) {
51699
+ const candidate = resolve5(basePath, name);
51700
+ try {
51701
+ const cs = await stat2(candidate);
51702
+ if (cs.isFile()) {
51703
+ basePath = candidate;
51704
+ found = true;
51705
+ break;
51706
+ }
51707
+ } catch {
51708
+ continue;
51709
+ }
51710
+ }
51711
+ if (!found) {
51712
+ throw new ConfigLoadError(
51713
+ `No binding.yaml found in directory: ${basePath}`,
51714
+ filePath
51715
+ );
51716
+ }
51717
+ }
51718
+ } catch (err) {
51719
+ if (err instanceof ConfigLoadError) throw err;
51720
+ throw new ConfigLoadError(
51721
+ `Base binding path not found: ${basePath}`,
51722
+ filePath
51723
+ );
51724
+ }
51725
+ if (seen.has(basePath)) {
51726
+ throw new ConfigLoadError(
51727
+ `Circular binding extends detected: ${basePath}`,
51728
+ filePath
51729
+ );
51730
+ }
51731
+ seen.add(basePath);
51732
+ const baseRaw = await loadRawBinding(basePath);
51733
+ const resolvedBase = await resolveBindingExtends(baseRaw, basePath, seen);
51734
+ return mergeBinding(resolvedBase, raw);
51735
+ }
51736
+ async function loadBindings(bindingPaths) {
51737
+ const results = [];
51738
+ for (const filePath of bindingPaths) {
51739
+ const raw = await loadRawBinding(filePath);
51740
+ const merged = await resolveBindingExtends(raw, filePath, /* @__PURE__ */ new Set([filePath]));
51741
+ const result = SoftwareBindingSchema.safeParse(merged);
51742
+ if (!result.success) {
51743
+ const issues = result.error.issues.map((i3) => ` ${i3.path.join(".")}: ${i3.message}`).join("\n");
51744
+ throw new ConfigLoadError(
51745
+ `Invalid binding schema in ${filePath}:
51746
+ ${issues}`,
51747
+ filePath
51748
+ );
51749
+ }
51750
+ results.push({ filePath, binding: result.data });
51751
+ }
51752
+ return results;
51753
+ }
51754
+
51652
51755
  // src/cli/commands/resolve.ts
51653
51756
  import { stringify } from "yaml";
51654
51757
  var DIR_DEFAULT = "agent-contracts.yaml";
@@ -53179,7 +53282,7 @@ import { Command as Command4 } from "commander";
53179
53282
 
53180
53283
  // src/renderer/renderer.ts
53181
53284
  import { readFile as readFile4, writeFile, mkdir } from "fs/promises";
53182
- import { dirname as dirname4 } from "path";
53285
+ import { dirname as dirname5 } from "path";
53183
53286
  import Handlebars from "handlebars";
53184
53287
 
53185
53288
  // src/renderer/context.ts
@@ -54302,7 +54405,7 @@ async function renderFromConfig(dsl, renderTargets) {
54302
54405
  if (target.context === "system") {
54303
54406
  const ctx = buildSystemContext(dsl);
54304
54407
  const output = compiled(ctx);
54305
- await mkdir(dirname4(target.output), { recursive: true });
54408
+ await mkdir(dirname5(target.output), { recursive: true });
54306
54409
  await writeFile(target.output, output, "utf8");
54307
54410
  outputFiles.push(target.output);
54308
54411
  } else {
@@ -54313,7 +54416,7 @@ async function renderFromConfig(dsl, renderTargets) {
54313
54416
  const ctx = buildEntityContext(dsl, target.context, entityId);
54314
54417
  const output = compiled(ctx);
54315
54418
  const outputPath = expandOutputPath(target.output, target.context, entityId);
54316
- await mkdir(dirname4(outputPath), { recursive: true });
54419
+ await mkdir(dirname5(outputPath), { recursive: true });
54317
54420
  await writeFile(outputPath, output, "utf8");
54318
54421
  outputFiles.push(outputPath);
54319
54422
  }
@@ -54507,7 +54610,7 @@ import { Command as Command6 } from "commander";
54507
54610
 
54508
54611
  // src/guardrail-generator/generator.ts
54509
54612
  import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2, chmod } from "fs/promises";
54510
- import { resolve as resolve5, dirname as dirname5 } from "path";
54613
+ import { resolve as resolve6, dirname as dirname6 } from "path";
54511
54614
  import Handlebars2 from "handlebars";
54512
54615
 
54513
54616
  // src/guardrail-generator/resolve-checks.ts
@@ -54648,7 +54751,7 @@ async function generateGuardrails(options) {
54648
54751
  if (pathResult.diagnostics.some((d2) => d2.severity === "error")) {
54649
54752
  continue;
54650
54753
  }
54651
- const targetPath = resolve5(config.configDir, pathResult.resolved);
54754
+ const targetPath = resolve6(config.configDir, pathResult.resolved);
54652
54755
  let templateContent;
54653
54756
  if (outputDef.inline_template) {
54654
54757
  templateContent = outputDef.inline_template;
@@ -54661,7 +54764,7 @@ async function generateGuardrails(options) {
54661
54764
  });
54662
54765
  continue;
54663
54766
  }
54664
- const templatePath = resolve5(config.configDir, outputDef.template);
54767
+ const templatePath = resolve6(config.configDir, outputDef.template);
54665
54768
  try {
54666
54769
  templateContent = await readFile5(templatePath, "utf8");
54667
54770
  } catch {
@@ -54698,9 +54801,9 @@ async function generateGuardrails(options) {
54698
54801
  };
54699
54802
  const compiled = Handlebars2.compile(templateContent, { noEscape: true });
54700
54803
  const output = compiled(groupCtx);
54701
- const groupTarget = resolve5(targetPath, groupKey);
54804
+ const groupTarget = resolve6(targetPath, groupKey);
54702
54805
  if (!dryRun) {
54703
- await mkdir2(dirname5(groupTarget), { recursive: true });
54806
+ await mkdir2(dirname6(groupTarget), { recursive: true });
54704
54807
  await writeFile2(groupTarget, output, "utf8");
54705
54808
  if (outputDef.executable) {
54706
54809
  await chmod(groupTarget, 493);
@@ -54712,7 +54815,7 @@ async function generateGuardrails(options) {
54712
54815
  const compiled = Handlebars2.compile(templateContent, { noEscape: true });
54713
54816
  const output = compiled(ctx);
54714
54817
  if (!dryRun) {
54715
- await mkdir2(dirname5(targetPath), { recursive: true });
54818
+ await mkdir2(dirname6(targetPath), { recursive: true });
54716
54819
  await writeFile2(targetPath, output, "utf8");
54717
54820
  if (outputDef.executable) {
54718
54821
  await chmod(targetPath, 493);