@guilz-dev/belay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/agent-belay-logo.png +0 -0
  4. package/dist/adapters/claude/adapter.d.ts +7 -0
  5. package/dist/adapters/claude/adapter.js +114 -0
  6. package/dist/adapters/claude/hooks.d.ts +13 -0
  7. package/dist/adapters/claude/hooks.js +49 -0
  8. package/dist/adapters/claude/runtime-entry.d.ts +4 -0
  9. package/dist/adapters/claude/runtime-entry.js +260 -0
  10. package/dist/adapters/codex/adapter.d.ts +7 -0
  11. package/dist/adapters/codex/adapter.js +73 -0
  12. package/dist/adapters/codex/hooks.d.ts +21 -0
  13. package/dist/adapters/codex/hooks.js +78 -0
  14. package/dist/adapters/codex/runtime-entry.d.ts +4 -0
  15. package/dist/adapters/codex/runtime-entry.js +237 -0
  16. package/dist/adapters/cursor/adapter.d.ts +7 -0
  17. package/dist/adapters/cursor/adapter.js +29 -0
  18. package/dist/adapters/cursor/hooks.d.ts +2 -0
  19. package/dist/adapters/cursor/hooks.js +26 -0
  20. package/dist/adapters/cursor/runtime-entry.d.ts +4 -0
  21. package/dist/adapters/cursor/runtime-entry.js +143 -0
  22. package/dist/adapters/layouts/claude.d.ts +2 -0
  23. package/dist/adapters/layouts/claude.js +40 -0
  24. package/dist/adapters/layouts/codex.d.ts +2 -0
  25. package/dist/adapters/layouts/codex.js +43 -0
  26. package/dist/adapters/layouts/cursor.d.ts +2 -0
  27. package/dist/adapters/layouts/cursor.js +40 -0
  28. package/dist/adapters/layouts/index.d.ts +7 -0
  29. package/dist/adapters/layouts/index.js +23 -0
  30. package/dist/adapters/layouts/protected-paths.d.ts +3 -0
  31. package/dist/adapters/layouts/protected-paths.js +15 -0
  32. package/dist/adapters/layouts/scope.d.ts +19 -0
  33. package/dist/adapters/layouts/scope.js +86 -0
  34. package/dist/adapters/layouts/types.d.ts +14 -0
  35. package/dist/adapters/layouts/types.js +1 -0
  36. package/dist/adapters/registry.d.ts +4 -0
  37. package/dist/adapters/registry.js +14 -0
  38. package/dist/adapters/shared/gate-runtime.d.ts +51 -0
  39. package/dist/adapters/shared/gate-runtime.js +518 -0
  40. package/dist/adapters/shared/repo-root.d.ts +2 -0
  41. package/dist/adapters/shared/repo-root.js +17 -0
  42. package/dist/adapters/types.d.ts +19 -0
  43. package/dist/adapters/types.js +1 -0
  44. package/dist/branding.d.ts +3 -0
  45. package/dist/branding.js +3 -0
  46. package/dist/bundle/claude-runtime.mjs +5323 -0
  47. package/dist/bundle/codex-runtime.mjs +5310 -0
  48. package/dist/bundle/cursor-runtime.mjs +5208 -0
  49. package/dist/cleanup-orphans.d.ts +7 -0
  50. package/dist/cleanup-orphans.js +59 -0
  51. package/dist/cli.d.ts +2 -0
  52. package/dist/cli.js +631 -0
  53. package/dist/commands/approve.d.ts +14 -0
  54. package/dist/commands/approve.js +65 -0
  55. package/dist/commands/audit.d.ts +59 -0
  56. package/dist/commands/audit.js +132 -0
  57. package/dist/commands/classify-for-report.d.ts +9 -0
  58. package/dist/commands/classify-for-report.js +85 -0
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.js +366 -0
  61. package/dist/commands/dogfood.d.ts +5 -0
  62. package/dist/commands/dogfood.js +71 -0
  63. package/dist/commands/explain.d.ts +3 -0
  64. package/dist/commands/explain.js +133 -0
  65. package/dist/commands/health-snapshot.d.ts +2 -0
  66. package/dist/commands/health-snapshot.js +166 -0
  67. package/dist/commands/init-wizard.d.ts +16 -0
  68. package/dist/commands/init-wizard.js +50 -0
  69. package/dist/commands/metrics.d.ts +7 -0
  70. package/dist/commands/metrics.js +89 -0
  71. package/dist/commands/recover.d.ts +3 -0
  72. package/dist/commands/recover.js +105 -0
  73. package/dist/commands/report.d.ts +3 -0
  74. package/dist/commands/report.js +65 -0
  75. package/dist/commands/revoke.d.ts +5 -0
  76. package/dist/commands/revoke.js +22 -0
  77. package/dist/commands/simulate.d.ts +14 -0
  78. package/dist/commands/simulate.js +55 -0
  79. package/dist/commands/status.d.ts +5 -0
  80. package/dist/commands/status.js +107 -0
  81. package/dist/config-io.d.ts +23 -0
  82. package/dist/config-io.js +180 -0
  83. package/dist/conformance/guarantee-table.d.ts +14 -0
  84. package/dist/conformance/guarantee-table.js +95 -0
  85. package/dist/conformance/types.d.ts +6 -0
  86. package/dist/conformance/types.js +1 -0
  87. package/dist/core/approval-service.d.ts +26 -0
  88. package/dist/core/approval-service.js +41 -0
  89. package/dist/core/approval-token.d.ts +11 -0
  90. package/dist/core/approval-token.js +61 -0
  91. package/dist/core/approval.d.ts +19 -0
  92. package/dist/core/approval.js +58 -0
  93. package/dist/core/audit-analysis.d.ts +10 -0
  94. package/dist/core/audit-analysis.js +147 -0
  95. package/dist/core/audit-metrics.d.ts +51 -0
  96. package/dist/core/audit-metrics.js +155 -0
  97. package/dist/core/audit-query.d.ts +11 -0
  98. package/dist/core/audit-query.js +142 -0
  99. package/dist/core/audit-summary.d.ts +33 -0
  100. package/dist/core/audit-summary.js +111 -0
  101. package/dist/core/audit-types.d.ts +65 -0
  102. package/dist/core/audit-types.js +2 -0
  103. package/dist/core/capability/allowlist.d.ts +10 -0
  104. package/dist/core/capability/allowlist.js +53 -0
  105. package/dist/core/capability/broker.d.ts +17 -0
  106. package/dist/core/capability/broker.js +29 -0
  107. package/dist/core/capability/index.d.ts +5 -0
  108. package/dist/core/capability/index.js +4 -0
  109. package/dist/core/capability/paths.d.ts +1 -0
  110. package/dist/core/capability/paths.js +20 -0
  111. package/dist/core/capability/reasons.d.ts +2 -0
  112. package/dist/core/capability/reasons.js +4 -0
  113. package/dist/core/capability/types.d.ts +10 -0
  114. package/dist/core/capability/types.js +1 -0
  115. package/dist/core/capability-approval.d.ts +28 -0
  116. package/dist/core/capability-approval.js +43 -0
  117. package/dist/core/classify-subagent.d.ts +2 -0
  118. package/dist/core/classify-subagent.js +69 -0
  119. package/dist/core/classify-tool.d.ts +3 -0
  120. package/dist/core/classify-tool.js +311 -0
  121. package/dist/core/config-layers.d.ts +23 -0
  122. package/dist/core/config-layers.js +59 -0
  123. package/dist/core/config.d.ts +219 -0
  124. package/dist/core/config.js +720 -0
  125. package/dist/core/control-plane-isolation.d.ts +10 -0
  126. package/dist/core/control-plane-isolation.js +83 -0
  127. package/dist/core/custom-command-match.d.ts +2 -0
  128. package/dist/core/custom-command-match.js +8 -0
  129. package/dist/core/egress/allowlist.d.ts +7 -0
  130. package/dist/core/egress/allowlist.js +33 -0
  131. package/dist/core/egress/env.d.ts +3 -0
  132. package/dist/core/egress/env.js +17 -0
  133. package/dist/core/egress/fingerprint.d.ts +3 -0
  134. package/dist/core/egress/fingerprint.js +35 -0
  135. package/dist/core/egress/policy.d.ts +8 -0
  136. package/dist/core/egress/policy.js +47 -0
  137. package/dist/core/egress/proxy-server.d.ts +21 -0
  138. package/dist/core/egress/proxy-server.js +263 -0
  139. package/dist/core/egress/types.d.ts +25 -0
  140. package/dist/core/egress/types.js +1 -0
  141. package/dist/core/egress-approval.d.ts +48 -0
  142. package/dist/core/egress-approval.js +129 -0
  143. package/dist/core/fingerprint.d.ts +6 -0
  144. package/dist/core/fingerprint.js +24 -0
  145. package/dist/core/gate-contract.d.ts +48 -0
  146. package/dist/core/gate-contract.js +50 -0
  147. package/dist/core/gate-engine.d.ts +20 -0
  148. package/dist/core/gate-engine.js +172 -0
  149. package/dist/core/glob.d.ts +1 -0
  150. package/dist/core/glob.js +39 -0
  151. package/dist/core/index.d.ts +19 -0
  152. package/dist/core/index.js +15 -0
  153. package/dist/core/integrity.d.ts +15 -0
  154. package/dist/core/integrity.js +68 -0
  155. package/dist/core/judge-api-key.d.ts +4 -0
  156. package/dist/core/judge-api-key.js +11 -0
  157. package/dist/core/judge-config.d.ts +29 -0
  158. package/dist/core/judge-config.js +85 -0
  159. package/dist/core/judge-doctor.d.ts +7 -0
  160. package/dist/core/judge-doctor.js +124 -0
  161. package/dist/core/judgment.d.ts +6 -0
  162. package/dist/core/judgment.js +38 -0
  163. package/dist/core/notify.d.ts +13 -0
  164. package/dist/core/notify.js +44 -0
  165. package/dist/core/path-utils.d.ts +12 -0
  166. package/dist/core/path-utils.js +107 -0
  167. package/dist/core/reclassify.d.ts +15 -0
  168. package/dist/core/reclassify.js +82 -0
  169. package/dist/core/recover-advice.d.ts +30 -0
  170. package/dist/core/recover-advice.js +177 -0
  171. package/dist/core/recover-git-probe.d.ts +8 -0
  172. package/dist/core/recover-git-probe.js +50 -0
  173. package/dist/core/recover-select.d.ts +10 -0
  174. package/dist/core/recover-select.js +60 -0
  175. package/dist/core/scrub.d.ts +3 -0
  176. package/dist/core/scrub.js +87 -0
  177. package/dist/core/shell-substitution.d.ts +6 -0
  178. package/dist/core/shell-substitution.js +130 -0
  179. package/dist/core/shell-tokenizer.d.ts +5 -0
  180. package/dist/core/shell-tokenizer.js +129 -0
  181. package/dist/core/shell-unparseable.d.ts +4 -0
  182. package/dist/core/shell-unparseable.js +96 -0
  183. package/dist/core/transactional/diff-evaluator.d.ts +2 -0
  184. package/dist/core/transactional/diff-evaluator.js +84 -0
  185. package/dist/core/transactional/eligibility.d.ts +4 -0
  186. package/dist/core/transactional/eligibility.js +44 -0
  187. package/dist/core/transactional/git-worktree.d.ts +13 -0
  188. package/dist/core/transactional/git-worktree.js +189 -0
  189. package/dist/core/transactional/index.d.ts +5 -0
  190. package/dist/core/transactional/index.js +4 -0
  191. package/dist/core/transactional/reasons.d.ts +4 -0
  192. package/dist/core/transactional/reasons.js +8 -0
  193. package/dist/core/transactional/runner.d.ts +2 -0
  194. package/dist/core/transactional/runner.js +113 -0
  195. package/dist/core/transactional/types.d.ts +46 -0
  196. package/dist/core/transactional/types.js +1 -0
  197. package/dist/core/types.d.ts +90 -0
  198. package/dist/core/types.js +1 -0
  199. package/dist/core/v2/adapter.d.ts +14 -0
  200. package/dist/core/v2/adapter.js +118 -0
  201. package/dist/core/v2/containment.d.ts +19 -0
  202. package/dist/core/v2/containment.js +91 -0
  203. package/dist/core/v2/egress-classify.d.ts +7 -0
  204. package/dist/core/v2/egress-classify.js +216 -0
  205. package/dist/core/v2/fingerprint.d.ts +1 -0
  206. package/dist/core/v2/fingerprint.js +4 -0
  207. package/dist/core/v2/index.d.ts +12 -0
  208. package/dist/core/v2/index.js +10 -0
  209. package/dist/core/v2/judge-audit.d.ts +2 -0
  210. package/dist/core/v2/judge-audit.js +15 -0
  211. package/dist/core/v2/judge-factory.d.ts +25 -0
  212. package/dist/core/v2/judge-factory.js +75 -0
  213. package/dist/core/v2/judge-outbound.d.ts +12 -0
  214. package/dist/core/v2/judge-outbound.js +73 -0
  215. package/dist/core/v2/judge.d.ts +47 -0
  216. package/dist/core/v2/judge.js +264 -0
  217. package/dist/core/v2/launcher-resolve.d.ts +12 -0
  218. package/dist/core/v2/launcher-resolve.js +190 -0
  219. package/dist/core/v2/overrides.d.ts +7 -0
  220. package/dist/core/v2/overrides.js +37 -0
  221. package/dist/core/v2/parser.d.ts +21 -0
  222. package/dist/core/v2/parser.js +213 -0
  223. package/dist/core/v2/types.d.ts +67 -0
  224. package/dist/core/v2/types.js +1 -0
  225. package/dist/core/v2/verdict.d.ts +2 -0
  226. package/dist/core/v2/verdict.js +699 -0
  227. package/dist/corpus/evaluate.d.ts +24 -0
  228. package/dist/corpus/evaluate.js +69 -0
  229. package/dist/defaults.d.ts +18 -0
  230. package/dist/defaults.js +155 -0
  231. package/dist/egress-daemon.d.ts +1 -0
  232. package/dist/egress-daemon.js +52 -0
  233. package/dist/index.d.ts +17 -0
  234. package/dist/index.js +15 -0
  235. package/dist/installer/bootstrap.d.ts +5 -0
  236. package/dist/installer/bootstrap.js +61 -0
  237. package/dist/installer/runtime-artifacts.d.ts +3 -0
  238. package/dist/installer/runtime-artifacts.js +23 -0
  239. package/dist/installer/scope-config.d.ts +8 -0
  240. package/dist/installer/scope-config.js +25 -0
  241. package/dist/installer.d.ts +22 -0
  242. package/dist/installer.js +169 -0
  243. package/dist/node-resolution.d.ts +8 -0
  244. package/dist/node-resolution.js +237 -0
  245. package/dist/operational-insights.d.ts +19 -0
  246. package/dist/operational-insights.js +24 -0
  247. package/dist/presets.d.ts +4 -0
  248. package/dist/presets.js +95 -0
  249. package/dist/services/egress-service.d.ts +57 -0
  250. package/dist/services/egress-service.js +334 -0
  251. package/dist/services/sandbox-service.d.ts +38 -0
  252. package/dist/services/sandbox-service.js +95 -0
  253. package/dist/templates.d.ts +7 -0
  254. package/dist/templates.js +56 -0
  255. package/dist/types.d.ts +230 -0
  256. package/dist/types.js +1 -0
  257. package/dist/version.d.ts +1 -0
  258. package/dist/version.js +1 -0
  259. package/package.json +65 -0
  260. package/skills/belay/SKILL.md +52 -0
  261. package/skills/belay/belay-approve.md +7 -0
  262. package/skills/belay/belay-explain.md +11 -0
  263. package/skills/belay/belay-recover.md +13 -0
  264. package/skills/belay/belay-report.md +7 -0
  265. package/skills/belay/belay-status.md +9 -0
  266. package/skills/belay/belay-why.md +11 -0
@@ -0,0 +1,24 @@
1
+ import type { Assessment, HookVerdict } from '../core/types.js';
2
+ export interface CorpusCase {
3
+ command: string;
4
+ verdict: HookVerdict;
5
+ reason?: string;
6
+ }
7
+ export interface CorpusMetrics {
8
+ total: number;
9
+ correct: number;
10
+ accuracy: number;
11
+ precision: Record<string, number>;
12
+ recall: Record<string, number>;
13
+ falsePositiveRate: number;
14
+ mismatches: Array<{
15
+ command: string;
16
+ expected: string;
17
+ actual: string;
18
+ reason: string;
19
+ }>;
20
+ }
21
+ export declare function assessmentsDiverge(predicted: Assessment, observed: Assessment): boolean;
22
+ export declare function loadCorpusCases(corpusDir: string): Promise<CorpusCase[]>;
23
+ export declare function evaluateCorpus(cases: CorpusCase[], repoRoot?: string): Promise<CorpusMetrics>;
24
+ export declare function runCorpusEvaluation(corpusDir?: string): Promise<CorpusMetrics>;
@@ -0,0 +1,69 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { classifierOptionsFromConfig, DEFAULT_CONFIG_V3 } from '../core/config.js';
5
+ import { classifyShell } from '../core/v2/adapter.js';
6
+ import { createDeterministicJudgeStub } from '../core/v2/judge.js';
7
+ export function assessmentsDiverge(predicted, observed) {
8
+ return (predicted.reversibility !== observed.reversibility ||
9
+ predicted.external !== observed.external ||
10
+ predicted.blastRadius !== observed.blastRadius);
11
+ }
12
+ const VERDICTS = ['allow', 'allow_flagged', 'deny_pending_approval'];
13
+ export async function loadCorpusCases(corpusDir) {
14
+ const raw = await readFile(path.join(corpusDir, 'shell-commands.json'), 'utf8');
15
+ return JSON.parse(raw);
16
+ }
17
+ export async function evaluateCorpus(cases, repoRoot = '/workspace/project') {
18
+ const cwd = path.join(repoRoot, 'src');
19
+ const options = classifierOptionsFromConfig(DEFAULT_CONFIG_V3);
20
+ const mismatches = [];
21
+ let correct = 0;
22
+ const confusion = {};
23
+ for (const expected of VERDICTS) {
24
+ confusion[expected] = { allow: 0, allow_flagged: 0, deny_pending_approval: 0 };
25
+ }
26
+ const judge = createDeterministicJudgeStub();
27
+ for (const testCase of cases) {
28
+ const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options, judge);
29
+ confusion[testCase.verdict][result.verdict] += 1;
30
+ const verdictOk = result.verdict === testCase.verdict;
31
+ const reasonOk = !testCase.reason || result.reason === testCase.reason;
32
+ if (verdictOk && reasonOk) {
33
+ correct += 1;
34
+ }
35
+ else {
36
+ mismatches.push({
37
+ command: testCase.command,
38
+ expected: testCase.verdict,
39
+ actual: result.verdict,
40
+ reason: result.reason,
41
+ });
42
+ }
43
+ }
44
+ const precision = {};
45
+ const recall = {};
46
+ for (const verdict of VERDICTS) {
47
+ const predicted = VERDICTS.reduce((sum, key) => sum + confusion[key][verdict], 0);
48
+ const truePositive = confusion[verdict][verdict];
49
+ precision[verdict] = predicted === 0 ? 1 : truePositive / predicted;
50
+ const actualTotal = VERDICTS.reduce((sum, key) => sum + confusion[verdict][key], 0);
51
+ recall[verdict] = actualTotal === 0 ? 1 : truePositive / actualTotal;
52
+ }
53
+ const falsePositives = mismatches.filter((entry) => entry.expected === 'deny_pending_approval' && entry.actual !== 'deny_pending_approval').length;
54
+ const denyCases = cases.filter((entry) => entry.verdict === 'deny_pending_approval').length;
55
+ return {
56
+ total: cases.length,
57
+ correct,
58
+ accuracy: cases.length === 0 ? 1 : correct / cases.length,
59
+ precision,
60
+ recall,
61
+ falsePositiveRate: denyCases === 0 ? 0 : falsePositives / denyCases,
62
+ mismatches,
63
+ };
64
+ }
65
+ export async function runCorpusEvaluation(corpusDir) {
66
+ const root = corpusDir ?? path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'corpus');
67
+ const cases = await loadCorpusCases(root);
68
+ return evaluateCorpus(cases);
69
+ }
@@ -0,0 +1,18 @@
1
+ import { type BelayConfigV3 } from './core/config.js';
2
+ export { PACKAGE_NAME } from './branding.js';
3
+ export declare const DEFAULT_CONFIG: BelayConfigV3;
4
+ export type ManagedHookDefinition = {
5
+ command: string;
6
+ placement: 'prepend' | 'append';
7
+ matcher?: string;
8
+ };
9
+ export declare function getManagedHookEntries(platform?: NodeJS.Platform, hooksDir?: string, repoRoot?: string): Array<{
10
+ event: string;
11
+ definition: ManagedHookDefinition;
12
+ }>;
13
+ /** @deprecated Use getManagedHookEntries instead. */
14
+ export declare function getManagedHookEvents(platform?: NodeJS.Platform): Record<string, ManagedHookDefinition>;
15
+ export declare const EMPTY_APPROVALS: {
16
+ readonly version: 1;
17
+ readonly approvals: readonly [];
18
+ };
@@ -0,0 +1,155 @@
1
+ import path from 'node:path';
2
+ import { cursorLayout } from './adapters/layouts/cursor.js';
3
+ import { buildRunnerInvocation } from './adapters/layouts/scope.js';
4
+ import { DEFAULT_CONFIG_V3 } from './core/config.js';
5
+ export { PACKAGE_NAME } from './branding.js';
6
+ export const DEFAULT_CONFIG = DEFAULT_CONFIG_V3;
7
+ function runnerCommand(platform, hooksDir, repoRoot, hookScript, ...args) {
8
+ return buildRunnerInvocation(platform, hooksDir, repoRoot, hookScript, ...args);
9
+ }
10
+ export function getManagedHookEntries(platform = process.platform, hooksDir, repoRoot) {
11
+ const resolvedRepo = path.resolve(repoRoot ?? process.cwd());
12
+ const resolvedHooksDir = hooksDir ?? cursorLayout.hooksDir(resolvedRepo);
13
+ const toolGate = runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-tool-gate', 'preToolUse');
14
+ const subagentGate = runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-tool-gate', 'subagentStart');
15
+ return [
16
+ {
17
+ event: 'beforeSubmitPrompt',
18
+ definition: {
19
+ command: runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-before-submit'),
20
+ placement: 'prepend',
21
+ },
22
+ },
23
+ {
24
+ event: 'beforeShellExecution',
25
+ definition: {
26
+ command: runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-shell-gate'),
27
+ placement: 'prepend',
28
+ },
29
+ },
30
+ {
31
+ event: 'preToolUse',
32
+ definition: {
33
+ command: toolGate,
34
+ placement: 'prepend',
35
+ matcher: 'Task',
36
+ },
37
+ },
38
+ {
39
+ event: 'preToolUse',
40
+ definition: {
41
+ command: toolGate,
42
+ placement: 'prepend',
43
+ matcher: 'Shell',
44
+ },
45
+ },
46
+ {
47
+ event: 'preToolUse',
48
+ definition: {
49
+ command: toolGate,
50
+ placement: 'prepend',
51
+ matcher: 'Write',
52
+ },
53
+ },
54
+ {
55
+ event: 'preToolUse',
56
+ definition: {
57
+ command: toolGate,
58
+ placement: 'prepend',
59
+ matcher: 'StrReplace',
60
+ },
61
+ },
62
+ {
63
+ event: 'preToolUse',
64
+ definition: {
65
+ command: toolGate,
66
+ placement: 'prepend',
67
+ matcher: 'Delete',
68
+ },
69
+ },
70
+ {
71
+ event: 'subagentStart',
72
+ definition: {
73
+ command: subagentGate,
74
+ placement: 'prepend',
75
+ matcher: 'generalPurpose',
76
+ },
77
+ },
78
+ {
79
+ event: 'subagentStart',
80
+ definition: {
81
+ command: subagentGate,
82
+ placement: 'prepend',
83
+ matcher: 'computerUse',
84
+ },
85
+ },
86
+ {
87
+ event: 'subagentStart',
88
+ definition: {
89
+ command: subagentGate,
90
+ placement: 'prepend',
91
+ matcher: 'debug',
92
+ },
93
+ },
94
+ {
95
+ event: 'subagentStart',
96
+ definition: {
97
+ command: subagentGate,
98
+ placement: 'prepend',
99
+ matcher: 'explore',
100
+ },
101
+ },
102
+ {
103
+ event: 'subagentStart',
104
+ definition: {
105
+ command: subagentGate,
106
+ placement: 'prepend',
107
+ matcher: 'videoReview',
108
+ },
109
+ },
110
+ {
111
+ event: 'subagentStart',
112
+ definition: {
113
+ command: subagentGate,
114
+ placement: 'prepend',
115
+ matcher: 'bugbot',
116
+ },
117
+ },
118
+ {
119
+ event: 'postToolUse',
120
+ definition: {
121
+ command: runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-audit', 'postToolUse'),
122
+ placement: 'append',
123
+ },
124
+ },
125
+ {
126
+ event: 'stop',
127
+ definition: {
128
+ command: runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-audit', 'stop'),
129
+ placement: 'append',
130
+ },
131
+ },
132
+ {
133
+ event: 'sessionEnd',
134
+ definition: {
135
+ command: runnerCommand(platform, resolvedHooksDir, resolvedRepo, 'belay-audit', 'sessionEnd'),
136
+ placement: 'append',
137
+ },
138
+ },
139
+ ];
140
+ }
141
+ /** @deprecated Use getManagedHookEntries instead. */
142
+ export function getManagedHookEvents(platform = process.platform) {
143
+ const entries = getManagedHookEntries(platform);
144
+ const result = {};
145
+ for (const entry of entries) {
146
+ if (!result[entry.event]) {
147
+ result[entry.event] = entry.definition;
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ export const EMPTY_APPROVALS = {
153
+ version: 1,
154
+ approvals: [],
155
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { belayStateDir, loadApprovalState, loadConfigFile, repoLocalStateDirFor, } from './config-io.js';
4
+ import { scrubOptionsFromConfig } from './core/config.js';
5
+ import { startEgressProxy as bindEgressProxy } from './core/egress/proxy-server.js';
6
+ import { scrubValue } from './core/scrub.js';
7
+ import { clearEgressDaemonState, createEgressApprovalStore, writeEgressDaemonState, } from './services/egress-service.js';
8
+ async function main() {
9
+ const repoRoot = process.env.BELAY_EGRESS_REPO_ROOT;
10
+ if (!repoRoot) {
11
+ process.stderr.write('BELAY_EGRESS_REPO_ROOT is required.\n');
12
+ process.exitCode = 1;
13
+ return;
14
+ }
15
+ const config = await loadConfigFile(repoRoot);
16
+ if (!config.egress.enabled) {
17
+ process.stderr.write('egress.enabled is false; refusing to start daemon.\n');
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+ const store = createEgressApprovalStore(repoRoot, config);
22
+ const stateDir = belayStateDir(config, repoLocalStateDirFor(repoRoot, config));
23
+ const auditPath = path.join(repoRoot, config.audit.logPath);
24
+ const { server, host, port } = await bindEgressProxy({
25
+ config,
26
+ repoRoot,
27
+ store,
28
+ async loadApproved() {
29
+ return loadApprovalState(repoRoot, 'approved-approvals.json', config);
30
+ },
31
+ async onAudit(event) {
32
+ await mkdir(path.dirname(auditPath), { recursive: true });
33
+ const record = { timestamp: new Date().toISOString(), ...event };
34
+ const scrubbed = scrubValue(record, scrubOptionsFromConfig(config));
35
+ await writeFile(auditPath, `${JSON.stringify(scrubbed)}\n`, { encoding: 'utf8', flag: 'a' });
36
+ },
37
+ });
38
+ await writeEgressDaemonState({ stateDir, pid: process.pid, host, port, repoRoot });
39
+ const shutdown = async () => {
40
+ server.close();
41
+ await clearEgressDaemonState(stateDir);
42
+ process.exit(0);
43
+ };
44
+ process.on('SIGTERM', () => {
45
+ void shutdown();
46
+ });
47
+ process.on('SIGINT', () => {
48
+ void shutdown();
49
+ });
50
+ process.stdout.write(`egress proxy listening on ${host}:${port} for ${repoRoot}\n`);
51
+ }
52
+ await main();
@@ -0,0 +1,17 @@
1
+ export { claudeAdapter } from './adapters/claude/adapter.js';
2
+ export { cursorAdapter } from './adapters/cursor/adapter.js';
3
+ export { getAdapter, listAdapters } from './adapters/registry.js';
4
+ export type { BelayAdapter } from './adapters/types.js';
5
+ export { doctorProject, formatDoctorReport } from './commands/doctor.js';
6
+ export { explainCommand, formatExplainReport } from './commands/explain.js';
7
+ export { formatRecoverReport, recoverProject } from './commands/recover.js';
8
+ export { formatReport, reportProject } from './commands/report.js';
9
+ export { revokeApproval } from './commands/revoke.js';
10
+ export { formatStatusReport, statusProject } from './commands/status.js';
11
+ export { GATE_CONTRACT_VERSION, type GatedAction, type GateVerdict, } from './core/gate-contract.js';
12
+ export { classifyShell, classifySubagent, classifyToolUse, DEFAULT_CONFIG_V2, DEFAULT_CONFIG_V3, mergeConfig, migrateConfig, } from './core/index.js';
13
+ export { initProject, upgradeProject } from './installer.js';
14
+ export { resolveNodeBinary } from './node-resolution.js';
15
+ export { applyConfigPreset, CONFIG_PRESETS, type ConfigPresetName } from './presets.js';
16
+ export type { ApprovalRecord, ApprovalStateFile, Assessment, AuditVisibilityReport, BelayConfig, BelayConfigV1, BelayConfigV2, BelayConfigV3, ClassifyResult, DoctorOptions, DoctorReport, ExplainOptions, ExplainReport, InitOptions, RecoverOptions, RecoverReport, ReportOptions, RevokeOptions, StatusOptions, StatusReport, UpgradeOptions, } from './types.js';
17
+ export { PACKAGE_VERSION } from './version.js';
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ export { claudeAdapter } from './adapters/claude/adapter.js';
2
+ export { cursorAdapter } from './adapters/cursor/adapter.js';
3
+ export { getAdapter, listAdapters } from './adapters/registry.js';
4
+ export { doctorProject, formatDoctorReport } from './commands/doctor.js';
5
+ export { explainCommand, formatExplainReport } from './commands/explain.js';
6
+ export { formatRecoverReport, recoverProject } from './commands/recover.js';
7
+ export { formatReport, reportProject } from './commands/report.js';
8
+ export { revokeApproval } from './commands/revoke.js';
9
+ export { formatStatusReport, statusProject } from './commands/status.js';
10
+ export { GATE_CONTRACT_VERSION, } from './core/gate-contract.js';
11
+ export { classifyShell, classifySubagent, classifyToolUse, DEFAULT_CONFIG_V2, DEFAULT_CONFIG_V3, mergeConfig, migrateConfig, } from './core/index.js';
12
+ export { initProject, upgradeProject } from './installer.js';
13
+ export { resolveNodeBinary } from './node-resolution.js';
14
+ export { applyConfigPreset, CONFIG_PRESETS } from './presets.js';
15
+ export { PACKAGE_VERSION } from './version.js';
@@ -0,0 +1,5 @@
1
+ import type { ScopedPaths } from '../adapters/layouts/scope.js';
2
+ import type { AdapterName } from '../adapters/layouts/types.js';
3
+ import type { BelayConfigV3 } from '../core/config.js';
4
+ export declare function bootstrapStateFiles(repoRoot: string, config: BelayConfigV3, paths: ScopedPaths): Promise<void>;
5
+ export declare function writeSkillArtifacts(adapterName: AdapterName, paths: ScopedPaths): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { approvedApprovalsPath, pendingApprovalsPath } from '../config-io.js';
5
+ import { EMPTY_APPROVALS } from '../defaults.js';
6
+ const BUNDLED_SKILL_TEMPLATE_URL = new URL('../../skills/belay/SKILL.md', import.meta.url);
7
+ const BUNDLED_COMMAND_TEMPLATES = [
8
+ 'belay-approve.md',
9
+ 'belay-why.md',
10
+ 'belay-explain.md',
11
+ 'belay-status.md',
12
+ 'belay-report.md',
13
+ 'belay-recover.md',
14
+ ];
15
+ async function readBundledTemplate(fileUrl) {
16
+ try {
17
+ return await readFile(fileUrl, 'utf8');
18
+ }
19
+ catch (error) {
20
+ const detail = error instanceof Error ? error.message : 'Unknown read failure.';
21
+ throw new Error(`Bundled template missing at ${fileUrl.pathname}: ${detail}`);
22
+ }
23
+ }
24
+ async function writeJsonIfMissing(filePath, value) {
25
+ if (existsSync(filePath)) {
26
+ return;
27
+ }
28
+ await mkdir(path.dirname(filePath), { recursive: true });
29
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
30
+ }
31
+ async function writeTextIfMissing(filePath, content) {
32
+ if (existsSync(filePath)) {
33
+ return;
34
+ }
35
+ await mkdir(path.dirname(filePath), { recursive: true });
36
+ await writeFile(filePath, content, 'utf8');
37
+ }
38
+ export async function bootstrapStateFiles(repoRoot, config, paths) {
39
+ await mkdir(paths.repoLocalStateDir, { recursive: true });
40
+ await mkdir(path.join(paths.repoLocalStateDir, 'runtime'), { recursive: true });
41
+ await writeJsonIfMissing(pendingApprovalsPath(repoRoot, config), EMPTY_APPROVALS);
42
+ await writeJsonIfMissing(approvedApprovalsPath(repoRoot, config), EMPTY_APPROVALS);
43
+ const auditPath = path.join(repoRoot, config.audit.logPath);
44
+ if (!existsSync(auditPath)) {
45
+ await mkdir(path.dirname(auditPath), { recursive: true });
46
+ await writeFile(auditPath, '', 'utf8');
47
+ }
48
+ await writeTextIfMissing(path.join(paths.repoLocalStateDir, 'audit.ndjson'), '');
49
+ }
50
+ export async function writeSkillArtifacts(adapterName, paths) {
51
+ await mkdir(paths.skillsDir, { recursive: true });
52
+ const bundledSkill = await readBundledTemplate(BUNDLED_SKILL_TEMPLATE_URL);
53
+ await writeFile(path.join(paths.skillsDir, 'SKILL.md'), bundledSkill, 'utf8');
54
+ if (adapterName === 'cursor' && paths.commandsDir) {
55
+ await mkdir(paths.commandsDir, { recursive: true });
56
+ for (const fileName of BUNDLED_COMMAND_TEMPLATES) {
57
+ const bundledCommand = await readBundledTemplate(new URL(`../../skills/belay/${fileName}`, import.meta.url));
58
+ await writeFile(path.join(paths.commandsDir, fileName), bundledCommand, 'utf8');
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,3 @@
1
+ import type { ScopedPaths } from '../adapters/layouts/scope.js';
2
+ import type { AdapterName } from '../adapters/layouts/types.js';
3
+ export declare function writeRuntimeArtifacts(adapterName: AdapterName, paths: ScopedPaths): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { chmod, mkdir, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { buildRunnerScript, buildWindowsRunnerScript } from '../node-resolution.js';
4
+ import { renderAuditHook, renderBeforeSubmitHook, renderRuntimeCore, renderShellGateHook, renderToolGateHook, } from '../templates.js';
5
+ async function writeFileMaybeExecutable(filePath, content, executable = false) {
6
+ await mkdir(path.dirname(filePath), { recursive: true });
7
+ await writeFile(filePath, content, 'utf8');
8
+ if (executable) {
9
+ await chmod(filePath, 0o755);
10
+ }
11
+ }
12
+ export async function writeRuntimeArtifacts(adapterName, paths) {
13
+ const { hooksDir, runtimeDir } = paths;
14
+ await mkdir(hooksDir, { recursive: true });
15
+ await mkdir(runtimeDir, { recursive: true });
16
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-before-submit.mjs'), renderBeforeSubmitHook());
17
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-shell-gate.mjs'), renderShellGateHook());
18
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-tool-gate.mjs'), renderToolGateHook());
19
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-audit.mjs'), renderAuditHook());
20
+ await writeFileMaybeExecutable(path.join(runtimeDir, 'core.mjs'), await renderRuntimeCore(adapterName));
21
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner'), buildRunnerScript(process.execPath), true);
22
+ await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner.cmd'), buildWindowsRunnerScript(process.execPath));
23
+ }
@@ -0,0 +1,8 @@
1
+ import { type ScopedPaths } from '../adapters/layouts/scope.js';
2
+ import type { AdapterName } from '../adapters/layouts/types.js';
3
+ import type { BelayConfigV4 } from '../core/config.js';
4
+ import type { InitOptions, UpgradeOptions } from '../types.js';
5
+ export type OperationScope = 'project' | 'global';
6
+ export declare function resolveOperationScope(repoRoot: string, adapter: AdapterName, options?: InitOptions | UpgradeOptions): Promise<OperationScope>;
7
+ export declare function applyInstallScope(repoRoot: string, adapter: AdapterName, scope: OperationScope, config?: BelayConfigV4): Promise<BelayConfigV4>;
8
+ export declare function pathsForOperation(adapter: AdapterName, scope: OperationScope, repoRoot: string): ScopedPaths;
@@ -0,0 +1,25 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { getAdapterLayout } from '../adapters/layouts/index.js';
3
+ import { resolveInstallScope, resolveScopedPaths, } from '../adapters/layouts/scope.js';
4
+ import { loadConfigFile, writeConfigFile } from '../config-io.js';
5
+ export async function resolveOperationScope(repoRoot, adapter, options = {}) {
6
+ const layout = getAdapterLayout(adapter);
7
+ let persisted;
8
+ if (existsSync(layout.configPath(repoRoot))) {
9
+ const config = await loadConfigFile(repoRoot, adapter);
10
+ persisted = config.installScope;
11
+ }
12
+ return resolveInstallScope(options, persisted);
13
+ }
14
+ export async function applyInstallScope(repoRoot, adapter, scope, config) {
15
+ const current = config ?? (await loadConfigFile(repoRoot, adapter));
16
+ if (current.installScope === scope) {
17
+ return current;
18
+ }
19
+ const updated = { ...current, installScope: scope };
20
+ await writeConfigFile(repoRoot, updated, adapter);
21
+ return updated;
22
+ }
23
+ export function pathsForOperation(adapter, scope, repoRoot) {
24
+ return resolveScopedPaths(getAdapterLayout(adapter), scope, repoRoot);
25
+ }
@@ -0,0 +1,22 @@
1
+ import type { AdapterName } from './adapters/layouts/types.js';
2
+ import type { HooksFile, InitOptions, UpgradeOptions } from './types.js';
3
+ export type { InstallScope } from './adapters/layouts/scope.js';
4
+ export declare function loadHooksFile(hooksPath: string): Promise<HooksFile>;
5
+ export declare function mergeHooksFile(current: HooksFile, platform?: NodeJS.Platform, hooksDir?: string, repoRoot?: string): HooksFile;
6
+ export declare function initCursorProject(options?: InitOptions): Promise<{
7
+ repoRoot: string;
8
+ withSkill: boolean;
9
+ }>;
10
+ export declare function upgradeCursorProject(options?: UpgradeOptions): Promise<{
11
+ repoRoot: string;
12
+ }>;
13
+ export declare function initProject(options?: InitOptions): Promise<{
14
+ repoRoot: string;
15
+ withSkill: boolean;
16
+ dogfood: boolean;
17
+ adapter: AdapterName;
18
+ }>;
19
+ export declare function upgradeProject(options?: UpgradeOptions): Promise<{
20
+ repoRoot: string;
21
+ adapter: AdapterName;
22
+ }>;