@primitive.ai/prim 0.1.0-alpha.7 → 0.1.0-alpha.70
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 +296 -37
- package/SKILL.md +269 -0
- package/dist/chunk-2KW7DVFJ.js +1400 -0
- package/dist/chunk-3EEFL6NJ.js +529 -0
- package/dist/chunk-4A2XB2PC.js +310 -0
- package/dist/chunk-4GLVJLEQ.js +55 -0
- package/dist/chunk-BQUJ4AFJ.js +771 -0
- package/dist/chunk-CUE2LKLR.js +155 -0
- package/dist/chunk-FI3LIRCB.js +262 -0
- package/dist/chunk-H64F6QHV.js +247 -0
- package/dist/chunk-KKP2NYZ3.js +505 -0
- package/dist/chunk-LEMX3IOG.js +79 -0
- package/dist/chunk-LMRYZL3D.js +209 -0
- package/dist/chunk-RO3F6JYV.js +126 -0
- package/dist/chunk-TJTVHDJ5.js +321 -0
- package/dist/chunk-UVIWZWM6.js +211 -0
- package/dist/chunk-WB7AUZ4C.js +52 -0
- package/dist/chunk-XGADQPCI.js +26 -0
- package/dist/chunk-YWO7O6QG.js +327 -0
- package/dist/chunk-Z7YPFWAZ.js +917 -0
- package/dist/chunk-ZOSNEO5N.js +77 -0
- package/dist/daemon/server.js +13353 -0
- package/dist/hook-runtime/daemon/server.js +13353 -0
- package/dist/hook-runtime/hooks/post-commit.js +1128 -0
- package/dist/hook-runtime/hooks/post-rewrite.js +1148 -0
- package/dist/hook-runtime/hooks/post-tool-use.js +7586 -0
- package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
- package/dist/hook-runtime/hooks/pre-tool-use.js +7099 -0
- package/dist/hook-runtime/hooks/prim-hook.js +7691 -0
- package/dist/hook-runtime/hooks/session-end.js +495 -0
- package/dist/hook-runtime/hooks/session-start.js +11284 -0
- package/dist/hook-runtime/index.js +42775 -0
- package/dist/hook-runtime/statusline-main.js +765 -0
- package/dist/hooks/post-commit.js +179 -0
- package/dist/hooks/post-rewrite.js +197 -0
- package/dist/hooks/post-tool-use.js +250 -0
- package/dist/hooks/pre-commit.js +21 -74
- package/dist/hooks/pre-tool-use.js +348 -0
- package/dist/hooks/prim-hook.js +195 -0
- package/dist/hooks/session-end.js +70 -0
- package/dist/hooks/session-start.js +271 -0
- package/dist/index.js +18920 -518
- package/dist/statusline-main.js +765 -0
- package/package.json +35 -13
- package/dist/chunk-3APLWTLB.js +0 -165
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
normalizeEnvelope,
|
|
4
|
+
parseAgent
|
|
5
|
+
} from "../chunk-WB7AUZ4C.js";
|
|
6
|
+
import {
|
|
7
|
+
daemonRequest
|
|
8
|
+
} from "../chunk-CUE2LKLR.js";
|
|
9
|
+
import "../chunk-3EEFL6NJ.js";
|
|
10
|
+
|
|
11
|
+
// src/hooks/session-end.ts
|
|
12
|
+
var STDIN_TIMEOUT_MS = 1e3;
|
|
13
|
+
var DAEMON_TIMEOUT_MS = 250;
|
|
14
|
+
function readStdin() {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const chunks = [];
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
reject(new Error("stdin read timeout"));
|
|
19
|
+
}, STDIN_TIMEOUT_MS);
|
|
20
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
21
|
+
process.stdin.on("end", () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
24
|
+
});
|
|
25
|
+
process.stdin.on("error", (err) => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
reject(err);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function emit() {
|
|
32
|
+
process.stdout.write("{}\n");
|
|
33
|
+
}
|
|
34
|
+
async function main() {
|
|
35
|
+
const agent = parseAgent(process.argv);
|
|
36
|
+
let raw;
|
|
37
|
+
try {
|
|
38
|
+
raw = await readStdin();
|
|
39
|
+
} catch {
|
|
40
|
+
emit();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let envelope;
|
|
44
|
+
try {
|
|
45
|
+
envelope = normalizeEnvelope(
|
|
46
|
+
JSON.parse(raw),
|
|
47
|
+
agent
|
|
48
|
+
);
|
|
49
|
+
} catch {
|
|
50
|
+
emit();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (envelope.hook_event_name !== "SessionEnd") {
|
|
54
|
+
emit();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
|
|
58
|
+
emit();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await daemonRequest(
|
|
62
|
+
"session_end",
|
|
63
|
+
{ sessionId: envelope.session_id },
|
|
64
|
+
{ timeoutMs: DAEMON_TIMEOUT_MS }
|
|
65
|
+
);
|
|
66
|
+
emit();
|
|
67
|
+
}
|
|
68
|
+
main().catch(() => {
|
|
69
|
+
emit();
|
|
70
|
+
});
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
bindRepository,
|
|
4
|
+
ensureEffectivePostCommitHook,
|
|
5
|
+
ensureEffectivePostRewriteHook,
|
|
6
|
+
hasUsableCodexGuidance,
|
|
7
|
+
refreshClaudePlugins,
|
|
8
|
+
warmBinCache
|
|
9
|
+
} from "../chunk-2KW7DVFJ.js";
|
|
10
|
+
import {
|
|
11
|
+
buildHookOutput,
|
|
12
|
+
handoffHookOutput
|
|
13
|
+
} from "../chunk-4GLVJLEQ.js";
|
|
14
|
+
import {
|
|
15
|
+
FEEDBACK_DEADLINE_MS,
|
|
16
|
+
acknowledgeDecisionFeedback,
|
|
17
|
+
leaseDecisionFeedback,
|
|
18
|
+
renderFeedback
|
|
19
|
+
} from "../chunk-LMRYZL3D.js";
|
|
20
|
+
import {
|
|
21
|
+
getOrCreateWorkspaceId
|
|
22
|
+
} from "../chunk-UVIWZWM6.js";
|
|
23
|
+
import {
|
|
24
|
+
prepareCodexContext
|
|
25
|
+
} from "../chunk-KKP2NYZ3.js";
|
|
26
|
+
import {
|
|
27
|
+
binFile
|
|
28
|
+
} from "../chunk-4A2XB2PC.js";
|
|
29
|
+
import {
|
|
30
|
+
gitToplevel,
|
|
31
|
+
isRepoActiveForCapture,
|
|
32
|
+
repoActiveFlag,
|
|
33
|
+
setRepoActive
|
|
34
|
+
} from "../chunk-TJTVHDJ5.js";
|
|
35
|
+
import {
|
|
36
|
+
getSiteUrl,
|
|
37
|
+
isSessionEnded
|
|
38
|
+
} from "../chunk-Z7YPFWAZ.js";
|
|
39
|
+
import {
|
|
40
|
+
normalizeEnvelope,
|
|
41
|
+
parseAgent
|
|
42
|
+
} from "../chunk-WB7AUZ4C.js";
|
|
43
|
+
import {
|
|
44
|
+
daemonRequest
|
|
45
|
+
} from "../chunk-CUE2LKLR.js";
|
|
46
|
+
import "../chunk-3EEFL6NJ.js";
|
|
47
|
+
|
|
48
|
+
// src/daemon/self-heal.ts
|
|
49
|
+
import { spawn } from "child_process";
|
|
50
|
+
function kickDaemonEnsure(options = {}) {
|
|
51
|
+
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
52
|
+
if (!primEntry) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const args = [primEntry, "daemon", "ensure"];
|
|
57
|
+
if ((options.platform ?? process.platform) === "darwin") {
|
|
58
|
+
args.push("--latest-bootstrap");
|
|
59
|
+
}
|
|
60
|
+
const child = (options.spawnProcess ?? spawn)(
|
|
61
|
+
options.nodeEntry ?? process.execPath,
|
|
62
|
+
args,
|
|
63
|
+
{ detached: true, stdio: "ignore" }
|
|
64
|
+
);
|
|
65
|
+
child.unref();
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/hooks/reauth-notice.ts
|
|
73
|
+
var REAUTH_NOTICE = "[prim] authentication ended \u2014 run `prim auth login` to resume decision capture";
|
|
74
|
+
function reauthNoticeFields(agent) {
|
|
75
|
+
if (agent === "claude_code") return { systemMessage: REAUTH_NOTICE };
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/hooks/session-start-core.ts
|
|
80
|
+
var DAEMON_TIMEOUT_MS = 250;
|
|
81
|
+
var REPOSITORY_BIND_TIMEOUT_MS = 1e3;
|
|
82
|
+
var PRIM_TRIGGER = "When this task chooses between plausible approaches or establishes or changes a lasting goal, priority, constraint, invariant, default, commitment, tradeoff, exception, or shared instruction";
|
|
83
|
+
var PRIM_EXCLUSIONS = "Never invoke `prim` for routine implementation that merely follows an existing decision made before this task or for temporary tactics; they never qualify, including for evaluation.";
|
|
84
|
+
function primReminder(action, rationaleAction) {
|
|
85
|
+
return `Primitive is active in this repository. ${PRIM_TRIGGER}, ${action} before finishing. ${PRIM_EXCLUSIONS} When a direct request replaces one lasting default with another but supplies no rationale, complete the work, ${rationaleAction}, and ask one concise rationale question at the task boundary, even if the user requested only implementation or recording fails.`;
|
|
86
|
+
}
|
|
87
|
+
var PRIM_SKILL_REMINDER = primReminder("invoke the `prim` skill", "invoke the skill");
|
|
88
|
+
var CODEX_PRIM_ACTION = "follow the installed Prim workflow and use the `prim` CLI";
|
|
89
|
+
var CODEX_PRIM_REMINDER = primReminder(CODEX_PRIM_ACTION, CODEX_PRIM_ACTION);
|
|
90
|
+
async function activeProjectRoot(cwd) {
|
|
91
|
+
try {
|
|
92
|
+
const root = gitToplevel(cwd);
|
|
93
|
+
if (!root || !isRepoActiveForCapture(cwd)) return null;
|
|
94
|
+
let shellGateActive = repoActiveFlag(root) === "true";
|
|
95
|
+
if (!shellGateActive) {
|
|
96
|
+
try {
|
|
97
|
+
setRepoActive(root, true);
|
|
98
|
+
shellGateActive = true;
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (shellGateActive) {
|
|
103
|
+
try {
|
|
104
|
+
ensureEffectivePostCommitHook(root);
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
ensureEffectivePostRewriteHook(root);
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
await bindRepository(root, {
|
|
114
|
+
signal: AbortSignal.timeout(REPOSITORY_BIND_TIMEOUT_MS),
|
|
115
|
+
quietRefresh: true
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
return root;
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function processSessionStart(raw, agent, feedbackSignal) {
|
|
125
|
+
let envelope;
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(raw);
|
|
128
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
129
|
+
return { output: buildHookOutput({}) };
|
|
130
|
+
}
|
|
131
|
+
envelope = normalizeEnvelope(parsed, agent);
|
|
132
|
+
} catch {
|
|
133
|
+
return { output: buildHookOutput({}) };
|
|
134
|
+
}
|
|
135
|
+
if (envelope.hook_event_name !== "SessionStart") {
|
|
136
|
+
return { output: buildHookOutput({}) };
|
|
137
|
+
}
|
|
138
|
+
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
|
|
139
|
+
return { output: buildHookOutput({}) };
|
|
140
|
+
}
|
|
141
|
+
kickDaemonEnsure();
|
|
142
|
+
await daemonRequest(
|
|
143
|
+
"session_start",
|
|
144
|
+
{ sessionId: envelope.session_id, callerEnv: getSiteUrl() },
|
|
145
|
+
{ timeoutMs: DAEMON_TIMEOUT_MS }
|
|
146
|
+
);
|
|
147
|
+
const cwd = envelope.cwd ?? process.cwd();
|
|
148
|
+
let projectRoot = null;
|
|
149
|
+
let active = false;
|
|
150
|
+
let skillState = { installed: 0, refreshed: 0 };
|
|
151
|
+
if (agent === "claude_code") {
|
|
152
|
+
projectRoot = await activeProjectRoot(cwd);
|
|
153
|
+
active = projectRoot !== null;
|
|
154
|
+
try {
|
|
155
|
+
skillState = await refreshClaudePlugins(
|
|
156
|
+
cwd,
|
|
157
|
+
active && projectRoot !== null ? { includeProject: true, projectRoot } : { includeProject: false }
|
|
158
|
+
);
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (isSessionEnded() && agent !== "codex") {
|
|
163
|
+
const notice = reauthNoticeFields(agent);
|
|
164
|
+
if (notice) {
|
|
165
|
+
return {
|
|
166
|
+
output: buildHookOutput({ ...notice, reloadSkills: skillState.refreshed > 0 })
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (agent === "codex") {
|
|
171
|
+
let context;
|
|
172
|
+
try {
|
|
173
|
+
context = await prepareCodexContext({
|
|
174
|
+
cwd,
|
|
175
|
+
sessionId: envelope.session_id,
|
|
176
|
+
startup: true,
|
|
177
|
+
includeDigest: false
|
|
178
|
+
});
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
projectRoot = await activeProjectRoot(cwd);
|
|
182
|
+
active = projectRoot !== null;
|
|
183
|
+
let proactive = false;
|
|
184
|
+
try {
|
|
185
|
+
proactive = projectRoot !== null && active && hasUsableCodexGuidance(projectRoot);
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, context?.context].filter((value) => value !== void 0).join("\n\n");
|
|
189
|
+
return {
|
|
190
|
+
output: buildHookOutput({ additionalContext }),
|
|
191
|
+
acknowledge: async () => {
|
|
192
|
+
await context?.acknowledge(true);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (agent === "claude_code") {
|
|
197
|
+
const additionalContext = active && skillState.installed > 0 ? PRIM_SKILL_REMINDER : void 0;
|
|
198
|
+
const reloadSkills = skillState.refreshed > 0;
|
|
199
|
+
if (active) {
|
|
200
|
+
const identity = getOrCreateWorkspaceId(cwd);
|
|
201
|
+
if (identity.status === "ready") {
|
|
202
|
+
const signal = feedbackSignal ?? AbortSignal.timeout(FEEDBACK_DEADLINE_MS);
|
|
203
|
+
const lease = await leaseDecisionFeedback({
|
|
204
|
+
workspaceId: identity.workspaceId,
|
|
205
|
+
currentSessionId: envelope.session_id,
|
|
206
|
+
signal
|
|
207
|
+
});
|
|
208
|
+
const rendered = lease ? renderFeedback(lease) : void 0;
|
|
209
|
+
return {
|
|
210
|
+
output: buildHookOutput({
|
|
211
|
+
systemMessage: rendered?.systemMessage,
|
|
212
|
+
additionalContext,
|
|
213
|
+
reloadSkills
|
|
214
|
+
}),
|
|
215
|
+
acknowledge: rendered ? async () => {
|
|
216
|
+
await acknowledgeDecisionFeedback({
|
|
217
|
+
protocolVersion: rendered.protocolVersion,
|
|
218
|
+
workspaceId: identity.workspaceId,
|
|
219
|
+
deliveries: rendered.deliveries,
|
|
220
|
+
signal
|
|
221
|
+
});
|
|
222
|
+
} : void 0
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { output: buildHookOutput({ additionalContext, reloadSkills }) };
|
|
227
|
+
}
|
|
228
|
+
await activeProjectRoot(cwd);
|
|
229
|
+
return { output: buildHookOutput({}) };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/hooks/session-start.ts
|
|
233
|
+
var STDIN_TIMEOUT_MS = 1e3;
|
|
234
|
+
var outputAttempted = false;
|
|
235
|
+
function emitOutput(output, acknowledge) {
|
|
236
|
+
outputAttempted = true;
|
|
237
|
+
return handoffHookOutput(output, acknowledge);
|
|
238
|
+
}
|
|
239
|
+
function readStdin() {
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
const chunks = [];
|
|
242
|
+
const timer = setTimeout(() => {
|
|
243
|
+
reject(new Error("stdin read timeout"));
|
|
244
|
+
}, STDIN_TIMEOUT_MS);
|
|
245
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
246
|
+
process.stdin.on("end", () => {
|
|
247
|
+
clearTimeout(timer);
|
|
248
|
+
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
249
|
+
});
|
|
250
|
+
process.stdin.on("error", (err) => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
reject(err);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async function main() {
|
|
257
|
+
warmBinCache();
|
|
258
|
+
const agent = parseAgent(process.argv);
|
|
259
|
+
let raw;
|
|
260
|
+
try {
|
|
261
|
+
raw = await readStdin();
|
|
262
|
+
} catch {
|
|
263
|
+
await emitOutput(buildHookOutput({}));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const result = await processSessionStart(raw, agent);
|
|
267
|
+
await emitOutput(result.output, result.acknowledge);
|
|
268
|
+
}
|
|
269
|
+
void main().catch(async () => {
|
|
270
|
+
if (!outputAttempted) await emitOutput(buildHookOutput({}));
|
|
271
|
+
});
|