@primitive.ai/prim 0.1.0-alpha.9 → 0.1.0-alpha.90

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.
Files changed (45) hide show
  1. package/README.md +295 -37
  2. package/SKILL.md +269 -0
  3. package/dist/chunk-2JRX7ZR4.js +247 -0
  4. package/dist/chunk-3EEFL6NJ.js +529 -0
  5. package/dist/chunk-4GLVJLEQ.js +55 -0
  6. package/dist/chunk-6IWNH776.js +77 -0
  7. package/dist/chunk-C7T7B45R.js +1400 -0
  8. package/dist/chunk-CUE2LKLR.js +155 -0
  9. package/dist/chunk-EZXEWFDG.js +210 -0
  10. package/dist/chunk-FI3LIRCB.js +262 -0
  11. package/dist/chunk-LEMX3IOG.js +79 -0
  12. package/dist/chunk-LMRYZL3D.js +209 -0
  13. package/dist/chunk-MAZWKJGL.js +18548 -0
  14. package/dist/chunk-PMRDE2DA.js +310 -0
  15. package/dist/chunk-PPQYQEIR.js +815 -0
  16. package/dist/chunk-R26A7TWY.js +36 -0
  17. package/dist/chunk-RO3F6JYV.js +126 -0
  18. package/dist/chunk-UZPBB7V4.js +505 -0
  19. package/dist/chunk-WB7AUZ4C.js +52 -0
  20. package/dist/chunk-YWO7O6QG.js +327 -0
  21. package/dist/chunk-Z7YPFWAZ.js +917 -0
  22. package/dist/daemon/server.js +21633 -0
  23. package/dist/hook-runtime/daemon/server.js +21633 -0
  24. package/dist/hook-runtime/hooks/post-commit.js +19258 -0
  25. package/dist/hook-runtime/hooks/post-rewrite.js +19273 -0
  26. package/dist/hook-runtime/hooks/post-tool-use.js +25846 -0
  27. package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
  28. package/dist/hook-runtime/hooks/pre-tool-use.js +25284 -0
  29. package/dist/hook-runtime/hooks/prim-hook.js +25870 -0
  30. package/dist/hook-runtime/hooks/session-end.js +495 -0
  31. package/dist/hook-runtime/hooks/session-start.js +28180 -0
  32. package/dist/hook-runtime/index.js +50516 -0
  33. package/dist/hook-runtime/statusline-main.js +765 -0
  34. package/dist/hooks/post-commit.js +188 -0
  35. package/dist/hooks/post-rewrite.js +201 -0
  36. package/dist/hooks/post-tool-use.js +263 -0
  37. package/dist/hooks/pre-commit.js +16 -101
  38. package/dist/hooks/pre-tool-use.js +367 -0
  39. package/dist/hooks/prim-hook.js +207 -0
  40. package/dist/hooks/session-end.js +70 -0
  41. package/dist/hooks/session-start.js +289 -0
  42. package/dist/index.js +9749 -425
  43. package/dist/statusline-main.js +765 -0
  44. package/package.json +35 -13
  45. package/dist/chunk-3APLWTLB.js +0 -165
@@ -0,0 +1,310 @@
1
+ import {
2
+ stripControlChars
3
+ } from "./chunk-Z7YPFWAZ.js";
4
+
5
+ // src/lib/bin-path.ts
6
+ import { existsSync, readFileSync } from "fs";
7
+ import { dirname, isAbsolute, join } from "path";
8
+ import { fileURLToPath } from "url";
9
+
10
+ // src/lib/semver.ts
11
+ function parseSemver(value) {
12
+ if (typeof value !== "string" || value.length > 256) return;
13
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u.exec(
14
+ value
15
+ );
16
+ if (!match) return;
17
+ const prerelease = match[4]?.split(".") ?? [];
18
+ if (prerelease.some((part) => /^\d+$/u.test(part) && part.length > 1 && part.startsWith("0"))) {
19
+ return;
20
+ }
21
+ return { core: [BigInt(match[1]), BigInt(match[2]), BigInt(match[3])], prerelease };
22
+ }
23
+ function compareSemver(left, right) {
24
+ const a = parseSemver(left);
25
+ const b = parseSemver(right);
26
+ if (!a || !b) return;
27
+ for (let i = 0; i < a.core.length; i += 1) {
28
+ if (a.core[i] !== b.core[i]) return a.core[i] < b.core[i] ? -1 : 1;
29
+ }
30
+ if (a.prerelease.length === 0 || b.prerelease.length === 0) {
31
+ return a.prerelease.length === b.prerelease.length ? 0 : a.prerelease.length === 0 ? 1 : -1;
32
+ }
33
+ for (let i = 0; i < Math.max(a.prerelease.length, b.prerelease.length); i += 1) {
34
+ const x = a.prerelease[i];
35
+ const y = b.prerelease[i];
36
+ if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
37
+ if (x === y) continue;
38
+ const xNumeric = /^\d+$/u.test(x);
39
+ const yNumeric = /^\d+$/u.test(y);
40
+ if (xNumeric && yNumeric) return BigInt(x) < BigInt(y) ? -1 : 1;
41
+ if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
42
+ return x < y ? -1 : 1;
43
+ }
44
+ return 0;
45
+ }
46
+
47
+ // src/lib/bin-path.ts
48
+ var PKG_NAME = "@primitive.ai/prim";
49
+ var ROOT_WALK_LIMIT = 6;
50
+ var STABLE_HOOK_LAUNCHER_NAME = "prim-hook-launcher-v1";
51
+ var resolvedRoot;
52
+ function locateRoot() {
53
+ if (resolvedRoot !== void 0) {
54
+ return resolvedRoot;
55
+ }
56
+ let dir = dirname(fileURLToPath(import.meta.url));
57
+ for (let depth = 0; depth < ROOT_WALK_LIMIT; depth++) {
58
+ const manifestPath = join(dir, "package.json");
59
+ if (existsSync(manifestPath)) {
60
+ try {
61
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
62
+ if (manifest.name === PKG_NAME && manifest.bin) {
63
+ resolvedRoot = { dir, version: manifest.version, bin: manifest.bin };
64
+ return resolvedRoot;
65
+ }
66
+ } catch {
67
+ }
68
+ }
69
+ const parent = dirname(dir);
70
+ if (parent === dir) {
71
+ break;
72
+ }
73
+ dir = parent;
74
+ }
75
+ resolvedRoot = null;
76
+ return resolvedRoot;
77
+ }
78
+ function binFile(bin) {
79
+ const root = locateRoot();
80
+ const rel = root?.bin[bin];
81
+ if (!root || !rel) {
82
+ return null;
83
+ }
84
+ return isAbsolute(rel) ? rel : join(root.dir, rel);
85
+ }
86
+ function packageVersion() {
87
+ return locateRoot()?.version ?? null;
88
+ }
89
+ function packageRoot() {
90
+ return locateRoot()?.dir ?? null;
91
+ }
92
+ function shellQuote(value) {
93
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
94
+ }
95
+ function shellWord(value) {
96
+ return /^[A-Za-z0-9_@%+=:,./-]+$/u.test(value) ? value : shellQuote(value);
97
+ }
98
+ function pinnedNpxArgs(bin, args = [], options = {}) {
99
+ const version = packageVersion();
100
+ if (!version) throw new Error("cannot determine Primitive package version");
101
+ return [
102
+ "--yes",
103
+ "--ignore-scripts",
104
+ ...options.preferOnline ? ["--prefer-online"] : [],
105
+ "-p",
106
+ `${PKG_NAME}@${version}`,
107
+ bin,
108
+ ...args
109
+ ];
110
+ }
111
+ function pinnedNpxCommand(bin, args = [], options = {}) {
112
+ return ["npx", ...pinnedNpxArgs(bin, args, options)].map(shellWord).join(" ");
113
+ }
114
+ function pinnedHookCommand(bin, args = "") {
115
+ const suffix = args ? ` ${args}` : "";
116
+ const fallback = `${pinnedNpxCommand(bin)}${suffix}`;
117
+ const file = binFile(bin);
118
+ if (!file) return fallback;
119
+ return `if [ -x ${shellQuote(process.execPath)} ] && [ -f ${shellQuote(file)} ]; then ${shellQuote(process.execPath)} ${shellQuote(file)}${suffix}; else ${fallback}; fi`;
120
+ }
121
+ var STABLE_HOOK_ARGUMENTS_RE = /^[-A-Za-z0-9_ ]*$/u;
122
+ var STABLE_HOOK_BIN_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/u;
123
+ function stableHookCommand(bin, args = "") {
124
+ if (!STABLE_HOOK_BIN_RE.test(bin) || !STABLE_HOOK_ARGUMENTS_RE.test(args)) {
125
+ throw new Error("invalid stable hook command");
126
+ }
127
+ const resolver = [
128
+ // #242 and earlier identify managed commands by this package marker. Keep
129
+ // it as inert data during the rolling window so an older uninstall or
130
+ // reinstall can remove the stable entry without invoking npm or PATH.
131
+ `prim_legacy_reader='-p ${PKG_NAME}@stable prim-shim.sh ${bin} '; `,
132
+ 'prim_absolute() { case "$1" in /*) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*//*|*/./*|*/../*|*/.|*/..|?*/) return 1 ;; esac; }; ',
133
+ 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
134
+ 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
135
+ 'else prim_home=${HOME:-}; prim_absolute "$prim_home" || exit 78; case "$prim_home" in /) prim_config=/.config/prim ;; *) prim_config="$prim_home/.config/prim" ;; esac; ',
136
+ "fi; fi; ",
137
+ `case "$prim_config" in /) prim_launcher=/${STABLE_HOOK_LAUNCHER_NAME} ;; *) prim_launcher="$prim_config/${STABLE_HOOK_LAUNCHER_NAME}" ;; esac; `,
138
+ 'exec "$prim_launcher" "$@"'
139
+ ].join("");
140
+ const suffix = args ? ` ${args}` : "";
141
+ return `/bin/sh -c ${shellQuote(resolver)} ${STABLE_HOOK_LAUNCHER_NAME} ${bin}${suffix}`;
142
+ }
143
+ function detachedHookShimCommand(bin, args = "") {
144
+ return `payload=$(cat); { trap '' HUP; printf '%s' "$payload" | { ${stableHookCommand(bin, args)}; }; } </dev/null >/dev/null 2>&1 &`;
145
+ }
146
+ function commandMatchesBin(command, bin) {
147
+ if (!command) {
148
+ return false;
149
+ }
150
+ const c = command.trim();
151
+ if (c === bin || c.startsWith(`${bin} `)) {
152
+ return true;
153
+ }
154
+ const exactBin = new RegExp(`(?:^|\\s)${bin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|;|$)`);
155
+ return c.includes(`command -v ${bin} `) || c.includes(`-p ${PKG_NAME}@`) && exactBin.test(c) || c.includes(STABLE_HOOK_LAUNCHER_NAME) && exactBin.test(c);
156
+ }
157
+ function exactPinnedNpxVersion(command, bin) {
158
+ const escapedBin = bin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
159
+ const shellQuoted = `'(?:[^']|'"'"')*'`;
160
+ const safeArgs = "(?: [A-Za-z0-9_@%+=:,./-]+)*";
161
+ const direct = new RegExp(
162
+ `^npx --yes --ignore-scripts -p @primitive\\.ai/prim@(?<version>[0-9A-Za-z.+-]+) ${escapedBin}${safeArgs}$`,
163
+ "u"
164
+ ).exec(command);
165
+ const wrapped = new RegExp(
166
+ `^if \\[ -x (?<node>${shellQuoted}) \\] && \\[ -f (?<entry>${shellQuoted}) \\]; then \\k<node> \\k<entry>(?<args>${safeArgs}); else npx --yes --ignore-scripts -p @primitive\\.ai/prim@(?<version>[0-9A-Za-z.+-]+) ${escapedBin}\\k<args>; fi$`,
167
+ "u"
168
+ ).exec(command);
169
+ const version = direct?.groups?.version ?? wrapped?.groups?.version;
170
+ return version && compareSemver(version, version) !== void 0 ? version : void 0;
171
+ }
172
+ function hookCommandResolution(command, bin) {
173
+ const trimmed = command?.trim();
174
+ if (!trimmed || !commandMatchesBin(trimmed, bin)) return void 0;
175
+ if (trimmed.includes(STABLE_HOOK_LAUNCHER_NAME)) return { kind: "stable_launcher" };
176
+ const version = exactPinnedNpxVersion(trimmed, bin);
177
+ return version ? { kind: "exact_npx_fallback", version } : { kind: "legacy_path" };
178
+ }
179
+ function hookCommandResolutions(commands, bins) {
180
+ const resolutions = [];
181
+ for (const command of commands) {
182
+ for (const bin of bins) {
183
+ const resolution = hookCommandResolution(command, bin);
184
+ if (resolution) resolutions.push(resolution);
185
+ }
186
+ }
187
+ return resolutions;
188
+ }
189
+
190
+ // src/lib/presence.ts
191
+ var DECISION_ORIGIN = "https://app.getprimitive.ai";
192
+ function validDecisionUrl(value) {
193
+ if (typeof value !== "string" || stripControlChars(value) !== value) {
194
+ return void 0;
195
+ }
196
+ try {
197
+ const url = new URL(value);
198
+ if (url.origin !== DECISION_ORIGIN || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !/^\/decisions\/[^/]+$/u.test(url.pathname)) {
199
+ return void 0;
200
+ }
201
+ return url.href;
202
+ } catch {
203
+ return void 0;
204
+ }
205
+ }
206
+ function decisionLink(label, decisionUrl, plain) {
207
+ const url = validDecisionUrl(decisionUrl);
208
+ if (plain || !url) {
209
+ return label;
210
+ }
211
+ return `\x1B]8;;${url}\x07\x1B[34;4m${label}\x1B[0m\x1B]8;;\x07`;
212
+ }
213
+ function formatLabeled(labels, cap) {
214
+ if (labels === void 0) {
215
+ return "\u2014";
216
+ }
217
+ if (labels.length === 0) {
218
+ return "just you";
219
+ }
220
+ if (labels.length <= cap) {
221
+ return labels.join(", ");
222
+ }
223
+ return `${labels.slice(0, cap).join(", ")} +${String(labels.length - cap)}`;
224
+ }
225
+ function formatTeammates(names, cap) {
226
+ return formatLabeled(names?.map(stripControlChars), cap);
227
+ }
228
+ function formatTeammatesWithArea(teammates, cap, plainLinks = false) {
229
+ return formatLabeled(
230
+ teammates?.map((t) => {
231
+ const name = stripControlChars(t.name);
232
+ const area = stripControlChars(t.area ?? "").trim();
233
+ const label = area ? `${name} - ${area}` : name;
234
+ return decisionLink(label, t.decisionUrl, plainLinks);
235
+ }),
236
+ cap
237
+ );
238
+ }
239
+
240
+ // src/lib/statusline-render.ts
241
+ function repositoryBindingDiagnosticLabel(state) {
242
+ if (state === "unbound") return "GitHub repo connection: required (run `prim github connect`)";
243
+ if (state === "invalid") return "GitHub repo connection: invalid (run `prim doctor`)";
244
+ return void 0;
245
+ }
246
+ var STATUSLINE_NAME_CAP = 3;
247
+ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {}) {
248
+ const ingestionSuffix = (status) => status === void 0 ? "" : ` \xB7 Decision ingestion ${status}`;
249
+ const ingestionStatus = snapshot === null || snapshot.healthy === false ? options.includeIngestionWhenUnavailable ? resolveIngestionStatus() : void 0 : resolveIngestionStatus();
250
+ if (snapshot?.healthy === false && snapshot.needsReauth) {
251
+ return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`${ingestionSuffix(ingestionStatus)})`;
252
+ }
253
+ const repositoryLabel = repositoryBindingDiagnosticLabel(
254
+ snapshot !== null && snapshot.healthy !== false || options.includeIngestionWhenUnavailable === true ? options.resolveRepositoryBindingState?.() : void 0
255
+ );
256
+ const repositorySuffix = repositoryLabel ? ` \xB7 ${repositoryLabel}` : "";
257
+ if (!snapshot) {
258
+ return `primitive ${version} (daemon: down${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
259
+ }
260
+ if (snapshot.healthy === false) {
261
+ if (snapshot.ingestion?.healthy === false) {
262
+ const pending = snapshot.ingestion.pendingCount;
263
+ const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
264
+ return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
265
+ }
266
+ if (snapshot.heartbeat?.healthy === false) {
267
+ return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
268
+ }
269
+ return `primitive ${version} (daemon: starting${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
270
+ }
271
+ if (snapshot.envMismatch) {
272
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: other env)`;
273
+ }
274
+ if (snapshot.principalMismatch) {
275
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: other account)`;
276
+ }
277
+ if (snapshot.presenceStale) {
278
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: stale)`;
279
+ }
280
+ let team;
281
+ if (snapshot.onlineTeammates !== void 0) {
282
+ team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP, options.plainLinks === true)}`;
283
+ } else if (snapshot.onlineNames !== void 0) {
284
+ team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
285
+ } else if (typeof snapshot.onlineCount === "number") {
286
+ team = `team: ${String(snapshot.onlineCount)} online`;
287
+ } else {
288
+ team = "team: \u2014";
289
+ }
290
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 ${team})`;
291
+ }
292
+
293
+ export {
294
+ compareSemver,
295
+ STABLE_HOOK_LAUNCHER_NAME,
296
+ binFile,
297
+ packageVersion,
298
+ packageRoot,
299
+ pinnedNpxArgs,
300
+ pinnedNpxCommand,
301
+ pinnedHookCommand,
302
+ stableHookCommand,
303
+ detachedHookShimCommand,
304
+ commandMatchesBin,
305
+ hookCommandResolution,
306
+ hookCommandResolutions,
307
+ formatTeammates,
308
+ repositoryBindingDiagnosticLabel,
309
+ formatStatusline
310
+ };