@aaac/contracts 0.1.15 → 0.1.16

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/index.js CHANGED
@@ -13,8 +13,11 @@ import {
13
13
  loadHookBindings,
14
14
  loadInitConfig,
15
15
  materializeHooks,
16
+ mergeCursorHooks,
16
17
  parseComponentFile,
18
+ parseCursorHooksFile,
17
19
  readGovernanceLock,
20
+ serializeCursorHooksFile,
18
21
  unmaterializeHooks,
19
22
  validateComponent,
20
23
  writeGeneratedFiles
@@ -22,7 +25,7 @@ import {
22
25
 
23
26
  // src/cli/index.ts
24
27
  import { readFileSync } from "fs";
25
- import { dirname as dirname2, resolve as resolve2 } from "path";
28
+ import { dirname as dirname3, resolve as resolve2 } from "path";
26
29
  import { fileURLToPath } from "url";
27
30
 
28
31
  // ../../node_modules/commander/lib/error.js
@@ -3649,6 +3652,72 @@ var commandDefinitions = {
3649
3652
  }
3650
3653
  }
3651
3654
  ]
3655
+ },
3656
+ "init": {
3657
+ "effects": {},
3658
+ "options": [
3659
+ {
3660
+ "name": "project-yaml",
3661
+ "schema": {
3662
+ "type": "string"
3663
+ }
3664
+ },
3665
+ {
3666
+ "name": "binary-path",
3667
+ "schema": {
3668
+ "type": "string"
3669
+ }
3670
+ }
3671
+ ]
3672
+ },
3673
+ "update": {
3674
+ "effects": {},
3675
+ "options": [
3676
+ {
3677
+ "name": "project-yaml",
3678
+ "schema": {
3679
+ "type": "string"
3680
+ }
3681
+ }
3682
+ ]
3683
+ },
3684
+ "uninstall": {
3685
+ "effects": {}
3686
+ },
3687
+ "install": {
3688
+ "effects": {},
3689
+ "options": [
3690
+ {
3691
+ "name": "global",
3692
+ "schema": {
3693
+ "type": "boolean"
3694
+ }
3695
+ },
3696
+ {
3697
+ "name": "dry-run",
3698
+ "schema": {
3699
+ "type": "boolean"
3700
+ }
3701
+ },
3702
+ {
3703
+ "name": "skip-cursor",
3704
+ "schema": {
3705
+ "type": "boolean"
3706
+ }
3707
+ },
3708
+ {
3709
+ "name": "skip-claude",
3710
+ "schema": {
3711
+ "type": "boolean"
3712
+ }
3713
+ },
3714
+ {
3715
+ "name": "skip-git",
3716
+ "schema": {
3717
+ "type": "boolean"
3718
+ }
3719
+ }
3720
+ ]
3652
3721
  }
3653
3722
  };
3654
3723
  function deriveCommandPolicy(command_id, optionValues) {
@@ -3675,8 +3744,230 @@ function deriveCommandPolicy(command_id, optionValues) {
3675
3744
  }
3676
3745
 
3677
3746
  // src/generated/contract.ts
3678
- var CONTRACT_YAML = "# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\ncli_contracts: 0.1.0\n\ninfo:\n title: AaaC CLI\n version: 0.1.0\n description: Agent as a Component \u2014 Contract compiler and code generator\n\ncommand_sets:\n aaac:\n summary: AaaC CLI \u2014 compile Component Contracts and generate adapters\n executable: aaac\n global_options:\n - name: verbose\n aliases: [v]\n schema: { type: boolean }\n description: Enable verbose output\n - name: quiet\n aliases: [q]\n schema: { type: boolean }\n description: Suppress non-essential output\n - name: resume\n schema: { type: string }\n description: Resume from a previous memory_ref ID\n commands:\n compile:\n summary: Compile a Component Contract YAML to Component IR\n arguments:\n - name: file\n required: true\n schema: { type: string }\n description: Path to the Component Contract YAML file\n options:\n - name: output\n aliases: [o]\n schema: { type: string }\n description: Output path for the compiled IR (JSON)\n exits:\n '0':\n description: Compilation succeeded\n stdout:\n format: json\n schema:\n type: object\n description: Component IR\n '1':\n description: Compilation failed (validation errors)\n stderr:\n format: text\n\n generate:\n summary: Generate adapters from a Component Contract\n arguments:\n - name: file\n required: true\n schema: { type: string }\n description: Path to the Component Contract YAML file\n options:\n - name: output-dir\n aliases: [o]\n schema: { type: string }\n description: Output directory for generated files\n - name: targets\n aliases: [t]\n schema:\n type: array\n items:\n type: string\n enum: [library, cli, runtime, claude, openai]\n description: Generation targets (defaults to projections in contract)\n - name: dry-run\n schema: { type: boolean }\n description: Show what would be generated without writing files\n effects:\n filesystem: read_write\n exits:\n '0':\n description: Generation succeeded\n stdout:\n format: text\n '1':\n description: Generation failed\n stderr:\n format: text\n\n validate:\n summary: Validate a Component Contract YAML\n arguments:\n - name: file\n required: true\n schema: { type: string }\n description: Path to the Component Contract YAML file\n options:\n - name: strict\n schema: { type: boolean }\n description: Enable strict validation (check implementation refs)\n exits:\n '0':\n description: Validation passed\n stdout:\n format: text\n '1':\n description: Validation failed\n stderr:\n format: text\n\n introspect:\n summary: Dump Component IR information\n arguments:\n - name: file\n required: true\n schema: { type: string }\n description: Path to the Component Contract YAML file\n options:\n - name: format\n aliases: [f]\n schema:\n type: string\n enum: [json, yaml]\n description: Output format (default yaml)\n exits:\n '0':\n description: Success\n stdout:\n format: json\n schema:\n type: object\n description: Component IR dump\n";
3679
- var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "AaaC CLI",\n "version": "0.1.0",\n "description": "Agent as a Component \u2014 Contract compiler and code generator"\n },\n "command_sets": {\n "aaac": {\n "summary": "AaaC CLI \u2014 compile Component Contracts and generate adapters",\n "executable": "aaac",\n "global_options": [\n {\n "name": "verbose",\n "aliases": [\n "v"\n ],\n "schema": {\n "type": "boolean"\n },\n "description": "Enable verbose output"\n },\n {\n "name": "quiet",\n "aliases": [\n "q"\n ],\n "schema": {\n "type": "boolean"\n },\n "description": "Suppress non-essential output"\n },\n {\n "name": "resume",\n "schema": {\n "type": "string"\n },\n "description": "Resume from a previous memory_ref ID"\n }\n ],\n "commands": {\n "compile": {\n "summary": "Compile a Component Contract YAML to Component IR",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "output",\n "aliases": [\n "o"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Output path for the compiled IR (JSON)"\n }\n ],\n "exits": {\n "0": {\n "description": "Compilation succeeded",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "description": "Component IR"\n }\n }\n },\n "1": {\n "description": "Compilation failed (validation errors)",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "generate": {\n "summary": "Generate adapters from a Component Contract",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "output-dir",\n "aliases": [\n "o"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Output directory for generated files"\n },\n {\n "name": "targets",\n "aliases": [\n "t"\n ],\n "schema": {\n "type": "array",\n "items": {\n "type": "string",\n "enum": [\n "library",\n "cli",\n "runtime",\n "claude",\n "openai"\n ]\n }\n },\n "description": "Generation targets (defaults to projections in contract)"\n },\n {\n "name": "dry-run",\n "schema": {\n "type": "boolean"\n },\n "description": "Show what would be generated without writing files"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Generation succeeded",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Generation failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "validate": {\n "summary": "Validate a Component Contract YAML",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "strict",\n "schema": {\n "type": "boolean"\n },\n "description": "Enable strict validation (check implementation refs)"\n }\n ],\n "exits": {\n "0": {\n "description": "Validation passed",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Validation failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "introspect": {\n "summary": "Dump Component IR information",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "json",\n "yaml"\n ]\n },\n "description": "Output format (default yaml)"\n }\n ],\n "exits": {\n "0": {\n "description": "Success",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "description": "Component IR dump"\n }\n }\n }\n }\n }\n }\n }\n }\n}';
3747
+ var CONTRACT_YAML = `# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json
3748
+ cli_contracts: 0.1.0
3749
+
3750
+ info:
3751
+ title: AaaC CLI
3752
+ version: 0.1.0
3753
+ description: Agent as a Component \u2014 Contract compiler and code generator
3754
+
3755
+ command_sets:
3756
+ aaac:
3757
+ summary: AaaC CLI \u2014 compile Component Contracts and generate adapters
3758
+ executable: aaac
3759
+ global_options:
3760
+ - name: verbose
3761
+ aliases: [v]
3762
+ schema: { type: boolean }
3763
+ description: Enable verbose output
3764
+ - name: quiet
3765
+ aliases: [q]
3766
+ schema: { type: boolean }
3767
+ description: Suppress non-essential output
3768
+ - name: resume
3769
+ schema: { type: string }
3770
+ description: Resume from a previous memory_ref ID
3771
+ commands:
3772
+ compile:
3773
+ summary: Compile a Component Contract YAML to Component IR
3774
+ arguments:
3775
+ - name: file
3776
+ required: true
3777
+ schema: { type: string }
3778
+ description: Path to the Component Contract YAML file
3779
+ options:
3780
+ - name: output
3781
+ aliases: [o]
3782
+ schema: { type: string }
3783
+ description: Output path for the compiled IR (JSON)
3784
+ exits:
3785
+ '0':
3786
+ description: Compilation succeeded
3787
+ stdout:
3788
+ format: json
3789
+ schema:
3790
+ type: object
3791
+ description: Component IR
3792
+ '1':
3793
+ description: Compilation failed (validation errors)
3794
+ stderr:
3795
+ format: text
3796
+
3797
+ generate:
3798
+ summary: Generate adapters from a Component Contract
3799
+ arguments:
3800
+ - name: file
3801
+ required: true
3802
+ schema: { type: string }
3803
+ description: Path to the Component Contract YAML file
3804
+ options:
3805
+ - name: output-dir
3806
+ aliases: [o]
3807
+ schema: { type: string }
3808
+ description: Output directory for generated files
3809
+ - name: targets
3810
+ aliases: [t]
3811
+ schema:
3812
+ type: array
3813
+ items:
3814
+ type: string
3815
+ enum: [library, cli, runtime, claude, openai]
3816
+ description: Generation targets (defaults to projections in contract)
3817
+ - name: dry-run
3818
+ schema: { type: boolean }
3819
+ description: Show what would be generated without writing files
3820
+ effects:
3821
+ filesystem: read_write
3822
+ exits:
3823
+ '0':
3824
+ description: Generation succeeded
3825
+ stdout:
3826
+ format: text
3827
+ '1':
3828
+ description: Generation failed
3829
+ stderr:
3830
+ format: text
3831
+
3832
+ validate:
3833
+ summary: Validate a Component Contract YAML
3834
+ arguments:
3835
+ - name: file
3836
+ required: true
3837
+ schema: { type: string }
3838
+ description: Path to the Component Contract YAML file
3839
+ options:
3840
+ - name: strict
3841
+ schema: { type: boolean }
3842
+ description: Enable strict validation (check implementation refs)
3843
+ exits:
3844
+ '0':
3845
+ description: Validation passed
3846
+ stdout:
3847
+ format: text
3848
+ '1':
3849
+ description: Validation failed
3850
+ stderr:
3851
+ format: text
3852
+
3853
+ introspect:
3854
+ summary: Dump Component IR information
3855
+ arguments:
3856
+ - name: file
3857
+ required: true
3858
+ schema: { type: string }
3859
+ description: Path to the Component Contract YAML file
3860
+ options:
3861
+ - name: format
3862
+ aliases: [f]
3863
+ schema:
3864
+ type: string
3865
+ enum: [json, yaml]
3866
+ description: Output format (default yaml)
3867
+ exits:
3868
+ '0':
3869
+ description: Success
3870
+ stdout:
3871
+ format: json
3872
+ schema:
3873
+ type: object
3874
+ description: Component IR dump
3875
+
3876
+ init:
3877
+ summary: Initialize a project \u2014 copy CLI binary, scaffold project.yaml, materialize git hooks
3878
+ arguments:
3879
+ - name: projectRoot
3880
+ required: false
3881
+ schema: { type: string }
3882
+ description: "Project root directory (default: current directory)"
3883
+ options:
3884
+ - name: project-yaml
3885
+ schema: { type: string }
3886
+ description: Path to project.yaml relative to projectRoot (default project.yaml)
3887
+ - name: binary-path
3888
+ schema: { type: string }
3889
+ description: "Path to the CLI binary to copy (default: process.argv[1]); for programmatic/test use"
3890
+ effects:
3891
+ filesystem: read_write
3892
+ exits:
3893
+ '0':
3894
+ description: Initialization succeeded
3895
+ stdout:
3896
+ format: text
3897
+ '1':
3898
+ description: Initialization failed
3899
+ stderr:
3900
+ format: text
3901
+
3902
+ update:
3903
+ summary: Re-read project.yaml and re-materialize git hooks
3904
+ arguments:
3905
+ - name: projectRoot
3906
+ required: false
3907
+ schema: { type: string }
3908
+ description: "Project root directory (default: current directory)"
3909
+ options:
3910
+ - name: project-yaml
3911
+ schema: { type: string }
3912
+ description: Path to project.yaml relative to projectRoot (default project.yaml)
3913
+ effects:
3914
+ filesystem: read_write
3915
+ exits:
3916
+ '0':
3917
+ description: Update succeeded
3918
+ stdout:
3919
+ format: text
3920
+ '1':
3921
+ description: Update failed
3922
+ stderr:
3923
+ format: text
3924
+
3925
+ uninstall:
3926
+ summary: Remove all managed hook blocks recorded in governance-manifest.lock
3927
+ arguments:
3928
+ - name: projectRoot
3929
+ required: false
3930
+ schema: { type: string }
3931
+ description: "Project root directory (default: current directory)"
3932
+ effects:
3933
+ filesystem: read_write
3934
+ exits:
3935
+ '0':
3936
+ description: Uninstall succeeded
3937
+ stdout:
3938
+ format: text
3939
+
3940
+ install:
3941
+ summary: Install user-global observability hooks (Cursor + Claude + Git) in one command \u2014 idempotent, non-destructive
3942
+ options:
3943
+ - name: global
3944
+ schema: { type: boolean }
3945
+ description: Install at user scope (~/.cursor, ~/.claude, git --global). Currently the only supported scope.
3946
+ - name: dry-run
3947
+ schema: { type: boolean }
3948
+ description: Preview the changes without writing any files or git config
3949
+ - name: skip-cursor
3950
+ schema: { type: boolean }
3951
+ description: Do not configure the Cursor user hooks
3952
+ - name: skip-claude
3953
+ schema: { type: boolean }
3954
+ description: Do not configure the Claude Code user hooks
3955
+ - name: skip-git
3956
+ schema: { type: boolean }
3957
+ description: Do not configure the global git hooks / core.hooksPath
3958
+ effects:
3959
+ filesystem: read_write
3960
+ exits:
3961
+ '0':
3962
+ description: Install succeeded (or previewed with --dry-run)
3963
+ stdout:
3964
+ format: text
3965
+ '1':
3966
+ description: Install failed
3967
+ stderr:
3968
+ format: text
3969
+ `;
3970
+ var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "AaaC CLI",\n "version": "0.1.0",\n "description": "Agent as a Component \u2014 Contract compiler and code generator"\n },\n "command_sets": {\n "aaac": {\n "summary": "AaaC CLI \u2014 compile Component Contracts and generate adapters",\n "executable": "aaac",\n "global_options": [\n {\n "name": "verbose",\n "aliases": [\n "v"\n ],\n "schema": {\n "type": "boolean"\n },\n "description": "Enable verbose output"\n },\n {\n "name": "quiet",\n "aliases": [\n "q"\n ],\n "schema": {\n "type": "boolean"\n },\n "description": "Suppress non-essential output"\n },\n {\n "name": "resume",\n "schema": {\n "type": "string"\n },\n "description": "Resume from a previous memory_ref ID"\n }\n ],\n "commands": {\n "compile": {\n "summary": "Compile a Component Contract YAML to Component IR",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "output",\n "aliases": [\n "o"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Output path for the compiled IR (JSON)"\n }\n ],\n "exits": {\n "0": {\n "description": "Compilation succeeded",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "description": "Component IR"\n }\n }\n },\n "1": {\n "description": "Compilation failed (validation errors)",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "generate": {\n "summary": "Generate adapters from a Component Contract",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "output-dir",\n "aliases": [\n "o"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Output directory for generated files"\n },\n {\n "name": "targets",\n "aliases": [\n "t"\n ],\n "schema": {\n "type": "array",\n "items": {\n "type": "string",\n "enum": [\n "library",\n "cli",\n "runtime",\n "claude",\n "openai"\n ]\n }\n },\n "description": "Generation targets (defaults to projections in contract)"\n },\n {\n "name": "dry-run",\n "schema": {\n "type": "boolean"\n },\n "description": "Show what would be generated without writing files"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Generation succeeded",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Generation failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "validate": {\n "summary": "Validate a Component Contract YAML",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "strict",\n "schema": {\n "type": "boolean"\n },\n "description": "Enable strict validation (check implementation refs)"\n }\n ],\n "exits": {\n "0": {\n "description": "Validation passed",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Validation failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "introspect": {\n "summary": "Dump Component IR information",\n "arguments": [\n {\n "name": "file",\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Path to the Component Contract YAML file"\n }\n ],\n "options": [\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "json",\n "yaml"\n ]\n },\n "description": "Output format (default yaml)"\n }\n ],\n "exits": {\n "0": {\n "description": "Success",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "description": "Component IR dump"\n }\n }\n }\n }\n },\n "init": {\n "summary": "Initialize a project \u2014 copy CLI binary, scaffold project.yaml, materialize git hooks",\n "arguments": [\n {\n "name": "projectRoot",\n "required": false,\n "schema": {\n "type": "string"\n },\n "description": "Project root directory (default: current directory)"\n }\n ],\n "options": [\n {\n "name": "project-yaml",\n "schema": {\n "type": "string"\n },\n "description": "Path to project.yaml relative to projectRoot (default project.yaml)"\n },\n {\n "name": "binary-path",\n "schema": {\n "type": "string"\n },\n "description": "Path to the CLI binary to copy (default: process.argv[1]); for programmatic/test use"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Initialization succeeded",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Initialization failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "update": {\n "summary": "Re-read project.yaml and re-materialize git hooks",\n "arguments": [\n {\n "name": "projectRoot",\n "required": false,\n "schema": {\n "type": "string"\n },\n "description": "Project root directory (default: current directory)"\n }\n ],\n "options": [\n {\n "name": "project-yaml",\n "schema": {\n "type": "string"\n },\n "description": "Path to project.yaml relative to projectRoot (default project.yaml)"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Update succeeded",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Update failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "uninstall": {\n "summary": "Remove all managed hook blocks recorded in governance-manifest.lock",\n "arguments": [\n {\n "name": "projectRoot",\n "required": false,\n "schema": {\n "type": "string"\n },\n "description": "Project root directory (default: current directory)"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Uninstall succeeded",\n "stdout": {\n "format": "text"\n }\n }\n }\n },\n "install": {\n "summary": "Install user-global observability hooks (Cursor + Claude + Git) in one command \u2014 idempotent, non-destructive",\n "options": [\n {\n "name": "global",\n "schema": {\n "type": "boolean"\n },\n "description": "Install at user scope (~/.cursor, ~/.claude, git --global). Currently the only supported scope."\n },\n {\n "name": "dry-run",\n "schema": {\n "type": "boolean"\n },\n "description": "Preview the changes without writing any files or git config"\n },\n {\n "name": "skip-cursor",\n "schema": {\n "type": "boolean"\n },\n "description": "Do not configure the Cursor user hooks"\n },\n {\n "name": "skip-claude",\n "schema": {\n "type": "boolean"\n },\n "description": "Do not configure the Claude Code user hooks"\n },\n {\n "name": "skip-git",\n "schema": {\n "type": "boolean"\n },\n "description": "Do not configure the global git hooks / core.hooksPath"\n }\n ],\n "effects": {\n "filesystem": "read_write"\n },\n "exits": {\n "0": {\n "description": "Install succeeded (or previewed with --dry-run)",\n "stdout": {\n "format": "text"\n }\n },\n "1": {\n "description": "Install failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n }\n }\n }\n }\n}';
3680
3971
 
3681
3972
  // src/generated/program.ts
3682
3973
  function createProgram(handlers2, version) {
@@ -3722,17 +4013,41 @@ function createProgram(handlers2, version) {
3722
4013
  }
3723
4014
  await handlers2.introspect(file, opts, globalOpts);
3724
4015
  });
3725
- program3.command("init").description("Initialize a project: copy the CLI binary, scaffold project.yaml, and materialize git hooks").argument("[projectRoot]", "Project root directory (default: current directory)").option("--project-yaml <value>", "Path to project.yaml relative to projectRoot (default: project.yaml)").action(async (projectRoot, opts, cmd) => {
4016
+ program3.command("init").description("Initialize a project \u2014 copy CLI binary, scaffold project.yaml, materialize git hooks").argument("[projectRoot]", "Project root directory (default: current directory)").option("--project-yaml <value>", "Path to project.yaml relative to projectRoot (default project.yaml)").option("--binary-path <value>", "Path to the CLI binary to copy (default: process.argv[1]); for programmatic/test use").action(async (projectRoot, opts, cmd) => {
3726
4017
  const globalOpts = cmd.optsWithGlobals();
4018
+ if (globalOpts.introspect) {
4019
+ const policy = deriveCommandPolicy("init", opts);
4020
+ console.log(JSON.stringify(policy, null, 2));
4021
+ return;
4022
+ }
3727
4023
  await handlers2.init(projectRoot, opts, globalOpts);
3728
4024
  });
3729
- program3.command("update").description("Re-read project.yaml and re-materialize git hooks").argument("[projectRoot]", "Project root directory (default: current directory)").option("--project-yaml <value>", "Path to project.yaml relative to projectRoot (default: project.yaml)").action(async (projectRoot, opts, cmd) => {
4025
+ program3.command("update").description("Re-read project.yaml and re-materialize git hooks").argument("[projectRoot]", "Project root directory (default: current directory)").option("--project-yaml <value>", "Path to project.yaml relative to projectRoot (default project.yaml)").action(async (projectRoot, opts, cmd) => {
3730
4026
  const globalOpts = cmd.optsWithGlobals();
4027
+ if (globalOpts.introspect) {
4028
+ const policy = deriveCommandPolicy("update", opts);
4029
+ console.log(JSON.stringify(policy, null, 2));
4030
+ return;
4031
+ }
3731
4032
  await handlers2.update(projectRoot, opts, globalOpts);
3732
4033
  });
3733
4034
  program3.command("uninstall").description("Remove all managed hook blocks recorded in governance-manifest.lock").argument("[projectRoot]", "Project root directory (default: current directory)").action(async (projectRoot, opts, cmd) => {
3734
4035
  const globalOpts = cmd.optsWithGlobals();
3735
- await handlers2.uninstall(projectRoot, opts, globalOpts);
4036
+ if (globalOpts.introspect) {
4037
+ const policy = deriveCommandPolicy("uninstall", opts);
4038
+ console.log(JSON.stringify(policy, null, 2));
4039
+ return;
4040
+ }
4041
+ await handlers2.uninstall(projectRoot, {}, globalOpts);
4042
+ });
4043
+ program3.command("install").description("Install user-global observability hooks (Cursor + Claude + Git) in one command \u2014 idempotent, non-destructive").option("--global", "Install at user scope (~/.cursor, ~/.claude, git --global). Currently the only supported scope.").option("--dry-run", "Preview the changes without writing any files or git config").option("--skip-cursor", "Do not configure the Cursor user hooks").option("--skip-claude", "Do not configure the Claude Code user hooks").option("--skip-git", "Do not configure the global git hooks / core.hooksPath").action(async (opts, cmd) => {
4044
+ const globalOpts = cmd.optsWithGlobals();
4045
+ if (globalOpts.introspect) {
4046
+ const policy = deriveCommandPolicy("install", opts);
4047
+ console.log(JSON.stringify(policy, null, 2));
4048
+ return;
4049
+ }
4050
+ await handlers2.install(opts, globalOpts);
3736
4051
  });
3737
4052
  program3.command("extract").description("Extract contract specification for this CLI tool.").argument("[commands...]", "Command IDs to extract. Use dot notation.").option("-a, --all", "Extract all commands.", false).option("--include-meta", "Include extraction metadata.", true).option("-F, --format <format>", "Output format (yaml or json).", "yaml").action(async (commands, opts, cmd) => {
3738
4053
  if (commands.length === 0 && !opts.all) {
@@ -3751,7 +4066,7 @@ function createProgram(handlers2, version) {
3751
4066
  type: "cli-contracts/extract",
3752
4067
  extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
3753
4068
  specVersion: doc.cli_contracts ?? "0.1.0",
3754
- commands: ["aaac.compile", "aaac.generate", "aaac.validate", "aaac.introspect", "aaac.init", "aaac.update", "aaac.uninstall"]
4069
+ commands: ["aaac.compile", "aaac.generate", "aaac.validate", "aaac.introspect", "aaac.init", "aaac.update", "aaac.uninstall", "aaac.install"]
3755
4070
  };
3756
4071
  }
3757
4072
  Object.assign(out, doc);
@@ -3768,7 +4083,7 @@ function createProgram(handlers2, version) {
3768
4083
  yamlLines.push("extractedAt: " + (/* @__PURE__ */ new Date()).toISOString());
3769
4084
  yamlLines.push("spec_version: " + (doc.cli_contracts ?? "0.1.0"));
3770
4085
  yamlLines.push("commands:");
3771
- for (const id of ["aaac.compile", "aaac.generate", "aaac.validate", "aaac.introspect", "aaac.init", "aaac.update", "aaac.uninstall"]) {
4086
+ for (const id of ["aaac.compile", "aaac.generate", "aaac.validate", "aaac.introspect", "aaac.init", "aaac.update", "aaac.uninstall", "aaac.install"]) {
3772
4087
  yamlLines.push(" - " + id);
3773
4088
  }
3774
4089
  }
@@ -3808,9 +4123,189 @@ function createProgram(handlers2, version) {
3808
4123
  }
3809
4124
 
3810
4125
  // src/cli/handlers.ts
4126
+ import { existsSync as existsSync2 } from "fs";
4127
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
4128
+ import { homedir } from "os";
4129
+ import { dirname as dirname2, join as join2, resolve } from "path";
4130
+
4131
+ // src/cli/global-install.ts
4132
+ import { execFileSync } from "child_process";
3811
4133
  import { existsSync } from "fs";
3812
- import { mkdir, writeFile } from "fs/promises";
3813
- import { dirname, join, resolve } from "path";
4134
+ import { chmod, mkdir, readFile, writeFile } from "fs/promises";
4135
+ import { dirname, join } from "path";
4136
+ var GLOBAL_RECORDER_CMD = "aaac-observ-global-record";
4137
+ var GLOBAL_CURSOR_HOOK_EVENTS = [
4138
+ "sessionStart",
4139
+ "sessionEnd",
4140
+ "subagentStart",
4141
+ "subagentStop",
4142
+ "preToolUse",
4143
+ "postToolUse",
4144
+ "afterFileEdit",
4145
+ "afterShellExecution"
4146
+ ];
4147
+ function buildGlobalCursorEntries(recorder = GLOBAL_RECORDER_CMD) {
4148
+ return GLOBAL_CURSOR_HOOK_EVENTS.map((event) => ({
4149
+ event,
4150
+ command: `${recorder} ${event} --source cursor-hook`
4151
+ }));
4152
+ }
4153
+ function cursorHooksPath(home) {
4154
+ return join(home, ".cursor", "hooks.json");
4155
+ }
4156
+ var GLOBAL_CLAUDE_HOOK_EVENTS = [
4157
+ "SessionStart",
4158
+ "PreToolUse",
4159
+ "PostToolUse",
4160
+ "Stop",
4161
+ "SubagentStop"
4162
+ ];
4163
+ function buildClaudeHookCommand(event, recorder = GLOBAL_RECORDER_CMD) {
4164
+ return `${recorder} ${event} --source claude-hook`;
4165
+ }
4166
+ function claudeSettingsPath(home) {
4167
+ return join(home, ".claude", "settings.json");
4168
+ }
4169
+ function parseClaudeSettings(raw) {
4170
+ if (!raw || !raw.trim()) return {};
4171
+ try {
4172
+ const parsed = JSON.parse(raw);
4173
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4174
+ } catch {
4175
+ return {};
4176
+ }
4177
+ }
4178
+ function serializeClaudeSettings(settings) {
4179
+ return JSON.stringify(settings, null, 2) + "\n";
4180
+ }
4181
+ function mergeClaudeSettings(existing, recorder = GLOBAL_RECORDER_CMD) {
4182
+ const settings = { ...existing };
4183
+ const hooks = {};
4184
+ for (const [event, groups] of Object.entries(existing.hooks ?? {})) {
4185
+ hooks[event] = groups.map((g) => ({ ...g, hooks: g.hooks.map((h) => ({ ...h })) }));
4186
+ }
4187
+ for (const event of GLOBAL_CLAUDE_HOOK_EVENTS) {
4188
+ const command = buildClaudeHookCommand(event, recorder);
4189
+ const groups = hooks[event] ?? [];
4190
+ const already = groups.some((g) => g.hooks.some((h) => h.command === command));
4191
+ if (!already) {
4192
+ groups.push({ hooks: [{ type: "command", command }] });
4193
+ }
4194
+ hooks[event] = groups;
4195
+ }
4196
+ settings.hooks = hooks;
4197
+ return settings;
4198
+ }
4199
+ var GLOBAL_GIT_HOOK_NAMES = [
4200
+ "pre-commit",
4201
+ "commit-msg",
4202
+ "post-commit",
4203
+ "pre-push"
4204
+ ];
4205
+ function globalGitHooksDir(home) {
4206
+ return join(home, ".aaac", "git-hooks");
4207
+ }
4208
+ function buildGlobalGitHookScript(recorder = GLOBAL_RECORDER_CMD) {
4209
+ return [
4210
+ "#!/bin/sh",
4211
+ "# >>> aaac-observ:global-git-hook (do not edit) >>>",
4212
+ "# Records promotion events from ANY repo, then chains to the repo-local hook.",
4213
+ 'hook_name="$(basename "$0")"',
4214
+ `${recorder} "$hook_name" --source git-hook >/dev/null 2>&1 || true`,
4215
+ 'repo_root="$(git rev-parse --show-toplevel 2>/dev/null)"',
4216
+ 'if [ -n "$repo_root" ] && [ -x "$repo_root/.git/hooks/$hook_name" ]; then',
4217
+ ' exec "$repo_root/.git/hooks/$hook_name" "$@"',
4218
+ "fi",
4219
+ "exit 0",
4220
+ "# <<< aaac-observ:global-git-hook <<<",
4221
+ ""
4222
+ ].join("\n");
4223
+ }
4224
+ var defaultGitConfigIO = {
4225
+ get(key) {
4226
+ try {
4227
+ const out = execFileSync("git", ["config", "--global", "--get", key], {
4228
+ encoding: "utf8",
4229
+ stdio: ["ignore", "pipe", "ignore"]
4230
+ });
4231
+ const trimmed = out.trim();
4232
+ return trimmed.length > 0 ? trimmed : void 0;
4233
+ } catch {
4234
+ return void 0;
4235
+ }
4236
+ },
4237
+ set(key, value) {
4238
+ execFileSync("git", ["config", "--global", key, value], { stdio: "ignore" });
4239
+ }
4240
+ };
4241
+ async function readIfExists(path2) {
4242
+ if (!existsSync(path2)) return void 0;
4243
+ return readFile(path2, "utf8");
4244
+ }
4245
+ async function materializeFile(target, path2, existing, next, dryRun, mode) {
4246
+ let action;
4247
+ if (existing === void 0) action = "create";
4248
+ else if (existing === next) action = "unchanged";
4249
+ else action = "update";
4250
+ if (!dryRun && action !== "unchanged") {
4251
+ await mkdir(dirname(path2), { recursive: true });
4252
+ await writeFile(path2, next, "utf8");
4253
+ if (mode !== void 0) await chmod(path2, mode);
4254
+ }
4255
+ return { target, path: path2, action };
4256
+ }
4257
+ async function installGlobalHooks(opts) {
4258
+ const {
4259
+ home,
4260
+ recorder = GLOBAL_RECORDER_CMD,
4261
+ dryRun = false,
4262
+ cursor = true,
4263
+ claude = true,
4264
+ git = true,
4265
+ gitConfig = defaultGitConfigIO
4266
+ } = opts;
4267
+ const actions = [];
4268
+ if (cursor) {
4269
+ const path2 = cursorHooksPath(home);
4270
+ const existingRaw = await readIfExists(path2);
4271
+ const merged = mergeCursorHooks(parseCursorHooksFile(existingRaw), buildGlobalCursorEntries(recorder));
4272
+ const next = serializeCursorHooksFile(merged);
4273
+ actions.push(await materializeFile("cursor", path2, existingRaw, next, dryRun));
4274
+ }
4275
+ if (claude) {
4276
+ const path2 = claudeSettingsPath(home);
4277
+ const existingRaw = await readIfExists(path2);
4278
+ const merged = mergeClaudeSettings(parseClaudeSettings(existingRaw), recorder);
4279
+ const next = serializeClaudeSettings(merged);
4280
+ actions.push(await materializeFile("claude", path2, existingRaw, next, dryRun));
4281
+ }
4282
+ if (git) {
4283
+ const hooksDir = globalGitHooksDir(home);
4284
+ const script = buildGlobalGitHookScript(recorder);
4285
+ for (const name of GLOBAL_GIT_HOOK_NAMES) {
4286
+ const path2 = join(hooksDir, name);
4287
+ const existingRaw = await readIfExists(path2);
4288
+ actions.push(await materializeFile("git", path2, existingRaw, script, dryRun, 493));
4289
+ }
4290
+ const current = gitConfig.get("core.hooksPath");
4291
+ if (current === hooksDir) {
4292
+ actions.push({ target: "git", path: "core.hooksPath", action: "unchanged" });
4293
+ } else if (current && current.length > 0) {
4294
+ actions.push({
4295
+ target: "git",
4296
+ path: "core.hooksPath",
4297
+ action: "skip",
4298
+ note: `core.hooksPath already set to ${current}; leave it and source ${hooksDir} from your hooks, or unset to let aaac manage it`
4299
+ });
4300
+ } else {
4301
+ if (!dryRun) gitConfig.set("core.hooksPath", hooksDir);
4302
+ actions.push({ target: "git", path: "core.hooksPath", action: "create" });
4303
+ }
4304
+ }
4305
+ return actions;
4306
+ }
4307
+
4308
+ // src/cli/handlers.ts
3814
4309
  import { stringify as stringifyYaml } from "yaml";
3815
4310
  function formatError(err, file) {
3816
4311
  if (err instanceof ComponentParseError) {
@@ -3842,7 +4337,7 @@ function resolveComponentPath(file) {
3842
4337
  return resolve(file);
3843
4338
  }
3844
4339
  function defaultOutputDir(componentPath) {
3845
- return join(dirname(componentPath), "generated");
4340
+ return join2(dirname2(componentPath), "generated");
3846
4341
  }
3847
4342
  function buildIntrospectionDump(ir) {
3848
4343
  const operations = {};
@@ -3877,7 +4372,7 @@ var handleCompile = async (file, options, parentOpts) => {
3877
4372
  const ir = await compileComponentFile(componentPath);
3878
4373
  const json = JSON.stringify(ir, null, 2);
3879
4374
  if (options.output) {
3880
- await writeFile(resolve(options.output), `${json}
4375
+ await writeFile2(resolve(options.output), `${json}
3881
4376
  `, "utf-8");
3882
4377
  if (!parentOpts.quiet) {
3883
4378
  process.stdout.write(`Wrote IR to ${resolve(options.output)}
@@ -3895,7 +4390,7 @@ async function resolveEmbeddedDslData(ir, componentPath) {
3895
4390
  if (!ir.embeddedDslDir || !ir.implementation) {
3896
4391
  return void 0;
3897
4392
  }
3898
- const implPath = resolve(dirname(componentPath), ir.implementation);
4393
+ const implPath = resolve(dirname2(componentPath), ir.implementation);
3899
4394
  const { resolve: resolveDsl } = await import("agent-contracts");
3900
4395
  const dslResult = await resolveDsl(implPath);
3901
4396
  return dslResult.data;
@@ -3939,10 +4434,10 @@ var handleGenerate = async (file, options, parentOpts) => {
3939
4434
  }
3940
4435
  }
3941
4436
  for (const generated of embedded) {
3942
- const embeddedDir = resolve(dirname(componentPath), generated.targetDir);
4437
+ const embeddedDir = resolve(dirname2(componentPath), generated.targetDir);
3943
4438
  const action = generated.overwrite ? "write" : "create";
3944
4439
  process.stdout.write(
3945
- ` ${action}: ${join(embeddedDir, generated.path)} (embedded DSL)
4440
+ ` ${action}: ${join2(embeddedDir, generated.path)} (embedded DSL)
3946
4441
  `
3947
4442
  );
3948
4443
  }
@@ -3951,10 +4446,10 @@ var handleGenerate = async (file, options, parentOpts) => {
3951
4446
  }
3952
4447
  const result = await writeGeneratedFiles(main, outputDir);
3953
4448
  for (const generated of embedded) {
3954
- const embeddedDir = resolve(dirname(componentPath), generated.targetDir);
3955
- await mkdir(embeddedDir, { recursive: true });
3956
- await writeFile(join(embeddedDir, generated.path), generated.content, "utf-8");
3957
- result.written.push(join(generated.targetDir, generated.path));
4449
+ const embeddedDir = resolve(dirname2(componentPath), generated.targetDir);
4450
+ await mkdir2(embeddedDir, { recursive: true });
4451
+ await writeFile2(join2(embeddedDir, generated.path), generated.content, "utf-8");
4452
+ result.written.push(join2(generated.targetDir, generated.path));
3958
4453
  }
3959
4454
  if (!parentOpts.quiet) {
3960
4455
  if (result.written.length > 0) {
@@ -3984,7 +4479,7 @@ var handleGenerate = async (file, options, parentOpts) => {
3984
4479
  };
3985
4480
  var handleValidate = async (file, options, parentOpts) => {
3986
4481
  const componentPath = resolveComponentPath(file);
3987
- const basePath = dirname(componentPath);
4482
+ const basePath = dirname2(componentPath);
3988
4483
  try {
3989
4484
  const dsl = await parseComponentFile(componentPath);
3990
4485
  const ir = compileComponent(dsl, { basePath });
@@ -4020,22 +4515,22 @@ var DEFAULT_PROJECT_YAML = "project.yaml";
4020
4515
  var handleInit = async (projectRoot, options, parentOpts) => {
4021
4516
  const root = resolve(projectRoot ?? ".");
4022
4517
  const quiet = Boolean(parentOpts.quiet);
4023
- const projectYamlPath = join(root, options.projectYaml ?? DEFAULT_PROJECT_YAML);
4518
+ const projectYamlPath = join2(root, options.projectYaml ?? DEFAULT_PROJECT_YAML);
4024
4519
  const srcBinary = resolve(options.binaryPath ?? process.argv[1]);
4025
4520
  const binaryResult = await copyCLIBinary(srcBinary, root);
4026
4521
  if (!quiet && binaryResult.action !== "unchanged") {
4027
4522
  process.stdout.write(` ${binaryResult.action}: ${binaryResult.path}
4028
4523
  `);
4029
4524
  }
4030
- if (!existsSync(projectYamlPath)) {
4031
- await writeFile(projectYamlPath, SCAFFOLD_PROJECT_YAML, "utf8");
4525
+ if (!existsSync2(projectYamlPath)) {
4526
+ await writeFile2(projectYamlPath, SCAFFOLD_PROJECT_YAML, "utf8");
4032
4527
  if (!quiet) {
4033
4528
  process.stdout.write(` created: ${DEFAULT_PROJECT_YAML}
4034
4529
  `);
4035
4530
  }
4036
4531
  }
4037
- const gitDir = join(root, ".git");
4038
- if (!existsSync(gitDir)) {
4532
+ const gitDir = join2(root, ".git");
4533
+ if (!existsSync2(gitDir)) {
4039
4534
  process.stderr.write(
4040
4535
  `Warning: .git/ not found in ${root}; skipping hook materialization
4041
4536
  `
@@ -4093,14 +4588,14 @@ var handleInit = async (projectRoot, options, parentOpts) => {
4093
4588
  var handleUpdate = async (projectRoot, options, parentOpts) => {
4094
4589
  const root = resolve(projectRoot ?? ".");
4095
4590
  const quiet = Boolean(parentOpts.quiet);
4096
- const projectYamlPath = join(root, options.projectYaml ?? DEFAULT_PROJECT_YAML);
4097
- if (!existsSync(projectYamlPath)) {
4591
+ const projectYamlPath = join2(root, options.projectYaml ?? DEFAULT_PROJECT_YAML);
4592
+ if (!existsSync2(projectYamlPath)) {
4098
4593
  fail(
4099
4594
  `Error: ${DEFAULT_PROJECT_YAML} not found at ${root}. Run 'aaac init' first.`
4100
4595
  );
4101
4596
  }
4102
- const gitDir = join(root, ".git");
4103
- if (!existsSync(gitDir)) {
4597
+ const gitDir = join2(root, ".git");
4598
+ if (!existsSync2(gitDir)) {
4104
4599
  process.stderr.write(
4105
4600
  `Warning: .git/ not found in ${root}; skipping hook materialization
4106
4601
  `
@@ -4146,7 +4641,7 @@ var handleUninstall = async (projectRoot, _options, parentOpts) => {
4146
4641
  if (!lock || lock.entries.length === 0) {
4147
4642
  if (!quiet) {
4148
4643
  process.stdout.write(
4149
- `Nothing to uninstall (no governance lock found at ${join(root, GOVERNANCE_LOCK_PATH)})
4644
+ `Nothing to uninstall (no governance lock found at ${join2(root, GOVERNANCE_LOCK_PATH)})
4150
4645
  `
4151
4646
  );
4152
4647
  }
@@ -4158,6 +4653,38 @@ var handleUninstall = async (projectRoot, _options, parentOpts) => {
4158
4653
  `);
4159
4654
  }
4160
4655
  };
4656
+ var handleInstall = async (options, parentOpts) => {
4657
+ const quiet = Boolean(parentOpts.quiet);
4658
+ const dryRun = Boolean(options.dryRun);
4659
+ const home = homedir();
4660
+ if (!home) {
4661
+ fail("Error: could not resolve home directory ($HOME)");
4662
+ }
4663
+ let actions;
4664
+ try {
4665
+ actions = await installGlobalHooks({
4666
+ home,
4667
+ dryRun,
4668
+ cursor: !options.skipCursor,
4669
+ claude: !options.skipClaude,
4670
+ git: !options.skipGit
4671
+ });
4672
+ } catch (err) {
4673
+ fail(`Error: ${err instanceof Error ? err.message : String(err)}`);
4674
+ }
4675
+ if (!quiet) {
4676
+ const prefix = dryRun ? "[dry-run] " : "";
4677
+ for (const a of actions) {
4678
+ const note = a.note ? ` \u2014 ${a.note}` : "";
4679
+ process.stdout.write(` ${prefix}${a.action}: [${a.target}] ${a.path}${note}
4680
+ `);
4681
+ }
4682
+ process.stdout.write(
4683
+ `${dryRun ? "Previewed" : "Installed"} user-global observability hooks (Cursor + Claude + Git)
4684
+ `
4685
+ );
4686
+ }
4687
+ };
4161
4688
  var handlers = {
4162
4689
  compile: handleCompile,
4163
4690
  generate: handleGenerate,
@@ -4165,11 +4692,12 @@ var handlers = {
4165
4692
  introspect: handleIntrospect,
4166
4693
  init: handleInit,
4167
4694
  update: handleUpdate,
4168
- uninstall: handleUninstall
4695
+ uninstall: handleUninstall,
4696
+ install: handleInstall
4169
4697
  };
4170
4698
 
4171
4699
  // src/cli/index.ts
4172
- var __dirname = dirname2(fileURLToPath(import.meta.url));
4700
+ var __dirname = dirname3(fileURLToPath(import.meta.url));
4173
4701
  var pkg = JSON.parse(
4174
4702
  readFileSync(resolve2(__dirname, "../../package.json"), "utf8")
4175
4703
  );