@guilz-dev/belay 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -2
- package/dist/adapters/codex/hooks.d.ts +1 -0
- package/dist/adapters/codex/hooks.js +3 -0
- package/dist/adapters/codex/runtime-entry.d.ts +3 -0
- package/dist/adapters/codex/runtime-entry.js +27 -4
- package/dist/adapters/cursor/cwd-resolution.d.ts +10 -0
- package/dist/adapters/cursor/cwd-resolution.js +58 -0
- package/dist/adapters/cursor/dispatcher-generation.d.ts +3 -0
- package/dist/adapters/cursor/dispatcher-generation.js +5 -0
- package/dist/adapters/cursor/hook-dispatch-entry.d.ts +7 -0
- package/dist/adapters/cursor/hook-dispatch-entry.js +126 -0
- package/dist/adapters/cursor/hook-router.d.ts +23 -0
- package/dist/adapters/cursor/hook-router.js +266 -0
- package/dist/adapters/cursor/hooks.d.ts +5 -0
- package/dist/adapters/cursor/hooks.js +115 -3
- package/dist/adapters/cursor/routing-layout.d.ts +5 -0
- package/dist/adapters/cursor/routing-layout.js +29 -0
- package/dist/adapters/cursor/runtime-entry.d.ts +18 -0
- package/dist/adapters/cursor/runtime-entry.js +135 -34
- package/dist/adapters/layouts/scope.d.ts +5 -0
- package/dist/adapters/layouts/scope.js +71 -0
- package/dist/adapters/shared/gate-runtime.js +27 -3
- package/dist/adapters/shared/repo-root.js +20 -1
- package/dist/bundle/claude-runtime.mjs +2660 -1951
- package/dist/bundle/codex-runtime.mjs +2680 -1953
- package/dist/bundle/cursor-dispatcher.mjs +443 -0
- package/dist/bundle/cursor-runtime.mjs +5638 -4697
- package/dist/cli.js +33 -3
- package/dist/commands/doctor.js +215 -16
- package/dist/commands/health-snapshot.d.ts +3 -0
- package/dist/commands/health-snapshot.js +61 -0
- package/dist/commands/recovery-checkpoints.d.ts +3 -3
- package/dist/commands/report.js +14 -0
- package/dist/commands/status.js +15 -0
- package/dist/commands/where.d.ts +4 -0
- package/dist/commands/where.js +52 -0
- package/dist/config-io.js +11 -2
- package/dist/core/approval-repo-lookup.d.ts +16 -0
- package/dist/core/approval-repo-lookup.js +48 -0
- package/dist/core/audit-io.d.ts +1 -1
- package/dist/core/audit-io.js +1 -1
- package/dist/core/audit-legacy-archive.js +43 -25
- package/dist/core/audit-query.d.ts +1 -0
- package/dist/core/audit-query.js +7 -0
- package/dist/core/audit-serialize.d.ts +3 -0
- package/dist/core/audit-serialize.js +39 -3
- package/dist/core/audit-summary.d.ts +9 -0
- package/dist/core/audit-summary.js +58 -1
- package/dist/core/audit-types.d.ts +4 -0
- package/dist/core/classify-tool.js +16 -8
- package/dist/core/effect-ir/argv-delegate.d.ts +9 -0
- package/dist/core/effect-ir/argv-delegate.js +42 -0
- package/dist/core/effect-ir/shell-lower/argv-delegate-gate.d.ts +3 -0
- package/dist/core/effect-ir/shell-lower/argv-delegate-gate.js +40 -0
- package/dist/core/effect-ir/shell-lower/augment.d.ts +4 -0
- package/dist/core/effect-ir/shell-lower/augment.js +97 -0
- package/dist/core/effect-ir/shell-lower/context.d.ts +10 -0
- package/dist/core/effect-ir/shell-lower/context.js +1 -0
- package/dist/core/effect-ir/shell-lower/decode-process.d.ts +9 -0
- package/dist/core/effect-ir/shell-lower/decode-process.js +201 -0
- package/dist/core/effect-ir/shell-lower/decoders/belay.d.ts +2 -0
- package/dist/core/effect-ir/shell-lower/decoders/belay.js +29 -0
- package/dist/core/effect-ir/shell-lower/decoders/builtins.d.ts +7 -0
- package/dist/core/effect-ir/shell-lower/decoders/builtins.js +194 -0
- package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.d.ts +4 -0
- package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.js +68 -0
- package/dist/core/effect-ir/shell-lower/decoders/docker.d.ts +3 -0
- package/dist/core/effect-ir/shell-lower/decoders/docker.js +43 -0
- package/dist/core/effect-ir/shell-lower/decoders/filesystem.d.ts +9 -0
- package/dist/core/effect-ir/shell-lower/decoders/filesystem.js +217 -0
- package/dist/core/effect-ir/shell-lower/decoders/prisma.d.ts +2 -0
- package/dist/core/effect-ir/shell-lower/decoders/prisma.js +54 -0
- package/dist/core/effect-ir/shell-lower/decoders/ruby.d.ts +11 -0
- package/dist/core/effect-ir/shell-lower/decoders/ruby.js +147 -0
- package/dist/core/effect-ir/shell-lower/decoders/toolchain.d.ts +7 -0
- package/dist/core/effect-ir/shell-lower/decoders/toolchain.js +155 -0
- package/dist/core/effect-ir/shell-lower/requirement.d.ts +12 -0
- package/dist/core/effect-ir/shell-lower/requirement.js +65 -0
- package/dist/core/effect-ir/shell-lower/segment.d.ts +11 -0
- package/dist/core/effect-ir/shell-lower/segment.js +82 -0
- package/dist/core/effect-ir/shell-lower/tokens.d.ts +26 -0
- package/dist/core/effect-ir/shell-lower/tokens.js +183 -0
- package/dist/core/effect-ir/shell-lower.d.ts +2 -7
- package/dist/core/effect-ir/shell-lower.js +122 -1511
- package/dist/core/glob.js +32 -16
- package/dist/core/integrity.d.ts +2 -2
- package/dist/core/integrity.js +52 -12
- package/dist/core/replay-scrub.d.ts +3 -0
- package/dist/core/replay-scrub.js +30 -3
- package/dist/core/shell-substitution.js +2 -2
- package/dist/core/shell-tokenizer.d.ts +28 -0
- package/dist/core/shell-tokenizer.js +111 -29
- package/dist/core/shell-unparseable.js +3 -1
- package/dist/core/verdict/docker-compose-run.d.ts +18 -0
- package/dist/core/verdict/docker-compose-run.js +136 -0
- package/dist/core/verdict/launcher-resolve.js +28 -28
- package/dist/core/verdict/parser.d.ts +3 -0
- package/dist/core/verdict/parser.js +23 -40
- package/dist/core/verdict/recursive-invocation.d.ts +20 -0
- package/dist/core/verdict/recursive-invocation.js +224 -0
- package/dist/defaults.d.ts +7 -3
- package/dist/defaults.js +35 -21
- package/dist/installer/bootstrap.d.ts +1 -0
- package/dist/installer/bootstrap.js +2 -2
- package/dist/installer/runtime-artifacts.js +43 -13
- package/dist/installer/scope-config.d.ts +2 -2
- package/dist/installer.d.ts +10 -1
- package/dist/installer.js +83 -7
- package/dist/node-resolution.d.ts +1 -0
- package/dist/node-resolution.js +61 -0
- package/dist/templates.d.ts +7 -4
- package/dist/templates.js +42 -4
- package/dist/types.d.ts +35 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/skills/belay/SKILL.md +5 -0
- package/skills/belay/belay-report.md +4 -1
|
@@ -1,23 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmod, mkdir, realpath, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import { buildRunnerScript, buildWindowsRunnerScript } from '../node-resolution.js';
|
|
4
|
-
import { renderAuditHook, renderBeforeSubmitHook, renderRuntimeCore, renderShellGateHook, renderToolGateHook, } from '../templates.js';
|
|
4
|
+
import { buildRunnerScript, buildWindowsPowerShellRunnerScript, buildWindowsRunnerScript, } from '../node-resolution.js';
|
|
5
|
+
import { renderAuditHook, renderBeforeSubmitHook, renderCursorDispatcher, renderRuntimeCore, renderShellGateHook, renderToolGateHook, } from '../templates.js';
|
|
5
6
|
async function writeFileMaybeExecutable(filePath, content, executable = false) {
|
|
6
7
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
await
|
|
8
|
+
const temporaryPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
9
|
+
try {
|
|
10
|
+
await writeFile(temporaryPath, content, { encoding: 'utf8', flag: 'wx' });
|
|
11
|
+
if (executable) {
|
|
12
|
+
await chmod(temporaryPath, 0o755);
|
|
13
|
+
}
|
|
14
|
+
await rename(temporaryPath, filePath);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await rm(temporaryPath, { force: true });
|
|
10
18
|
}
|
|
11
19
|
}
|
|
12
20
|
export async function writeRuntimeArtifacts(adapterName, paths) {
|
|
13
21
|
const { hooksDir, runtimeDir } = paths;
|
|
22
|
+
const cursorOrigin = adapterName === 'cursor'
|
|
23
|
+
? paths.scope === 'global'
|
|
24
|
+
? { scope: 'global' }
|
|
25
|
+
: { scope: 'project', repoRoot: await realpath(paths.repoRoot) }
|
|
26
|
+
: undefined;
|
|
27
|
+
const artifacts = {
|
|
28
|
+
auditHook: renderAuditHook(adapterName, cursorOrigin),
|
|
29
|
+
beforeSubmitHook: renderBeforeSubmitHook(adapterName, cursorOrigin),
|
|
30
|
+
core: await renderRuntimeCore(adapterName),
|
|
31
|
+
dispatcher: adapterName === 'cursor' ? await renderCursorDispatcher() : undefined,
|
|
32
|
+
runner: buildRunnerScript(process.execPath),
|
|
33
|
+
runnerCmd: buildWindowsRunnerScript(process.execPath),
|
|
34
|
+
runnerPowerShell: adapterName === 'cursor' ? buildWindowsPowerShellRunnerScript(process.execPath) : undefined,
|
|
35
|
+
shellGateHook: renderShellGateHook(adapterName, cursorOrigin),
|
|
36
|
+
toolGateHook: renderToolGateHook(adapterName, cursorOrigin),
|
|
37
|
+
};
|
|
14
38
|
await mkdir(hooksDir, { recursive: true });
|
|
15
39
|
await mkdir(runtimeDir, { recursive: true });
|
|
16
|
-
await writeFileMaybeExecutable(path.join(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await writeFileMaybeExecutable(path.join(
|
|
21
|
-
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner'),
|
|
22
|
-
|
|
40
|
+
await writeFileMaybeExecutable(path.join(runtimeDir, 'core.mjs'), artifacts.core);
|
|
41
|
+
if (artifacts.dispatcher !== undefined) {
|
|
42
|
+
await writeFileMaybeExecutable(path.join(runtimeDir, 'dispatcher.mjs'), artifacts.dispatcher);
|
|
43
|
+
}
|
|
44
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner'), artifacts.runner, true);
|
|
45
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner.cmd'), artifacts.runnerCmd);
|
|
46
|
+
if (artifacts.runnerPowerShell !== undefined) {
|
|
47
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-runner.ps1'), artifacts.runnerPowerShell);
|
|
48
|
+
}
|
|
49
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-before-submit.mjs'), artifacts.beforeSubmitHook);
|
|
50
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-shell-gate.mjs'), artifacts.shellGateHook);
|
|
51
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-tool-gate.mjs'), artifacts.toolGateHook);
|
|
52
|
+
await writeFileMaybeExecutable(path.join(hooksDir, 'belay-audit.mjs'), artifacts.auditHook);
|
|
23
53
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ScopedPaths } from '../adapters/layouts/scope.js';
|
|
2
2
|
import type { AdapterName } from '../adapters/layouts/types.js';
|
|
3
3
|
import type { BelayConfigV4 } from '../core/config.js';
|
|
4
|
-
import type { InitOptions, UpgradeOptions } from '../types.js';
|
|
4
|
+
import type { InitOptions, UninstallOptions, UpgradeOptions } from '../types.js';
|
|
5
5
|
export type OperationScope = 'project' | 'global';
|
|
6
|
-
export declare function resolveOperationScope(repoRoot: string, adapter: AdapterName, options?: InitOptions | UpgradeOptions): Promise<OperationScope>;
|
|
6
|
+
export declare function resolveOperationScope(repoRoot: string, adapter: AdapterName, options?: InitOptions | UpgradeOptions | UninstallOptions): Promise<OperationScope>;
|
|
7
7
|
export declare function applyInstallScope(repoRoot: string, adapter: AdapterName, scope: OperationScope, config?: BelayConfigV4): Promise<BelayConfigV4>;
|
|
8
8
|
export declare function pathsForOperation(adapter: AdapterName, scope: OperationScope, repoRoot: string): ScopedPaths;
|
package/dist/installer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdapterName } from './adapters/layouts/types.js';
|
|
2
|
-
import type { HooksFile, InitOptions, UpgradeOptions } from './types.js';
|
|
2
|
+
import type { HooksFile, InitOptions, UninstallOptions, UpgradeOptions } from './types.js';
|
|
3
3
|
export type { InstallScope } from './adapters/layouts/scope.js';
|
|
4
4
|
export declare function loadHooksFile(hooksPath: string): Promise<HooksFile>;
|
|
5
5
|
export declare function mergeHooksFile(current: HooksFile, platform?: NodeJS.Platform, hooksDir?: string, repoRoot?: string): HooksFile;
|
|
@@ -10,6 +10,10 @@ export declare function initCursorProject(options?: InitOptions): Promise<{
|
|
|
10
10
|
export declare function upgradeCursorProject(options?: UpgradeOptions): Promise<{
|
|
11
11
|
repoRoot: string;
|
|
12
12
|
}>;
|
|
13
|
+
export declare function uninstallCursorProject(options?: UninstallOptions): Promise<{
|
|
14
|
+
repoRoot: string;
|
|
15
|
+
scope: 'project' | 'global';
|
|
16
|
+
}>;
|
|
13
17
|
export declare function initProject(options?: InitOptions): Promise<{
|
|
14
18
|
repoRoot: string;
|
|
15
19
|
withSkill: boolean;
|
|
@@ -20,3 +24,8 @@ export declare function upgradeProject(options?: UpgradeOptions): Promise<{
|
|
|
20
24
|
repoRoot: string;
|
|
21
25
|
adapter: AdapterName;
|
|
22
26
|
}>;
|
|
27
|
+
export declare function uninstallProject(options?: UninstallOptions): Promise<{
|
|
28
|
+
repoRoot: string;
|
|
29
|
+
adapter: AdapterName;
|
|
30
|
+
scope: 'project' | 'global';
|
|
31
|
+
}>;
|
package/dist/installer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { mergeCursorHooksFile } from './adapters/cursor/hooks.js';
|
|
4
|
+
import { hasManagedCursorHookEntries, mergeCursorHooksFile, stripCursorHooksFile, } from './adapters/cursor/hooks.js';
|
|
5
5
|
import { cursorLayout } from './adapters/layouts/cursor.js';
|
|
6
6
|
import { resolveScopedPaths } from './adapters/layouts/scope.js';
|
|
7
7
|
import { getAdapter } from './adapters/registry.js';
|
|
@@ -13,7 +13,7 @@ import { isFreshConfigInput, mergeConfig, normalizeConfig, } from './core/config
|
|
|
13
13
|
import { runtimeIntegrityFiles, writeIntegrityManifest } from './core/integrity.js';
|
|
14
14
|
import { hasValidCloudConsent, isCloudJudgeConfig, migrateImplicitLocalJudgeIfNeeded, resolveInitJudgeConfig, } from './core/judge-config.js';
|
|
15
15
|
import { resolveJudgeTransport } from './core/judge-runtime-detection.js';
|
|
16
|
-
import { bootstrapStateFiles, writeSkillArtifacts } from './installer/bootstrap.js';
|
|
16
|
+
import { bootstrapStateFiles, CURSOR_COMMAND_ARTIFACTS, writeSkillArtifacts, } from './installer/bootstrap.js';
|
|
17
17
|
import { writeRuntimeArtifacts } from './installer/runtime-artifacts.js';
|
|
18
18
|
import { applyInstallScope, resolveOperationScope } from './installer/scope-config.js';
|
|
19
19
|
import { applyConfigPreset } from './presets.js';
|
|
@@ -64,7 +64,6 @@ export async function initCursorProject(options = {}) {
|
|
|
64
64
|
const mergedHooks = mergeCursorHooksFile(hooksFile, process.platform, paths.hooksDir, repoRoot);
|
|
65
65
|
await ensureDir(paths.hooksDir);
|
|
66
66
|
const config = await mergeAndWriteConfig(repoRoot, 'cursor');
|
|
67
|
-
await applyInstallScope(repoRoot, 'cursor', scope, config);
|
|
68
67
|
await writeRuntimeArtifacts('cursor', paths);
|
|
69
68
|
await bootstrapStateFiles(repoRoot, config, paths);
|
|
70
69
|
if (withSkill) {
|
|
@@ -72,8 +71,12 @@ export async function initCursorProject(options = {}) {
|
|
|
72
71
|
}
|
|
73
72
|
await mkdir(path.dirname(paths.hooksSettingsPath), { recursive: true });
|
|
74
73
|
await writeFile(paths.hooksSettingsPath, `${JSON.stringify(mergedHooks, null, 2)}\n`, 'utf8');
|
|
74
|
+
const installedConfig = await applyInstallScope(repoRoot, 'cursor', scope, config);
|
|
75
|
+
if (scope === 'global') {
|
|
76
|
+
await cleanupStaleProjectCursorInstall(repoRoot);
|
|
77
|
+
}
|
|
75
78
|
await writeIntegrityManifest(repoRoot, cursorLayout, runtimeIntegrityFiles(cursorLayout, paths));
|
|
76
|
-
await archiveLegacyAuditLogIfNeeded(repoRoot,
|
|
79
|
+
await archiveLegacyAuditLogIfNeeded(repoRoot, installedConfig);
|
|
77
80
|
return { repoRoot, withSkill };
|
|
78
81
|
}
|
|
79
82
|
export async function upgradeCursorProject(options = {}) {
|
|
@@ -81,19 +84,83 @@ export async function upgradeCursorProject(options = {}) {
|
|
|
81
84
|
const scope = await resolveOperationScope(repoRoot, 'cursor', options);
|
|
82
85
|
const paths = resolveScopedPaths(cursorLayout, scope, repoRoot);
|
|
83
86
|
const config = await mergeAndWriteConfig(repoRoot, 'cursor');
|
|
84
|
-
await applyInstallScope(repoRoot, 'cursor', scope, config);
|
|
85
87
|
await writeRuntimeArtifacts('cursor', paths);
|
|
86
88
|
const hooksFile = await loadHooksFile(paths.hooksSettingsPath);
|
|
87
89
|
const merged = mergeCursorHooksFile(hooksFile, process.platform, paths.hooksDir, repoRoot);
|
|
88
90
|
await mkdir(path.dirname(paths.hooksSettingsPath), { recursive: true });
|
|
89
91
|
await writeFile(paths.hooksSettingsPath, `${JSON.stringify(merged, null, 2)}\n`, 'utf8');
|
|
92
|
+
if (scope === 'project') {
|
|
93
|
+
const globalPaths = resolveScopedPaths(cursorLayout, 'global', repoRoot);
|
|
94
|
+
if (existsSync(globalPaths.hooksSettingsPath)) {
|
|
95
|
+
const globalHooks = await loadHooksFile(globalPaths.hooksSettingsPath);
|
|
96
|
+
if (hasManagedCursorHookEntries(globalHooks, process.platform, globalPaths.hooksDir, repoRoot)) {
|
|
97
|
+
await writeRuntimeArtifacts('cursor', globalPaths);
|
|
98
|
+
const mergedGlobal = mergeCursorHooksFile(globalHooks, process.platform, globalPaths.hooksDir, repoRoot);
|
|
99
|
+
await writeFile(globalPaths.hooksSettingsPath, `${JSON.stringify(mergedGlobal, null, 2)}\n`, 'utf8');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
90
103
|
if (options.withSkill) {
|
|
91
104
|
await writeSkillArtifacts('cursor', paths);
|
|
92
105
|
}
|
|
106
|
+
const installedConfig = await applyInstallScope(repoRoot, 'cursor', scope, config);
|
|
107
|
+
if (scope === 'global') {
|
|
108
|
+
await cleanupStaleProjectCursorInstall(repoRoot);
|
|
109
|
+
}
|
|
93
110
|
await writeIntegrityManifest(repoRoot, cursorLayout, runtimeIntegrityFiles(cursorLayout, paths));
|
|
94
|
-
await archiveLegacyAuditLogIfNeeded(repoRoot,
|
|
111
|
+
await archiveLegacyAuditLogIfNeeded(repoRoot, installedConfig);
|
|
95
112
|
return { repoRoot };
|
|
96
113
|
}
|
|
114
|
+
const BELAY_HOOK_ARTIFACTS = [
|
|
115
|
+
'belay-before-submit.mjs',
|
|
116
|
+
'belay-shell-gate.mjs',
|
|
117
|
+
'belay-tool-gate.mjs',
|
|
118
|
+
'belay-audit.mjs',
|
|
119
|
+
'belay-runner',
|
|
120
|
+
'belay-runner.cmd',
|
|
121
|
+
'belay-runner.ps1',
|
|
122
|
+
];
|
|
123
|
+
async function removeBelayHookArtifacts(paths) {
|
|
124
|
+
for (const fileName of BELAY_HOOK_ARTIFACTS) {
|
|
125
|
+
await rm(path.join(paths.hooksDir, fileName), { force: true });
|
|
126
|
+
}
|
|
127
|
+
for (const fileName of ['core.mjs', 'dispatcher.mjs']) {
|
|
128
|
+
await rm(path.join(paths.runtimeDir, fileName), { force: true });
|
|
129
|
+
}
|
|
130
|
+
await rm(path.join(paths.skillsDir, 'SKILL.md'), { force: true });
|
|
131
|
+
if (paths.commandsDir) {
|
|
132
|
+
for (const fileName of CURSOR_COMMAND_ARTIFACTS) {
|
|
133
|
+
await rm(path.join(paths.commandsDir, fileName), { force: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function cleanupStaleProjectCursorInstall(repoRoot) {
|
|
138
|
+
const projectPaths = resolveScopedPaths(cursorLayout, 'project', repoRoot);
|
|
139
|
+
if (!existsSync(projectPaths.hooksSettingsPath)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const projectHooks = await loadHooksFile(projectPaths.hooksSettingsPath);
|
|
143
|
+
if (!hasManagedCursorHookEntries(projectHooks, process.platform, projectPaths.hooksDir, repoRoot)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const stripped = stripCursorHooksFile(projectHooks, process.platform, projectPaths.hooksDir, repoRoot);
|
|
147
|
+
await writeFile(projectPaths.hooksSettingsPath, `${JSON.stringify(stripped, null, 2)}\n`, 'utf8');
|
|
148
|
+
await removeBelayHookArtifacts(projectPaths);
|
|
149
|
+
}
|
|
150
|
+
export async function uninstallCursorProject(options = {}) {
|
|
151
|
+
const repoRoot = path.resolve(options.targetDir ?? process.cwd());
|
|
152
|
+
const scope = await resolveOperationScope(repoRoot, 'cursor', options);
|
|
153
|
+
const paths = resolveScopedPaths(cursorLayout, scope, repoRoot);
|
|
154
|
+
const hooksSettingsExisted = existsSync(paths.hooksSettingsPath);
|
|
155
|
+
const hooksFile = await loadHooksFile(paths.hooksSettingsPath);
|
|
156
|
+
const stripped = stripCursorHooksFile(hooksFile, process.platform, paths.hooksDir, repoRoot);
|
|
157
|
+
if (hooksSettingsExisted) {
|
|
158
|
+
await mkdir(path.dirname(paths.hooksSettingsPath), { recursive: true });
|
|
159
|
+
await writeFile(paths.hooksSettingsPath, `${JSON.stringify(stripped, null, 2)}\n`, 'utf8');
|
|
160
|
+
}
|
|
161
|
+
await removeBelayHookArtifacts(paths);
|
|
162
|
+
return { repoRoot, scope };
|
|
163
|
+
}
|
|
97
164
|
function resolveAdapterName(options, repoRoot) {
|
|
98
165
|
if (options.adapter === 'claude') {
|
|
99
166
|
return 'claude';
|
|
@@ -225,3 +292,12 @@ export async function upgradeProject(options = {}) {
|
|
|
225
292
|
await refreshIntegrityManifest(repoRoot, adapterName);
|
|
226
293
|
return { repoRoot, adapter: adapterName };
|
|
227
294
|
}
|
|
295
|
+
export async function uninstallProject(options = {}) {
|
|
296
|
+
const repoRoot = path.resolve(options.targetDir ?? process.cwd());
|
|
297
|
+
const adapterName = resolveAdapterName(options, repoRoot);
|
|
298
|
+
if (adapterName !== 'cursor') {
|
|
299
|
+
throw new Error(`belay uninstall is only supported for Cursor adapter today. Use --adapter cursor or uninstall hooks manually for ${adapterName}.`);
|
|
300
|
+
}
|
|
301
|
+
const result = await uninstallCursorProject({ ...options, targetDir: repoRoot });
|
|
302
|
+
return { repoRoot: result.repoRoot, adapter: adapterName, scope: result.scope };
|
|
303
|
+
}
|
|
@@ -6,3 +6,4 @@ export interface NodeResolutionResult {
|
|
|
6
6
|
export declare function resolveNodeBinary(): NodeResolutionResult;
|
|
7
7
|
export declare function buildRunnerScript(defaultNodePath: string): string;
|
|
8
8
|
export declare function buildWindowsRunnerScript(defaultNodePath: string): string;
|
|
9
|
+
export declare function buildWindowsPowerShellRunnerScript(defaultNodePath: string): string;
|
package/dist/node-resolution.js
CHANGED
|
@@ -235,3 +235,64 @@ echo {}
|
|
|
235
235
|
exit /b 0
|
|
236
236
|
`;
|
|
237
237
|
}
|
|
238
|
+
export function buildWindowsPowerShellRunnerScript(defaultNodePath) {
|
|
239
|
+
const encodedDefault = Buffer.from(defaultNodePath, 'utf8').toString('base64');
|
|
240
|
+
return `$ErrorActionPreference = 'Stop'
|
|
241
|
+
|
|
242
|
+
$DefaultNode = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encodedDefault}'))
|
|
243
|
+
$HookName = if ($args.Count -gt 0) { [string] $args[0] } else { '' }
|
|
244
|
+
$HookArguments = if ($args.Count -gt 1) { @($args[1..($args.Count - 1)]) } else { @() }
|
|
245
|
+
|
|
246
|
+
function Resolve-Node {
|
|
247
|
+
if ($DefaultNode -and (Test-Path -LiteralPath $DefaultNode -PathType Leaf)) {
|
|
248
|
+
return $DefaultNode
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
$PathNode = Get-Command node.exe -CommandType Application -ErrorAction SilentlyContinue
|
|
252
|
+
if ($null -ne $PathNode) {
|
|
253
|
+
return $PathNode.Source
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if ($env:USERPROFILE) {
|
|
257
|
+
$NvmRoot = Join-Path $env:USERPROFILE '.nvm'
|
|
258
|
+
if (Test-Path -LiteralPath $NvmRoot -PathType Container) {
|
|
259
|
+
foreach ($VersionDirectory in Get-ChildItem -LiteralPath $NvmRoot -Directory | Sort-Object Name -Descending) {
|
|
260
|
+
$Candidate = Join-Path $VersionDirectory.FullName 'node.exe'
|
|
261
|
+
if (Test-Path -LiteralPath $Candidate -PathType Leaf) {
|
|
262
|
+
return $Candidate
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return $null
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function Fail-Audit {
|
|
272
|
+
[Console]::Error.WriteLine('belay: unable to resolve Node for audit hook')
|
|
273
|
+
[Console]::Out.WriteLine('{}')
|
|
274
|
+
exit 0
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (-not $HookName) {
|
|
278
|
+
Fail-Audit
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
$NodeBin = Resolve-Node
|
|
282
|
+
if (-not $NodeBin) {
|
|
283
|
+
if ($HookName -eq 'belay-before-submit') {
|
|
284
|
+
[Console]::Out.WriteLine('{"continue":false,"user_message":"belay could not resolve Node. Install or expose Node, run belay doctor, then retry."}')
|
|
285
|
+
exit 0
|
|
286
|
+
}
|
|
287
|
+
if ($HookName -in @('belay-shell-gate', 'belay-tool-gate')) {
|
|
288
|
+
[Console]::Out.WriteLine('{"permission":"deny","user_message":"belay could not resolve Node. Install or expose Node, run belay doctor, then retry."}')
|
|
289
|
+
exit 0
|
|
290
|
+
}
|
|
291
|
+
Fail-Audit
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
$ScriptPath = Join-Path $PSScriptRoot "$HookName.mjs"
|
|
295
|
+
& $NodeBin $ScriptPath @HookArguments
|
|
296
|
+
exit $LASTEXITCODE
|
|
297
|
+
`;
|
|
298
|
+
}
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { CursorHookOrigin } from './adapters/cursor/hook-router.js';
|
|
2
|
+
import type { AdapterName } from './adapters/layouts/types.js';
|
|
1
3
|
import type { BelayConfigV3 } from './core/config.js';
|
|
2
4
|
export declare function renderConfig(config: BelayConfigV3): string;
|
|
3
|
-
export declare function renderBeforeSubmitHook(): string;
|
|
4
|
-
export declare function renderShellGateHook(): string;
|
|
5
|
-
export declare function renderToolGateHook(): string;
|
|
6
|
-
export declare function renderAuditHook(): string;
|
|
5
|
+
export declare function renderBeforeSubmitHook(adapter: AdapterName, cursorOrigin?: CursorHookOrigin): string;
|
|
6
|
+
export declare function renderShellGateHook(adapter: AdapterName, cursorOrigin?: CursorHookOrigin): string;
|
|
7
|
+
export declare function renderToolGateHook(adapter: AdapterName, cursorOrigin?: CursorHookOrigin): string;
|
|
8
|
+
export declare function renderAuditHook(adapter: AdapterName, cursorOrigin?: CursorHookOrigin): string;
|
|
9
|
+
export declare function renderCursorDispatcher(): Promise<string>;
|
|
7
10
|
export declare function renderRuntimeCore(adapter?: 'cursor' | 'claude' | 'codex'): Promise<string>;
|
package/dist/templates.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { CURSOR_DISPATCHER_GENERATION_HEADER } from './adapters/cursor/dispatcher-generation.js';
|
|
4
5
|
import { hashValue } from './core/fingerprint.js';
|
|
5
6
|
import { PACKAGE_VERSION } from './version.js';
|
|
6
7
|
function inlineJson(value) {
|
|
@@ -9,26 +10,54 @@ function inlineJson(value) {
|
|
|
9
10
|
export function renderConfig(config) {
|
|
10
11
|
return `${inlineJson(config)}\n`;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
function requireCursorOrigin(origin) {
|
|
14
|
+
if (!origin) {
|
|
15
|
+
throw new Error('Cursor hook origin is required when rendering dispatcher shims.');
|
|
16
|
+
}
|
|
17
|
+
return origin;
|
|
18
|
+
}
|
|
19
|
+
function renderCursorDispatchHook(origin, kind, eventName) {
|
|
20
|
+
return `import { dispatchCursorHook } from '../belay/runtime/dispatcher.mjs'
|
|
21
|
+
|
|
22
|
+
await dispatchCursorHook({
|
|
23
|
+
origin: ${JSON.stringify(origin)},
|
|
24
|
+
kind: ${JSON.stringify(kind)},
|
|
25
|
+
eventName: ${eventName},
|
|
26
|
+
})
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
export function renderBeforeSubmitHook(adapter, cursorOrigin) {
|
|
30
|
+
if (adapter === 'cursor') {
|
|
31
|
+
return renderCursorDispatchHook(requireCursorOrigin(cursorOrigin), 'before-submit', JSON.stringify('beforeSubmitPrompt'));
|
|
32
|
+
}
|
|
13
33
|
return `import { runBeforeSubmitPromptHook } from '../belay/runtime/core.mjs'
|
|
14
34
|
|
|
15
35
|
await runBeforeSubmitPromptHook()
|
|
16
36
|
`;
|
|
17
37
|
}
|
|
18
|
-
export function renderShellGateHook() {
|
|
38
|
+
export function renderShellGateHook(adapter, cursorOrigin) {
|
|
39
|
+
if (adapter === 'cursor') {
|
|
40
|
+
return renderCursorDispatchHook(requireCursorOrigin(cursorOrigin), 'shell-gate', JSON.stringify('beforeShellExecution'));
|
|
41
|
+
}
|
|
19
42
|
return `import { runShellGateHook } from '../belay/runtime/core.mjs'
|
|
20
43
|
|
|
21
44
|
await runShellGateHook()
|
|
22
45
|
`;
|
|
23
46
|
}
|
|
24
|
-
export function renderToolGateHook() {
|
|
47
|
+
export function renderToolGateHook(adapter, cursorOrigin) {
|
|
48
|
+
if (adapter === 'cursor') {
|
|
49
|
+
return renderCursorDispatchHook(requireCursorOrigin(cursorOrigin), 'tool-gate', "process.argv[2] ?? 'preToolUse'");
|
|
50
|
+
}
|
|
25
51
|
return `import { runToolGateHook } from '../belay/runtime/core.mjs'
|
|
26
52
|
|
|
27
53
|
const eventName = process.argv[2] ?? 'preToolUse'
|
|
28
54
|
await runToolGateHook(eventName)
|
|
29
55
|
`;
|
|
30
56
|
}
|
|
31
|
-
export function renderAuditHook() {
|
|
57
|
+
export function renderAuditHook(adapter, cursorOrigin) {
|
|
58
|
+
if (adapter === 'cursor') {
|
|
59
|
+
return renderCursorDispatchHook(requireCursorOrigin(cursorOrigin), 'audit', "process.argv[2] ?? 'postToolUse'");
|
|
60
|
+
}
|
|
32
61
|
return `import { runAuditHook } from '../belay/runtime/core.mjs'
|
|
33
62
|
|
|
34
63
|
const eventName = process.argv[2] ?? 'postToolUse'
|
|
@@ -44,6 +73,15 @@ async function readRuntimeBundle(adapter = 'cursor') {
|
|
|
44
73
|
throw new Error('Runtime bundle missing. Run pnpm build before belay init or upgrade.');
|
|
45
74
|
}
|
|
46
75
|
}
|
|
76
|
+
export async function renderCursorDispatcher() {
|
|
77
|
+
const bundlePath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'bundle', 'cursor-dispatcher.mjs');
|
|
78
|
+
try {
|
|
79
|
+
return `${CURSOR_DISPATCHER_GENERATION_HEADER}${await readFile(bundlePath, 'utf8')}`;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
throw new Error('Runtime bundle missing. Run pnpm build before belay init or upgrade.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
47
85
|
export async function renderRuntimeCore(adapter = 'cursor') {
|
|
48
86
|
const bundle = await readRuntimeBundle(adapter);
|
|
49
87
|
const withoutStamp = bundle
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export type { BelayConfig, BelayConfigV1, BelayConfigV2, BelayConfigV3, BelayControlPlaneConfig, BelayEgressConfig, BelayOverridesConfig, BelayPolicyConfig, BelayRedactionConfig, BelaySandboxConfig, BelayTransactionalConfig, UnknownLocalEffectPolicy, } from './core/config.js';
|
|
2
2
|
export type { ApprovalRecord, ApprovalStateFile, Assessment, BelayMode, ClassifyResult, HookVerdict, } from './core/types.js';
|
|
3
3
|
import type { InstallScope } from './adapters/layouts/scope.js';
|
|
4
|
-
import type { RecentAskEntry } from './core/audit-summary.js';
|
|
4
|
+
import type { RecentAskEntry, RecentHostDenialEntry } from './core/audit-summary.js';
|
|
5
5
|
import type { BelayEgressConfig, BelayOverridesConfig, BelayPolicyConfig, BelaySandboxConfig } from './core/config.js';
|
|
6
6
|
import type { ApprovalRecord, ClassifyResult } from './core/types.js';
|
|
7
7
|
export interface HookEntry {
|
|
8
8
|
command: string;
|
|
9
9
|
matcher?: string;
|
|
10
|
+
failClosed?: boolean;
|
|
10
11
|
}
|
|
11
12
|
export interface HooksFile {
|
|
12
13
|
version: number;
|
|
@@ -42,6 +43,32 @@ export interface UpgradeOptions {
|
|
|
42
43
|
/** Opt-in: migrate implicit factory-default ollama judge to host default provider. */
|
|
43
44
|
migrateJudgeDefault?: boolean;
|
|
44
45
|
}
|
|
46
|
+
export interface UninstallOptions {
|
|
47
|
+
targetDir?: string;
|
|
48
|
+
adapter?: AdapterName;
|
|
49
|
+
scope?: InstallScope;
|
|
50
|
+
}
|
|
51
|
+
export interface WhereOptions {
|
|
52
|
+
targetDir?: string;
|
|
53
|
+
adapter?: AdapterName;
|
|
54
|
+
scope?: InstallScope;
|
|
55
|
+
json?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface WhereReport {
|
|
58
|
+
cwd: string;
|
|
59
|
+
repoRoot: string;
|
|
60
|
+
adapter: AdapterName;
|
|
61
|
+
installScope: 'project' | 'global';
|
|
62
|
+
configPresent: boolean;
|
|
63
|
+
cliExecutable?: string;
|
|
64
|
+
cliPackageRoot: string;
|
|
65
|
+
configPath: string;
|
|
66
|
+
hooksSettingsPath: string;
|
|
67
|
+
hooksDir: string;
|
|
68
|
+
runtimeDir: string;
|
|
69
|
+
skillsDir: string;
|
|
70
|
+
commandsDir?: string;
|
|
71
|
+
}
|
|
45
72
|
export interface DoctorOptions {
|
|
46
73
|
targetDir?: string;
|
|
47
74
|
fix?: boolean;
|
|
@@ -88,6 +115,10 @@ export interface StatusOptions {
|
|
|
88
115
|
export interface HealthSnapshotOptions {
|
|
89
116
|
targetDir?: string;
|
|
90
117
|
adapter?: AdapterName;
|
|
118
|
+
/** Override the host home directory when inspecting adapter-level execution settings. */
|
|
119
|
+
homeDir?: string;
|
|
120
|
+
/** Override Cursor config-path environment variables for deterministic host inspection. */
|
|
121
|
+
cursorConfigEnv?: Partial<Pick<NodeJS.ProcessEnv, 'CURSOR_CONFIG_DIR' | 'XDG_CONFIG_HOME'>>;
|
|
91
122
|
}
|
|
92
123
|
export interface HealthSnapshot {
|
|
93
124
|
repoRoot: string;
|
|
@@ -172,6 +203,9 @@ export interface AuditVisibilityReport {
|
|
|
172
203
|
allowCount: number;
|
|
173
204
|
silentPassRate: number;
|
|
174
205
|
recentAsks: RecentAskEntry[];
|
|
206
|
+
hostDeniedAfterAllowCount?: number;
|
|
207
|
+
recentHostDenials?: RecentHostDenialEntry[];
|
|
208
|
+
unrecognizedHostFailureCount?: number;
|
|
175
209
|
warnings: string[];
|
|
176
210
|
notes: string[];
|
|
177
211
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "0.9.
|
|
1
|
+
export declare const PACKAGE_VERSION = "0.9.4";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/sync-version.mjs — do not edit.
|
|
2
|
-
export const PACKAGE_VERSION = '0.9.
|
|
2
|
+
export const PACKAGE_VERSION = '0.9.4';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guilz-dev/belay",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Belay-style approval and audit gating for agent runtimes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,10 +57,13 @@
|
|
|
57
57
|
"typecheck": "tsc --noEmit",
|
|
58
58
|
"test": "pnpm build && vitest run",
|
|
59
59
|
"test:structural": "pnpm build && vitest run src/__tests__/verdict/structural-suite.test.ts",
|
|
60
|
-
"test:docker": "pnpm build && vitest run src/__tests__/capability/boundary-container-isolation.test.ts src/__tests__/capability/boundary-driver-container.test.ts",
|
|
60
|
+
"test:docker": "pnpm build && vitest run --config vitest.live.config.ts src/__tests__/capability/boundary-container-isolation.test.ts src/__tests__/capability/boundary-container-workspace-mount.test.ts src/__tests__/capability/boundary-driver-container.test.ts src/__tests__/contained-execution-docker.integration.test.ts",
|
|
61
|
+
"test:llm": "pnpm build && vitest run --config vitest.live.config.ts src/__tests__/verdict/llm/judge-accuracy.test.ts",
|
|
61
62
|
"test:stable": "pnpm build && vitest run && vitest run && vitest run",
|
|
62
63
|
"corpus": "pnpm build && node scripts/corpus.mjs",
|
|
63
64
|
"probe:adversarial": "pnpm build && node scripts/adversarial-probe.mjs",
|
|
65
|
+
"probe:cursor-shell-rewrite": "node scripts/cursor-shell-rewrite-probe.mjs",
|
|
66
|
+
"probe:native-seatbelt-boundary": "node scripts/native-seatbelt-boundary-probe.mjs --live",
|
|
64
67
|
"probe:coverage": "pnpm build && node scripts/coverage-probe.mjs",
|
|
65
68
|
"probe:coverage:repeat": "pnpm build && node scripts/coverage-probe.mjs --repeat 3",
|
|
66
69
|
"corpus:ratchet": "pnpm build && node scripts/corpus-ratchet.mjs"
|
package/skills/belay/SKILL.md
CHANGED
|
@@ -54,6 +54,11 @@ To restore the legacy two-step UX (approve, then always retry manually), set
|
|
|
54
54
|
For why it was blocked, use `/belay why <command>` or `belay explain --command "<command>"`.
|
|
55
55
|
For the latest pending ask, use `/belay explain` or `belay explain`.
|
|
56
56
|
|
|
57
|
+
If `belay status` or `belay report` shows `Host denied after Belay allow`, the denial came from
|
|
58
|
+
the editor/agent host after Belay returned allow. There is no Belay approval ID to approve. Keep
|
|
59
|
+
host protection in Belay audit mode and approve only the exact host prompt when execution is
|
|
60
|
+
intended.
|
|
61
|
+
|
|
57
62
|
**Do not use command allowlists** (`overrides.allow`) or legacy standing-allow records
|
|
58
63
|
(shell, tool, or subagent) to fix blocks; none of them change runtime authorization.
|
|
59
64
|
Improve EffectPlan semantics, approve once with `/belay-approve`, or use an exact
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
Show audit visibility: ask/flag/allow counts, silent-pass rate,
|
|
1
|
+
Show audit visibility: ask/flag/allow counts, silent-pass rate, recent asks, and host
|
|
2
|
+
`permission_denied` failures correlated to a preceding Belay allow.
|
|
2
3
|
|
|
3
4
|
```bash
|
|
4
5
|
belay report
|
|
5
6
|
```
|
|
6
7
|
|
|
7
8
|
For full install health plus audit summary, use `belay status`.
|
|
9
|
+
|
|
10
|
+
`Host denied after Belay allow` is host-policy telemetry, not a pending Belay approval.
|