@cluesmith/codev 3.2.0 → 3.2.2
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/dashboard-dist/assets/{index-DM7OU-EI.js → index-BZ1x7x1X.js} +34 -34
- package/dashboard-dist/assets/index-BZ1x7x1X.js.map +1 -0
- package/dashboard-dist/index.html +1 -1
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +17 -0
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/architect.d.ts.map +1 -1
- package/dist/agent-farm/commands/architect.js +6 -17
- package/dist/agent-farm/commands/architect.js.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +3 -2
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/db.d.ts +10 -0
- package/dist/agent-farm/commands/db.d.ts.map +1 -1
- package/dist/agent-farm/commands/db.js +77 -0
- package/dist/agent-farm/commands/db.js.map +1 -1
- package/dist/agent-farm/commands/send.d.ts +40 -12
- package/dist/agent-farm/commands/send.d.ts.map +1 -1
- package/dist/agent-farm/commands/send.js +101 -34
- package/dist/agent-farm/commands/send.js.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.d.ts +12 -7
- package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.js +40 -19
- package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts +15 -6
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +24 -17
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/stop.js +2 -2
- package/dist/agent-farm/commands/stop.js.map +1 -1
- package/dist/agent-farm/commands/tower.d.ts +1 -0
- package/dist/agent-farm/commands/tower.d.ts.map +1 -1
- package/dist/agent-farm/commands/tower.js +50 -0
- package/dist/agent-farm/commands/tower.js.map +1 -1
- package/dist/agent-farm/db/consolidate.d.ts +66 -0
- package/dist/agent-farm/db/consolidate.d.ts.map +1 -0
- package/dist/agent-farm/db/consolidate.js +406 -0
- package/dist/agent-farm/db/consolidate.js.map +1 -0
- package/dist/agent-farm/db/index.d.ts +13 -4
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +100 -470
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/schema.d.ts +11 -4
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +91 -2
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +2 -0
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js +1 -0
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/lib/builder-lookup.d.ts.map +1 -1
- package/dist/agent-farm/lib/builder-lookup.js +9 -3
- package/dist/agent-farm/lib/builder-lookup.js.map +1 -1
- package/dist/agent-farm/servers/codev-config-watcher.d.ts +37 -0
- package/dist/agent-farm/servers/codev-config-watcher.d.ts.map +1 -0
- package/dist/agent-farm/servers/{worktree-config-watcher.js → codev-config-watcher.js} +15 -14
- package/dist/agent-farm/servers/codev-config-watcher.js.map +1 -0
- package/dist/agent-farm/servers/command-relay.d.ts +54 -0
- package/dist/agent-farm/servers/command-relay.d.ts.map +1 -0
- package/dist/agent-farm/servers/command-relay.js +94 -0
- package/dist/agent-farm/servers/command-relay.js.map +1 -0
- package/dist/agent-farm/servers/overview.d.ts.map +1 -1
- package/dist/agent-farm/servers/overview.js +12 -6
- package/dist/agent-farm/servers/overview.js.map +1 -1
- package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-instances.js +65 -55
- package/dist/agent-farm/servers/tower-instances.js.map +1 -1
- package/dist/agent-farm/servers/tower-messages.d.ts +9 -0
- package/dist/agent-farm/servers/tower-messages.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-messages.js +24 -0
- package/dist/agent-farm/servers/tower-messages.js.map +1 -1
- package/dist/agent-farm/servers/tower-routes.d.ts +1 -1
- package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-routes.js +111 -35
- package/dist/agent-farm/servers/tower-routes.js.map +1 -1
- package/dist/agent-farm/servers/tower-server.js +41 -27
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.js +22 -5
- package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
- package/dist/agent-farm/servers/tower-types.d.ts +2 -0
- package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-utils.d.ts +50 -5
- package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-utils.js +64 -14
- package/dist/agent-farm/servers/tower-utils.js.map +1 -1
- package/dist/agent-farm/state.d.ts +34 -21
- package/dist/agent-farm/state.d.ts.map +1 -1
- package/dist/agent-farm/state.js +125 -79
- package/dist/agent-farm/state.js.map +1 -1
- package/dist/agent-farm/types.d.ts +14 -0
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/architect-name.d.ts +6 -33
- package/dist/agent-farm/utils/architect-name.d.ts.map +1 -1
- package/dist/agent-farm/utils/architect-name.js +7 -62
- package/dist/agent-farm/utils/architect-name.js.map +1 -1
- package/dist/agent-farm/utils/config.d.ts +24 -1
- package/dist/agent-farm/utils/config.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.js +59 -3
- package/dist/agent-farm/utils/config.js.map +1 -1
- package/dist/agent-farm/utils/harness.d.ts +45 -2
- package/dist/agent-farm/utils/harness.d.ts.map +1 -1
- package/dist/agent-farm/utils/harness.js +21 -0
- package/dist/agent-farm/utils/harness.js.map +1 -1
- package/dist/agent-farm/utils/workspace-path.d.ts +16 -0
- package/dist/agent-farm/utils/workspace-path.d.ts.map +1 -0
- package/dist/agent-farm/utils/workspace-path.js +26 -0
- package/dist/agent-farm/utils/workspace-path.js.map +1 -0
- package/dist/agent-farm/utils/worktree-write-guard.d.ts +56 -0
- package/dist/agent-farm/utils/worktree-write-guard.d.ts.map +1 -0
- package/dist/agent-farm/utils/worktree-write-guard.js +222 -0
- package/dist/agent-farm/utils/worktree-write-guard.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/consult/index.d.ts +50 -2
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +125 -7
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +25 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/porch/verdict.js +2 -2
- package/dist/commands/porch/verdict.js.map +1 -1
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/forge-contracts.d.ts +8 -0
- package/dist/lib/forge-contracts.d.ts.map +1 -1
- package/dist/lib/forge-contracts.js.map +1 -1
- package/dist/lib/managed-block.d.ts +8 -2
- package/dist/lib/managed-block.d.ts.map +1 -1
- package/dist/lib/managed-block.js +32 -7
- package/dist/lib/managed-block.js.map +1 -1
- package/package.json +3 -3
- package/scripts/forge/gitea/issue-view.sh +4 -1
- package/scripts/forge/github/issue-view.sh +2 -2
- package/scripts/forge/gitlab/issue-view.sh +4 -1
- package/scripts/forge/linear/issue-view.sh +3 -2
- package/skeleton/resources/commands/consult.md +14 -0
- package/skeleton/roles/builder.md +21 -0
- package/skeleton/templates/AGENTS.md +16 -0
- package/skeleton/templates/CLAUDE.md +16 -0
- package/dashboard-dist/assets/index-DM7OU-EI.js.map +0 -1
- package/dist/agent-farm/db/migrate.d.ts +0 -20
- package/dist/agent-farm/db/migrate.d.ts.map +0 -1
- package/dist/agent-farm/db/migrate.js +0 -107
- package/dist/agent-farm/db/migrate.js.map +0 -1
- package/dist/agent-farm/servers/worktree-config-watcher.d.ts +0 -36
- package/dist/agent-farm/servers/worktree-config-watcher.d.ts.map +0 -1
- package/dist/agent-farm/servers/worktree-config-watcher.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/config.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,MAAM,EAAc,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIxE,OAAO,EAAkB,KAAK,eAAe,EAA4B,MAAM,cAAc,CAAC;AAC9F,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,qBAAqB,EAA+B,MAAM,wBAAwB,CAAC;AAKzI,YAAY,EAAE,sBAAsB,EAAE,cAAc,EAAE,CAAC;AAevD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBlE;AAED;;;;;;;;;;GAUG;AACH,iBAAS,iBAAiB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAuCnE;AA8GD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAE1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAa5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,eAAe,CAS3E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,eAAe,CASzE;AAOD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,sBAAsB,CAUhF;AAqBD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAW9E;AAgBD;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAelC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrE;AAGD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,IAAI,kBAAkB,EAAE,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration management for Agent Farm
|
|
3
3
|
*/
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
5
6
|
import { resolve, dirname } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import { execSync } from 'node:child_process';
|
|
@@ -200,6 +201,7 @@ export function getResolvedCommands(workspaceRoot) {
|
|
|
200
201
|
const userConfig = loadUserConfig(root);
|
|
201
202
|
return {
|
|
202
203
|
architect: cliOverrides.architect ||
|
|
204
|
+
process.env.TOWER_ARCHITECT_CMD ||
|
|
203
205
|
resolveCommand(userConfig?.shell?.architect, DEFAULT_COMMANDS.architect),
|
|
204
206
|
builder: cliOverrides.builder ||
|
|
205
207
|
resolveCommand(userConfig?.shell?.builder, DEFAULT_COMMANDS.builder),
|
|
@@ -210,21 +212,32 @@ export function getResolvedCommands(workspaceRoot) {
|
|
|
210
212
|
/**
|
|
211
213
|
* Get the resolved harness provider for the architect shell.
|
|
212
214
|
* Resolution: explicit architectHarness → auto-detect from architect command → default claude.
|
|
215
|
+
*
|
|
216
|
+
* The command auto-detect source is the *override-aware* resolved command
|
|
217
|
+
* (getResolvedCommands → cliOverrides / TOWER_ARCHITECT_CMD / config), not the
|
|
218
|
+
* raw config value. Without this, `--architect-cmd gemini` / `TOWER_ARCHITECT_CMD=gemini`
|
|
219
|
+
* with no matching harness config would launch gemini but resolve the claude
|
|
220
|
+
* harness, re-arming the #929 resume crash-loop (claude buildResume injecting
|
|
221
|
+
* `--resume <stale-claude-uuid>` into a non-claude command).
|
|
213
222
|
*/
|
|
214
223
|
export function getArchitectHarness(workspaceRoot) {
|
|
215
224
|
const root = workspaceRoot || findWorkspaceRoot();
|
|
216
225
|
const userConfig = loadUserConfig(root);
|
|
217
|
-
const architectCmd =
|
|
226
|
+
const architectCmd = getResolvedCommands(root).architect;
|
|
218
227
|
return resolveHarness(userConfig?.shell?.architectHarness, userConfig?.harness, architectCmd);
|
|
219
228
|
}
|
|
220
229
|
/**
|
|
221
230
|
* Get the resolved harness provider for the builder shell.
|
|
222
231
|
* Resolution: explicit builderHarness → auto-detect from builder command → default claude.
|
|
232
|
+
*
|
|
233
|
+
* Like getArchitectHarness, the command auto-detect source is the override-aware
|
|
234
|
+
* resolved command (so `--builder-cmd gemini` with no matching harness config
|
|
235
|
+
* resolves the gemini harness, not claude — see #929).
|
|
223
236
|
*/
|
|
224
237
|
export function getBuilderHarness(workspaceRoot) {
|
|
225
238
|
const root = workspaceRoot || findWorkspaceRoot();
|
|
226
239
|
const userConfig = loadUserConfig(root);
|
|
227
|
-
const builderCmd =
|
|
240
|
+
const builderCmd = getResolvedCommands(root).builder;
|
|
228
241
|
return resolveHarness(userConfig?.shell?.builderHarness, userConfig?.harness, builderCmd);
|
|
229
242
|
}
|
|
230
243
|
// ResolvedWorktreeConfig + WorktreeDevUrl now live in
|
|
@@ -248,6 +261,49 @@ export function getWorktreeConfig(workspaceRoot) {
|
|
|
248
261
|
devUrls: resolveDevUrls(w),
|
|
249
262
|
};
|
|
250
263
|
}
|
|
264
|
+
const ACTIVITY_EVENTS = new Set(['window-focus', 'builder-active']);
|
|
265
|
+
/**
|
|
266
|
+
* Read the `activityHooks` array from one config file. Returns `undefined` when the
|
|
267
|
+
* file is missing/invalid or doesn't define the key — so a present-but-empty list in
|
|
268
|
+
* a higher layer can still REPLACE a lower one (array-replace, matching deepMerge).
|
|
269
|
+
*/
|
|
270
|
+
function readActivityHooksLayer(configPath) {
|
|
271
|
+
try {
|
|
272
|
+
const parsed = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
273
|
+
if (!('activityHooks' in parsed)) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
return Array.isArray(parsed.activityHooks) ? parsed.activityHooks : [];
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return undefined; // absent / unreadable / invalid JSON → layer not present
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Resolved `activityHooks` for a workspace.
|
|
284
|
+
*
|
|
285
|
+
* SECURITY: hooks EXECUTE (the VSCode extension opens their url), so they are
|
|
286
|
+
* resolved ONLY from the user's trusted personal config layers — `~/.codev/config.json`
|
|
287
|
+
* (global, across all repos) and `<root>/.codev/config.local.json` (per-engineer,
|
|
288
|
+
* gitignored) — and NEVER from the committed `.codev/config.json`, which a cloned repo
|
|
289
|
+
* controls (a committed hook would be a zero-click RCE). The project-local layer
|
|
290
|
+
* replaces the global one when present. Malformed entries (no url, or no valid `on`
|
|
291
|
+
* event) are dropped.
|
|
292
|
+
*/
|
|
293
|
+
export function getActivityHooks(workspaceRoot) {
|
|
294
|
+
const root = workspaceRoot || findWorkspaceRoot();
|
|
295
|
+
const local = readActivityHooksLayer(resolve(root, '.codev', 'config.local.json'));
|
|
296
|
+
const global = readActivityHooksLayer(resolve(homedir(), '.codev', 'config.json'));
|
|
297
|
+
const raw = local ?? global ?? [];
|
|
298
|
+
const hooks = raw.flatMap((h) => {
|
|
299
|
+
const on = (h?.on ?? []).filter((e) => ACTIVITY_EVENTS.has(e));
|
|
300
|
+
if (!h?.url || on.length === 0) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
return [{ on, url: h.url, background: h.background ?? false }];
|
|
304
|
+
});
|
|
305
|
+
return { hooks };
|
|
306
|
+
}
|
|
251
307
|
/**
|
|
252
308
|
* Filter malformed entries (missing/empty `label` or `url`). Both
|
|
253
309
|
* fields are mandatory by schema — no default-label fallback.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,12 +32,55 @@ export interface HarnessProvider {
|
|
|
32
32
|
/**
|
|
33
33
|
* Optional: files to write in the worktree before launching the agent.
|
|
34
34
|
* Used by harnesses that rely on file-based configuration (e.g., OpenCode
|
|
35
|
-
* uses opencode.json's instructions field for role injection
|
|
35
|
+
* uses opencode.json's instructions field for role injection; Claude uses it
|
|
36
|
+
* to install the worktree write-guard hook — Issue #1018).
|
|
37
|
+
*
|
|
38
|
+
* `worktreePath` is the absolute path to the builder's worktree, needed by
|
|
39
|
+
* harnesses that bake worktree-specific values into generated files.
|
|
36
40
|
*/
|
|
37
|
-
getWorktreeFiles?(roleContent: string, roleFilePath: string): Array<{
|
|
41
|
+
getWorktreeFiles?(roleContent: string, roleFilePath: string, worktreePath: string): Array<{
|
|
38
42
|
relativePath: string;
|
|
39
43
|
content: string;
|
|
40
44
|
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional: conversation-session support, for agents whose CLI can pin and
|
|
47
|
+
* resume a session by id (Issue #832). Harnesses that omit this are treated as
|
|
48
|
+
* having no resumable sessions — architects on those agents always spawn fresh
|
|
49
|
+
* and nothing is persisted. Keeps agent-specific session flags out of Tower.
|
|
50
|
+
*
|
|
51
|
+
* This is the stored-UUID mechanism (architect resume): an id is minted at spawn,
|
|
52
|
+
* pinned via `newSessionArgs`, persisted on the architect row, and replayed via
|
|
53
|
+
* `resumeArgs`. It disambiguates siblings sharing one cwd, which `buildResume`
|
|
54
|
+
* (mtime discovery) cannot. The two coexist: `buildResume` serves builder resume
|
|
55
|
+
* and the legacy sole-architect fallback; `session` serves architect stored-UUID resume.
|
|
56
|
+
*/
|
|
57
|
+
session?: {
|
|
58
|
+
/** Args to START a new session pinned to `sessionId` (caller merges role injection). */
|
|
59
|
+
newSessionArgs(sessionId: string): string[];
|
|
60
|
+
/** Args to RESUME an existing session by id (caller skips role injection). */
|
|
61
|
+
resumeArgs(sessionId: string): string[];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Optional: discover a resumable prior session for the given working dir and
|
|
65
|
+
* return how to resume it — in BOTH forms, mirroring buildRoleInjection /
|
|
66
|
+
* buildScriptRoleInjection:
|
|
67
|
+
* - args: Node argv for spawn() call sites (architect launch)
|
|
68
|
+
* - scriptFragment: shell-escaped fragment for bash script generation (builder)
|
|
69
|
+
* Returns null when no resumable session exists or this harness has no
|
|
70
|
+
* cwd-keyed session store → callers fall back to a fresh launch. Only Claude
|
|
71
|
+
* implements it (store: ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl).
|
|
72
|
+
*
|
|
73
|
+
* Discovery-based (newest jsonl by mtime): used for builder resume (#831/#929) and,
|
|
74
|
+
* for architects (#832), only as the harness-gated legacy fallback when `main` has
|
|
75
|
+
* no stored session id yet (codex/gemini omit it → no stale-jsonl crash-loop).
|
|
76
|
+
*/
|
|
77
|
+
buildResume?(absolutePath: string, opts?: {
|
|
78
|
+
homeDir?: string;
|
|
79
|
+
}): {
|
|
80
|
+
sessionId: string;
|
|
81
|
+
args: string[];
|
|
82
|
+
scriptFragment: string;
|
|
83
|
+
} | null;
|
|
41
84
|
}
|
|
42
85
|
/** Custom harness definition from .codev/config.json */
|
|
43
86
|
export interface CustomHarnessConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAC7D,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC7B,CAAC;IAEF;;;;OAIG;IACH,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QACnE,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC7B,CAAC;IAEF;;;;;;;;OAQG;IACH,gBAAgB,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC;QACxF,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAEH;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE;QACR,wFAAwF;QACxF,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC5C,8EAA8E;QAC9E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KACzC,CAAC;IAEF;;;;;;;;;;;;;OAaG;IACH,WAAW,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAC/D,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,IAAI,CAAC;CACV;AAED,wDAAwD;AACxD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAMD,eAAO,MAAM,cAAc,EAAE,eA4B5B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,eAS3B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAS5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,eAc9B,CAAC;AAyBF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAMD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CAmBvF;AAMD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAyC9F;AAMD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAa5E;AAMD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACrD,OAAO,CAAC,EAAE,MAAM,GACf,eAAe,CA+BjB"}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @see codev/specs/591-af-workspace-failure-with-code.md
|
|
13
13
|
*/
|
|
14
|
+
import { findLatestSessionId } from './claude-session-discovery.js';
|
|
15
|
+
import { buildWorktreeGuardFiles } from './worktree-write-guard.js';
|
|
14
16
|
// =============================================================================
|
|
15
17
|
// Built-in providers
|
|
16
18
|
// =============================================================================
|
|
@@ -23,6 +25,25 @@ export const CLAUDE_HARNESS = {
|
|
|
23
25
|
fragment: `--append-system-prompt "$(cat '${shellEscapeSingleQuote(filePath)}')"`,
|
|
24
26
|
env: {},
|
|
25
27
|
}),
|
|
28
|
+
buildResume: (absolutePath, opts) => {
|
|
29
|
+
const sessionId = findLatestSessionId(absolutePath, opts);
|
|
30
|
+
if (!sessionId)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
sessionId,
|
|
34
|
+
args: ['--resume', sessionId],
|
|
35
|
+
scriptFragment: `--resume '${shellEscapeSingleQuote(sessionId)}'`,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
// Install the worktree write-guard PreToolUse hook (Issue #1018) so a builder
|
|
39
|
+
// cannot silently write outside its worktree (e.g. into the main checkout).
|
|
40
|
+
getWorktreeFiles: (_content, _filePath, worktreePath) => buildWorktreeGuardFiles(worktreePath),
|
|
41
|
+
// Issue #832: Claude pins/resumes a conversation by UUID and stores each session
|
|
42
|
+
// as ~/.claude/projects/<encoded-cwd>/<id>.jsonl.
|
|
43
|
+
session: {
|
|
44
|
+
newSessionArgs: (sessionId) => ['--session-id', sessionId],
|
|
45
|
+
resumeArgs: (sessionId) => ['--resume', sessionId],
|
|
46
|
+
},
|
|
26
47
|
};
|
|
27
48
|
export const CODEX_HARNESS = {
|
|
28
49
|
buildRoleInjection: (_content, filePath) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace-path canonicalization (single source of truth).
|
|
3
|
+
*
|
|
4
|
+
* The `workspace_path` column keys the architect and builders tables. Writers
|
|
5
|
+
* and readers MUST agree on its exact form, so every callsite normalizes through
|
|
6
|
+
* this one helper: the symlink-dereferenced real path when it exists on disk,
|
|
7
|
+
* else `path.resolve` for not-yet-existing paths (fresh installs).
|
|
8
|
+
*
|
|
9
|
+
* This is a leaf module (imports only node builtins) so both the data layer
|
|
10
|
+
* (state.ts, db/consolidate.ts) and the server layer (servers/tower-utils.ts and
|
|
11
|
+
* its importers) can share it without a dependency cycle — the reason the data
|
|
12
|
+
* layer previously kept an inline copy rather than importing from the
|
|
13
|
+
* server-layer tower-utils.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeWorkspacePath(workspacePath: string): string;
|
|
16
|
+
//# sourceMappingURL=workspace-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-path.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/workspace-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAOpE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace-path canonicalization (single source of truth).
|
|
3
|
+
*
|
|
4
|
+
* The `workspace_path` column keys the architect and builders tables. Writers
|
|
5
|
+
* and readers MUST agree on its exact form, so every callsite normalizes through
|
|
6
|
+
* this one helper: the symlink-dereferenced real path when it exists on disk,
|
|
7
|
+
* else `path.resolve` for not-yet-existing paths (fresh installs).
|
|
8
|
+
*
|
|
9
|
+
* This is a leaf module (imports only node builtins) so both the data layer
|
|
10
|
+
* (state.ts, db/consolidate.ts) and the server layer (servers/tower-utils.ts and
|
|
11
|
+
* its importers) can share it without a dependency cycle — the reason the data
|
|
12
|
+
* layer previously kept an inline copy rather than importing from the
|
|
13
|
+
* server-layer tower-utils.
|
|
14
|
+
*/
|
|
15
|
+
import { realpathSync } from 'node:fs';
|
|
16
|
+
import { resolve } from 'node:path';
|
|
17
|
+
export function normalizeWorkspacePath(workspacePath) {
|
|
18
|
+
try {
|
|
19
|
+
return realpathSync(workspacePath);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Path doesn't exist yet — normalize without realpath.
|
|
23
|
+
return resolve(workspacePath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=workspace-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-path.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/workspace-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,sBAAsB,CAAC,aAAqB;IAC1D,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;QACvD,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder worktree write-guard (Issue #1018).
|
|
3
|
+
*
|
|
4
|
+
* A strict-mode builder runs in a git worktree nested inside the main checkout.
|
|
5
|
+
* The Write/Edit tools require absolute paths, so the model must *synthesize*
|
|
6
|
+
* one. When the runtime anchors that path at the inferred canonical repo root
|
|
7
|
+
* instead of the worktree cwd, the `.builders/<id>/` segment is dropped and the
|
|
8
|
+
* write silently lands in the main checkout (a real, writable, byte-identical
|
|
9
|
+
* sibling tree). Instructions/memory don't hold against this because it's
|
|
10
|
+
* intrinsic path-synthesis behavior that drifts across model/CLI upgrades — only
|
|
11
|
+
* a deterministic guard holds.
|
|
12
|
+
*
|
|
13
|
+
* This module is the single source of truth for the guard. It emits two files
|
|
14
|
+
* into each Claude builder worktree at spawn time (via CLAUDE_HARNESS):
|
|
15
|
+
* - `.claude/hooks/worktree-write-guard.cjs` — a self-contained Node hook
|
|
16
|
+
* - `.claude/settings.local.json` — a PreToolUse hook wiring on Write/Edit
|
|
17
|
+
*
|
|
18
|
+
* The hook denies any absolute Write/Edit path that resolves outside the
|
|
19
|
+
* worktree root, with an allowlist for temp dirs and `$HOME/.claude` (so builder
|
|
20
|
+
* memory/config writes still work). The worktree root is baked into the hook
|
|
21
|
+
* command as `CODEV_WORKTREE_ROOT` (deterministic), with `git rev-parse
|
|
22
|
+
* --show-toplevel` as a runtime fallback.
|
|
23
|
+
*
|
|
24
|
+
* @see codev/plans/1018-builder-worktree-write-guard-p.md
|
|
25
|
+
*/
|
|
26
|
+
/** Worktree-relative path of the emitted guard script. */
|
|
27
|
+
export declare const GUARD_SCRIPT_RELPATH = ".claude/hooks/worktree-write-guard.cjs";
|
|
28
|
+
/** Worktree-relative path of the emitted settings file. */
|
|
29
|
+
export declare const GUARD_SETTINGS_RELPATH = ".claude/settings.local.json";
|
|
30
|
+
/**
|
|
31
|
+
* The self-contained Node guard script, emitted verbatim into each worktree.
|
|
32
|
+
*
|
|
33
|
+
* Kept as a string constant (not a separate asset) so it ships with the
|
|
34
|
+
* compiled package automatically — `tsc` does not copy non-TS files to `dist`.
|
|
35
|
+
* It is exercised directly by the unit test, which writes it to a temp `.cjs`
|
|
36
|
+
* and spawns it with fixture stdin, so the constant is the tested artifact.
|
|
37
|
+
*
|
|
38
|
+
* Self-contained: no `require` of any project code (worktrees have no access to
|
|
39
|
+
* the package's node_modules, and neither do adopter repos). Node core only.
|
|
40
|
+
*
|
|
41
|
+
* Fail-open by design: any error (bad JSON, missing root, unexpected shape)
|
|
42
|
+
* allows the tool call. A safety net must never brick a builder session; the
|
|
43
|
+
* worst case of a failure is reverting to today's unguarded behavior.
|
|
44
|
+
*/
|
|
45
|
+
export declare const WORKTREE_WRITE_GUARD_SCRIPT = "#!/usr/bin/env node\n// AUTO-GENERATED by @cluesmith/codev (Issue #1018). Do not edit in the worktree;\n// edit packages/codev/src/agent-farm/utils/worktree-write-guard.ts instead.\n'use strict';\n\nconst fs = require('node:fs');\nconst path = require('node:path');\nconst { execSync } = require('node:child_process');\n\n// Tools whose file_path we guard. NotebookEdit (notebook_path) is out of scope.\nconst GUARDED_TOOLS = new Set(['Write', 'Edit', 'MultiEdit']);\n\nfunction allow() {\n process.exit(0);\n}\n\nfunction deny(target, root) {\n const reason =\n 'Blocked: ' + target + ' is outside this builder\\'s worktree (' + root + '). ' +\n 'Builders are isolated to their worktree \u2014 a path anchored at the main checkout ' +\n 'root silently pollutes it. Re-root the path under ' + root +\n ' (prefer a worktree-relative path). Allowed exceptions: temp dirs and ~/.claude.';\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'deny',\n permissionDecisionReason: reason,\n },\n }));\n process.exit(0);\n}\n\n// Resolve a path to a canonical absolute form WITHOUT requiring it to exist:\n// realpath the longest existing ancestor (resolving symlinks, e.g. macOS\n// /tmp -> /private/tmp), then re-append the non-existent tail.\nfunction canonicalize(p, cwd) {\n let abs = path.isAbsolute(p) ? p : path.resolve(cwd, p);\n abs = path.normalize(abs);\n let tail = '';\n let cur = abs;\n for (;;) {\n try {\n const real = fs.realpathSync(cur);\n return tail ? path.join(real, tail) : real;\n } catch (e) {\n const parent = path.dirname(cur);\n if (parent === cur) {\n return abs;\n }\n tail = tail ? path.join(path.basename(cur), tail) : path.basename(cur);\n cur = parent;\n }\n }\n}\n\nfunction isInside(child, parent) {\n if (!parent) {\n return false;\n }\n if (child === parent) {\n return true;\n }\n return child.startsWith(parent + path.sep);\n}\n\nfunction main(raw) {\n let input;\n try {\n input = JSON.parse(raw);\n } catch (e) {\n return allow();\n }\n\n const toolName = input && input.tool_name;\n if (!GUARDED_TOOLS.has(toolName)) {\n return allow();\n }\n\n const toolInput = (input && input.tool_input) || {};\n const filePath = toolInput.file_path;\n if (!filePath || typeof filePath !== 'string') {\n return allow();\n }\n\n const cwd = (input && input.cwd) || process.cwd();\n\n // Worktree root: baked-in env (deterministic) first, then git as a fallback.\n let root = process.env.CODEV_WORKTREE_ROOT;\n if (!root) {\n try {\n root = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf8' }).trim();\n } catch (e) {\n root = '';\n }\n }\n if (!root) {\n // Can't determine the boundary \u2014 fail open rather than block every write.\n return allow();\n }\n root = canonicalize(root, cwd);\n\n const target = canonicalize(filePath, cwd);\n\n if (isInside(target, root)) {\n return allow();\n }\n\n // Allowlist: temp dirs (scratch) and ~/.claude (builder memory + config).\n const allowRoots = [];\n const addAllow = (p) => {\n if (p) {\n allowRoots.push(canonicalize(p, cwd));\n }\n };\n addAllow('/tmp');\n addAllow('/private/tmp');\n addAllow(process.env.TMPDIR);\n if (process.env.HOME) {\n addAllow(path.join(process.env.HOME, '.claude'));\n }\n\n for (const a of allowRoots) {\n if (isInside(target, a)) {\n return allow();\n }\n }\n\n return deny(target, root);\n}\n\nlet buf = '';\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (d) => {\n buf += d;\n});\nprocess.stdin.on('end', () => {\n try {\n main(buf);\n } catch (e) {\n // Never brick a session on an unexpected error.\n allow();\n }\n});\n";
|
|
46
|
+
/**
|
|
47
|
+
* Build the worktree files that install the write-guard for a Claude builder.
|
|
48
|
+
*
|
|
49
|
+
* @param worktreePath absolute path to the builder's worktree
|
|
50
|
+
* @returns files (relativePath + content) for writeWorktreeFiles()
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildWorktreeGuardFiles(worktreePath: string): Array<{
|
|
53
|
+
relativePath: string;
|
|
54
|
+
content: string;
|
|
55
|
+
}>;
|
|
56
|
+
//# sourceMappingURL=worktree-write-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-write-guard.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/utils/worktree-write-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,2CAA2C,CAAC;AAE7E,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,2BAA2B,swHA+IvC,CAAC;AAOF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,GACnB,KAAK,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBlD"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder worktree write-guard (Issue #1018).
|
|
3
|
+
*
|
|
4
|
+
* A strict-mode builder runs in a git worktree nested inside the main checkout.
|
|
5
|
+
* The Write/Edit tools require absolute paths, so the model must *synthesize*
|
|
6
|
+
* one. When the runtime anchors that path at the inferred canonical repo root
|
|
7
|
+
* instead of the worktree cwd, the `.builders/<id>/` segment is dropped and the
|
|
8
|
+
* write silently lands in the main checkout (a real, writable, byte-identical
|
|
9
|
+
* sibling tree). Instructions/memory don't hold against this because it's
|
|
10
|
+
* intrinsic path-synthesis behavior that drifts across model/CLI upgrades — only
|
|
11
|
+
* a deterministic guard holds.
|
|
12
|
+
*
|
|
13
|
+
* This module is the single source of truth for the guard. It emits two files
|
|
14
|
+
* into each Claude builder worktree at spawn time (via CLAUDE_HARNESS):
|
|
15
|
+
* - `.claude/hooks/worktree-write-guard.cjs` — a self-contained Node hook
|
|
16
|
+
* - `.claude/settings.local.json` — a PreToolUse hook wiring on Write/Edit
|
|
17
|
+
*
|
|
18
|
+
* The hook denies any absolute Write/Edit path that resolves outside the
|
|
19
|
+
* worktree root, with an allowlist for temp dirs and `$HOME/.claude` (so builder
|
|
20
|
+
* memory/config writes still work). The worktree root is baked into the hook
|
|
21
|
+
* command as `CODEV_WORKTREE_ROOT` (deterministic), with `git rev-parse
|
|
22
|
+
* --show-toplevel` as a runtime fallback.
|
|
23
|
+
*
|
|
24
|
+
* @see codev/plans/1018-builder-worktree-write-guard-p.md
|
|
25
|
+
*/
|
|
26
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
27
|
+
/** Worktree-relative path of the emitted guard script. */
|
|
28
|
+
export const GUARD_SCRIPT_RELPATH = '.claude/hooks/worktree-write-guard.cjs';
|
|
29
|
+
/** Worktree-relative path of the emitted settings file. */
|
|
30
|
+
export const GUARD_SETTINGS_RELPATH = '.claude/settings.local.json';
|
|
31
|
+
/**
|
|
32
|
+
* The self-contained Node guard script, emitted verbatim into each worktree.
|
|
33
|
+
*
|
|
34
|
+
* Kept as a string constant (not a separate asset) so it ships with the
|
|
35
|
+
* compiled package automatically — `tsc` does not copy non-TS files to `dist`.
|
|
36
|
+
* It is exercised directly by the unit test, which writes it to a temp `.cjs`
|
|
37
|
+
* and spawns it with fixture stdin, so the constant is the tested artifact.
|
|
38
|
+
*
|
|
39
|
+
* Self-contained: no `require` of any project code (worktrees have no access to
|
|
40
|
+
* the package's node_modules, and neither do adopter repos). Node core only.
|
|
41
|
+
*
|
|
42
|
+
* Fail-open by design: any error (bad JSON, missing root, unexpected shape)
|
|
43
|
+
* allows the tool call. A safety net must never brick a builder session; the
|
|
44
|
+
* worst case of a failure is reverting to today's unguarded behavior.
|
|
45
|
+
*/
|
|
46
|
+
export const WORKTREE_WRITE_GUARD_SCRIPT = `#!/usr/bin/env node
|
|
47
|
+
// AUTO-GENERATED by @cluesmith/codev (Issue #1018). Do not edit in the worktree;
|
|
48
|
+
// edit packages/codev/src/agent-farm/utils/worktree-write-guard.ts instead.
|
|
49
|
+
'use strict';
|
|
50
|
+
|
|
51
|
+
const fs = require('node:fs');
|
|
52
|
+
const path = require('node:path');
|
|
53
|
+
const { execSync } = require('node:child_process');
|
|
54
|
+
|
|
55
|
+
// Tools whose file_path we guard. NotebookEdit (notebook_path) is out of scope.
|
|
56
|
+
const GUARDED_TOOLS = new Set(['Write', 'Edit', 'MultiEdit']);
|
|
57
|
+
|
|
58
|
+
function allow() {
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function deny(target, root) {
|
|
63
|
+
const reason =
|
|
64
|
+
'Blocked: ' + target + ' is outside this builder\\'s worktree (' + root + '). ' +
|
|
65
|
+
'Builders are isolated to their worktree — a path anchored at the main checkout ' +
|
|
66
|
+
'root silently pollutes it. Re-root the path under ' + root +
|
|
67
|
+
' (prefer a worktree-relative path). Allowed exceptions: temp dirs and ~/.claude.';
|
|
68
|
+
process.stdout.write(JSON.stringify({
|
|
69
|
+
hookSpecificOutput: {
|
|
70
|
+
hookEventName: 'PreToolUse',
|
|
71
|
+
permissionDecision: 'deny',
|
|
72
|
+
permissionDecisionReason: reason,
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Resolve a path to a canonical absolute form WITHOUT requiring it to exist:
|
|
79
|
+
// realpath the longest existing ancestor (resolving symlinks, e.g. macOS
|
|
80
|
+
// /tmp -> /private/tmp), then re-append the non-existent tail.
|
|
81
|
+
function canonicalize(p, cwd) {
|
|
82
|
+
let abs = path.isAbsolute(p) ? p : path.resolve(cwd, p);
|
|
83
|
+
abs = path.normalize(abs);
|
|
84
|
+
let tail = '';
|
|
85
|
+
let cur = abs;
|
|
86
|
+
for (;;) {
|
|
87
|
+
try {
|
|
88
|
+
const real = fs.realpathSync(cur);
|
|
89
|
+
return tail ? path.join(real, tail) : real;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
const parent = path.dirname(cur);
|
|
92
|
+
if (parent === cur) {
|
|
93
|
+
return abs;
|
|
94
|
+
}
|
|
95
|
+
tail = tail ? path.join(path.basename(cur), tail) : path.basename(cur);
|
|
96
|
+
cur = parent;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isInside(child, parent) {
|
|
102
|
+
if (!parent) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (child === parent) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return child.startsWith(parent + path.sep);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function main(raw) {
|
|
112
|
+
let input;
|
|
113
|
+
try {
|
|
114
|
+
input = JSON.parse(raw);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return allow();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const toolName = input && input.tool_name;
|
|
120
|
+
if (!GUARDED_TOOLS.has(toolName)) {
|
|
121
|
+
return allow();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const toolInput = (input && input.tool_input) || {};
|
|
125
|
+
const filePath = toolInput.file_path;
|
|
126
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
127
|
+
return allow();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const cwd = (input && input.cwd) || process.cwd();
|
|
131
|
+
|
|
132
|
+
// Worktree root: baked-in env (deterministic) first, then git as a fallback.
|
|
133
|
+
let root = process.env.CODEV_WORKTREE_ROOT;
|
|
134
|
+
if (!root) {
|
|
135
|
+
try {
|
|
136
|
+
root = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf8' }).trim();
|
|
137
|
+
} catch (e) {
|
|
138
|
+
root = '';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!root) {
|
|
142
|
+
// Can't determine the boundary — fail open rather than block every write.
|
|
143
|
+
return allow();
|
|
144
|
+
}
|
|
145
|
+
root = canonicalize(root, cwd);
|
|
146
|
+
|
|
147
|
+
const target = canonicalize(filePath, cwd);
|
|
148
|
+
|
|
149
|
+
if (isInside(target, root)) {
|
|
150
|
+
return allow();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Allowlist: temp dirs (scratch) and ~/.claude (builder memory + config).
|
|
154
|
+
const allowRoots = [];
|
|
155
|
+
const addAllow = (p) => {
|
|
156
|
+
if (p) {
|
|
157
|
+
allowRoots.push(canonicalize(p, cwd));
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
addAllow('/tmp');
|
|
161
|
+
addAllow('/private/tmp');
|
|
162
|
+
addAllow(process.env.TMPDIR);
|
|
163
|
+
if (process.env.HOME) {
|
|
164
|
+
addAllow(path.join(process.env.HOME, '.claude'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const a of allowRoots) {
|
|
168
|
+
if (isInside(target, a)) {
|
|
169
|
+
return allow();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return deny(target, root);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let buf = '';
|
|
177
|
+
process.stdin.setEncoding('utf8');
|
|
178
|
+
process.stdin.on('data', (d) => {
|
|
179
|
+
buf += d;
|
|
180
|
+
});
|
|
181
|
+
process.stdin.on('end', () => {
|
|
182
|
+
try {
|
|
183
|
+
main(buf);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
// Never brick a session on an unexpected error.
|
|
186
|
+
allow();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
`;
|
|
190
|
+
/** Single-quote-escape a value for safe embedding in a POSIX shell command. */
|
|
191
|
+
function shellSingleQuote(value) {
|
|
192
|
+
return value.replace(/'/g, `'\\''`);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Build the worktree files that install the write-guard for a Claude builder.
|
|
196
|
+
*
|
|
197
|
+
* @param worktreePath absolute path to the builder's worktree
|
|
198
|
+
* @returns files (relativePath + content) for writeWorktreeFiles()
|
|
199
|
+
*/
|
|
200
|
+
export function buildWorktreeGuardFiles(worktreePath) {
|
|
201
|
+
const root = isAbsolute(worktreePath) ? worktreePath : resolve(worktreePath);
|
|
202
|
+
const scriptAbs = join(root, GUARD_SCRIPT_RELPATH);
|
|
203
|
+
// Bake the worktree root in (deterministic) and run via `node` so the emitted
|
|
204
|
+
// script needs no executable bit.
|
|
205
|
+
const command = `CODEV_WORKTREE_ROOT='${shellSingleQuote(root)}' ` +
|
|
206
|
+
`node '${shellSingleQuote(scriptAbs)}'`;
|
|
207
|
+
const settings = {
|
|
208
|
+
hooks: {
|
|
209
|
+
PreToolUse: [
|
|
210
|
+
{
|
|
211
|
+
matcher: 'Write|Edit|MultiEdit',
|
|
212
|
+
hooks: [{ type: 'command', command }],
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
return [
|
|
218
|
+
{ relativePath: GUARD_SCRIPT_RELPATH, content: WORKTREE_WRITE_GUARD_SCRIPT },
|
|
219
|
+
{ relativePath: GUARD_SETTINGS_RELPATH, content: JSON.stringify(settings, null, 2) + '\n' },
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=worktree-write-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-write-guard.js","sourceRoot":"","sources":["../../../src/agent-farm/utils/worktree-write-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,wCAAwC,CAAC;AAE7E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,6BAA6B,CAAC;AAEpE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+I1C,CAAC;AAEF,+EAA+E;AAC/E,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,YAAoB;IAEpB,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAEnD,8EAA8E;IAC9E,kCAAkC;IAClC,MAAM,OAAO,GACX,wBAAwB,gBAAgB,CAAC,IAAI,CAAC,IAAI;QAClD,SAAS,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC;IAE1C,MAAM,QAAQ,GAAG;QACf,KAAK,EAAE;YACL,UAAU,EAAE;gBACV;oBACE,OAAO,EAAE,sBAAsB;oBAC/B,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;iBACtC;aACF;SACF;KACF,CAAC;IAEF,OAAO;QACL,EAAE,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,2BAA2B,EAAE;QAC5E,EAAE,YAAY,EAAE,sBAAsB,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;KAC5F,CAAC;AACJ,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG;AA+WH;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvD"}
|