@heyditto/cli 3.6.4 → 3.7.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.
- package/README.md +29 -0
- package/dist/agents/claude-hooks.d.ts +73 -0
- package/dist/agents/claude-hooks.js +114 -0
- package/dist/agents/claude-hooks.js.map +1 -0
- package/dist/agents/claude-mcp.d.ts +1 -1
- package/dist/agents/claude-mcp.js +2 -2
- package/dist/agents/claude-mcp.js.map +1 -1
- package/dist/agents/claude-stop-hook.d.ts +48 -0
- package/dist/agents/claude-stop-hook.js +271 -0
- package/dist/agents/claude-stop-hook.js.map +1 -0
- package/dist/agents/claude.js +6 -2
- package/dist/agents/claude.js.map +1 -1
- package/dist/agents/launch.d.ts +6 -0
- package/dist/agents/launch.js +49 -0
- package/dist/agents/launch.js.map +1 -1
- package/dist/agents/types.d.ts +3 -0
- package/dist/agents/types.js.map +1 -1
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/dittoconfig/profiles.d.ts +5 -0
- package/dist/dittoconfig/profiles.js +25 -2
- package/dist/dittoconfig/profiles.js.map +1 -1
- package/dist/review-commands.js +3 -2
- package/dist/review-commands.js.map +1 -1
- package/dist/update-lock.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -637,6 +637,7 @@ Options shared by both commands:
|
|
|
637
637
|
| `--resume [id]` / `-c, --continue` | resume a local session (`heyditto sessions`) / the agent's most recent conversation. Claude also takes a Claude session id or title here, including sessions started in Claude Desktop or plain `claude`, and runs in the directory that session ran in; with no value, a terminal gets a searchable picker. Claude's own `-r` / `--resume` / `-c` after `--` do the same. |
|
|
638
638
|
| `--yolo` / `--yellow` / `--plan` | bypass permissions / auto-accept edits / plan mode (Claude only) |
|
|
639
639
|
| `--mcp` / `--no-mcp` | attach the endpoint's tools to Claude Code as its own MCP servers, or keep them on the router (default: the endpoint's `claude_tool_surface`, else MCP) |
|
|
640
|
+
| `--no-hooks` | leave out the Claude Code hooks the repository's `.ditto` profile declares (`claude_hooks`); Claude only |
|
|
640
641
|
| `-p, --prompt <text>` | headless run: `claude -p` or `codex exec` |
|
|
641
642
|
| `-m, --model <id>` | model to request (default: the agent's own model id passes through and the endpoint routes it) |
|
|
642
643
|
| `-w, --worktree [name]` | run inside `<repo>/.worktrees/<name>` on a branch of that name; `.worktrees/` is added to `.gitignore` |
|
|
@@ -1303,6 +1304,34 @@ says so. Tracked artifact directories are flagged rather than claimed excluded.
|
|
|
1303
1304
|
`heyditto offload` refuses to delete a folder when unrelated files, unknown projects or
|
|
1304
1305
|
escaping symlinks beneath it would be lost.
|
|
1305
1306
|
|
|
1307
|
+
### Claude Code hooks (`claude_hooks`)
|
|
1308
|
+
|
|
1309
|
+
A profile can attach Ditto's own Claude Code hooks to `heyditto claude`
|
|
1310
|
+
sessions in this repository. They apply in every inference mode (Router,
|
|
1311
|
+
`--cliproxy`, `--mcp-only`):
|
|
1312
|
+
|
|
1313
|
+
```toml
|
|
1314
|
+
# .ditto/endpoints/dev.toml
|
|
1315
|
+
[settings]
|
|
1316
|
+
claude_hooks = { stop = { checks = ["unpushed"], repos = [".", "workspaces/*/*"] } }
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
`stop.checks = ["unpushed"]` is the only hook so far. When Claude finishes a
|
|
1320
|
+
turn while a listed repository has uncommitted changes, or commits its remote
|
|
1321
|
+
does not have, the hook asks Claude once to push them or to say why they are
|
|
1322
|
+
left. `repos` are paths relative to this repository; `*` matches one directory
|
|
1323
|
+
name; the default is `["."]`. Remote problems that cannot be confirmed (no
|
|
1324
|
+
remote, offline) are not reported.
|
|
1325
|
+
|
|
1326
|
+
The setting is read from the checked-in file at launch and is not sent to the
|
|
1327
|
+
endpoint by `repo apply`, so a launch outside this repository, or with another
|
|
1328
|
+
endpoint, gets none. `--no-hooks` turns them off for one launch. A session with
|
|
1329
|
+
hooks is no longer plain Claude Code, so the banner lists them
|
|
1330
|
+
(`hooks=Stop (unpushed) (.ditto/endpoints/dev.toml)`). Hooks do not give the
|
|
1331
|
+
Router control of inference in CLIProxy or native mode. See
|
|
1332
|
+
[docs/claude-hooks.md](docs/claude-hooks.md) for the survey of hook events and
|
|
1333
|
+
what comes next.
|
|
1334
|
+
|
|
1306
1335
|
### Cloud-assisted CLIProxy inference
|
|
1307
1336
|
|
|
1308
1337
|
To use subscription inference with Router optimizations, cloud session recording
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ditto's own Claude Code hooks (DITTO-2479).
|
|
3
|
+
*
|
|
4
|
+
* `heyditto claude` already shapes a session through `--settings`: native MCP
|
|
5
|
+
* puts the endpoint's tools and permission rules there, and Remote Control
|
|
6
|
+
* adds the hooks that relay turn boundaries to the app. This adds hooks that
|
|
7
|
+
* Ditto owns as policy, declared per repository in the endpoint profile:
|
|
8
|
+
*
|
|
9
|
+
* # .ditto/endpoints/dev.toml
|
|
10
|
+
* [settings]
|
|
11
|
+
* claude_hooks = { stop = { checks = ["unpushed"], repos = [".", "workspaces/*\/*"] } }
|
|
12
|
+
*
|
|
13
|
+
* They are read from the checked-in profile at launch and never sent to the
|
|
14
|
+
* endpoint: `repos` are paths in this repository, meaningless anywhere else,
|
|
15
|
+
* so a launch outside the repository (or `--no-hooks`) gets none.
|
|
16
|
+
*
|
|
17
|
+
* Only `Stop` is implemented. Its one check, `unpushed`, asks Claude to keep
|
|
18
|
+
* going once when a listed repository has uncommitted changes or commits its
|
|
19
|
+
* remote does not have. See docs/claude-hooks.md for the survey of the other
|
|
20
|
+
* events and why they wait.
|
|
21
|
+
*/
|
|
22
|
+
export declare const STOP_CHECKS: readonly ["unpushed"];
|
|
23
|
+
export type StopCheck = (typeof STOP_CHECKS)[number];
|
|
24
|
+
export interface StopHookConfig {
|
|
25
|
+
checks: StopCheck[];
|
|
26
|
+
/** Directories relative to the repository root; `*` matches one path segment. */
|
|
27
|
+
repos: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface ClaudeHooksConfig {
|
|
30
|
+
stop?: StopHookConfig;
|
|
31
|
+
}
|
|
32
|
+
/** What the hook script receives (argv, base64url JSON): no secrets, only paths and check names. */
|
|
33
|
+
export interface StopHookArgs {
|
|
34
|
+
root: string;
|
|
35
|
+
checks: StopCheck[];
|
|
36
|
+
repos: string[];
|
|
37
|
+
/** Where the policy came from, quoted back to Claude so it knows whose rule it is. */
|
|
38
|
+
source: string;
|
|
39
|
+
}
|
|
40
|
+
/** The longest a Stop hook may take: Claude waits for it before the turn ends. */
|
|
41
|
+
export declare const STOP_HOOK_TIMEOUT_S = 60;
|
|
42
|
+
export declare class ClaudeHooksError extends Error {
|
|
43
|
+
constructor(message: string);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validates `settings.claude_hooks`. Unknown keys and checks fail: a typo in
|
|
47
|
+
* version-controlled policy must not silently mean "no hook".
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseClaudeHooks(raw: unknown, where: string): ClaudeHooksConfig | undefined;
|
|
50
|
+
/** Absolute path of the compiled Stop hook next to this module. */
|
|
51
|
+
export declare function stopHookScriptPath(): string;
|
|
52
|
+
export declare function encodeHookArgs(args: StopHookArgs): string;
|
|
53
|
+
export declare function decodeHookArgs(raw: string): StopHookArgs | undefined;
|
|
54
|
+
/** What a launch attaches: the `--settings` fragment and a banner summary. */
|
|
55
|
+
export interface ClaudeHookAttachment {
|
|
56
|
+
settings: {
|
|
57
|
+
hooks: Record<string, unknown[]>;
|
|
58
|
+
};
|
|
59
|
+
/** e.g. ["Stop (unpushed)"]. */
|
|
60
|
+
summary: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The `--settings` hooks for a repository's config, or undefined when it asks
|
|
64
|
+
* for none. Hooks from `--settings` are added next to the user's own Claude
|
|
65
|
+
* Code hooks: withClaudeSettings merges this into the one `--settings` value,
|
|
66
|
+
* and Remote Control's relay hooks concatenate onto the same event lists.
|
|
67
|
+
*/
|
|
68
|
+
export declare function claudeHookAttachment(config: ClaudeHooksConfig | undefined, opts: {
|
|
69
|
+
root: string;
|
|
70
|
+
source: string;
|
|
71
|
+
script?: string;
|
|
72
|
+
node?: string;
|
|
73
|
+
}): ClaudeHookAttachment | undefined;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
/**
|
|
4
|
+
* Ditto's own Claude Code hooks (DITTO-2479).
|
|
5
|
+
*
|
|
6
|
+
* `heyditto claude` already shapes a session through `--settings`: native MCP
|
|
7
|
+
* puts the endpoint's tools and permission rules there, and Remote Control
|
|
8
|
+
* adds the hooks that relay turn boundaries to the app. This adds hooks that
|
|
9
|
+
* Ditto owns as policy, declared per repository in the endpoint profile:
|
|
10
|
+
*
|
|
11
|
+
* # .ditto/endpoints/dev.toml
|
|
12
|
+
* [settings]
|
|
13
|
+
* claude_hooks = { stop = { checks = ["unpushed"], repos = [".", "workspaces/*\/*"] } }
|
|
14
|
+
*
|
|
15
|
+
* They are read from the checked-in profile at launch and never sent to the
|
|
16
|
+
* endpoint: `repos` are paths in this repository, meaningless anywhere else,
|
|
17
|
+
* so a launch outside the repository (or `--no-hooks`) gets none.
|
|
18
|
+
*
|
|
19
|
+
* Only `Stop` is implemented. Its one check, `unpushed`, asks Claude to keep
|
|
20
|
+
* going once when a listed repository has uncommitted changes or commits its
|
|
21
|
+
* remote does not have. See docs/claude-hooks.md for the survey of the other
|
|
22
|
+
* events and why they wait.
|
|
23
|
+
*/
|
|
24
|
+
export const STOP_CHECKS = ["unpushed"];
|
|
25
|
+
/** The longest a Stop hook may take: Claude waits for it before the turn ends. */
|
|
26
|
+
export const STOP_HOOK_TIMEOUT_S = 60;
|
|
27
|
+
export class ClaudeHooksError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "ClaudeHooksError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isTable(v) {
|
|
34
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
35
|
+
}
|
|
36
|
+
function stringList(v, field) {
|
|
37
|
+
if (!Array.isArray(v) || v.some((s) => typeof s !== "string" || !s.trim()))
|
|
38
|
+
throw new ClaudeHooksError(`${field} must be a list of non-empty strings`);
|
|
39
|
+
return [...new Set(v.map((s) => s.trim()))];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates `settings.claude_hooks`. Unknown keys and checks fail: a typo in
|
|
43
|
+
* version-controlled policy must not silently mean "no hook".
|
|
44
|
+
*/
|
|
45
|
+
export function parseClaudeHooks(raw, where) {
|
|
46
|
+
if (raw === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
const field = `${where}.claude_hooks`;
|
|
49
|
+
if (!isTable(raw))
|
|
50
|
+
throw new ClaudeHooksError(`${field} must be a table, e.g. { stop = { checks = ["unpushed"] } }`);
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [event, value] of Object.entries(raw)) {
|
|
53
|
+
if (event !== "stop")
|
|
54
|
+
throw new ClaudeHooksError(`${field}.${event}: unknown hook (only "stop" is supported)`);
|
|
55
|
+
if (!isTable(value))
|
|
56
|
+
throw new ClaudeHooksError(`${field}.stop must be a table, e.g. { checks = ["unpushed"] }`);
|
|
57
|
+
for (const k of Object.keys(value)) {
|
|
58
|
+
if (k !== "checks" && k !== "repos")
|
|
59
|
+
throw new ClaudeHooksError(`${field}.stop.${k}: unknown key (checks, repos)`);
|
|
60
|
+
}
|
|
61
|
+
const checks = stringList(value.checks ?? [], `${field}.stop.checks`);
|
|
62
|
+
for (const check of checks) {
|
|
63
|
+
if (!STOP_CHECKS.includes(check))
|
|
64
|
+
throw new ClaudeHooksError(`${field}.stop.checks: unknown check "${check}" (known: ${STOP_CHECKS.join(", ")})`);
|
|
65
|
+
}
|
|
66
|
+
const repos = value.repos === undefined ? ["."] : stringList(value.repos, `${field}.stop.repos`);
|
|
67
|
+
for (const r of repos) {
|
|
68
|
+
if (path.isAbsolute(r) || r.split(/[\\/]/).includes(".."))
|
|
69
|
+
throw new ClaudeHooksError(`${field}.stop.repos: "${r}" must be a path inside the repository`);
|
|
70
|
+
}
|
|
71
|
+
// An empty check list is an explicit "off", kept so the banner can say so.
|
|
72
|
+
out.stop = { checks: checks, repos };
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/** Absolute path of the compiled Stop hook next to this module. */
|
|
77
|
+
export function stopHookScriptPath() {
|
|
78
|
+
return fileURLToPath(new URL("./claude-stop-hook.js", import.meta.url));
|
|
79
|
+
}
|
|
80
|
+
function shellQuote(s) {
|
|
81
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
82
|
+
}
|
|
83
|
+
export function encodeHookArgs(args) {
|
|
84
|
+
return Buffer.from(JSON.stringify(args)).toString("base64url");
|
|
85
|
+
}
|
|
86
|
+
export function decodeHookArgs(raw) {
|
|
87
|
+
try {
|
|
88
|
+
const v = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
89
|
+
if (typeof v.root !== "string" || !Array.isArray(v.checks) || !Array.isArray(v.repos))
|
|
90
|
+
return undefined;
|
|
91
|
+
return v;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The `--settings` hooks for a repository's config, or undefined when it asks
|
|
99
|
+
* for none. Hooks from `--settings` are added next to the user's own Claude
|
|
100
|
+
* Code hooks: withClaudeSettings merges this into the one `--settings` value,
|
|
101
|
+
* and Remote Control's relay hooks concatenate onto the same event lists.
|
|
102
|
+
*/
|
|
103
|
+
export function claudeHookAttachment(config, opts) {
|
|
104
|
+
const stop = config?.stop;
|
|
105
|
+
if (!stop || stop.checks.length === 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
const args = { root: path.resolve(opts.root), checks: stop.checks, repos: stop.repos, source: opts.source };
|
|
108
|
+
const command = [opts.node ?? process.execPath, opts.script ?? stopHookScriptPath(), encodeHookArgs(args)].map(shellQuote).join(" ");
|
|
109
|
+
return {
|
|
110
|
+
settings: { hooks: { Stop: [{ hooks: [{ type: "command", command, timeout: STOP_HOOK_TIMEOUT_S }] }] } },
|
|
111
|
+
summary: [`Stop (${stop.checks.join(", ")})`],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=claude-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-hooks.js","sourceRoot":"","sources":["../../src/agents/claude-hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -130,7 +130,7 @@ export declare function writePrivateSettings(json: string, tmp?: string): string
|
|
|
130
130
|
* silently replacing either. Inline JSON is merged in place. A settings file is
|
|
131
131
|
* read from Claude's launch directory and merged into a private copy whose path
|
|
132
132
|
* replaces it. A file that cannot be read stops the launch, since dropping it
|
|
133
|
-
* would lose the user's settings and dropping ours their MCP permissions.
|
|
133
|
+
* would lose the user's settings and dropping ours their MCP permissions or hooks.
|
|
134
134
|
*/
|
|
135
135
|
export declare function withClaudeSettings(args: string[], settings: Json, io?: SettingsIO): string[];
|
|
136
136
|
/** The permission settings an attachment contributes. */
|
|
@@ -238,7 +238,7 @@ export function writePrivateSettings(json, tmp = os.tmpdir()) {
|
|
|
238
238
|
* silently replacing either. Inline JSON is merged in place. A settings file is
|
|
239
239
|
* read from Claude's launch directory and merged into a private copy whose path
|
|
240
240
|
* replaces it. A file that cannot be read stops the launch, since dropping it
|
|
241
|
-
* would lose the user's settings and dropping ours their MCP permissions.
|
|
241
|
+
* would lose the user's settings and dropping ours their MCP permissions or hooks.
|
|
242
242
|
*/
|
|
243
243
|
export function withClaudeSettings(args, settings, io = {}) {
|
|
244
244
|
const read = io.read ?? ((f) => readFileSync(f, "utf8"));
|
|
@@ -259,7 +259,7 @@ export function withClaudeSettings(args, settings, io = {}) {
|
|
|
259
259
|
parsed = JSON.parse(isJSON ? value : read(path.resolve(io.cwd ?? process.cwd(), value)));
|
|
260
260
|
}
|
|
261
261
|
catch (err) {
|
|
262
|
-
throw new Error(`--settings ${value}: cannot be merged with
|
|
262
|
+
throw new Error(`--settings ${value}: cannot be merged with Ditto's session settings (${err.message})`);
|
|
263
263
|
}
|
|
264
264
|
if (!isObject(parsed))
|
|
265
265
|
throw new Error(`--settings ${value}: not a JSON object`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-mcp.js","sourceRoot":"","sources":["../../src/agents/claude-mcp.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"claude-mcp.js","sourceRoot":"","sources":["../../src/agents/claude-mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type StopHookArgs } from "./claude-hooks.js";
|
|
3
|
+
/**
|
|
4
|
+
* All checks share one deadline inside the hook's 60 s timeout
|
|
5
|
+
* (STOP_HOOK_TIMEOUT_S), so a slow repository costs the ones after it their
|
|
6
|
+
* check, never the block decision for the ones already found.
|
|
7
|
+
*/
|
|
8
|
+
export declare const STOP_BUDGET_MS = 50000;
|
|
9
|
+
export interface RepoFinding {
|
|
10
|
+
/** Path relative to the root, "." for the root itself. */
|
|
11
|
+
repo: string;
|
|
12
|
+
problems: string[];
|
|
13
|
+
}
|
|
14
|
+
type Git = (cwd: string, args: string[], timeoutMs?: number) => Promise<string | undefined>;
|
|
15
|
+
/** Runs git; undefined on any failure, so callers can only ever under-report. */
|
|
16
|
+
export declare const runGit: Git;
|
|
17
|
+
/**
|
|
18
|
+
* Expands the profile's repo patterns under `root`. `*` matches one directory
|
|
19
|
+
* name (hidden ones excluded); anything else is literal. Only directories that
|
|
20
|
+
* hold a `.git` (a clone or a worktree) and resolve inside `root` count, so a
|
|
21
|
+
* symlink in a pattern never points the checks at another repository.
|
|
22
|
+
* Sorted, deduplicated, bounded.
|
|
23
|
+
*/
|
|
24
|
+
export declare function expandRepos(root: string, patterns: string[]): Promise<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* What is not yet durable in one repository: uncommitted changes, and commits
|
|
27
|
+
* that are not on its remote. A branch with no upstream is checked against the
|
|
28
|
+
* remote itself, because a single-branch clone pushed with `git push -u` can
|
|
29
|
+
* have no remote-tracking ref and would otherwise always read as unpushed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function unpushedProblems(repo: string, git?: Git): Promise<string[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Every listed repository's problems; repositories with none are omitted.
|
|
34
|
+
* Each git call gets only what is left of the shared budget, and repositories
|
|
35
|
+
* reached after it runs out are not checked.
|
|
36
|
+
*/
|
|
37
|
+
export declare function stopFindings(args: StopHookArgs, git?: Git, budgetMs?: number): Promise<RepoFinding[]>;
|
|
38
|
+
/** The text Claude reads when the stop is blocked. */
|
|
39
|
+
export declare function stopReason(findings: RepoFinding[], source: string): string;
|
|
40
|
+
/** The hook's stdout for one Stop input, or "" to let the turn end. */
|
|
41
|
+
export declare function stopHookOutput(input: Record<string, unknown> | undefined, args: StopHookArgs, git?: Git): Promise<string>;
|
|
42
|
+
/**
|
|
43
|
+
* The Stop input. No input at all reads as a first stop. Input that cannot be
|
|
44
|
+
* parsed (cut short, or not JSON) returns null: the hook cannot tell whether
|
|
45
|
+
* it already blocked this stop, so it must not block it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function hookInput(raw: string): Record<string, unknown> | undefined | null;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ditto's Claude Code `Stop` hook: `claude-stop-hook.js <base64url args>`.
|
|
4
|
+
*
|
|
5
|
+
* Claude Code runs it when the agent finishes responding and passes the hook
|
|
6
|
+
* input on stdin. When a repository the profile lists still has uncommitted
|
|
7
|
+
* changes or commits its remote lacks, it prints `{"decision":"block",
|
|
8
|
+
* "reason":…}`, which sends the reason back to Claude so it can push the work
|
|
9
|
+
* or say why it is left. It blocks at most once per stop: when Claude is
|
|
10
|
+
* already continuing because of a Stop hook (`stop_hook_active`), it lets the
|
|
11
|
+
* turn end, so a condition Claude cannot fix never traps the session.
|
|
12
|
+
*
|
|
13
|
+
* It never fails the turn. Any error, an unreadable repository, or a remote
|
|
14
|
+
* that cannot be reached counts as "nothing to report", and the process exits
|
|
15
|
+
* 0 whatever happens. Only facts it established are reported.
|
|
16
|
+
*/
|
|
17
|
+
import { execFile } from "node:child_process";
|
|
18
|
+
import { realpathSync } from "node:fs";
|
|
19
|
+
import { readdir, realpath, stat } from "node:fs/promises";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { decodeHookArgs } from "./claude-hooks.js";
|
|
23
|
+
/** Bounds a hook that walks globs: Claude waits for it. */
|
|
24
|
+
const MAX_REPOS = 64;
|
|
25
|
+
const GIT_TIMEOUT_MS = 5_000;
|
|
26
|
+
const REMOTE_TIMEOUT_MS = 8_000;
|
|
27
|
+
/**
|
|
28
|
+
* All checks share one deadline inside the hook's 60 s timeout
|
|
29
|
+
* (STOP_HOOK_TIMEOUT_S), so a slow repository costs the ones after it their
|
|
30
|
+
* check, never the block decision for the ones already found.
|
|
31
|
+
*/
|
|
32
|
+
export const STOP_BUDGET_MS = 50_000;
|
|
33
|
+
/** Runs git; undefined on any failure, so callers can only ever under-report. */
|
|
34
|
+
export const runGit = (cwd, args, timeoutMs = GIT_TIMEOUT_MS) => new Promise((resolve) => {
|
|
35
|
+
execFile("git", ["-C", cwd, ...args], { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" } }, (err, stdout) => {
|
|
36
|
+
resolve(err ? undefined : stdout);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
async function isDir(p) {
|
|
40
|
+
try {
|
|
41
|
+
return (await stat(p)).isDirectory();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function exists(p) {
|
|
48
|
+
try {
|
|
49
|
+
await stat(p);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** `dir` resolves (symlinks included) to `root` or somewhere beneath it. */
|
|
57
|
+
async function within(realRoot, dir) {
|
|
58
|
+
try {
|
|
59
|
+
const rel = path.relative(realRoot, await realpath(dir));
|
|
60
|
+
return rel === "" || (rel !== ".." && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Expands the profile's repo patterns under `root`. `*` matches one directory
|
|
68
|
+
* name (hidden ones excluded); anything else is literal. Only directories that
|
|
69
|
+
* hold a `.git` (a clone or a worktree) and resolve inside `root` count, so a
|
|
70
|
+
* symlink in a pattern never points the checks at another repository.
|
|
71
|
+
* Sorted, deduplicated, bounded.
|
|
72
|
+
*/
|
|
73
|
+
export async function expandRepos(root, patterns) {
|
|
74
|
+
const found = new Set();
|
|
75
|
+
let realRoot;
|
|
76
|
+
try {
|
|
77
|
+
realRoot = await realpath(root);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
for (const pattern of patterns) {
|
|
83
|
+
if (found.size >= MAX_REPOS)
|
|
84
|
+
break;
|
|
85
|
+
let dirs = [path.resolve(root)];
|
|
86
|
+
for (const segment of pattern.split(/[\\/]/).filter((s) => s && s !== ".")) {
|
|
87
|
+
const next = [];
|
|
88
|
+
for (const dir of dirs) {
|
|
89
|
+
if (segment !== "*") {
|
|
90
|
+
const p = path.join(dir, segment);
|
|
91
|
+
if (await isDir(p))
|
|
92
|
+
next.push(p);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
for (const e of entries)
|
|
103
|
+
if (e.isDirectory() && !e.name.startsWith("."))
|
|
104
|
+
next.push(path.join(dir, e.name));
|
|
105
|
+
}
|
|
106
|
+
dirs = next.sort();
|
|
107
|
+
}
|
|
108
|
+
for (const dir of dirs) {
|
|
109
|
+
if (found.size >= MAX_REPOS)
|
|
110
|
+
break;
|
|
111
|
+
if ((await exists(path.join(dir, ".git"))) && (await within(realRoot, dir)))
|
|
112
|
+
found.add(dir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return [...found].sort();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* What is not yet durable in one repository: uncommitted changes, and commits
|
|
119
|
+
* that are not on its remote. A branch with no upstream is checked against the
|
|
120
|
+
* remote itself, because a single-branch clone pushed with `git push -u` can
|
|
121
|
+
* have no remote-tracking ref and would otherwise always read as unpushed.
|
|
122
|
+
*/
|
|
123
|
+
export async function unpushedProblems(repo, git = runGit) {
|
|
124
|
+
const problems = [];
|
|
125
|
+
const status = await git(repo, ["status", "--porcelain=v1", "--untracked-files=normal"]);
|
|
126
|
+
if (status === undefined)
|
|
127
|
+
return problems;
|
|
128
|
+
const changed = status.split("\n").filter((l) => l.trim()).length;
|
|
129
|
+
if (changed > 0)
|
|
130
|
+
problems.push(`${changed} uncommitted change${changed === 1 ? "" : "s"}`);
|
|
131
|
+
const branch = (await git(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]))?.trim();
|
|
132
|
+
if (!branch)
|
|
133
|
+
return problems; // Detached HEAD: no branch to push.
|
|
134
|
+
const head = (await git(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]))?.trim();
|
|
135
|
+
if (!head)
|
|
136
|
+
return problems; // No commits yet.
|
|
137
|
+
const upstream = (await git(repo, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]))?.trim();
|
|
138
|
+
if (upstream) {
|
|
139
|
+
const ahead = Number((await git(repo, ["rev-list", "--count", "@{upstream}..HEAD"]))?.trim());
|
|
140
|
+
if (ahead > 0)
|
|
141
|
+
problems.push(`${ahead} commit${ahead === 1 ? "" : "s"} on ${branch} not pushed to ${upstream}`);
|
|
142
|
+
return problems;
|
|
143
|
+
}
|
|
144
|
+
const remotes = (await git(repo, ["remote"]))?.split("\n").map((r) => r.trim()).filter(Boolean) ?? [];
|
|
145
|
+
if (remotes.length === 0)
|
|
146
|
+
return problems; // A local-only repository is a choice, not a lapse.
|
|
147
|
+
// Commits every remote-tracking ref already contains were never ours to push.
|
|
148
|
+
const local = Number((await git(repo, ["rev-list", "--count", "HEAD", "--not", "--remotes"]))?.trim());
|
|
149
|
+
if (!(local > 0))
|
|
150
|
+
return problems;
|
|
151
|
+
const remote = remotes.includes("origin") ? "origin" : remotes[0];
|
|
152
|
+
const listed = await git(repo, ["ls-remote", "--heads", remote, `refs/heads/${branch}`], REMOTE_TIMEOUT_MS);
|
|
153
|
+
if (listed === undefined)
|
|
154
|
+
return problems; // Offline or no access: report nothing rather than guess.
|
|
155
|
+
const remoteSha = listed.trim().split(/\s+/)[0];
|
|
156
|
+
if (!remoteSha)
|
|
157
|
+
problems.push(`branch ${branch} is not on ${remote} (${local} local commit${local === 1 ? "" : "s"})`);
|
|
158
|
+
else if (remoteSha !== head) {
|
|
159
|
+
// The remote may be ahead (someone else pushed) rather than behind; only
|
|
160
|
+
// a remote tip that HEAD does not contain is unpushed work we can name.
|
|
161
|
+
// A remote tip this clone never fetched cannot be compared: say nothing.
|
|
162
|
+
const known = await git(repo, ["cat-file", "-e", `${remoteSha}^{commit}`]);
|
|
163
|
+
const contains = known === undefined ? "" : await git(repo, ["merge-base", "--is-ancestor", head, remoteSha]);
|
|
164
|
+
if (contains === undefined)
|
|
165
|
+
problems.push(`${remote}/${branch} is at ${remoteSha.slice(0, 12)}, local HEAD ${head.slice(0, 12)} is not on it`);
|
|
166
|
+
}
|
|
167
|
+
return problems;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Every listed repository's problems; repositories with none are omitted.
|
|
171
|
+
* Each git call gets only what is left of the shared budget, and repositories
|
|
172
|
+
* reached after it runs out are not checked.
|
|
173
|
+
*/
|
|
174
|
+
export async function stopFindings(args, git = runGit, budgetMs = STOP_BUDGET_MS) {
|
|
175
|
+
if (!args.checks.includes("unpushed"))
|
|
176
|
+
return [];
|
|
177
|
+
const deadline = Date.now() + budgetMs;
|
|
178
|
+
const bounded = (cwd, gitArgs, timeoutMs = GIT_TIMEOUT_MS) => {
|
|
179
|
+
const left = deadline - Date.now();
|
|
180
|
+
return left <= 0 ? Promise.resolve(undefined) : git(cwd, gitArgs, Math.min(timeoutMs, left));
|
|
181
|
+
};
|
|
182
|
+
const findings = [];
|
|
183
|
+
for (const repo of await expandRepos(args.root, args.repos)) {
|
|
184
|
+
if (Date.now() >= deadline)
|
|
185
|
+
break;
|
|
186
|
+
const problems = await unpushedProblems(repo, bounded);
|
|
187
|
+
if (problems.length)
|
|
188
|
+
findings.push({ repo: path.relative(args.root, repo) || ".", problems });
|
|
189
|
+
}
|
|
190
|
+
return findings;
|
|
191
|
+
}
|
|
192
|
+
/** The text Claude reads when the stop is blocked. */
|
|
193
|
+
export function stopReason(findings, source) {
|
|
194
|
+
const lines = findings.map((f) => `- ${f.repo}: ${f.problems.join("; ")}`);
|
|
195
|
+
return [
|
|
196
|
+
`Ditto stop check (${source}): work in this repository is not pushed yet.`,
|
|
197
|
+
...lines,
|
|
198
|
+
"Commit and push it to its branch (open a draft PR if it is unfinished), or tell the user why it is being left. Ditto asks once; the turn can end after your next reply.",
|
|
199
|
+
].join("\n");
|
|
200
|
+
}
|
|
201
|
+
/** The hook's stdout for one Stop input, or "" to let the turn end. */
|
|
202
|
+
export async function stopHookOutput(input, args, git = runGit) {
|
|
203
|
+
// Already continuing because a Stop hook blocked: never block twice.
|
|
204
|
+
if (input?.stop_hook_active === true)
|
|
205
|
+
return "";
|
|
206
|
+
const findings = await stopFindings(args, git);
|
|
207
|
+
if (findings.length === 0)
|
|
208
|
+
return "";
|
|
209
|
+
return JSON.stringify({ decision: "block", reason: stopReason(findings, args.source) });
|
|
210
|
+
}
|
|
211
|
+
function readStdin(timeoutMs) {
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
if (process.stdin.isTTY)
|
|
214
|
+
return resolve("");
|
|
215
|
+
let data = "";
|
|
216
|
+
const timer = setTimeout(() => resolve(data), timeoutMs);
|
|
217
|
+
process.stdin.setEncoding("utf8");
|
|
218
|
+
process.stdin.on("data", (c) => (data += c));
|
|
219
|
+
process.stdin.on("end", () => {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
resolve(data);
|
|
222
|
+
});
|
|
223
|
+
process.stdin.on("error", () => {
|
|
224
|
+
clearTimeout(timer);
|
|
225
|
+
resolve(data);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The Stop input. No input at all reads as a first stop. Input that cannot be
|
|
231
|
+
* parsed (cut short, or not JSON) returns null: the hook cannot tell whether
|
|
232
|
+
* it already blocked this stop, so it must not block it.
|
|
233
|
+
*/
|
|
234
|
+
export function hookInput(raw) {
|
|
235
|
+
if (!raw.trim())
|
|
236
|
+
return undefined;
|
|
237
|
+
try {
|
|
238
|
+
const v = JSON.parse(raw);
|
|
239
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function main() {
|
|
246
|
+
const args = decodeHookArgs(process.argv[2] ?? "");
|
|
247
|
+
if (!args)
|
|
248
|
+
return;
|
|
249
|
+
const input = hookInput(await readStdin(5000));
|
|
250
|
+
if (input === null)
|
|
251
|
+
return;
|
|
252
|
+
const out = await stopHookOutput(input, args);
|
|
253
|
+
// stdout is an asynchronous pipe on macOS: exit only once it is flushed.
|
|
254
|
+
if (out)
|
|
255
|
+
await new Promise((resolve) => process.stdout.write(`${out}\n`, () => resolve()));
|
|
256
|
+
}
|
|
257
|
+
/** Run as a script (by Claude Code), not imported (by tests). */
|
|
258
|
+
function invokedDirectly() {
|
|
259
|
+
try {
|
|
260
|
+
return Boolean(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (invokedDirectly()) {
|
|
267
|
+
main()
|
|
268
|
+
.catch(() => undefined)
|
|
269
|
+
.finally(() => process.exit(0));
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=claude-stop-hook.js.map
|