@primitive.ai/prim 0.1.0-alpha.90 → 0.1.0-alpha.91
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 +20 -12
- package/SKILL.md +6 -6
- package/dist/{chunk-LMRYZL3D.js → chunk-5AFRMOPX.js} +1 -1
- package/dist/{chunk-RO3F6JYV.js → chunk-5GYQ4J3D.js} +2 -2
- package/dist/{chunk-3EEFL6NJ.js → chunk-67VNLLBN.js} +261 -1
- package/dist/{chunk-LEMX3IOG.js → chunk-BTNNEH2W.js} +1 -1
- package/dist/chunk-EWZMDD44.js +1474 -0
- package/dist/{chunk-YWO7O6QG.js → chunk-FBAYCS3X.js} +2 -2
- package/dist/{chunk-R26A7TWY.js → chunk-FKXFUUWJ.js} +1 -1
- package/dist/{chunk-C7T7B45R.js → chunk-FOLKHXLJ.js} +150 -21
- package/dist/chunk-H4UNYFUC.js +196 -0
- package/dist/chunk-HU4PW6I6.js +264 -0
- package/dist/{chunk-CUE2LKLR.js → chunk-KFXF2V5G.js} +1 -1
- package/dist/{chunk-6IWNH776.js → chunk-LFFLHSQO.js} +4 -3
- package/dist/{chunk-MAZWKJGL.js → chunk-M6RIJVRE.js} +42 -232
- package/dist/{chunk-2JRX7ZR4.js → chunk-NAHHFSLH.js} +16 -5
- package/dist/{chunk-FI3LIRCB.js → chunk-OAAP2MY6.js} +2 -2
- package/dist/{chunk-Z7YPFWAZ.js → chunk-PCVGBE5B.js} +10 -268
- package/dist/{chunk-UZPBB7V4.js → chunk-VW3FDYRT.js} +60 -48
- package/dist/chunk-WX52U7Z2.js +112 -0
- package/dist/{chunk-PPQYQEIR.js → chunk-ZB4F75SZ.js} +28 -0
- package/dist/daemon/server.js +20 -20
- package/dist/hook-runtime/daemon/server.js +20 -20
- package/dist/hook-runtime/hooks/post-commit.js +20 -20
- package/dist/hook-runtime/hooks/post-rewrite.js +20 -20
- package/dist/hook-runtime/hooks/post-tool-use.js +651 -132
- package/dist/hook-runtime/hooks/pre-tool-use.js +644 -96
- package/dist/hook-runtime/hooks/prim-hook.js +612 -96
- package/dist/hook-runtime/hooks/session-end.js +610 -27
- package/dist/hook-runtime/hooks/session-start.js +1132 -533
- package/dist/hook-runtime/index.js +735 -264
- package/dist/hook-runtime/statusline-main.js +48 -4
- package/dist/hooks/post-commit.js +11 -9
- package/dist/hooks/post-rewrite.js +11 -9
- package/dist/hooks/post-tool-use.js +73 -48
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +46 -44
- package/dist/hooks/prim-hook.js +47 -25
- package/dist/hooks/session-end.js +15 -26
- package/dist/hooks/session-start.js +59 -32
- package/dist/index.js +558 -1436
- package/dist/statusline-main.js +48 -4
- package/package.json +1 -1
- package/dist/chunk-PMRDE2DA.js +0 -310
- package/dist/chunk-WB7AUZ4C.js +0 -52
|
@@ -524,7 +524,7 @@ var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
|
|
|
524
524
|
var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
|
|
525
525
|
var GIT_TIMEOUT_MS2 = 1e3;
|
|
526
526
|
var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
527
|
-
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
|
|
527
|
+
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json", ".cursor/hooks.json"];
|
|
528
528
|
var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
|
|
529
529
|
function repoActiveFlag(cwd) {
|
|
530
530
|
try {
|
|
@@ -731,9 +731,8 @@ function debug(msg) {
|
|
|
731
731
|
`);
|
|
732
732
|
}
|
|
733
733
|
}
|
|
734
|
-
async function renderStatusline() {
|
|
734
|
+
async function renderStatusline(cwd = process.cwd()) {
|
|
735
735
|
const version = readPackageVersion();
|
|
736
|
-
const cwd = process.cwd();
|
|
737
736
|
let ingestion;
|
|
738
737
|
const resolveIngestion = () => {
|
|
739
738
|
ingestion ??= decisionIngestionStatus(cwd);
|
|
@@ -754,10 +753,55 @@ async function renderStatusline() {
|
|
|
754
753
|
});
|
|
755
754
|
}
|
|
756
755
|
|
|
756
|
+
// src/hooks/hook-stdin.ts
|
|
757
|
+
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
758
|
+
function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
|
|
759
|
+
return new Promise((resolve4, reject) => {
|
|
760
|
+
const chunks = [];
|
|
761
|
+
let bytes = 0;
|
|
762
|
+
let settled = false;
|
|
763
|
+
const finish = (error) => {
|
|
764
|
+
if (settled) return;
|
|
765
|
+
settled = true;
|
|
766
|
+
clearTimeout(timer);
|
|
767
|
+
process.stdin.removeAllListeners("data");
|
|
768
|
+
process.stdin.removeAllListeners("end");
|
|
769
|
+
process.stdin.removeAllListeners("error");
|
|
770
|
+
if (error) reject(error);
|
|
771
|
+
else resolve4(Buffer.concat(chunks, bytes).toString("utf-8"));
|
|
772
|
+
};
|
|
773
|
+
const timer = setTimeout(() => finish(new Error("stdin read timeout")), timeoutMs);
|
|
774
|
+
process.stdin.on("data", (chunk) => {
|
|
775
|
+
bytes += chunk.length;
|
|
776
|
+
if (bytes > maxBytes) {
|
|
777
|
+
finish(new Error("stdin envelope exceeds size limit"));
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
chunks.push(chunk);
|
|
781
|
+
});
|
|
782
|
+
process.stdin.on("end", () => finish());
|
|
783
|
+
process.stdin.on("error", (error) => finish(error));
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
|
|
757
787
|
// src/statusline-main.ts
|
|
788
|
+
async function inputCwd() {
|
|
789
|
+
try {
|
|
790
|
+
const raw = await readHookStdin(200, 64 * 1024);
|
|
791
|
+
const parsed = JSON.parse(raw);
|
|
792
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
793
|
+
const input = parsed;
|
|
794
|
+
if (typeof input.cwd === "string" && input.cwd.length > 0) return input.cwd;
|
|
795
|
+
if (input.workspace && typeof input.workspace === "object" && !Array.isArray(input.workspace)) {
|
|
796
|
+
const current = input.workspace.current_dir;
|
|
797
|
+
if (typeof current === "string" && current.length > 0) return current;
|
|
798
|
+
}
|
|
799
|
+
} catch {
|
|
800
|
+
}
|
|
801
|
+
}
|
|
758
802
|
async function main() {
|
|
759
803
|
try {
|
|
760
|
-
process.stdout.write(await renderStatusline());
|
|
804
|
+
process.stdout.write(await renderStatusline(await inputCwd() ?? process.cwd()));
|
|
761
805
|
} catch {
|
|
762
806
|
process.stdout.write(`primitive ${readPackageVersion()} (daemon: unavailable)`);
|
|
763
807
|
}
|
|
@@ -2,25 +2,27 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commitAttributionFromEnvironment,
|
|
4
4
|
toCommitMove
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-NAHHFSLH.js";
|
|
6
6
|
import {
|
|
7
7
|
getOrCreateWorkspaceId
|
|
8
8
|
} from "../chunk-EZXEWFDG.js";
|
|
9
9
|
import {
|
|
10
10
|
appendMove,
|
|
11
11
|
resolveOrg
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-FBAYCS3X.js";
|
|
13
|
+
import "../chunk-OAAP2MY6.js";
|
|
14
|
+
import "../chunk-BTNNEH2W.js";
|
|
15
15
|
import {
|
|
16
16
|
cachedCollectScopeAdmits,
|
|
17
|
-
githubRepositoryFullName,
|
|
18
17
|
isRepoActiveForCapture,
|
|
19
|
-
repoSyncId
|
|
18
|
+
repoSyncId
|
|
19
|
+
} from "../chunk-M6RIJVRE.js";
|
|
20
|
+
import "../chunk-PCVGBE5B.js";
|
|
21
|
+
import {
|
|
22
|
+
githubRepositoryFullName,
|
|
20
23
|
resolveRepositoryContext
|
|
21
|
-
} from "../chunk-
|
|
22
|
-
import "../chunk-
|
|
23
|
-
import "../chunk-3EEFL6NJ.js";
|
|
24
|
+
} from "../chunk-H4UNYFUC.js";
|
|
25
|
+
import "../chunk-67VNLLBN.js";
|
|
24
26
|
|
|
25
27
|
// src/hooks/post-commit.ts
|
|
26
28
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -2,25 +2,27 @@
|
|
|
2
2
|
import {
|
|
3
3
|
canonicalRewritePairs,
|
|
4
4
|
toRewriteMove
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-NAHHFSLH.js";
|
|
6
6
|
import {
|
|
7
7
|
getOrCreateWorkspaceId
|
|
8
8
|
} from "../chunk-EZXEWFDG.js";
|
|
9
9
|
import {
|
|
10
10
|
appendMove,
|
|
11
11
|
resolveOrg
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-FBAYCS3X.js";
|
|
13
|
+
import "../chunk-OAAP2MY6.js";
|
|
14
|
+
import "../chunk-BTNNEH2W.js";
|
|
15
15
|
import {
|
|
16
16
|
cachedCollectScopeAdmits,
|
|
17
|
-
githubRepositoryFullName,
|
|
18
17
|
isRepoActiveForCapture,
|
|
19
|
-
repoSyncId
|
|
18
|
+
repoSyncId
|
|
19
|
+
} from "../chunk-M6RIJVRE.js";
|
|
20
|
+
import "../chunk-PCVGBE5B.js";
|
|
21
|
+
import {
|
|
22
|
+
githubRepositoryFullName,
|
|
20
23
|
resolveRepositoryContext
|
|
21
|
-
} from "../chunk-
|
|
22
|
-
import "../chunk-
|
|
23
|
-
import "../chunk-3EEFL6NJ.js";
|
|
24
|
+
} from "../chunk-H4UNYFUC.js";
|
|
25
|
+
import "../chunk-67VNLLBN.js";
|
|
24
26
|
|
|
25
27
|
// src/hooks/post-rewrite.ts
|
|
26
28
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -1,53 +1,58 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
requireDurableIngestAcknowledgement
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-FKXFUUWJ.js";
|
|
5
5
|
import {
|
|
6
6
|
enrichHookPayloadWithFileRefs,
|
|
7
7
|
preserveHookFileMetadata
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-LFFLHSQO.js";
|
|
9
9
|
import {
|
|
10
10
|
postToolInvocationId,
|
|
11
11
|
toMove,
|
|
12
12
|
toolOutcomeFor
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-NAHHFSLH.js";
|
|
14
14
|
import {
|
|
15
15
|
getOrCreateWorkspaceId
|
|
16
16
|
} from "../chunk-EZXEWFDG.js";
|
|
17
17
|
import {
|
|
18
18
|
appendMove,
|
|
19
19
|
resolveOrg
|
|
20
|
-
} from "../chunk-
|
|
21
|
-
import "../chunk-
|
|
20
|
+
} from "../chunk-FBAYCS3X.js";
|
|
21
|
+
import "../chunk-ZB4F75SZ.js";
|
|
22
22
|
import {
|
|
23
23
|
appendCodexContext,
|
|
24
24
|
prepareCodexContext
|
|
25
|
-
} from "../chunk-
|
|
26
|
-
import "../chunk-
|
|
25
|
+
} from "../chunk-VW3FDYRT.js";
|
|
26
|
+
import "../chunk-WX52U7Z2.js";
|
|
27
27
|
import {
|
|
28
28
|
scrubFromCwd
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-OAAP2MY6.js";
|
|
30
30
|
import {
|
|
31
31
|
bold,
|
|
32
32
|
color
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-BTNNEH2W.js";
|
|
34
34
|
import {
|
|
35
35
|
cachedCollectScopeAdmits,
|
|
36
|
-
currentBranch,
|
|
37
36
|
isRepoActiveForCapture,
|
|
38
|
-
repoSyncId
|
|
39
|
-
|
|
40
|
-
} from "../chunk-MAZWKJGL.js";
|
|
37
|
+
repoSyncId
|
|
38
|
+
} from "../chunk-M6RIJVRE.js";
|
|
41
39
|
import {
|
|
42
40
|
getClient,
|
|
43
41
|
terminalSafeLine
|
|
44
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-PCVGBE5B.js";
|
|
45
43
|
import {
|
|
46
44
|
normalizeEnvelope,
|
|
47
|
-
parseAgent
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
parseAgent,
|
|
46
|
+
readHookStdin,
|
|
47
|
+
shouldSuppressImportedCursorHandler
|
|
48
|
+
} from "../chunk-HU4PW6I6.js";
|
|
49
|
+
import "../chunk-EWZMDD44.js";
|
|
50
|
+
import {
|
|
51
|
+
currentBranch,
|
|
52
|
+
resolveRepositoryContext
|
|
53
|
+
} from "../chunk-H4UNYFUC.js";
|
|
54
|
+
import "../chunk-KFXF2V5G.js";
|
|
55
|
+
import "../chunk-67VNLLBN.js";
|
|
51
56
|
|
|
52
57
|
// src/hooks/post-tool-use.ts
|
|
53
58
|
import { readFileSync } from "fs";
|
|
@@ -87,6 +92,7 @@ var STDIN_TIMEOUT_MS = 1e3;
|
|
|
87
92
|
var EDITING_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit", "Bash"]);
|
|
88
93
|
var CODEX_EDITING_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Bash"]);
|
|
89
94
|
var HERMES_EDITING_TOOLS = /* @__PURE__ */ new Set(["write_file", "patch"]);
|
|
95
|
+
var CURSOR_EDITING_TOOLS = /* @__PURE__ */ new Set(["Write", "Delete", "Shell"]);
|
|
90
96
|
function editingToolsFor(agent) {
|
|
91
97
|
if (agent === "codex") {
|
|
92
98
|
return CODEX_EDITING_TOOLS;
|
|
@@ -94,6 +100,9 @@ function editingToolsFor(agent) {
|
|
|
94
100
|
if (agent === "hermes") {
|
|
95
101
|
return HERMES_EDITING_TOOLS;
|
|
96
102
|
}
|
|
103
|
+
if (agent === "cursor") {
|
|
104
|
+
return CURSOR_EDITING_TOOLS;
|
|
105
|
+
}
|
|
97
106
|
return EDITING_TOOLS;
|
|
98
107
|
}
|
|
99
108
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -105,23 +114,6 @@ function resolveCliVersion() {
|
|
|
105
114
|
return "unknown";
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
|
-
function readStdin() {
|
|
109
|
-
return new Promise((resolve, reject) => {
|
|
110
|
-
const chunks = [];
|
|
111
|
-
const timer = setTimeout(() => {
|
|
112
|
-
reject(new Error("stdin read timeout"));
|
|
113
|
-
}, STDIN_TIMEOUT_MS);
|
|
114
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
115
|
-
process.stdin.on("end", () => {
|
|
116
|
-
clearTimeout(timer);
|
|
117
|
-
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
118
|
-
});
|
|
119
|
-
process.stdin.on("error", (err) => {
|
|
120
|
-
clearTimeout(timer);
|
|
121
|
-
reject(err);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
117
|
async function emit(output = {}) {
|
|
126
118
|
return await new Promise((resolve) => {
|
|
127
119
|
try {
|
|
@@ -136,6 +128,30 @@ async function emitWithAcknowledgment(output, acknowledge) {
|
|
|
136
128
|
const handedOff = await emit(output);
|
|
137
129
|
await acknowledge?.(handedOff);
|
|
138
130
|
}
|
|
131
|
+
async function emitCursorContext(envelope) {
|
|
132
|
+
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0 || typeof envelope.cwd !== "string" || envelope.cwd.length === 0) {
|
|
133
|
+
await emit();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const context = await prepareCodexContext({
|
|
138
|
+
cwd: envelope.cwd,
|
|
139
|
+
sessionId: envelope.session_id,
|
|
140
|
+
includeDigest: true,
|
|
141
|
+
namespace: "cursor"
|
|
142
|
+
});
|
|
143
|
+
await emitWithAcknowledgment(
|
|
144
|
+
context.context ? { additional_context: context.context } : {},
|
|
145
|
+
context.acknowledge
|
|
146
|
+
);
|
|
147
|
+
} catch {
|
|
148
|
+
await emit();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function finish(agent, envelope) {
|
|
152
|
+
if (agent === "cursor" && envelope) await emitCursorContext(envelope);
|
|
153
|
+
else await emit();
|
|
154
|
+
}
|
|
139
155
|
function debug(msg) {
|
|
140
156
|
if (process.env.PRIM_HOOK_VERBOSE === "1") {
|
|
141
157
|
process.stderr.write(`[prim-post-tool-use] ${msg}
|
|
@@ -146,33 +162,38 @@ async function main() {
|
|
|
146
162
|
const agent = parseAgent(process.argv);
|
|
147
163
|
let raw;
|
|
148
164
|
try {
|
|
149
|
-
raw = await
|
|
165
|
+
raw = await readHookStdin(STDIN_TIMEOUT_MS);
|
|
150
166
|
} catch {
|
|
151
167
|
await emit();
|
|
152
168
|
return;
|
|
153
169
|
}
|
|
154
170
|
let parsed;
|
|
155
171
|
try {
|
|
156
|
-
|
|
172
|
+
const incoming = JSON.parse(raw);
|
|
173
|
+
if (agent !== "cursor" && shouldSuppressImportedCursorHandler(incoming, "prim-post-tool-use")) {
|
|
174
|
+
await emit();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
parsed = normalizeEnvelope(incoming, agent);
|
|
157
178
|
} catch {
|
|
158
179
|
await emit();
|
|
159
180
|
return;
|
|
160
181
|
}
|
|
161
182
|
let envelope = parsed;
|
|
162
|
-
const isToolResult = envelope.hook_event_name === "PostToolUse" || agent === "claude_code" && envelope.hook_event_name === "PostToolUseFailure";
|
|
183
|
+
const isToolResult = envelope.hook_event_name === "PostToolUse" || (agent === "claude_code" || agent === "cursor") && envelope.hook_event_name === "PostToolUseFailure";
|
|
163
184
|
const isHermesDenial = agent === "hermes" && envelope.hook_event_name === "post_approval_response" && toolOutcomeFor(parsed, agent) === "prevented";
|
|
164
185
|
if (!isToolResult && !isHermesDenial) {
|
|
165
|
-
await
|
|
186
|
+
await finish(agent, envelope);
|
|
166
187
|
return;
|
|
167
188
|
}
|
|
168
189
|
const invocationId = postToolInvocationId(parsed, agent);
|
|
169
190
|
if (isHermesDenial && !invocationId) {
|
|
170
|
-
await
|
|
191
|
+
await finish(agent, envelope);
|
|
171
192
|
return;
|
|
172
193
|
}
|
|
173
194
|
const toolName = typeof envelope.tool_name === "string" ? envelope.tool_name : "";
|
|
174
195
|
if (!isHermesDenial && !editingToolsFor(agent).has(toolName)) {
|
|
175
|
-
await
|
|
196
|
+
await finish(agent, envelope);
|
|
176
197
|
return;
|
|
177
198
|
}
|
|
178
199
|
if (isHermesDenial && (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) && typeof envelope.extra?.session_key === "string" && envelope.extra.session_key.length > 0) {
|
|
@@ -180,17 +201,17 @@ async function main() {
|
|
|
180
201
|
envelope = parsed;
|
|
181
202
|
}
|
|
182
203
|
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
|
|
183
|
-
await
|
|
204
|
+
await finish(agent, envelope);
|
|
184
205
|
return;
|
|
185
206
|
}
|
|
186
207
|
const cwd = parsed.cwd ?? process.cwd();
|
|
187
208
|
if (!isRepoActiveForCapture(cwd)) {
|
|
188
|
-
await
|
|
209
|
+
await finish(agent, envelope);
|
|
189
210
|
return;
|
|
190
211
|
}
|
|
191
212
|
const resolvedRepository = resolveRepositoryContext(cwd);
|
|
192
213
|
if (!resolvedRepository) {
|
|
193
|
-
await
|
|
214
|
+
await finish(agent, envelope);
|
|
194
215
|
return;
|
|
195
216
|
}
|
|
196
217
|
const repository = { ...resolvedRepository, repoSyncId: repoSyncId(cwd) };
|
|
@@ -203,23 +224,27 @@ async function main() {
|
|
|
203
224
|
const { resolution } = enrichment;
|
|
204
225
|
if (!isHermesDenial) {
|
|
205
226
|
if (resolution.shellMutation === "none") {
|
|
206
|
-
await
|
|
227
|
+
await finish(agent, envelope);
|
|
207
228
|
return;
|
|
208
229
|
}
|
|
209
230
|
if (resolution.shellMutation === void 0 && resolution.fileRefs.length === 0) {
|
|
210
|
-
await
|
|
231
|
+
await finish(agent, envelope);
|
|
211
232
|
return;
|
|
212
233
|
}
|
|
213
234
|
}
|
|
214
235
|
const pathsComplete = !resolution.targetsIncomplete && !resolution.targetsTruncated && resolution.rejected.length === 0 && resolution.shellMutation !== "unresolved";
|
|
215
236
|
const hasPathEvidence = resolution.fileRefs.length > 0 || resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation === "unresolved";
|
|
237
|
+
if (agent === "cursor" && (resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation === "unresolved")) {
|
|
238
|
+
await finish(agent, envelope);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
216
241
|
if (!cachedCollectScopeAdmits(cwd, {
|
|
217
242
|
repository: resolvedRepository.repoFullName,
|
|
218
243
|
branch: currentBranch(cwd),
|
|
219
244
|
agent,
|
|
220
245
|
...isHermesDenial || !hasPathEvidence ? {} : { paths: resolution.fileRefs, pathsComplete }
|
|
221
246
|
})) {
|
|
222
|
-
await
|
|
247
|
+
await finish(agent, envelope);
|
|
223
248
|
return;
|
|
224
249
|
}
|
|
225
250
|
const enriched = enrichment.parsed;
|
|
@@ -256,7 +281,7 @@ async function main() {
|
|
|
256
281
|
} catch {
|
|
257
282
|
}
|
|
258
283
|
}
|
|
259
|
-
await
|
|
284
|
+
await finish(agent, envelope);
|
|
260
285
|
}
|
|
261
286
|
main().catch(async () => {
|
|
262
287
|
await emit();
|
package/dist/hooks/pre-commit.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
checkAffectedDecisions,
|
|
4
4
|
formatDecisionsWarning
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
5
|
+
} from "../chunk-5GYQ4J3D.js";
|
|
6
|
+
import "../chunk-PCVGBE5B.js";
|
|
7
|
+
import "../chunk-KFXF2V5G.js";
|
|
8
|
+
import "../chunk-67VNLLBN.js";
|
|
9
9
|
|
|
10
10
|
// src/hooks/pre-commit.ts
|
|
11
11
|
import { execSync } from "child_process";
|
|
@@ -2,53 +2,60 @@
|
|
|
2
2
|
import {
|
|
3
3
|
analyzeShellTargets,
|
|
4
4
|
buildCodexOutput,
|
|
5
|
+
buildCursorOutput,
|
|
5
6
|
buildHermesOutput,
|
|
6
7
|
buildHookOutput,
|
|
7
8
|
demoteForMode,
|
|
8
9
|
extractFileTargets,
|
|
9
10
|
failOpenCodex,
|
|
11
|
+
failOpenCursor,
|
|
10
12
|
failOpenHermes,
|
|
11
13
|
failOpenOutput,
|
|
12
14
|
readHookMode
|
|
13
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-ZB4F75SZ.js";
|
|
14
16
|
import {
|
|
15
17
|
appendCodexContext,
|
|
16
18
|
hasVisibleCodexMessage,
|
|
17
19
|
prepareCodexContext
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import
|
|
20
|
-
packageVersion
|
|
21
|
-
} from "../chunk-PMRDE2DA.js";
|
|
20
|
+
} from "../chunk-VW3FDYRT.js";
|
|
21
|
+
import "../chunk-WX52U7Z2.js";
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_RULES,
|
|
24
24
|
scrub
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-OAAP2MY6.js";
|
|
26
26
|
import {
|
|
27
27
|
boundedHealthError
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-BTNNEH2W.js";
|
|
29
29
|
import {
|
|
30
30
|
cachedCollectScopeAdmits,
|
|
31
|
-
canonicalGitRoot,
|
|
32
|
-
canonicalRepositoryPath,
|
|
33
|
-
currentBranch,
|
|
34
|
-
githubRepositoryFullName,
|
|
35
31
|
isRepoActive,
|
|
36
32
|
repoSyncId
|
|
37
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-M6RIJVRE.js";
|
|
38
34
|
import {
|
|
39
35
|
getClient
|
|
40
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-PCVGBE5B.js";
|
|
41
37
|
import {
|
|
42
38
|
normalizeEnvelope,
|
|
43
|
-
parseAgent
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
parseAgent,
|
|
40
|
+
readHookStdin,
|
|
41
|
+
shouldSuppressImportedCursorHandler
|
|
42
|
+
} from "../chunk-HU4PW6I6.js";
|
|
43
|
+
import {
|
|
44
|
+
packageVersion
|
|
45
|
+
} from "../chunk-EWZMDD44.js";
|
|
46
|
+
import {
|
|
47
|
+
canonicalGitRoot,
|
|
48
|
+
canonicalRepositoryPath,
|
|
49
|
+
currentBranch,
|
|
50
|
+
githubRepositoryFullName
|
|
51
|
+
} from "../chunk-H4UNYFUC.js";
|
|
52
|
+
import "../chunk-KFXF2V5G.js";
|
|
53
|
+
import "../chunk-67VNLLBN.js";
|
|
47
54
|
|
|
48
55
|
// src/hooks/preflight-v3.ts
|
|
49
|
-
import { Buffer
|
|
56
|
+
import { Buffer } from "buffer";
|
|
50
57
|
var PREFLIGHT_PROTOCOL_VERSION = 3;
|
|
51
|
-
var PREFLIGHT_TIMEOUT_MS =
|
|
58
|
+
var PREFLIGHT_TIMEOUT_MS = 6500;
|
|
52
59
|
var MAX_PREFLIGHT_PATHS = 32;
|
|
53
60
|
var MAX_PROPOSAL_BYTES = 6144;
|
|
54
61
|
var MAX_CLIENT_VERSION_CHARS = 32;
|
|
@@ -64,7 +71,8 @@ function resolvePreflightTargets(args) {
|
|
|
64
71
|
let coverage = "complete";
|
|
65
72
|
let mutation = "present";
|
|
66
73
|
let definite = false;
|
|
67
|
-
|
|
74
|
+
const shellTool = (args.agent === "claude_code" || args.agent === "codex") && args.toolName === "Bash" || args.agent === "cursor" && args.toolName === "Shell";
|
|
75
|
+
if (shellTool) {
|
|
68
76
|
const command = typeof args.toolInput === "string" ? args.toolInput : args.toolInput && typeof args.toolInput === "object" ? args.toolInput.command : void 0;
|
|
69
77
|
if (typeof command !== "string") {
|
|
70
78
|
return { paths: [], coverage: "unverified", mutation: "present" };
|
|
@@ -97,7 +105,7 @@ function resolvePreflightTargets(args) {
|
|
|
97
105
|
return { paths: resolved, coverage, mutation };
|
|
98
106
|
}
|
|
99
107
|
function truncateUtf8(value, maxBytes) {
|
|
100
|
-
const encoded =
|
|
108
|
+
const encoded = Buffer.from(value);
|
|
101
109
|
if (encoded.byteLength <= maxBytes) return value;
|
|
102
110
|
return encoded.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD$/u, "");
|
|
103
111
|
}
|
|
@@ -174,23 +182,6 @@ function unverifiedResult(message) {
|
|
|
174
182
|
|
|
175
183
|
// src/hooks/pre-tool-use.ts
|
|
176
184
|
var STDIN_TIMEOUT_MS = 1e3;
|
|
177
|
-
async function readStdin() {
|
|
178
|
-
return new Promise((resolve, reject) => {
|
|
179
|
-
const chunks = [];
|
|
180
|
-
const timer = setTimeout(() => {
|
|
181
|
-
reject(new Error("stdin read timeout"));
|
|
182
|
-
}, STDIN_TIMEOUT_MS);
|
|
183
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
184
|
-
process.stdin.on("end", () => {
|
|
185
|
-
clearTimeout(timer);
|
|
186
|
-
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
187
|
-
});
|
|
188
|
-
process.stdin.on("error", (err) => {
|
|
189
|
-
clearTimeout(timer);
|
|
190
|
-
reject(err);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
185
|
var agent = parseAgent(process.argv);
|
|
195
186
|
async function emit(output) {
|
|
196
187
|
return await new Promise((resolve) => {
|
|
@@ -208,6 +199,7 @@ async function emitWithAcknowledgment(output, acknowledge) {
|
|
|
208
199
|
}
|
|
209
200
|
function failOpen() {
|
|
210
201
|
if (agent === "hermes") return failOpenHermes();
|
|
202
|
+
if (agent === "cursor") return failOpenCursor();
|
|
211
203
|
return agent === "codex" ? failOpenCodex() : failOpenOutput();
|
|
212
204
|
}
|
|
213
205
|
function invocationId(envelope) {
|
|
@@ -232,6 +224,10 @@ async function emitUnverified(message, envelope) {
|
|
|
232
224
|
process.stderr.write(`[primitive] ${message}
|
|
233
225
|
`);
|
|
234
226
|
await emit(failOpenHermes());
|
|
227
|
+
} else if (agent === "cursor") {
|
|
228
|
+
process.stderr.write(`[primitive] ${message}
|
|
229
|
+
`);
|
|
230
|
+
await emit(buildCursorOutput("allow", [result]));
|
|
235
231
|
} else {
|
|
236
232
|
let output = agent === "codex" ? buildCodexOutput("allow", [result]) : buildHookOutput("allow", [result]);
|
|
237
233
|
if (agent === "codex" && typeof envelope?.session_id === "string" && envelope.session_id) {
|
|
@@ -253,17 +249,19 @@ async function emitUnverified(message, envelope) {
|
|
|
253
249
|
async function main() {
|
|
254
250
|
let raw;
|
|
255
251
|
try {
|
|
256
|
-
raw = await
|
|
252
|
+
raw = await readHookStdin(STDIN_TIMEOUT_MS);
|
|
257
253
|
} catch {
|
|
258
254
|
await emit(failOpen());
|
|
259
255
|
return;
|
|
260
256
|
}
|
|
261
257
|
let envelope;
|
|
262
258
|
try {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
259
|
+
const incoming = JSON.parse(raw);
|
|
260
|
+
if (agent !== "cursor" && shouldSuppressImportedCursorHandler(incoming, "prim-pre-tool-use")) {
|
|
261
|
+
await emit(failOpenCursor());
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
envelope = normalizeEnvelope(incoming, agent);
|
|
267
265
|
} catch {
|
|
268
266
|
await emit(failOpen());
|
|
269
267
|
return;
|
|
@@ -298,6 +296,10 @@ async function main() {
|
|
|
298
296
|
await emit(failOpen());
|
|
299
297
|
return;
|
|
300
298
|
}
|
|
299
|
+
if (agent === "cursor" && targets.coverage === "unverified") {
|
|
300
|
+
await emitUnverified("tool targets were ambiguous or outside the active repository", envelope);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
301
303
|
const binding = repoSyncId(cwd);
|
|
302
304
|
const branch = currentBranch(cwd);
|
|
303
305
|
const sessionId = envelope.session_id;
|
|
@@ -350,7 +352,7 @@ async function main() {
|
|
|
350
352
|
if (advisory) process.stderr.write(`${advisory}
|
|
351
353
|
`);
|
|
352
354
|
}
|
|
353
|
-
let output = agent === "hermes" ? buildHermesOutput(aggregate, [result]) : agent === "codex" ? buildCodexOutput(aggregate, [result]) : buildHookOutput(aggregate, [result]);
|
|
355
|
+
let output = agent === "hermes" ? buildHermesOutput(aggregate, [result]) : agent === "cursor" ? buildCursorOutput(aggregate, [result]) : agent === "codex" ? buildCodexOutput(aggregate, [result]) : buildHookOutput(aggregate, [result]);
|
|
354
356
|
if (agent === "codex" && hasVisibleCodexMessage(output) && typeof sessionId === "string" && sessionId.length > 0) {
|
|
355
357
|
try {
|
|
356
358
|
const context = await prepareCodexContext({ cwd, sessionId, includeDigest: false });
|