@primitive.ai/prim 0.1.0-alpha.41 → 0.1.0-alpha.43
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 +64 -6
- package/dist/{chunk-UWAJCF7P.js → chunk-BSZGI5RL.js} +1 -1
- package/dist/{chunk-S2O4P4A3.js → chunk-F5QCFBJ6.js} +14 -3
- package/dist/{chunk-HSZPTVKU.js → chunk-F7O7V6ZM.js} +88 -12
- package/dist/chunk-IMAIBPUC.js +210 -0
- package/dist/{chunk-H4OR42TJ.js → chunk-OKUXEBPT.js} +149 -36
- package/dist/{chunk-AAGJFO7C.js → chunk-QY75BQMF.js} +25 -1
- package/dist/chunk-S5NUHFAD.js +55 -0
- package/dist/chunk-UISU3A7W.js +202 -0
- package/dist/{chunk-26VA3ADF.js → chunk-W6PAKEDO.js} +32 -6
- package/dist/daemon/server.js +1150 -77
- package/dist/hooks/post-commit.js +5 -5
- package/dist/hooks/post-tool-use.js +31 -19
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +3 -5
- package/dist/hooks/prim-hook.js +77 -20
- package/dist/hooks/session-start.js +79 -24
- package/dist/index.js +1609 -320
- package/dist/statusline-main.js +280 -0
- package/package.json +6 -5
- package/dist/chunk-LUPD2JSH.js +0 -78
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
toCommitMove
|
|
4
|
+
} from "../chunk-F5QCFBJ6.js";
|
|
2
5
|
import {
|
|
3
6
|
appendMove,
|
|
4
7
|
resolveOrg
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import
|
|
7
|
-
toCommitMove
|
|
8
|
-
} from "../chunk-S2O4P4A3.js";
|
|
9
|
-
import "../chunk-26VA3ADF.js";
|
|
8
|
+
} from "../chunk-OKUXEBPT.js";
|
|
9
|
+
import "../chunk-W6PAKEDO.js";
|
|
10
10
|
|
|
11
11
|
// src/hooks/post-commit.ts
|
|
12
12
|
import { execSync, spawn } from "child_process";
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
bold,
|
|
4
|
-
color
|
|
5
|
-
|
|
4
|
+
color,
|
|
5
|
+
requireDurableIngestAcknowledgement
|
|
6
|
+
} from "../chunk-QY75BQMF.js";
|
|
6
7
|
import {
|
|
7
8
|
scrubFromCwd
|
|
8
9
|
} from "../chunk-6LAQVM26.js";
|
|
9
10
|
import {
|
|
10
11
|
toMove
|
|
11
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-F5QCFBJ6.js";
|
|
12
13
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
appendMove,
|
|
15
|
+
resolveOrg
|
|
16
|
+
} from "../chunk-OKUXEBPT.js";
|
|
15
17
|
import {
|
|
18
|
+
getOrCreateWorkspaceId
|
|
19
|
+
} from "../chunk-IMAIBPUC.js";
|
|
20
|
+
import {
|
|
21
|
+
isRepoActiveForCapture,
|
|
16
22
|
warmBinCache
|
|
17
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-F7O7V6ZM.js";
|
|
18
24
|
import {
|
|
19
25
|
getClient
|
|
20
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-W6PAKEDO.js";
|
|
21
27
|
import {
|
|
22
28
|
normalizeEnvelope,
|
|
23
29
|
parseAgent
|
|
@@ -28,6 +34,18 @@ import { readFileSync } from "fs";
|
|
|
28
34
|
import { dirname, join } from "path";
|
|
29
35
|
import { fileURLToPath } from "url";
|
|
30
36
|
|
|
37
|
+
// src/hooks/post-tool-delivery.ts
|
|
38
|
+
var INGEST_TIMEOUT_MS = 4e3;
|
|
39
|
+
async function deliverPostToolMove(move, orgId) {
|
|
40
|
+
appendMove(move, orgId);
|
|
41
|
+
const response = await getClient().post(
|
|
42
|
+
"/api/cli/moves/ingest",
|
|
43
|
+
{ batch: [move] },
|
|
44
|
+
{ signal: AbortSignal.timeout(INGEST_TIMEOUT_MS) }
|
|
45
|
+
);
|
|
46
|
+
return requireDurableIngestAcknowledgement(response, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
// src/hooks/verdict-footer.ts
|
|
32
50
|
function renderVerdictFooter(ctx) {
|
|
33
51
|
const successPrefix = color("\u2713 Conflict caught before merge", "green");
|
|
@@ -46,7 +64,6 @@ function isVerdictFooterContext(value) {
|
|
|
46
64
|
|
|
47
65
|
// src/hooks/post-tool-use.ts
|
|
48
66
|
var STDIN_TIMEOUT_MS = 1e3;
|
|
49
|
-
var INGEST_TIMEOUT_MS = 4e3;
|
|
50
67
|
var EDITING_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit"]);
|
|
51
68
|
var CODEX_EDITING_TOOLS = /* @__PURE__ */ new Set(["apply_patch"]);
|
|
52
69
|
var HERMES_EDITING_TOOLS = /* @__PURE__ */ new Set(["write_file", "patch"]);
|
|
@@ -94,14 +111,6 @@ function debug(msg) {
|
|
|
94
111
|
`);
|
|
95
112
|
}
|
|
96
113
|
}
|
|
97
|
-
async function ingestMove(move) {
|
|
98
|
-
const client = getClient();
|
|
99
|
-
return await client.post(
|
|
100
|
-
"/api/cli/moves/ingest",
|
|
101
|
-
{ batch: [move] },
|
|
102
|
-
{ signal: AbortSignal.timeout(INGEST_TIMEOUT_MS) }
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
114
|
async function main() {
|
|
106
115
|
warmBinCache();
|
|
107
116
|
const agent = parseAgent(process.argv);
|
|
@@ -139,11 +148,14 @@ async function main() {
|
|
|
139
148
|
emit();
|
|
140
149
|
return;
|
|
141
150
|
}
|
|
142
|
-
const
|
|
151
|
+
const identity = getOrCreateWorkspaceId(cwd);
|
|
152
|
+
const workspaceId = identity.status === "ready" ? identity.workspaceId : void 0;
|
|
153
|
+
const base = toMove(parsed, resolveCliVersion(), agent, workspaceId);
|
|
143
154
|
const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
|
|
155
|
+
const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd: move.env.cwd });
|
|
144
156
|
try {
|
|
145
|
-
const result = await
|
|
146
|
-
debug(`ingested ${move.moveId} (${toolName})`);
|
|
157
|
+
const result = await deliverPostToolMove(move, orgId);
|
|
158
|
+
debug(`durably ingested ${move.moveId} (${toolName})`);
|
|
147
159
|
if (isVerdictFooterContext(result.verdictFooter)) {
|
|
148
160
|
process.stderr.write(`${renderVerdictFooter(result.verdictFooter)}
|
|
149
161
|
`);
|
package/dist/hooks/pre-commit.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
isRepoActiveForCapture
|
|
4
|
-
} from "../chunk-LUPD2JSH.js";
|
|
5
|
-
import {
|
|
3
|
+
isRepoActiveForCapture,
|
|
6
4
|
warmBinCache
|
|
7
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-F7O7V6ZM.js";
|
|
8
6
|
import {
|
|
9
7
|
getClient,
|
|
10
8
|
getSiteUrl
|
|
11
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-W6PAKEDO.js";
|
|
12
10
|
import {
|
|
13
11
|
daemonRequest
|
|
14
12
|
} from "../chunk-UTKQTZHL.js";
|
package/dist/hooks/prim-hook.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
appendMove,
|
|
4
|
-
resolveOrg
|
|
5
|
-
} from "../chunk-H4OR42TJ.js";
|
|
6
2
|
import {
|
|
7
3
|
scrubFromCwd
|
|
8
4
|
} from "../chunk-6LAQVM26.js";
|
|
9
5
|
import {
|
|
10
6
|
shouldFlushAfter,
|
|
11
7
|
toMove
|
|
12
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-F5QCFBJ6.js";
|
|
9
|
+
import {
|
|
10
|
+
appendMove,
|
|
11
|
+
resolveOrg
|
|
12
|
+
} from "../chunk-OKUXEBPT.js";
|
|
13
|
+
import {
|
|
14
|
+
buildHookOutput,
|
|
15
|
+
handoffHookOutput
|
|
16
|
+
} from "../chunk-S5NUHFAD.js";
|
|
17
|
+
import {
|
|
18
|
+
FEEDBACK_DEADLINE_MS,
|
|
19
|
+
acknowledgeDecisionFeedback,
|
|
20
|
+
leaseDecisionFeedback,
|
|
21
|
+
renderFeedback
|
|
22
|
+
} from "../chunk-UISU3A7W.js";
|
|
13
23
|
import {
|
|
14
|
-
|
|
15
|
-
} from "../chunk-
|
|
24
|
+
getOrCreateWorkspaceId
|
|
25
|
+
} from "../chunk-IMAIBPUC.js";
|
|
16
26
|
import {
|
|
27
|
+
isRepoActiveForCapture,
|
|
17
28
|
warmBinCache
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import "../chunk-
|
|
29
|
+
} from "../chunk-F7O7V6ZM.js";
|
|
30
|
+
import "../chunk-W6PAKEDO.js";
|
|
20
31
|
import {
|
|
21
32
|
normalizeEnvelope,
|
|
22
33
|
parseAgent
|
|
@@ -28,6 +39,11 @@ import { readFileSync } from "fs";
|
|
|
28
39
|
import { dirname, join } from "path";
|
|
29
40
|
import { fileURLToPath } from "url";
|
|
30
41
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
var outputAttempted = false;
|
|
43
|
+
function emitOutput(output, acknowledge) {
|
|
44
|
+
outputAttempted = true;
|
|
45
|
+
return handoffHookOutput(output, acknowledge);
|
|
46
|
+
}
|
|
31
47
|
function resolveCliVersion() {
|
|
32
48
|
try {
|
|
33
49
|
const pkg = JSON.parse(readFileSync(join(here, "..", "..", "package.json"), "utf-8"));
|
|
@@ -43,26 +59,67 @@ function spawnBackgroundFlush() {
|
|
|
43
59
|
stdio: "ignore"
|
|
44
60
|
}).unref();
|
|
45
61
|
}
|
|
46
|
-
|
|
62
|
+
function debug(area, error) {
|
|
63
|
+
if (!process.env.PRIM_HOOK_DEBUG) return;
|
|
64
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
65
|
+
process.stderr.write(`[prim-hook] ${area} failed: ${detail}
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
async function main() {
|
|
69
|
+
const feedbackSignal = AbortSignal.timeout(FEEDBACK_DEADLINE_MS);
|
|
47
70
|
warmBinCache();
|
|
48
71
|
const agent = parseAgent(process.argv);
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
let raw;
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
raw = readFileSync(0, "utf-8");
|
|
76
|
+
parsed = normalizeEnvelope(JSON.parse(raw), agent);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
debug("capture", error);
|
|
79
|
+
await emitOutput(buildHookOutput({}));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
51
82
|
const cwd = parsed.cwd ?? process.cwd();
|
|
52
|
-
|
|
53
|
-
|
|
83
|
+
const isClaudeStop = agent === "claude_code" && parsed.hook_event_name === "Stop";
|
|
84
|
+
if (!isRepoActiveForCapture(cwd)) {
|
|
85
|
+
if (isClaudeStop) await emitOutput(buildHookOutput({}));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const identity = getOrCreateWorkspaceId(cwd);
|
|
89
|
+
const workspaceId = identity.status === "ready" ? identity.workspaceId : void 0;
|
|
90
|
+
try {
|
|
91
|
+
const base = toMove(parsed, resolveCliVersion(), agent, workspaceId);
|
|
54
92
|
const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
|
|
55
93
|
const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd: move.env.cwd });
|
|
56
94
|
appendMove(move, orgId);
|
|
57
95
|
if (shouldFlushAfter(move.eventType)) {
|
|
58
96
|
spawnBackgroundFlush();
|
|
59
97
|
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
debug("capture", error);
|
|
60
100
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
101
|
+
if (!isClaudeStop) return;
|
|
102
|
+
const sessionId = typeof parsed.session_id === "string" ? parsed.session_id : "";
|
|
103
|
+
if (!workspaceId || !sessionId) {
|
|
104
|
+
await emitOutput(buildHookOutput({}));
|
|
105
|
+
return;
|
|
66
106
|
}
|
|
107
|
+
const lease = await leaseDecisionFeedback(
|
|
108
|
+
{ workspaceId, currentSessionId: sessionId, signal: feedbackSignal },
|
|
109
|
+
{ onError: (error) => debug("feedback", error) }
|
|
110
|
+
);
|
|
111
|
+
const rendered = lease ? renderFeedback(lease) : void 0;
|
|
112
|
+
await emitOutput(
|
|
113
|
+
buildHookOutput({ systemMessage: rendered?.systemMessage }),
|
|
114
|
+
rendered ? async () => {
|
|
115
|
+
await acknowledgeDecisionFeedback(
|
|
116
|
+
{ workspaceId, deliveries: rendered.deliveries, signal: feedbackSignal },
|
|
117
|
+
{ onError: (error) => debug("feedback", error) }
|
|
118
|
+
);
|
|
119
|
+
} : void 0
|
|
120
|
+
);
|
|
67
121
|
}
|
|
68
|
-
|
|
122
|
+
void main().catch(async (error) => {
|
|
123
|
+
debug("capture", error);
|
|
124
|
+
if (!outputAttempted) await emitOutput(buildHookOutput({}));
|
|
125
|
+
});
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
buildHookOutput,
|
|
4
|
+
handoffHookOutput
|
|
5
|
+
} from "../chunk-S5NUHFAD.js";
|
|
6
|
+
import {
|
|
7
|
+
FEEDBACK_DEADLINE_MS,
|
|
8
|
+
acknowledgeDecisionFeedback,
|
|
9
|
+
leaseDecisionFeedback,
|
|
10
|
+
renderFeedback
|
|
11
|
+
} from "../chunk-UISU3A7W.js";
|
|
12
|
+
import {
|
|
13
|
+
getOrCreateWorkspaceId
|
|
14
|
+
} from "../chunk-IMAIBPUC.js";
|
|
15
|
+
import {
|
|
16
|
+
binFile,
|
|
17
|
+
isRepoActiveForCapture,
|
|
3
18
|
warmBinCache
|
|
4
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-F7O7V6ZM.js";
|
|
5
20
|
import {
|
|
6
21
|
getSiteUrl
|
|
7
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-W6PAKEDO.js";
|
|
8
23
|
import {
|
|
9
24
|
daemonRequest
|
|
10
25
|
} from "../chunk-UTKQTZHL.js";
|
|
@@ -13,9 +28,34 @@ import {
|
|
|
13
28
|
parseAgent
|
|
14
29
|
} from "../chunk-TCDUH7AN.js";
|
|
15
30
|
|
|
31
|
+
// src/daemon/self-heal.ts
|
|
32
|
+
import { spawn } from "child_process";
|
|
33
|
+
function kickDaemonEnsure(options = {}) {
|
|
34
|
+
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
35
|
+
if (!primEntry) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const child = (options.spawnProcess ?? spawn)(
|
|
40
|
+
options.nodeEntry ?? process.execPath,
|
|
41
|
+
[primEntry, "daemon", "ensure"],
|
|
42
|
+
{ detached: true, stdio: "ignore" }
|
|
43
|
+
);
|
|
44
|
+
child.unref();
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
16
51
|
// src/hooks/session-start.ts
|
|
17
52
|
var STDIN_TIMEOUT_MS = 1e3;
|
|
18
53
|
var DAEMON_TIMEOUT_MS = 250;
|
|
54
|
+
var outputAttempted = false;
|
|
55
|
+
function emitOutput(output, acknowledge) {
|
|
56
|
+
outputAttempted = true;
|
|
57
|
+
return handoffHookOutput(output, acknowledge);
|
|
58
|
+
}
|
|
19
59
|
function readStdin() {
|
|
20
60
|
return new Promise((resolve, reject) => {
|
|
21
61
|
const chunks = [];
|
|
@@ -33,28 +73,15 @@ function readStdin() {
|
|
|
33
73
|
});
|
|
34
74
|
});
|
|
35
75
|
}
|
|
36
|
-
function emit(additionalContext) {
|
|
37
|
-
if (!additionalContext) {
|
|
38
|
-
process.stdout.write("{}\n");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const out = {
|
|
42
|
-
hookSpecificOutput: {
|
|
43
|
-
hookEventName: "SessionStart",
|
|
44
|
-
additionalContext
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
process.stdout.write(`${JSON.stringify(out)}
|
|
48
|
-
`);
|
|
49
|
-
}
|
|
50
76
|
async function main() {
|
|
77
|
+
const feedbackSignal = AbortSignal.timeout(FEEDBACK_DEADLINE_MS);
|
|
51
78
|
warmBinCache();
|
|
52
79
|
const agent = parseAgent(process.argv);
|
|
53
80
|
let raw;
|
|
54
81
|
try {
|
|
55
82
|
raw = await readStdin();
|
|
56
83
|
} catch {
|
|
57
|
-
|
|
84
|
+
await emitOutput(buildHookOutput({}));
|
|
58
85
|
return;
|
|
59
86
|
}
|
|
60
87
|
let envelope;
|
|
@@ -64,17 +91,18 @@ async function main() {
|
|
|
64
91
|
agent
|
|
65
92
|
);
|
|
66
93
|
} catch {
|
|
67
|
-
|
|
94
|
+
await emitOutput(buildHookOutput({}));
|
|
68
95
|
return;
|
|
69
96
|
}
|
|
70
97
|
if (envelope.hook_event_name !== "SessionStart") {
|
|
71
|
-
|
|
98
|
+
await emitOutput(buildHookOutput({}));
|
|
72
99
|
return;
|
|
73
100
|
}
|
|
74
101
|
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
|
|
75
|
-
|
|
102
|
+
await emitOutput(buildHookOutput({}));
|
|
76
103
|
return;
|
|
77
104
|
}
|
|
105
|
+
kickDaemonEnsure();
|
|
78
106
|
await daemonRequest(
|
|
79
107
|
"session_start",
|
|
80
108
|
{ sessionId: envelope.session_id },
|
|
@@ -89,12 +117,39 @@ async function main() {
|
|
|
89
117
|
{ timeoutMs: DAEMON_TIMEOUT_MS }
|
|
90
118
|
);
|
|
91
119
|
if (snapshot && !snapshot.presenceStale && typeof snapshot.onlineCount === "number") {
|
|
92
|
-
|
|
120
|
+
await emitOutput(
|
|
121
|
+
buildHookOutput({ additionalContext: `[prim] team: ${snapshot.onlineCount} online` })
|
|
122
|
+
);
|
|
93
123
|
return;
|
|
94
124
|
}
|
|
95
125
|
}
|
|
96
|
-
|
|
126
|
+
if (agent === "claude_code") {
|
|
127
|
+
const cwd = envelope.cwd ?? process.cwd();
|
|
128
|
+
if (isRepoActiveForCapture(cwd)) {
|
|
129
|
+
const identity = getOrCreateWorkspaceId(cwd);
|
|
130
|
+
if (identity.status === "ready") {
|
|
131
|
+
const lease = await leaseDecisionFeedback({
|
|
132
|
+
workspaceId: identity.workspaceId,
|
|
133
|
+
currentSessionId: envelope.session_id,
|
|
134
|
+
signal: feedbackSignal
|
|
135
|
+
});
|
|
136
|
+
const rendered = lease ? renderFeedback(lease) : void 0;
|
|
137
|
+
await emitOutput(
|
|
138
|
+
buildHookOutput({ systemMessage: rendered?.systemMessage }),
|
|
139
|
+
rendered ? async () => {
|
|
140
|
+
await acknowledgeDecisionFeedback({
|
|
141
|
+
workspaceId: identity.workspaceId,
|
|
142
|
+
deliveries: rendered.deliveries,
|
|
143
|
+
signal: feedbackSignal
|
|
144
|
+
});
|
|
145
|
+
} : void 0
|
|
146
|
+
);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
await emitOutput(buildHookOutput({}));
|
|
97
152
|
}
|
|
98
|
-
main().catch(() => {
|
|
99
|
-
|
|
153
|
+
void main().catch(async () => {
|
|
154
|
+
if (!outputAttempted) await emitOutput(buildHookOutput({}));
|
|
100
155
|
});
|