@connexum/ai-governance 1.0.0-beta.15 → 1.0.0-beta.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.
@@ -116,10 +116,46 @@ export interface GovernanceJsonConfig {
116
116
  */
117
117
  export declare function readLicenseServerUrl(projectDir: string): string;
118
118
  export declare function generateConfig(packs: string[], license: LicenseInfo, licenseServerUrl?: string, runtime?: GovernanceJsonConfig['runtime']): GovernanceJsonConfig;
119
+ /**
120
+ * P0 install-safety guard. Governance is a PROJECT-LOCAL install: hooks are
121
+ * written to `<projectDir>/.claude/settings.json`. If `init` runs from the
122
+ * home directory, that path resolves to the GLOBAL Claude Code config
123
+ * (`~/.claude/settings.json`) and the hooks fire in EVERY project on the
124
+ * machine — the root cause of the 2026-06-03 global-hook-hang incident, where
125
+ * audit-logger.sh stacked up and froze Claude Code across all terminals.
126
+ *
127
+ * Throws an actionable Error if `projectDir` is the home directory, or if
128
+ * `<projectDir>/.claude` is the global Claude config dir (honoring a relocated
129
+ * CLAUDE_CONFIG_DIR). The CLI handler converts the throw into a clean
130
+ * stderr message + exit 1; calling installHooks directly still fails closed.
131
+ */
132
+ export declare function assertSafeInstallDir(projectDir: string): void;
119
133
  export declare function installHooks(projectDir: string, config: GovernanceJsonConfig): {
120
134
  installed: number;
121
135
  errors: string[];
122
136
  };
137
+ /**
138
+ * Clean removal escape hatch for `ai-governance uninstall`.
139
+ *
140
+ * The 2026-06-03 incident had NO uninstall path, so recovery meant manually
141
+ * killing processes and `rm -rf`. This:
142
+ * 1. strips ONLY our hooks from `<projectDir>/.claude/settings.json`
143
+ * (identified by a command referencing `<projectDir>/.governance/hooks/`),
144
+ * preserving any hooks the user added themselves;
145
+ * 2. removes the generated hook scripts and `.governance.json`;
146
+ * 3. PRESERVES the tamper-evident audit chain (`.governance/audit/`) by
147
+ * default — it is compliance evidence — deleting it only when `purge` is
148
+ * set.
149
+ *
150
+ * Idempotent and non-throwing on a project with nothing installed.
151
+ */
152
+ export declare function uninstallGovernance(projectDir: string, opts?: {
153
+ purge?: boolean;
154
+ }): {
155
+ hooksRemoved: number;
156
+ removedPaths: string[];
157
+ auditPreserved: string | null;
158
+ };
123
159
  /** Structured aiIntegrations block in the nonInteractiveInit JSON output. */
124
160
  export interface AiIntegrationsResult {
125
161
  claudeCode: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -55,7 +55,9 @@ exports.detectIde = detectIde;
55
55
  exports.validateLegacyLicense = validateLegacyLicense;
56
56
  exports.readLicenseServerUrl = readLicenseServerUrl;
57
57
  exports.generateConfig = generateConfig;
58
+ exports.assertSafeInstallDir = assertSafeInstallDir;
58
59
  exports.installHooks = installHooks;
60
+ exports.uninstallGovernance = uninstallGovernance;
59
61
  exports.writeClaudeSlashCommands = writeClaudeSlashCommands;
60
62
  exports.writeGovernanceScannerAgent = writeGovernanceScannerAgent;
61
63
  exports.writeCursorGovernanceRules = writeCursorGovernanceRules;
@@ -68,6 +70,7 @@ exports.verifyChain = verifyChain;
68
70
  exports.generateAutowrapArtifacts = generateAutowrapArtifacts;
69
71
  const fs = __importStar(require("fs"));
70
72
  const path = __importStar(require("path"));
73
+ const os = __importStar(require("os"));
71
74
  const crypto = __importStar(require("crypto"));
72
75
  const readline = __importStar(require("readline"));
73
76
  const https = __importStar(require("https"));
@@ -311,6 +314,31 @@ const IS_WINDOWS = process.platform === 'win32';
311
314
  const HOOK_EXT = IS_WINDOWS ? '.ps1' : '.sh';
312
315
  const HOOK_RUNNER = IS_WINDOWS ? 'pwsh' : 'bash';
313
316
  const HOOK_SUBDIR = IS_WINDOWS ? 'powershell' : '';
317
+ // P0 no-hang bound. Every generated hook carries this timeout (seconds) so a
318
+ // hook can never block the agent's tool call — the 2026-06-03 incident had a
319
+ // hung audit-logger.sh stack up for >1h because the entries had no timeout.
320
+ // Enforced two ways: the host honors the `timeout` field, and where a coreutils
321
+ // timeout binary exists the command self-limits via `timeout -k`. Honors
322
+ // Invariant 2 (advisory, never in the synchronous critical path).
323
+ const HOOK_TIMEOUT_SECONDS = 10;
324
+ // Resolve a coreutils timeout binary present on this machine: GNU `timeout`
325
+ // (Linux) or `gtimeout` (macOS + coreutils). Returns null on Windows or when
326
+ // neither exists (a default macOS) — callers must then NOT wrap the command in
327
+ // it, or the hook would fail with "command not found" and silently not run.
328
+ function resolveTimeoutBin() {
329
+ if (IS_WINDOWS)
330
+ return null;
331
+ const { spawnSync } = require('node:child_process');
332
+ for (const bin of ['timeout', 'gtimeout']) {
333
+ try {
334
+ if (spawnSync('sh', ['-c', `command -v ${bin}`], { stdio: 'ignore' }).status === 0) {
335
+ return bin;
336
+ }
337
+ }
338
+ catch { /* fall through */ }
339
+ }
340
+ return null;
341
+ }
314
342
  function detectIde(projectDir) {
315
343
  const markers = [
316
344
  { dir: '.claude', ide: 'claude-code' },
@@ -508,7 +536,52 @@ function generateConfig(packs, license, licenseServerUrl, runtime) {
508
536
  };
509
537
  }
510
538
  // --- Hook installation ---
539
+ /**
540
+ * P0 install-safety guard. Governance is a PROJECT-LOCAL install: hooks are
541
+ * written to `<projectDir>/.claude/settings.json`. If `init` runs from the
542
+ * home directory, that path resolves to the GLOBAL Claude Code config
543
+ * (`~/.claude/settings.json`) and the hooks fire in EVERY project on the
544
+ * machine — the root cause of the 2026-06-03 global-hook-hang incident, where
545
+ * audit-logger.sh stacked up and froze Claude Code across all terminals.
546
+ *
547
+ * Throws an actionable Error if `projectDir` is the home directory, or if
548
+ * `<projectDir>/.claude` is the global Claude config dir (honoring a relocated
549
+ * CLAUDE_CONFIG_DIR). The CLI handler converts the throw into a clean
550
+ * stderr message + exit 1; calling installHooks directly still fails closed.
551
+ */
552
+ function assertSafeInstallDir(projectDir) {
553
+ // Resolve symlinks so a link pointing at $HOME can't slip past a string-only
554
+ // comparison (review S1). realpathSync throws ENOENT for a path that doesn't
555
+ // exist yet — fall back to lexical resolution then (a non-existent dir can't
556
+ // be a symlink to home).
557
+ const real = (p) => {
558
+ try {
559
+ return fs.realpathSync(p);
560
+ }
561
+ catch {
562
+ return path.resolve(p);
563
+ }
564
+ };
565
+ const resolved = real(projectDir);
566
+ const home = real(os.homedir());
567
+ if (resolved === home) {
568
+ throw new Error(`Refusing to install governance into your home directory (${home}).\n` +
569
+ `That would write hooks into your GLOBAL Claude Code config ` +
570
+ `(~/.claude/settings.json) and run them in every project on this machine.\n` +
571
+ `cd into a specific project (one with package.json or .git) and run ` +
572
+ `'ai-governance init' there.`);
573
+ }
574
+ const globalClaudeDir = real(process.env['CLAUDE_CONFIG_DIR'] || path.join(home, '.claude'));
575
+ if (path.join(resolved, '.claude') === globalClaudeDir) {
576
+ throw new Error(`Refusing to install: ${path.join(projectDir, '.claude')} is your global ` +
577
+ `Claude config directory. Governance must be installed into a project, not ` +
578
+ `globally, or its hooks run in every project on this machine.\n` +
579
+ `cd into a specific project and run 'ai-governance init' there.`);
580
+ }
581
+ }
511
582
  function installHooks(projectDir, config) {
583
+ // Fail closed before any scaffolding: never poison the global Claude config.
584
+ assertSafeInstallDir(projectDir);
512
585
  const errors = [];
513
586
  let installed = 0;
514
587
  // Find the package's hooks directory (bash or powershell based on OS)
@@ -585,13 +658,27 @@ function installClaudeCodeSettings(projectDir, config, hooksDestDir) {
585
658
  fs.mkdirSync(claudeSettingsDir, { recursive: true });
586
659
  }
587
660
  const settingsFile = path.join(claudeSettingsDir, 'settings.json');
588
- const buildBlock = (hookFile) => ({
589
- matcher: '*',
590
- hooks: [{
591
- type: 'command',
592
- command: `${HOOK_RUNNER} "${path.join(hooksDestDir, hookFile)}"`,
593
- }],
594
- });
661
+ // Resolve a coreutils timeout binary on THIS machine (init runs on the
662
+ // customer's box, same OS as runtime). GNU `timeout` ships on Linux;
663
+ // `gtimeout` is the macOS+coreutils name; a default macOS has neither and
664
+ // Windows has none. When present we wrap the hook command as a hard
665
+ // self-limit; when absent we emit the plain command and rely solely on the
666
+ // host-enforced `timeout` field below — never a hard dependency on a binary
667
+ // that may be missing (which would make the hook fail to run).
668
+ const timeoutBin = resolveTimeoutBin();
669
+ const buildBlock = (hookFile) => {
670
+ const hookPath = path.join(hooksDestDir, hookFile);
671
+ // `-k 2` escalates to SIGKILL 2s after SIGTERM. Both layers honor
672
+ // Invariant 2: the host-enforced `timeout` field bounds the hook on every
673
+ // platform; the wrapper adds a process-level self-limit where available.
674
+ const command = timeoutBin
675
+ ? `${timeoutBin} -k 2 ${HOOK_TIMEOUT_SECONDS} ${HOOK_RUNNER} "${hookPath}"`
676
+ : `${HOOK_RUNNER} "${hookPath}"`;
677
+ return {
678
+ matcher: '*',
679
+ hooks: [{ type: 'command', command, timeout: HOOK_TIMEOUT_SECONDS }],
680
+ };
681
+ };
595
682
  const preEntries = config.hooks.preToolUse.map(buildBlock);
596
683
  const postEntries = config.hooks.postToolUse.map(buildBlock);
597
684
  let settings = {};
@@ -604,13 +691,119 @@ function installClaudeCodeSettings(projectDir, config, hooksDestDir) {
604
691
  const existingHooks = (settings['hooks'] && typeof settings['hooks'] === 'object' && !Array.isArray(settings['hooks']))
605
692
  ? settings['hooks']
606
693
  : {};
694
+ // Merge, never clobber. A previous version replaced the whole
695
+ // PreToolUse/PostToolUse arrays, which wiped any hooks the user added and
696
+ // made re-running `init` destructive. Keep the user's hooks, drop only our
697
+ // own previous entries (identified by a command referencing this project's
698
+ // hooks dir), then append the fresh set — idempotent, no duplicates.
699
+ // Match on the hooks dir WITH a trailing separator so a sibling like
700
+ // `.governance/hooks-extra/` is not mistaken for ours (review S2). Generated
701
+ // commands always reference `<hooksDestDir>/<file>`, so the separator is
702
+ // present for our entries.
703
+ const ourHooksPrefix = hooksDestDir + path.sep;
704
+ const isOurs = (block) => {
705
+ const inner = block?.hooks;
706
+ return Array.isArray(inner) && inner.some((h) => typeof h?.command === 'string' &&
707
+ (h.command).includes(ourHooksPrefix));
708
+ };
709
+ const mergeEvent = (existing, fresh) => [
710
+ ...(Array.isArray(existing) ? existing.filter((b) => !isOurs(b)) : []),
711
+ ...fresh,
712
+ ];
607
713
  settings['hooks'] = {
608
714
  ...existingHooks,
609
- PreToolUse: preEntries,
610
- PostToolUse: postEntries,
715
+ PreToolUse: mergeEvent(existingHooks['PreToolUse'], preEntries),
716
+ PostToolUse: mergeEvent(existingHooks['PostToolUse'], postEntries),
611
717
  };
612
718
  fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
613
719
  }
720
+ /**
721
+ * Clean removal escape hatch for `ai-governance uninstall`.
722
+ *
723
+ * The 2026-06-03 incident had NO uninstall path, so recovery meant manually
724
+ * killing processes and `rm -rf`. This:
725
+ * 1. strips ONLY our hooks from `<projectDir>/.claude/settings.json`
726
+ * (identified by a command referencing `<projectDir>/.governance/hooks/`),
727
+ * preserving any hooks the user added themselves;
728
+ * 2. removes the generated hook scripts and `.governance.json`;
729
+ * 3. PRESERVES the tamper-evident audit chain (`.governance/audit/`) by
730
+ * default — it is compliance evidence — deleting it only when `purge` is
731
+ * set.
732
+ *
733
+ * Idempotent and non-throwing on a project with nothing installed.
734
+ */
735
+ function uninstallGovernance(projectDir, opts = {}) {
736
+ const removedPaths = [];
737
+ let hooksRemoved = 0;
738
+ const govDir = path.join(projectDir, '.governance');
739
+ const govHooksDir = path.join(govDir, 'hooks');
740
+ // 1. Strip our hooks from .claude/settings.json, keeping the user's own.
741
+ const settingsFile = path.join(projectDir, '.claude', 'settings.json');
742
+ if (fs.existsSync(settingsFile)) {
743
+ try {
744
+ const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8'));
745
+ const hooks = settings['hooks'];
746
+ if (hooks && typeof hooks === 'object' && !Array.isArray(hooks)) {
747
+ const hookMap = hooks;
748
+ for (const event of ['PreToolUse', 'PostToolUse']) {
749
+ const arr = hookMap[event];
750
+ if (!Array.isArray(arr))
751
+ continue;
752
+ // Trailing separator so a sibling like `.governance/hooks-extra/` is
753
+ // not removed as ours (review S2). Our commands reference
754
+ // `<govHooksDir>/<file>`, so the separator is present for our entries.
755
+ const ourHooksPrefix = govHooksDir + path.sep;
756
+ hookMap[event] = arr.filter((block) => {
757
+ const inner = block?.hooks;
758
+ const isOurs = Array.isArray(inner) && inner.some((h) => typeof h?.command === 'string' &&
759
+ (h.command).includes(ourHooksPrefix));
760
+ if (isOurs)
761
+ hooksRemoved++;
762
+ return !isOurs;
763
+ });
764
+ }
765
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
766
+ }
767
+ }
768
+ catch {
769
+ // Malformed settings.json: leave it untouched for the user to inspect.
770
+ }
771
+ }
772
+ // 2. Remove the runtime config file (carries serviceToken etc.).
773
+ const cfg = path.join(projectDir, '.governance.json');
774
+ if (fs.existsSync(cfg)) {
775
+ fs.rmSync(cfg, { force: true });
776
+ removedPaths.push(cfg);
777
+ }
778
+ // 3. Remove generated artifacts; preserve the audit chain unless purging.
779
+ let auditPreserved = null;
780
+ if (fs.existsSync(govDir)) {
781
+ const auditDir = path.join(govDir, 'audit');
782
+ const hasAudit = !opts.purge && fs.existsSync(auditDir) &&
783
+ fs.readdirSync(auditDir).length > 0;
784
+ if (opts.purge) {
785
+ fs.rmSync(govDir, { recursive: true, force: true });
786
+ removedPaths.push(govDir);
787
+ }
788
+ else {
789
+ for (const entry of fs.readdirSync(govDir)) {
790
+ if (entry === 'audit' && hasAudit)
791
+ continue;
792
+ const p = path.join(govDir, entry);
793
+ fs.rmSync(p, { recursive: true, force: true });
794
+ removedPaths.push(p);
795
+ }
796
+ if (hasAudit) {
797
+ auditPreserved = auditDir;
798
+ }
799
+ else if (fs.readdirSync(govDir).length === 0) {
800
+ fs.rmSync(govDir, { recursive: true, force: true });
801
+ removedPaths.push(govDir);
802
+ }
803
+ }
804
+ }
805
+ return { hooksRemoved, removedPaths, auditPreserved };
806
+ }
614
807
  const IDE_RULES_SPECS = {
615
808
  cursor: {
616
809
  ideKey: 'cursor',
@@ -2116,6 +2309,8 @@ Usage:
2116
2309
  ai-governance verify-chain <logDir> Verify audit chain integrity (standalone)
2117
2310
  [--json] Output machine-readable JSON
2118
2311
  ai-governance status Show governance status
2312
+ ai-governance uninstall Cleanly remove governance from this project
2313
+ [--purge] Also delete the audit chain (default: preserved)
2119
2314
  ai-governance discover Scan project + push signed manifests
2120
2315
  --root <path> Project directory to scan
2121
2316
  --org-id <org> Organisation identifier
@@ -2190,6 +2385,15 @@ Connexum Network Inc.
2190
2385
  if (isDirectRun) {
2191
2386
  const projectDir = process.cwd();
2192
2387
  if (command === 'init') {
2388
+ // P0: refuse to scaffold into the home dir / global Claude config before
2389
+ // doing any provisioning. Friendly message instead of a deep stack trace.
2390
+ try {
2391
+ assertSafeInstallDir(projectDir);
2392
+ }
2393
+ catch (e) {
2394
+ process.stderr.write(`[CLI] ${e.message}\n`);
2395
+ process.exit(1);
2396
+ }
2193
2397
  const ciMode = args.includes('--ci');
2194
2398
  // S-07: Parse vendor code flags
2195
2399
  const vendorCodeIdx = args.indexOf('--vendor-code');
@@ -2662,6 +2866,23 @@ if (isDirectRun) {
2662
2866
  else if (command === 'status') {
2663
2867
  showStatus(projectDir);
2664
2868
  }
2869
+ else if (command === 'uninstall') {
2870
+ // Clean removal escape hatch. Strips our hooks from .claude/settings.json
2871
+ // (keeping the user's own), removes generated scripts + .governance.json,
2872
+ // and preserves the audit chain unless --purge.
2873
+ const purge = args.includes('--purge');
2874
+ const result = uninstallGovernance(projectDir, { purge });
2875
+ process.stdout.write(`[CLI] Removed ${result.hooksRemoved} governance hook(s) from .claude/settings.json\n`);
2876
+ for (const p of result.removedPaths) {
2877
+ process.stdout.write(`[CLI] Removed ${p}\n`);
2878
+ }
2879
+ if (result.auditPreserved) {
2880
+ process.stdout.write(`[CLI] Audit chain preserved at ${result.auditPreserved} ` +
2881
+ `(re-run with --purge to delete it too).\n`);
2882
+ }
2883
+ process.stdout.write('[CLI] Governance uninstalled. Restart Claude Code for the change to take effect.\n');
2884
+ process.exit(0);
2885
+ }
2665
2886
  else if (command === 'packs') {
2666
2887
  // W2: per-project pack selection CLI subcommand
2667
2888
  Promise.resolve().then(() => __importStar(require('./packs.js'))).then(({ runPacksCommand }) => {