@aaac/contracts 0.1.13 → 0.1.15

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.
@@ -64,6 +64,14 @@ var GovernanceSchema = z.object({
64
64
  guardrails: z.boolean().optional(),
65
65
  observability: z.boolean().optional()
66
66
  }).optional();
67
+ var BindingRefSchema = z.object({
68
+ /** npm package name implementing the binding (e.g. "@aaac/binding-cursor"). */
69
+ package: z.string().min(1),
70
+ /** CLI command name to expose (defaults to the binding id). */
71
+ command: z.string().optional(),
72
+ /** Binding-specific configuration; validated by the binding package. */
73
+ config: z.record(z.string(), z.unknown()).default({})
74
+ });
67
75
  var ComponentSchema = z.object({
68
76
  schema: z.string().regex(SCHEMA_VERSION_PATTERN, 'schema must match pattern "aaac/x.y"'),
69
77
  info: z.object({
@@ -77,6 +85,7 @@ var ComponentSchema = z.object({
77
85
  z.enum(["library", "cli", "claude", "openai"])
78
86
  ),
79
87
  governance: GovernanceSchema,
88
+ bindings: z.record(z.string(), BindingRefSchema).optional(),
80
89
  operations: z.record(z.string(), OperationSchema)
81
90
  }).superRefine((component, ctx) => {
82
91
  const usesAgentic = Object.values(component.operations).some(
@@ -148,6 +157,7 @@ function normalizeComponent(raw) {
148
157
  embedded_dsl_dir: raw.embedded_dsl_dir,
149
158
  projections: raw.projections,
150
159
  governance: raw.governance,
160
+ bindings: raw.bindings,
151
161
  operations: Object.keys(normalizedOps).length > 0 ? normalizedOps : raw.operations
152
162
  };
153
163
  }
@@ -568,9 +578,21 @@ function compileComponent(dsl, options = {}) {
568
578
  guardrails: dsl.governance.guardrails,
569
579
  observability: dsl.governance.observability
570
580
  } : void 0,
581
+ bindings: dsl.bindings ? compileBindings(dsl.bindings) : void 0,
571
582
  operations
572
583
  };
573
584
  }
585
+ function compileBindings(bindings) {
586
+ const result = {};
587
+ for (const [id, binding] of Object.entries(bindings)) {
588
+ result[id] = {
589
+ package: binding.package,
590
+ command: binding.command ?? id,
591
+ config: binding.config ?? {}
592
+ };
593
+ }
594
+ return result;
595
+ }
574
596
  async function maybeValidateComponent(ir, options) {
575
597
  if (options.validate === false) {
576
598
  return;
@@ -593,6 +615,44 @@ async function compileComponentFile(filePath, options = {}) {
593
615
  return ir;
594
616
  }
595
617
 
618
+ // src/generators/bindings-generator.ts
619
+ var bindingsGenerator = {
620
+ generate(ir, _options) {
621
+ if (!ir.bindings || Object.keys(ir.bindings).length === 0) {
622
+ return [];
623
+ }
624
+ const entries = Object.entries(ir.bindings).map(([id, binding]) => {
625
+ return ` ${JSON.stringify(id)}: {
626
+ package: ${JSON.stringify(binding.package)},
627
+ command: ${JSON.stringify(binding.command)},
628
+ config: ${JSON.stringify(binding.config)},
629
+ },`;
630
+ });
631
+ const content = `// Auto-generated by @aaac/contracts. Do not edit.
632
+
633
+ export interface BindingEntry {
634
+ /** npm package implementing the binding. */
635
+ package: string;
636
+ /** CLI command name exposed for this binding. */
637
+ command: string;
638
+ /** Binding-specific configuration (validated by the binding package). */
639
+ config: Record<string, unknown>;
640
+ }
641
+
642
+ export const bindings: Record<string, BindingEntry> = {
643
+ ${entries.join("\n")}
644
+ };
645
+ `;
646
+ return [
647
+ {
648
+ path: "bindings.generated.ts",
649
+ content,
650
+ overwrite: true
651
+ }
652
+ ];
653
+ }
654
+ };
655
+
596
656
  // src/generators/cli-generator.ts
597
657
  import { stringify as stringifyYaml } from "yaml";
598
658
  function unwrapSchema(ref) {
@@ -878,6 +938,49 @@ function buildPromptCommand() {
878
938
  function countAgenticOperations(ir) {
879
939
  return Object.values(ir.operations).filter((op) => !op.handler).length;
880
940
  }
941
+ function buildBindingCommands(ir) {
942
+ const commands = {};
943
+ if (!ir.bindings) {
944
+ return commands;
945
+ }
946
+ for (const [bindingId, binding] of Object.entries(ir.bindings)) {
947
+ const cmd = binding.command;
948
+ commands[`${cmd}.sync`] = {
949
+ summary: `Materialize ${bindingId} binding output files from the embedded DSL`,
950
+ options: [
951
+ {
952
+ name: "dry-run",
953
+ schema: { type: "boolean" },
954
+ description: "Report changes without writing files"
955
+ }
956
+ ],
957
+ exits: {
958
+ "0": { description: "Synced" },
959
+ "1": { description: "Materialization failed" }
960
+ },
961
+ "x-agent": {
962
+ risk_level: "low",
963
+ requires_confirmation: false,
964
+ idempotent: true,
965
+ binding: bindingId
966
+ }
967
+ };
968
+ commands[`${cmd}.check`] = {
969
+ summary: `Fail if ${bindingId} binding output drifts from the DSL`,
970
+ exits: {
971
+ "0": { description: "In sync" },
972
+ "2": { description: "Drift detected" }
973
+ },
974
+ "x-agent": {
975
+ risk_level: "low",
976
+ requires_confirmation: false,
977
+ idempotent: true,
978
+ binding: bindingId
979
+ }
980
+ };
981
+ }
982
+ return commands;
983
+ }
881
984
  function buildCliContract(ir) {
882
985
  const commands = {};
883
986
  for (const [name, op] of Object.entries(ir.operations)) {
@@ -886,6 +989,7 @@ function buildCliContract(ir) {
886
989
  if (ir.governance?.guardrails === true) {
887
990
  Object.assign(commands, buildGuardCommands());
888
991
  }
992
+ Object.assign(commands, buildBindingCommands(ir));
889
993
  if (countAgenticOperations(ir) >= 2) {
890
994
  commands.prompt = buildPromptCommand();
891
995
  }
@@ -1079,7 +1183,7 @@ var clientGenerator = {
1079
1183
  ctx: AaacInvocationContext,
1080
1184
  memoryRef?: string,
1081
1185
  ): Promise<${names.output}> {
1082
- return runtime.execute(${JSON.stringify(operationName)}, options, input, ctx, memoryRef);
1186
+ return runtime.execute(${JSON.stringify(operationName)}, options, input, ctx, memoryRef) as Promise<${names.output}>;
1083
1187
  }`;
1084
1188
  });
1085
1189
  if (agenticOpsCount >= 2) {
@@ -1088,7 +1192,7 @@ var clientGenerator = {
1088
1192
  input: { message: string },
1089
1193
  ctx: AaacInvocationContext,
1090
1194
  ): Promise<PromptResult> {
1091
- return runtime.executePrompt(input.message, options, ctx) as Promise<PromptResult>;
1195
+ return runtime.executePrompt(input.message, options as unknown as Record<string, unknown>, ctx) as Promise<PromptResult>;
1092
1196
  }`);
1093
1197
  }
1094
1198
  const runtimeTypeImports = agenticOpsCount >= 2 ? `import type { PromptResult, PromptExecuteOptions } from "@aaac/runtime";
@@ -1426,7 +1530,8 @@ var ALL_GENERATORS = [
1426
1530
  runtimeGenerator,
1427
1531
  cliGenerator,
1428
1532
  handlerGenerator,
1429
- embeddedDslGenerator
1533
+ embeddedDslGenerator,
1534
+ bindingsGenerator
1430
1535
  ];
1431
1536
  function generateAll(ir, options) {
1432
1537
  return ALL_GENERATORS.flatMap((gen) => gen.generate(ir, options));
@@ -1463,6 +1568,488 @@ async function writeGeneratedFiles(files, outputDir) {
1463
1568
  return { written, skipped, warnings };
1464
1569
  }
1465
1570
 
1571
+ // src/cli/governance.ts
1572
+ import { createHash } from "crypto";
1573
+ import { existsSync as existsSync3 } from "fs";
1574
+ import { chmod, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
1575
+ import { dirname as dirname6, join as join4, resolve as resolve4 } from "path";
1576
+ import { parse as parseYaml4 } from "yaml";
1577
+ import { z as z3 } from "zod";
1578
+
1579
+ // src/cli/observ-config.ts
1580
+ import { existsSync as existsSync2 } from "fs";
1581
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
1582
+ import { dirname as dirname5, join as join3, resolve as resolve3 } from "path";
1583
+ import { parse as parseYaml3 } from "yaml";
1584
+ import { z as z2 } from "zod";
1585
+ var OBSERV_CONFIG_DIR = ".agent-logs/config";
1586
+ var EVENT_MAPPING_JSON = `${OBSERV_CONFIG_DIR}/event-mapping.json`;
1587
+ var ARTIFACT_LOOKUP_JSON = `${OBSERV_CONFIG_DIR}/artifact-lookup.json`;
1588
+ var TASK_PATTERNS_JSON = `${OBSERV_CONFIG_DIR}/task-patterns.json`;
1589
+ function generateTaskPatterns() {
1590
+ return [
1591
+ { tag_prefix: "DSL_TASK", field: "task.id" },
1592
+ { tag_prefix: "AGENT_ROLE", field: "task.target_agent" },
1593
+ { tag_prefix: "WORKFLOW_PHASE", field: "workflow.phase" }
1594
+ ];
1595
+ }
1596
+ function generateArtifactLookup(artifactDeclarations) {
1597
+ const patterns = [];
1598
+ for (const decl of artifactDeclarations) {
1599
+ for (const pattern of decl.path_patterns) {
1600
+ patterns.push({ artifact_id: decl.artifact_id, pattern });
1601
+ }
1602
+ }
1603
+ return patterns;
1604
+ }
1605
+ function extractEventMappingRules(parsed) {
1606
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1607
+ const obj = parsed;
1608
+ const rules = obj.event_mapping ?? obj.mappings;
1609
+ if (rules && typeof rules === "object") {
1610
+ return rules;
1611
+ }
1612
+ }
1613
+ return {};
1614
+ }
1615
+ var ArtifactContractsSchema = z2.object({
1616
+ artifacts: z2.record(
1617
+ z2.string(),
1618
+ z2.object({ path_patterns: z2.array(z2.string()).optional() }).passthrough()
1619
+ ).optional()
1620
+ });
1621
+ function parseArtifactDeclarations(parsed) {
1622
+ const { artifacts } = ArtifactContractsSchema.parse(parsed ?? {});
1623
+ if (!artifacts) return [];
1624
+ const decls = [];
1625
+ for (const [artifact_id, decl] of Object.entries(artifacts)) {
1626
+ if (decl.path_patterns && decl.path_patterns.length > 0) {
1627
+ decls.push({ artifact_id, path_patterns: decl.path_patterns });
1628
+ }
1629
+ }
1630
+ return decls;
1631
+ }
1632
+ function serialize(value) {
1633
+ return JSON.stringify(value, null, 2) + "\n";
1634
+ }
1635
+ async function writeJsonIfChanged(absPath, value) {
1636
+ const serialized = serialize(value);
1637
+ const existed = existsSync2(absPath);
1638
+ const existing = existed ? await readFile2(absPath, "utf8") : void 0;
1639
+ if (existing === serialized) {
1640
+ return "unchanged";
1641
+ }
1642
+ await mkdir2(dirname5(absPath), { recursive: true });
1643
+ await writeFile2(absPath, serialized, "utf8");
1644
+ return existed ? "updated" : "created";
1645
+ }
1646
+ async function generateObservConfig(options) {
1647
+ const root = resolve3(options.projectRoot);
1648
+ const generated = [];
1649
+ if (options.eventMapping) {
1650
+ const bindingPath = resolve3(root, options.eventMapping);
1651
+ if (existsSync2(bindingPath)) {
1652
+ const parsed = parseYaml3(await readFile2(bindingPath, "utf8"));
1653
+ const mappings = extractEventMappingRules(parsed);
1654
+ const action = await writeJsonIfChanged(
1655
+ join3(root, EVENT_MAPPING_JSON),
1656
+ { mappings }
1657
+ );
1658
+ generated.push({ path: EVENT_MAPPING_JSON, action });
1659
+ }
1660
+ }
1661
+ if (options.artifactContracts) {
1662
+ const contractsPath = resolve3(root, options.artifactContracts);
1663
+ if (existsSync2(contractsPath)) {
1664
+ const parsed = parseYaml3(await readFile2(contractsPath, "utf8"));
1665
+ const lookup = generateArtifactLookup(parseArtifactDeclarations(parsed));
1666
+ const action = await writeJsonIfChanged(
1667
+ join3(root, ARTIFACT_LOOKUP_JSON),
1668
+ lookup
1669
+ );
1670
+ generated.push({ path: ARTIFACT_LOOKUP_JSON, action });
1671
+ }
1672
+ }
1673
+ {
1674
+ const action = await writeJsonIfChanged(
1675
+ join3(root, TASK_PATTERNS_JSON),
1676
+ generateTaskPatterns()
1677
+ );
1678
+ generated.push({ path: TASK_PATTERNS_JSON, action });
1679
+ }
1680
+ const lockEntries = generated.map((g) => ({
1681
+ path: g.path,
1682
+ type: "generated"
1683
+ }));
1684
+ return { generated, lockEntries };
1685
+ }
1686
+
1687
+ // src/cli/governance.ts
1688
+ var MANAGED_BLOCK_START = "# >>> aaac-governance:managed (do not edit) >>>";
1689
+ var MANAGED_BLOCK_END = "# <<< aaac-governance:managed <<<";
1690
+ var GOVERNANCE_LOCK_PATH = ".agent-logs/governance-manifest.lock";
1691
+ var DEFAULT_RECORDER = ".cursor/hooks/observ-record.sh";
1692
+ var DEFAULT_CURSOR_HOOKS_JSON = ".cursor/hooks.json";
1693
+ var DEFAULT_BINARY_DEST = "bin/aaac.js";
1694
+ var CURSOR_HOOKS_VERSION = 1;
1695
+ var EnvironmentsSchema = z3.object({
1696
+ git: z3.object({ hooks: z3.array(z3.string()).optional() }).optional(),
1697
+ cursor: z3.object({ hooks_json: z3.string().optional() }).optional()
1698
+ }).optional();
1699
+ var BindingsObservabilitySchema = z3.object({
1700
+ event_mapping: z3.string().optional(),
1701
+ recorder: z3.string().optional()
1702
+ }).optional();
1703
+ var BindingsSchema = z3.object({
1704
+ observability: BindingsObservabilitySchema,
1705
+ hooks: z3.array(z3.string()).optional()
1706
+ }).optional();
1707
+ var InitProjectConfigSchema = z3.object({
1708
+ environments: EnvironmentsSchema,
1709
+ bindings: BindingsSchema,
1710
+ artifacts: z3.string().optional()
1711
+ });
1712
+ async function loadInitConfig(configPath) {
1713
+ const absPath = resolve4(configPath);
1714
+ const raw = await readFile3(absPath, "utf8");
1715
+ const parsed = parseYaml4(raw);
1716
+ return InitProjectConfigSchema.parse(parsed ?? {});
1717
+ }
1718
+ var HookCommandSchema = z3.object({
1719
+ command: z3.string(),
1720
+ description: z3.string().optional()
1721
+ });
1722
+ var HookBindingSchema = z3.object({
1723
+ hooks: z3.record(z3.string(), z3.array(HookCommandSchema)).optional()
1724
+ });
1725
+ async function loadHookBindings(projectRoot, bindingPaths) {
1726
+ const result = {};
1727
+ if (!bindingPaths?.length) return result;
1728
+ const root = resolve4(projectRoot);
1729
+ for (const bindingPath of bindingPaths) {
1730
+ const absPath = resolve4(root, bindingPath);
1731
+ if (!existsSync3(absPath)) continue;
1732
+ const raw = await readFile3(absPath, "utf8");
1733
+ const parsed = HookBindingSchema.parse(parseYaml4(raw) ?? {});
1734
+ if (!parsed.hooks) continue;
1735
+ for (const [hookName, commands] of Object.entries(parsed.hooks)) {
1736
+ const list = result[hookName] ?? [];
1737
+ for (const { command } of commands) {
1738
+ if (!list.includes(command)) list.push(command);
1739
+ }
1740
+ result[hookName] = list;
1741
+ }
1742
+ }
1743
+ return result;
1744
+ }
1745
+ function escapeRegex(value) {
1746
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1747
+ }
1748
+ function createManagedBlockRegex() {
1749
+ return new RegExp(
1750
+ `${escapeRegex(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegex(MANAGED_BLOCK_END)}\\n?`,
1751
+ "g"
1752
+ );
1753
+ }
1754
+ function buildManagedBlock(blockContent) {
1755
+ return `${MANAGED_BLOCK_START}
1756
+ ${blockContent.trimEnd()}
1757
+ ${MANAGED_BLOCK_END}
1758
+ `;
1759
+ }
1760
+ function injectManagedBlock(existingContent, blockContent) {
1761
+ const managedBlock = buildManagedBlock(blockContent);
1762
+ const re = createManagedBlockRegex();
1763
+ if (re.test(existingContent)) {
1764
+ return existingContent.replace(createManagedBlockRegex(), managedBlock);
1765
+ }
1766
+ const trimmed = existingContent.replace(/\s+$/, "");
1767
+ if (trimmed.length === 0) {
1768
+ return `#!/bin/sh
1769
+ ${managedBlock}`;
1770
+ }
1771
+ const separator = trimmed.endsWith("\n") ? "" : "\n";
1772
+ return `${trimmed}${separator}${managedBlock}`;
1773
+ }
1774
+ function removeManagedBlock(existingContent) {
1775
+ if (!existingContent.includes(MANAGED_BLOCK_START)) {
1776
+ return existingContent;
1777
+ }
1778
+ const withoutBlock = existingContent.replace(createManagedBlockRegex(), "");
1779
+ const meaningful = withoutBlock.split("\n").filter((line) => {
1780
+ const t = line.trim();
1781
+ return t.length > 0 && t !== "#!/bin/sh" && t !== "#!/bin/bash";
1782
+ });
1783
+ if (meaningful.length === 0) {
1784
+ return "";
1785
+ }
1786
+ return withoutBlock.replace(/\n{3,}/g, "\n\n").replace(/\s+$/, "\n");
1787
+ }
1788
+ function parseCursorHooksFile(raw) {
1789
+ if (!raw || !raw.trim()) {
1790
+ return { version: CURSOR_HOOKS_VERSION, hooks: {} };
1791
+ }
1792
+ const parsed = JSON.parse(raw);
1793
+ if (Array.isArray(parsed)) {
1794
+ return flatEntriesToFile(
1795
+ parsed.filter(
1796
+ (entry) => entry !== null && typeof entry === "object" && typeof entry.event === "string" && typeof entry.command === "string"
1797
+ )
1798
+ );
1799
+ }
1800
+ if (parsed && typeof parsed === "object") {
1801
+ const obj = parsed;
1802
+ const version = typeof obj.version === "number" ? obj.version : CURSOR_HOOKS_VERSION;
1803
+ const hooks = {};
1804
+ const rawHooks = obj.hooks;
1805
+ if (rawHooks && typeof rawHooks === "object" && !Array.isArray(rawHooks)) {
1806
+ for (const [event, commands] of Object.entries(rawHooks)) {
1807
+ if (Array.isArray(commands)) {
1808
+ hooks[event] = commands.filter(
1809
+ (c) => c !== null && typeof c === "object" && typeof c.command === "string"
1810
+ );
1811
+ }
1812
+ }
1813
+ }
1814
+ return { version, hooks };
1815
+ }
1816
+ return { version: CURSOR_HOOKS_VERSION, hooks: {} };
1817
+ }
1818
+ function serializeCursorHooksFile(file) {
1819
+ return JSON.stringify(file, null, 2) + "\n";
1820
+ }
1821
+ function flatEntriesToFile(entries) {
1822
+ return mergeCursorHooks({ version: CURSOR_HOOKS_VERSION, hooks: {} }, entries);
1823
+ }
1824
+ function mergeCursorHooks(existing, newEntries) {
1825
+ const hooks = {};
1826
+ for (const [event, commands] of Object.entries(existing.hooks)) {
1827
+ hooks[event] = commands.map((c) => ({ ...c }));
1828
+ }
1829
+ for (const entry of newEntries) {
1830
+ const { event, ...command } = entry;
1831
+ const list = hooks[event] ?? [];
1832
+ const idx = list.findIndex((c) => c.command === command.command);
1833
+ if (idx >= 0) {
1834
+ list[idx] = command;
1835
+ } else {
1836
+ list.push(command);
1837
+ }
1838
+ hooks[event] = list;
1839
+ }
1840
+ return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
1841
+ }
1842
+ function removeCursorHooks(existing, commands) {
1843
+ const removeSet = new Set(commands);
1844
+ const hooks = {};
1845
+ for (const [event, list] of Object.entries(existing.hooks)) {
1846
+ const filtered = list.filter((c) => !removeSet.has(c.command));
1847
+ if (filtered.length > 0) {
1848
+ hooks[event] = filtered;
1849
+ }
1850
+ }
1851
+ return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
1852
+ }
1853
+ function buildGitHookBlock(hookName, recorder, gateCommands = []) {
1854
+ const lines = [`${recorder} ${hookName} "$@" || true`];
1855
+ for (const command of gateCommands) {
1856
+ lines.push(`${command} || exit 1`);
1857
+ }
1858
+ return lines.join("\n");
1859
+ }
1860
+ var DEFAULT_CURSOR_HOOK_EVENTS = [
1861
+ "sessionStart",
1862
+ "sessionEnd",
1863
+ "subagentStart",
1864
+ "subagentStop",
1865
+ "beforeSubmitPrompt",
1866
+ "afterShellExecution",
1867
+ "preToolUse",
1868
+ "postToolUse",
1869
+ "postToolUseFailure",
1870
+ "afterFileEdit",
1871
+ "preCompact",
1872
+ "stop"
1873
+ ];
1874
+ function buildDefaultCursorHooks(recorder) {
1875
+ return DEFAULT_CURSOR_HOOK_EVENTS.map((event) => ({
1876
+ event,
1877
+ command: `${recorder} ${event}`
1878
+ }));
1879
+ }
1880
+ async function ensureParentDir(filePath) {
1881
+ await mkdir3(dirname6(filePath), { recursive: true });
1882
+ }
1883
+ async function readTextFileIfExists(filePath) {
1884
+ if (!existsSync3(filePath)) return void 0;
1885
+ return readFile3(filePath, "utf8");
1886
+ }
1887
+ async function writeGovernanceLock(projectRoot, entries) {
1888
+ const lockPath = join4(projectRoot, GOVERNANCE_LOCK_PATH);
1889
+ const lock = {
1890
+ materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
1891
+ entries
1892
+ };
1893
+ await ensureParentDir(lockPath);
1894
+ await writeFile3(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf8");
1895
+ return GOVERNANCE_LOCK_PATH;
1896
+ }
1897
+ async function readGovernanceLock(projectRoot) {
1898
+ const lockPath = join4(projectRoot, GOVERNANCE_LOCK_PATH);
1899
+ if (!existsSync3(lockPath)) return void 0;
1900
+ const raw = await readFile3(lockPath, "utf8");
1901
+ if (!raw.trim()) return void 0;
1902
+ return JSON.parse(raw);
1903
+ }
1904
+ async function materializeHooks(options) {
1905
+ const projectRoot = resolve4(options.projectRoot);
1906
+ const result = { materialized: [], lockEntries: [] };
1907
+ const recorder = options.observabilityBinding?.recorder ?? DEFAULT_RECORDER;
1908
+ if (options.environments.git?.hooks?.length) {
1909
+ for (const hookName of options.environments.git.hooks) {
1910
+ const relPath = join4(".git/hooks", hookName);
1911
+ const absPath = join4(projectRoot, relPath);
1912
+ const blockContent = buildGitHookBlock(
1913
+ hookName,
1914
+ recorder,
1915
+ options.hookCommands?.[hookName]
1916
+ );
1917
+ const existing = await readTextFileIfExists(absPath) ?? "";
1918
+ const updated = injectManagedBlock(existing, blockContent);
1919
+ let action = "unchanged";
1920
+ if (!existsSync3(absPath)) {
1921
+ action = "created";
1922
+ } else if (updated !== existing) {
1923
+ action = "updated";
1924
+ }
1925
+ if (action !== "unchanged") {
1926
+ await ensureParentDir(absPath);
1927
+ await writeFile3(absPath, updated, "utf8");
1928
+ await chmod(absPath, 493);
1929
+ }
1930
+ result.materialized.push({ path: relPath, action });
1931
+ result.lockEntries.push({ path: relPath, type: "git-hook" });
1932
+ }
1933
+ }
1934
+ if (options.environments.cursor) {
1935
+ const rawHooksJsonValue = options.environments.cursor.hooks_json;
1936
+ const hooksJsonPath = rawHooksJsonValue ? resolve4(projectRoot, rawHooksJsonValue) : join4(projectRoot, DEFAULT_CURSOR_HOOKS_JSON);
1937
+ const relPath = hooksJsonPath.startsWith(projectRoot + "/") ? hooksJsonPath.slice(projectRoot.length + 1) : DEFAULT_CURSOR_HOOKS_JSON;
1938
+ const newEntries = buildDefaultCursorHooks(recorder);
1939
+ const existingRaw = await readTextFileIfExists(hooksJsonPath);
1940
+ const existing = parseCursorHooksFile(existingRaw);
1941
+ const merged = mergeCursorHooks(existing, newEntries);
1942
+ const serialized = serializeCursorHooksFile(merged);
1943
+ let action = "unchanged";
1944
+ if (!existsSync3(hooksJsonPath)) {
1945
+ action = "created";
1946
+ } else if (serialized !== existingRaw) {
1947
+ action = "updated";
1948
+ }
1949
+ if (action !== "unchanged") {
1950
+ await ensureParentDir(hooksJsonPath);
1951
+ await writeFile3(hooksJsonPath, serialized, "utf8");
1952
+ }
1953
+ result.materialized.push({ path: relPath, action });
1954
+ result.lockEntries.push({ path: relPath, type: "cursor-hook" });
1955
+ }
1956
+ if (options.observabilityBinding) {
1957
+ const { generated, lockEntries } = await generateObservConfig({
1958
+ projectRoot,
1959
+ eventMapping: options.observabilityBinding.event_mapping,
1960
+ artifactContracts: options.artifactContracts
1961
+ });
1962
+ for (const g of generated) {
1963
+ result.materialized.push({ path: g.path, action: g.action });
1964
+ }
1965
+ result.lockEntries.push(...lockEntries);
1966
+ }
1967
+ if (result.lockEntries.length > 0) {
1968
+ const lockExisted = existsSync3(join4(projectRoot, GOVERNANCE_LOCK_PATH));
1969
+ await writeGovernanceLock(projectRoot, result.lockEntries);
1970
+ result.materialized.push({
1971
+ path: GOVERNANCE_LOCK_PATH,
1972
+ action: lockExisted ? "updated" : "created"
1973
+ });
1974
+ result.lockEntries.push({ path: GOVERNANCE_LOCK_PATH, type: "generated" });
1975
+ }
1976
+ return result;
1977
+ }
1978
+ async function unmaterializeHooks(projectRoot, lockEntries) {
1979
+ const root = resolve4(projectRoot);
1980
+ for (const entry of lockEntries) {
1981
+ if (entry.type === "generated") {
1982
+ const absPath = join4(root, entry.path);
1983
+ if (existsSync3(absPath)) {
1984
+ await writeFile3(absPath, "", "utf8");
1985
+ }
1986
+ continue;
1987
+ }
1988
+ if (entry.type === "git-hook") {
1989
+ const absPath = join4(root, entry.path);
1990
+ const existing = await readTextFileIfExists(absPath);
1991
+ if (existing === void 0) continue;
1992
+ const updated = removeManagedBlock(existing);
1993
+ await writeFile3(absPath, updated, "utf8");
1994
+ continue;
1995
+ }
1996
+ if (entry.type === "cursor-hook") {
1997
+ const absPath = join4(root, entry.path);
1998
+ const existingRaw = await readTextFileIfExists(absPath);
1999
+ if (existingRaw === void 0) continue;
2000
+ const existing = parseCursorHooksFile(existingRaw);
2001
+ const commandsToRemove = buildDefaultCursorHooks(DEFAULT_RECORDER).map((h) => h.command);
2002
+ const updated = removeCursorHooks(existing, commandsToRemove);
2003
+ await writeFile3(absPath, serializeCursorHooksFile(updated), "utf8");
2004
+ }
2005
+ }
2006
+ const lockPath = join4(root, GOVERNANCE_LOCK_PATH);
2007
+ if (existsSync3(lockPath)) {
2008
+ await writeFile3(lockPath, "", "utf8");
2009
+ }
2010
+ }
2011
+ async function copyCLIBinary(srcPath, projectRoot, destRelPath = DEFAULT_BINARY_DEST) {
2012
+ const dstPath = join4(projectRoot, destRelPath);
2013
+ const srcContent = await readFile3(srcPath);
2014
+ const srcHash = createHash("sha256").update(srcContent).digest("hex");
2015
+ if (existsSync3(dstPath)) {
2016
+ const dstContent = await readFile3(dstPath);
2017
+ const dstHash = createHash("sha256").update(dstContent).digest("hex");
2018
+ if (srcHash === dstHash) {
2019
+ return { path: destRelPath, action: "unchanged" };
2020
+ }
2021
+ await writeFile3(dstPath, srcContent);
2022
+ await chmod(dstPath, 493);
2023
+ return { path: destRelPath, action: "updated" };
2024
+ }
2025
+ await ensureParentDir(dstPath);
2026
+ await writeFile3(dstPath, srcContent);
2027
+ await chmod(dstPath, 493);
2028
+ return { path: destRelPath, action: "created" };
2029
+ }
2030
+ var SCAFFOLD_PROJECT_YAML = `schema: aaac/project/0.1
2031
+
2032
+ # Uncomment and configure to enable git hook governance:
2033
+ #
2034
+ # environments:
2035
+ # git:
2036
+ # # commit-msg/post-commit are observability-only (promotion axis): post-commit
2037
+ # # records promotion.commit + promotion.file, feeding Enricher R1/R2/R5.
2038
+ # hooks: [pre-commit, commit-msg, post-commit, pre-push]
2039
+ # cursor:
2040
+ # hooks_json: .cursor/hooks.json
2041
+ #
2042
+ # bindings:
2043
+ # observability:
2044
+ # event_mapping: ./bindings/observability.yaml
2045
+ # recorder: aaac-observ
2046
+ # hooks:
2047
+ # # Each file declares gate commands per git hook; a non-zero exit
2048
+ # # blocks the commit/push. Only hooks listed in environments.git.hooks
2049
+ # # are materialized.
2050
+ # - ./bindings/git.yaml
2051
+ `;
2052
+
1466
2053
  export {
1467
2054
  SUPPORTED_SCHEMA_VERSIONS,
1468
2055
  SCHEMA_VERSION_PATTERN,
@@ -1470,6 +2057,7 @@ export {
1470
2057
  OptionSchema,
1471
2058
  ArtifactSlotValueSchema,
1472
2059
  OperationSchema,
2060
+ BindingRefSchema,
1473
2061
  ComponentSchema,
1474
2062
  ComponentParseError,
1475
2063
  parseComponentString,
@@ -1484,12 +2072,24 @@ export {
1484
2072
  compileComponent,
1485
2073
  compileComponentAsync,
1486
2074
  compileComponentFile,
2075
+ bindingsGenerator,
1487
2076
  cliGenerator,
1488
2077
  clientGenerator,
1489
2078
  handlerGenerator,
1490
2079
  runtimeGenerator,
1491
2080
  typesGenerator,
1492
2081
  generateAll,
1493
- writeGeneratedFiles
2082
+ writeGeneratedFiles,
2083
+ GOVERNANCE_LOCK_PATH,
2084
+ loadInitConfig,
2085
+ loadHookBindings,
2086
+ parseCursorHooksFile,
2087
+ serializeCursorHooksFile,
2088
+ mergeCursorHooks,
2089
+ readGovernanceLock,
2090
+ materializeHooks,
2091
+ unmaterializeHooks,
2092
+ copyCLIBinary,
2093
+ SCAFFOLD_PROJECT_YAML
1494
2094
  };
1495
- //# sourceMappingURL=chunk-MWZFJ2VL.js.map
2095
+ //# sourceMappingURL=chunk-F57V745S.js.map