@primitive.ai/prim 0.1.0-alpha.92 → 0.1.0-alpha.94

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 (42) hide show
  1. package/README.md +84 -10
  2. package/SKILL.md +5 -2
  3. package/dist/{chunk-67VNLLBN.js → chunk-2GP5UFV7.js} +1 -0
  4. package/dist/{chunk-26P57LAR.js → chunk-AXQQMX2U.js} +203 -9
  5. package/dist/{chunk-M6RIJVRE.js → chunk-DSWT6BCC.js} +78 -4
  6. package/dist/{chunk-FBAYCS3X.js → chunk-DXJQR4QI.js} +150 -38
  7. package/dist/chunk-FXF4JWK2.js +162 -0
  8. package/dist/{chunk-PCVGBE5B.js → chunk-G2VMA2QO.js} +2 -1
  9. package/dist/{chunk-NAHHFSLH.js → chunk-ICM2XCXS.js} +2 -2
  10. package/dist/{chunk-5HRR4AOR.js → chunk-IMK4JKTA.js} +1 -1
  11. package/dist/{chunk-OAAP2MY6.js → chunk-K7D2EYBS.js} +2 -2
  12. package/dist/{chunk-ADUTW66W.js → chunk-O7YAW7G6.js} +6 -6
  13. package/dist/{chunk-5AFRMOPX.js → chunk-OH3EZXYX.js} +1 -1
  14. package/dist/{chunk-KFXF2V5G.js → chunk-OTHKG76J.js} +1 -1
  15. package/dist/chunk-OYHYCK4K.js +13 -0
  16. package/dist/{chunk-FKXFUUWJ.js → chunk-TYAHC25F.js} +1 -1
  17. package/dist/{chunk-5GYQ4J3D.js → chunk-UKUAAAXE.js} +2 -2
  18. package/dist/{chunk-LGGMJYY4.js → chunk-WUXFLE7X.js} +720 -369
  19. package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
  20. package/dist/daemon/server.js +372 -123
  21. package/dist/hook-runtime/daemon/server.js +372 -123
  22. package/dist/hook-runtime/hooks/post-commit.js +192 -58
  23. package/dist/hook-runtime/hooks/post-rewrite.js +58 -40
  24. package/dist/hook-runtime/hooks/post-tool-use.js +150 -67
  25. package/dist/hook-runtime/hooks/pre-tool-use.js +87 -15
  26. package/dist/hook-runtime/hooks/prim-hook.js +197 -107
  27. package/dist/hook-runtime/hooks/session-end.js +53 -5
  28. package/dist/hook-runtime/hooks/session-start.js +880 -280
  29. package/dist/hook-runtime/index.js +4008 -2438
  30. package/dist/hook-runtime/statusline-main.js +23 -1
  31. package/dist/hooks/post-commit.js +15 -8
  32. package/dist/hooks/post-rewrite.js +11 -8
  33. package/dist/hooks/post-tool-use.js +13 -15
  34. package/dist/hooks/pre-commit.js +4 -4
  35. package/dist/hooks/pre-tool-use.js +10 -12
  36. package/dist/hooks/prim-hook.js +17 -14
  37. package/dist/hooks/session-end.js +4 -4
  38. package/dist/hooks/session-start.js +35 -45
  39. package/dist/index.js +1401 -620
  40. package/dist/statusline-main.js +23 -1
  41. package/package.json +1 -1
  42. package/dist/chunk-BTNNEH2W.js +0 -79
@@ -53,8 +53,8 @@ var require_ucs2length = __commonJS({
53
53
 
54
54
  // src/hooks/prim-hook.ts
55
55
  import { spawn } from "child_process";
56
- import { readFileSync as readFileSync14 } from "fs";
57
- import { dirname as dirname11, join as join16 } from "path";
56
+ import { readFileSync as readFileSync15 } from "fs";
57
+ import { dirname as dirname12, join as join17 } from "path";
58
58
  import { fileURLToPath as fileURLToPath2 } from "url";
59
59
 
60
60
  // src/binding.ts
@@ -1342,17 +1342,30 @@ async function acknowledgeDecisionFeedback(input, dependencies = {}) {
1342
1342
  import {
1343
1343
  appendFileSync,
1344
1344
  closeSync as closeSync2,
1345
- existsSync as existsSync4,
1345
+ existsSync as existsSync5,
1346
1346
  fstatSync,
1347
1347
  mkdirSync as mkdirSync4,
1348
1348
  openSync as openSync2,
1349
1349
  opendirSync,
1350
- readFileSync as readFileSync5,
1350
+ readFileSync as readFileSync6,
1351
1351
  readSync,
1352
- readdirSync
1352
+ readdirSync as readdirSync2
1353
+ } from "fs";
1354
+ import { dirname as dirname6, join as join7 } from "path";
1355
+
1356
+ // src/drain-progress.ts
1357
+ import {
1358
+ chmodSync as chmodSync2,
1359
+ existsSync as existsSync4,
1360
+ readFileSync as readFileSync5,
1361
+ readdirSync,
1362
+ rmSync as rmSync4,
1363
+ rmdirSync
1353
1364
  } from "fs";
1354
- import { dirname as dirname5, join as join6 } from "path";
1355
- var JOURNAL_DIR = join6(primConfigDirectory(), "moves");
1365
+ import { basename, dirname as dirname5, join as join6 } from "path";
1366
+
1367
+ // src/journal.ts
1368
+ var JOURNAL_DIR = join7(primConfigDirectory(), "moves");
1356
1369
  var UNBOUND_BUCKET = "_unbound";
1357
1370
  var JOURNAL_BASENAME = "journal.ndjson";
1358
1371
  var JOURNAL_STATS_SAMPLE_BYTES = 64 * 1024;
@@ -1364,15 +1377,15 @@ function envSlug(apiUrl) {
1364
1377
  return slug;
1365
1378
  }
1366
1379
  function currentEnvDir() {
1367
- return join6(JOURNAL_DIR, envSlug(getSiteUrl()));
1380
+ return join7(JOURNAL_DIR, envSlug(getSiteUrl()));
1368
1381
  }
1369
1382
  var DIR_MODE = 448;
1370
1383
  var FILE_MODE2 = 384;
1371
1384
  var SAFE_BUCKET = /^[A-Za-z0-9_-]+$/;
1372
1385
  var RESERVED_BUCKETS = /* @__PURE__ */ new Set([UNBOUND_BUCKET, "_legacy"]);
1373
1386
  function appendMoveToPath(path, move) {
1374
- const dir = dirname5(path);
1375
- if (!existsSync4(dir)) {
1387
+ const dir = dirname6(path);
1388
+ if (!existsSync5(dir)) {
1376
1389
  mkdirSync4(dir, { recursive: true, mode: DIR_MODE });
1377
1390
  }
1378
1391
  appendFileSync(path, `${JSON.stringify(move)}
@@ -1380,10 +1393,10 @@ function appendMoveToPath(path, move) {
1380
1393
  }
1381
1394
  function bucketDir(orgId) {
1382
1395
  const safe = orgId !== void 0 && SAFE_BUCKET.test(orgId) && !RESERVED_BUCKETS.has(orgId);
1383
- return join6(currentEnvDir(), safe ? orgId : UNBOUND_BUCKET);
1396
+ return join7(currentEnvDir(), safe ? orgId : UNBOUND_BUCKET);
1384
1397
  }
1385
1398
  function journalPath(orgId) {
1386
- return join6(bucketDir(orgId), JOURNAL_BASENAME);
1399
+ return join7(bucketDir(orgId), JOURNAL_BASENAME);
1387
1400
  }
1388
1401
  function appendMove(move, orgId) {
1389
1402
  appendMoveToPath(journalPath(orgId), move);
@@ -1392,15 +1405,15 @@ var FLUSHING_PREFIX = `${JOURNAL_BASENAME}.flushing.`;
1392
1405
 
1393
1406
  // src/lib/activation.ts
1394
1407
  import { execFileSync as execFileSync2 } from "child_process";
1395
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
1408
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
1396
1409
  import { homedir as homedir2 } from "os";
1397
- import { join as join7 } from "path";
1410
+ import { join as join8 } from "path";
1398
1411
 
1399
1412
  // src/lib/git.ts
1400
1413
  import { execFileSync } from "child_process";
1401
1414
  import { createHash as createHash3 } from "crypto";
1402
1415
  import { lstatSync, realpathSync } from "fs";
1403
- import { basename, dirname as dirname6, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "path";
1416
+ import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "path";
1404
1417
  var GIT_TIMEOUT_MS = 1e3;
1405
1418
  var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
1406
1419
  var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
@@ -1519,7 +1532,7 @@ function nearestExistingAncestor(path) {
1519
1532
  } catch (error) {
1520
1533
  const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
1521
1534
  if (code !== "ENOENT" && code !== "ENOTDIR") return null;
1522
- const parent = dirname6(cursor);
1535
+ const parent = dirname7(cursor);
1523
1536
  if (parent === cursor) return null;
1524
1537
  suffix.unshift(cursor.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)));
1525
1538
  cursor = parent;
@@ -1560,9 +1573,9 @@ function canonicalRepositoryPathLegacy(filePath, cwd, repositoryRoot = canonical
1560
1573
  return null;
1561
1574
  }
1562
1575
  try {
1563
- const parent = realpathSync.native(dirname6(absolute));
1576
+ const parent = realpathSync.native(dirname7(absolute));
1564
1577
  if (!lstatSync(parent).isDirectory()) return null;
1565
- target = resolve2(parent, basename(absolute));
1578
+ target = resolve2(parent, basename2(absolute));
1566
1579
  } catch {
1567
1580
  return null;
1568
1581
  }
@@ -1643,10 +1656,10 @@ function hasProjectPrimInstall(cwd) {
1643
1656
  const root = gitToplevel(cwd);
1644
1657
  if (root === null || root === homedir2()) return false;
1645
1658
  for (const rel of PROJECT_INSTALL_FILES) {
1646
- const path = join7(root, rel);
1647
- if (existsSync5(path)) {
1659
+ const path = join8(root, rel);
1660
+ if (existsSync6(path)) {
1648
1661
  try {
1649
- const content = readFileSync6(path, "utf-8");
1662
+ const content = readFileSync7(path, "utf-8");
1650
1663
  if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
1651
1664
  } catch {
1652
1665
  }
@@ -19735,12 +19748,18 @@ function cachedCollectScopeAdmits(cwd, facts) {
19735
19748
  );
19736
19749
  }
19737
19750
 
19751
+ // src/lib/unattended.ts
19752
+ var UNATTENDED_ENV = "PRIM_UNATTENDED";
19753
+ function unattendedEnv(env = process.env) {
19754
+ return { ...env, [UNATTENDED_ENV]: "1" };
19755
+ }
19756
+
19738
19757
  // src/lib/workspace-id.ts
19739
19758
  import { execFileSync as execFileSync3 } from "child_process";
19740
19759
  import { randomUUID as randomUUID2 } from "crypto";
19741
19760
  import {
19742
19761
  constants as constants2,
19743
- chmodSync as chmodSync2,
19762
+ chmodSync as chmodSync3,
19744
19763
  closeSync as closeSync3,
19745
19764
  fchmodSync as fchmodSync2,
19746
19765
  fsyncSync as fsyncSync2,
@@ -19748,13 +19767,13 @@ import {
19748
19767
  lstatSync as lstatSync2,
19749
19768
  mkdirSync as mkdirSync5,
19750
19769
  openSync as openSync3,
19751
- readFileSync as readFileSync7,
19770
+ readFileSync as readFileSync8,
19752
19771
  statSync as statSync2,
19753
19772
  unlinkSync,
19754
19773
  writeFileSync as writeFileSync3
19755
19774
  } from "fs";
19756
- import { dirname as dirname7, isAbsolute as isAbsolute3, join as join8, resolve as resolve3 } from "path";
19757
- var WORKSPACE_ID_RELATIVE_PATH = join8("prim", "workspace-id");
19775
+ import { dirname as dirname8, isAbsolute as isAbsolute3, join as join9, resolve as resolve3 } from "path";
19776
+ var WORKSPACE_ID_RELATIVE_PATH = join9("prim", "workspace-id");
19758
19777
  var MAX_ANCESTORS = 64;
19759
19778
  var MAX_GIT_FILE_BYTES = 4096;
19760
19779
  var MAX_ID_FILE_BYTES = 128;
@@ -19813,19 +19832,19 @@ function resolveWorkspaceIdPath(cwd) {
19813
19832
  function resolveFromFilesystem(cwd) {
19814
19833
  let current = resolve3(cwd);
19815
19834
  for (let depth = 0; depth < MAX_ANCESTORS; depth += 1) {
19816
- const dotGit = join8(current, ".git");
19835
+ const dotGit = join9(current, ".git");
19817
19836
  try {
19818
19837
  const stat = lstatSync2(dotGit);
19819
19838
  if (stat.isDirectory() && isGitDirectory(dotGit)) {
19820
- return join8(dotGit, WORKSPACE_ID_RELATIVE_PATH);
19839
+ return join9(dotGit, WORKSPACE_ID_RELATIVE_PATH);
19821
19840
  }
19822
19841
  if (stat.isFile() && stat.size <= MAX_GIT_FILE_BYTES) {
19823
- const contents = readFileSync7(dotGit, "utf8");
19842
+ const contents = readFileSync8(dotGit, "utf8");
19824
19843
  const match = /^gitdir: (.+?)(?:\r?\n)?$/.exec(contents);
19825
19844
  if (match?.[1]) {
19826
19845
  const gitDirectory = isAbsolute3(match[1]) ? resolve3(match[1]) : resolve3(current, match[1]);
19827
19846
  if (isGitDirectory(gitDirectory)) {
19828
- return join8(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
19847
+ return join9(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
19829
19848
  }
19830
19849
  }
19831
19850
  }
@@ -19836,7 +19855,7 @@ function resolveFromFilesystem(cwd) {
19836
19855
  return null;
19837
19856
  }
19838
19857
  }
19839
- const parent = dirname7(current);
19858
+ const parent = dirname8(current);
19840
19859
  if (parent === current) break;
19841
19860
  current = parent;
19842
19861
  }
@@ -19844,7 +19863,7 @@ function resolveFromFilesystem(cwd) {
19844
19863
  }
19845
19864
  function isGitDirectory(path) {
19846
19865
  try {
19847
- return statSync2(join8(path, "HEAD")).isFile();
19866
+ return statSync2(join9(path, "HEAD")).isFile();
19848
19867
  } catch {
19849
19868
  return false;
19850
19869
  }
@@ -19863,7 +19882,7 @@ function inspectPath(path) {
19863
19882
  return { status: "corrupt", path, reason: "invalid_content" };
19864
19883
  }
19865
19884
  try {
19866
- const contents = readFileSync7(path, "utf8");
19885
+ const contents = readFileSync8(path, "utf8");
19867
19886
  const workspaceId = contents.endsWith("\n") ? contents.slice(0, -1) : contents;
19868
19887
  if ((contents === workspaceId || contents === `${workspaceId}
19869
19888
  `) && isCanonicalWorkspaceId(workspaceId)) {
@@ -19880,14 +19899,14 @@ function inspectPath(path) {
19880
19899
  }
19881
19900
  }
19882
19901
  function createWorkspaceId(path) {
19883
- const parent = dirname7(path);
19902
+ const parent = dirname8(path);
19884
19903
  const workspaceId = randomUUID2();
19885
- const temporaryPath = join8(parent, `.workspace-id-${process.pid}-${randomUUID2()}.tmp`);
19904
+ const temporaryPath = join9(parent, `.workspace-id-${process.pid}-${randomUUID2()}.tmp`);
19886
19905
  let temporaryCreated = false;
19887
19906
  let publishing = false;
19888
19907
  try {
19889
19908
  mkdirSync5(parent, { recursive: true, mode: 448 });
19890
- chmodSync2(parent, 448);
19909
+ chmodSync3(parent, 448);
19891
19910
  const fd = openSync3(
19892
19911
  temporaryPath,
19893
19912
  constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY,
@@ -19954,22 +19973,22 @@ function parseAgent(argv) {
19954
19973
  // src/hooks/codex-context.ts
19955
19974
  import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
19956
19975
  import {
19957
- chmodSync as chmodSync3,
19958
- existsSync as existsSync7,
19976
+ chmodSync as chmodSync4,
19977
+ existsSync as existsSync8,
19959
19978
  mkdirSync as mkdirSync6,
19960
- readFileSync as readFileSync9,
19961
- readdirSync as readdirSync2,
19979
+ readFileSync as readFileSync10,
19980
+ readdirSync as readdirSync3,
19962
19981
  renameSync as renameSync2,
19963
- rmSync as rmSync4,
19982
+ rmSync as rmSync5,
19964
19983
  statSync as statSync3,
19965
19984
  unlinkSync as unlinkSync2,
19966
19985
  writeFileSync as writeFileSync4
19967
19986
  } from "fs";
19968
- import { join as join11 } from "path";
19987
+ import { join as join12 } from "path";
19969
19988
 
19970
19989
  // src/daemon/client.ts
19971
19990
  import { createConnection } from "net";
19972
- import { join as join9 } from "path";
19991
+ import { join as join10 } from "path";
19973
19992
 
19974
19993
  // src/daemon/principal.ts
19975
19994
  import { createHash as createHash4 } from "crypto";
@@ -20019,7 +20038,7 @@ function parseDaemonResponseEnvelope(value) {
20019
20038
  }
20020
20039
 
20021
20040
  // src/daemon/client.ts
20022
- var SOCK_PATH = join9(primConfigDirectory(), "sock");
20041
+ var SOCK_PATH = join10(primConfigDirectory(), "sock");
20023
20042
  var DEFAULT_TIMEOUT_MS2 = 250;
20024
20043
  var nextRequestId = 1;
20025
20044
  function daemonRequest(method, params = {}, opts = {}) {
@@ -20120,8 +20139,8 @@ function isActionablePrivateDraft(row) {
20120
20139
  }
20121
20140
 
20122
20141
  // src/lib/bin-path.ts
20123
- import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
20124
- import { dirname as dirname8, isAbsolute as isAbsolute4, join as join10 } from "path";
20142
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
20143
+ import { dirname as dirname9, isAbsolute as isAbsolute4, join as join11 } from "path";
20125
20144
  import { fileURLToPath } from "url";
20126
20145
  var PKG_NAME = "@primitive.ai/prim";
20127
20146
  var ROOT_WALK_LIMIT = 6;
@@ -20131,12 +20150,12 @@ function locateRoot() {
20131
20150
  if (resolvedRoot !== void 0) {
20132
20151
  return resolvedRoot;
20133
20152
  }
20134
- let dir = dirname8(fileURLToPath(import.meta.url));
20153
+ let dir = dirname9(fileURLToPath(import.meta.url));
20135
20154
  for (let depth = 0; depth < ROOT_WALK_LIMIT; depth++) {
20136
- const manifestPath = join10(dir, "package.json");
20137
- if (existsSync6(manifestPath)) {
20155
+ const manifestPath = join11(dir, "package.json");
20156
+ if (existsSync7(manifestPath)) {
20138
20157
  try {
20139
- const manifest = JSON.parse(readFileSync8(manifestPath, "utf-8"));
20158
+ const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
20140
20159
  if (manifest.name === PKG_NAME && manifest.bin) {
20141
20160
  resolvedRoot = { dir, version: manifest.version, bin: manifest.bin };
20142
20161
  return resolvedRoot;
@@ -20144,7 +20163,7 @@ function locateRoot() {
20144
20163
  } catch {
20145
20164
  }
20146
20165
  }
20147
- const parent = dirname8(dir);
20166
+ const parent = dirname9(dir);
20148
20167
  if (parent === dir) {
20149
20168
  break;
20150
20169
  }
@@ -20161,6 +20180,13 @@ function shellQuote(value) {
20161
20180
  }
20162
20181
  var STABLE_HOOK_ARGUMENTS_RE = /^[-A-Za-z0-9_ ]*$/u;
20163
20182
  var STABLE_HOOK_BIN_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/u;
20183
+ var PRIM_CONFIG_SHELL_RESOLVER = [
20184
+ 'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
20185
+ 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
20186
+ 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
20187
+ '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; ',
20188
+ "fi; fi; "
20189
+ ].join("");
20164
20190
  function stableHookCommand(bin, args = "") {
20165
20191
  if (!STABLE_HOOK_BIN_RE.test(bin) || !STABLE_HOOK_ARGUMENTS_RE.test(args)) {
20166
20192
  throw new Error("invalid stable hook command");
@@ -20170,11 +20196,7 @@ function stableHookCommand(bin, args = "") {
20170
20196
  // it as inert data during the rolling window so an older uninstall or
20171
20197
  // reinstall can remove the stable entry without invoking npm or PATH.
20172
20198
  `prim_legacy_reader='-p ${PKG_NAME}@stable prim-shim.sh ${bin} '; `,
20173
- 'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
20174
- 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
20175
- 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
20176
- '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; ',
20177
- "fi; fi; ",
20199
+ PRIM_CONFIG_SHELL_RESOLVER,
20178
20200
  `case "$prim_config" in /) prim_launcher=/${STABLE_HOOK_LAUNCHER_NAME} ;; *) prim_launcher="$prim_config/${STABLE_HOOK_LAUNCHER_NAME}" ;; esac; `,
20179
20201
  'exec "$prim_launcher" "$@"'
20180
20202
  ].join("");
@@ -20185,6 +20207,36 @@ function detachedHookShimCommand(bin, args = "") {
20185
20207
  return `payload=$(cat); { trap '' HUP; printf '%s' "$payload" | { ${stableHookCommand(bin, args)}; }; } </dev/null >/dev/null 2>&1 &`;
20186
20208
  }
20187
20209
 
20210
+ // src/lib/ansi.ts
20211
+ var MAX_HEALTH_ERROR_LENGTH = 240;
20212
+ function boundedHealthError(value) {
20213
+ if (!value) return void 0;
20214
+ const clean = terminalSafeLine(value);
20215
+ if (!clean) return void 0;
20216
+ return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
20217
+ }
20218
+
20219
+ // src/lib/delivery-backlog.ts
20220
+ var MS_PER_SECOND = 1e3;
20221
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
20222
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
20223
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
20224
+ function isNonNegativeInteger(value) {
20225
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
20226
+ }
20227
+ function isPositiveInteger(value) {
20228
+ return isNonNegativeInteger(value) && value > 0;
20229
+ }
20230
+ function deliveryBacklogState(ingestion) {
20231
+ if (ingestion?.healthy !== false) return void 0;
20232
+ const failures = ingestion.consecutiveFailures;
20233
+ const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
20234
+ if (failedWithoutProgress) return "failing";
20235
+ const retained = ingestion.lastRetainedBucketCount;
20236
+ if (!isNonNegativeInteger(retained)) return "failing";
20237
+ return retained > 0 ? "retained" : "draining";
20238
+ }
20239
+
20188
20240
  // src/lib/presence.ts
20189
20241
  var DECISION_ORIGIN = "https://app.getprimitive.ai";
20190
20242
  function validDecisionUrl(value) {
@@ -20259,7 +20311,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
20259
20311
  if (snapshot.ingestion?.healthy === false) {
20260
20312
  const pending = snapshot.ingestion.pendingCount;
20261
20313
  const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
20262
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
20314
+ const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
20315
+ return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
20263
20316
  }
20264
20317
  if (snapshot.heartbeat?.healthy === false) {
20265
20318
  return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
@@ -20301,10 +20354,10 @@ var STATE_VERSION = 1;
20301
20354
  var DRAFT_STATE_VERSION = 2;
20302
20355
  var NO_CURSOR = -1;
20303
20356
  function stateRoot(namespace) {
20304
- return join11(primConfigDirectory(), namespace, "decision-digests");
20357
+ return join12(primConfigDirectory(), namespace, "decision-digests");
20305
20358
  }
20306
20359
  function draftStateRoot(namespace) {
20307
- return join11(primConfigDirectory(), namespace, "decision-draft-deliveries");
20360
+ return join12(primConfigDirectory(), namespace, "decision-draft-deliveries");
20308
20361
  }
20309
20362
  function workspaceFor(cwd) {
20310
20363
  try {
@@ -20316,12 +20369,12 @@ function workspaceFor(cwd) {
20316
20369
  function statePath(args) {
20317
20370
  const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
20318
20371
  const key = createHash5("sha256").update(identity).digest("hex");
20319
- return join11(stateRoot(args.namespace), `${key}.json`);
20372
+ return join12(stateRoot(args.namespace), `${key}.json`);
20320
20373
  }
20321
20374
  function draftStatePath(args) {
20322
20375
  const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
20323
20376
  const key = createHash5("sha256").update(identity).digest("hex");
20324
- return join11(draftStateRoot(args.namespace), `${key}.json`);
20377
+ return join12(draftStateRoot(args.namespace), `${key}.json`);
20325
20378
  }
20326
20379
  function parseState(value) {
20327
20380
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -20360,25 +20413,25 @@ function parseDraftState(value) {
20360
20413
  };
20361
20414
  }
20362
20415
  function readState(path) {
20363
- if (!existsSync7(path)) return void 0;
20416
+ if (!existsSync8(path)) return void 0;
20364
20417
  try {
20365
- return parseState(JSON.parse(readFileSync9(path, "utf8")));
20418
+ return parseState(JSON.parse(readFileSync10(path, "utf8")));
20366
20419
  } catch {
20367
20420
  return void 0;
20368
20421
  }
20369
20422
  }
20370
20423
  function readDraftState(path) {
20371
- if (!existsSync7(path)) return void 0;
20424
+ if (!existsSync8(path)) return void 0;
20372
20425
  try {
20373
- return parseDraftState(JSON.parse(readFileSync9(path, "utf8")));
20426
+ return parseDraftState(JSON.parse(readFileSync10(path, "utf8")));
20374
20427
  } catch {
20375
20428
  return void 0;
20376
20429
  }
20377
20430
  }
20378
20431
  function writeStateFile(path, directory, state) {
20379
20432
  mkdirSync6(directory, { recursive: true, mode: 448 });
20380
- chmodSync3(directory, 448);
20381
- const temporary = join11(
20433
+ chmodSync4(directory, 448);
20434
+ const temporary = join12(
20382
20435
  directory,
20383
20436
  `.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes3(8).toString("hex")}`
20384
20437
  );
@@ -20389,7 +20442,7 @@ function writeStateFile(path, directory, state) {
20389
20442
  mode: 384,
20390
20443
  flag: "wx"
20391
20444
  });
20392
- chmodSync3(temporary, 384);
20445
+ chmodSync4(temporary, 384);
20393
20446
  renameSync2(temporary, path);
20394
20447
  } finally {
20395
20448
  try {
@@ -20399,17 +20452,17 @@ function writeStateFile(path, directory, state) {
20399
20452
  }
20400
20453
  }
20401
20454
  function cleanupStateFiles(directory, now) {
20402
- if (!existsSync7(directory)) return;
20455
+ if (!existsSync8(directory)) return;
20403
20456
  let names;
20404
20457
  try {
20405
- names = readdirSync2(directory);
20458
+ names = readdirSync3(directory);
20406
20459
  } catch {
20407
20460
  return;
20408
20461
  }
20409
20462
  const files = [];
20410
20463
  const residue = [];
20411
20464
  for (const name of names) {
20412
- const path = join11(directory, name);
20465
+ const path = join12(directory, name);
20413
20466
  try {
20414
20467
  const mtimeMs = statSync3(path).mtimeMs;
20415
20468
  if (name.endsWith(".json")) {
@@ -20431,7 +20484,7 @@ function cleanupStateFiles(directory, now) {
20431
20484
  for (const path of residue) {
20432
20485
  try {
20433
20486
  if (now - statSync3(path).mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
20434
- rmSync4(path, { recursive: true, force: true });
20487
+ rmSync5(path, { recursive: true, force: true });
20435
20488
  }
20436
20489
  } catch {
20437
20490
  }
@@ -20747,31 +20800,78 @@ async function processCodexMessageContext(envelope, deps = defaultDeps) {
20747
20800
  }
20748
20801
 
20749
20802
  // src/commands/cursor-install.ts
20750
- import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
20803
+ import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
20751
20804
  import { homedir as homedir4 } from "os";
20752
- import { isAbsolute as isAbsolute6, join as join14 } from "path";
20805
+ import { isAbsolute as isAbsolute6, join as join15 } from "path";
20753
20806
 
20754
20807
  // src/lib/hook-runtime.ts
20755
20808
  import { createHash as createHash6, randomBytes as randomBytes4 } from "crypto";
20756
20809
  import {
20757
20810
  constants as constants3,
20758
20811
  accessSync,
20759
- chmodSync as chmodSync4,
20812
+ chmodSync as chmodSync5,
20760
20813
  copyFileSync,
20761
- existsSync as existsSync8,
20814
+ existsSync as existsSync9,
20762
20815
  lstatSync as lstatSync3,
20763
20816
  mkdirSync as mkdirSync7,
20764
20817
  mkdtempSync,
20765
- readFileSync as readFileSync10,
20766
- readdirSync as readdirSync3,
20818
+ readFileSync as readFileSync11,
20819
+ readdirSync as readdirSync4,
20767
20820
  realpathSync as realpathSync2,
20768
20821
  renameSync as renameSync3,
20769
- rmSync as rmSync5,
20822
+ rmSync as rmSync6,
20770
20823
  statSync as statSync4,
20771
20824
  utimesSync,
20772
20825
  writeFileSync as writeFileSync5
20773
20826
  } from "fs";
20774
- import { delimiter, dirname as dirname9, isAbsolute as isAbsolute5, join as join12, resolve as resolve4 } from "path";
20827
+ import { delimiter, dirname as dirname10, isAbsolute as isAbsolute5, join as join13, resolve as resolve4 } from "path";
20828
+
20829
+ // src/lib/git-hook-contract.ts
20830
+ var GIT_HOOK_ENTRYPOINT_CONTENT = `#!/bin/sh
20831
+ # prim git hook entrypoint v1 \u2014 managed by prim; do not edit.
20832
+ # Run by the "# >>> prim <hook> hook >>>" block in a Git hook file. Acts only
20833
+ # where prim is active (git config prim.active true) and never fails the hook.
20834
+ case "$0" in */*) prim_dir=\${0%/*} ;; *) exit 0 ;; esac
20835
+ prim_launcher="$prim_dir/${STABLE_HOOK_LAUNCHER_NAME}"
20836
+ [ -x "$prim_launcher" ] || exit 0
20837
+ [ "$(git config --get prim.active 2>/dev/null)" = "true" ] || exit 0
20838
+ prim_hook=\${1-}
20839
+ if [ "$#" -gt 0 ]; then shift; fi
20840
+ case "$prim_hook" in
20841
+ pre-commit)
20842
+ "$prim_launcher" prim-pre-commit "$@" || :
20843
+ ;;
20844
+ post-commit)
20845
+ prim_sha=$(git rev-parse --verify HEAD 2>/dev/null) || exit 0
20846
+ case "$prim_sha" in *[!0-9a-f]* | "") exit 0 ;; esac
20847
+ case "\${#prim_sha}" in 40 | 64) ;; *) exit 0 ;; esac
20848
+ prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
20849
+ prim_observed=$(mktemp "\${TMPDIR:-/tmp}/prim-post-commit-observed.XXXXXXXX" 2>/dev/null) || prim_observed=
20850
+ (
20851
+ trap '' HUP
20852
+ trap 'if [ -n "$prim_observed" ]; then rm -f "$prim_observed"; fi' 0
20853
+ PRIM_COMMIT_SHA=$prim_sha PRIM_COMMIT_BRANCH=$prim_branch PRIM_COMMIT_OBSERVED_FILE=$prim_observed "$prim_launcher" prim-post-commit
20854
+ ) </dev/null >/dev/null 2>&1 &
20855
+ ;;
20856
+ post-rewrite)
20857
+ case "\${1-}" in amend | rebase) prim_source=$1 ;; *) exit 0 ;; esac
20858
+ prim_pairs=$(mktemp "\${TMPDIR:-/tmp}/prim-post-rewrite-pairs.XXXXXXXX" 2>/dev/null) || exit 0
20859
+ if ! chmod 600 "$prim_pairs" 2>/dev/null || ! cat >"$prim_pairs"; then
20860
+ rm -f "$prim_pairs"
20861
+ exit 0
20862
+ fi
20863
+ prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
20864
+ (
20865
+ trap '' HUP
20866
+ trap 'rm -f "$prim_pairs"' 0
20867
+ PRIM_REWRITE_SOURCE=$prim_source PRIM_REWRITE_BRANCH=$prim_branch PRIM_REWRITE_PAIRS_FILE=$prim_pairs "$prim_launcher" prim-post-rewrite
20868
+ ) </dev/null >/dev/null 2>&1 &
20869
+ ;;
20870
+ esac
20871
+ exit 0
20872
+ `;
20873
+
20874
+ // src/lib/hook-runtime.ts
20775
20875
  var RELEASE_PREFIX = "release-";
20776
20876
  var MAX_MANIFEST_BYTES = 64 * 1024;
20777
20877
  var RELEASE_GRACE_MS = 10 * 60 * 1e3;
@@ -20817,9 +20917,9 @@ exec "$prim_node" "$prim_entry" "$@"
20817
20917
  `;
20818
20918
 
20819
20919
  // src/commands/claude-install.ts
20820
- import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
20920
+ import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
20821
20921
  import { homedir as homedir3 } from "os";
20822
- import { join as join13 } from "path";
20922
+ import { join as join14 } from "path";
20823
20923
  var CAPTURE_BIN = "prim-hook";
20824
20924
  var GATE_BIN = "prim-pre-tool-use";
20825
20925
  var POST_TOOL_USE_BIN = "prim-post-tool-use";
@@ -20828,7 +20928,7 @@ var SESSION_END_BIN = "prim-session-end";
20828
20928
  var STATUSLINE_BIN = "prim-statusline";
20829
20929
  var STATUSLINE_ARGS = "";
20830
20930
  var STATUSLINE_COMMAND = stableHookCommand(STATUSLINE_BIN, STATUSLINE_ARGS);
20831
- var USER_SCOPE_PATH = join13(homedir3(), ".claude", "settings.json");
20931
+ var USER_SCOPE_PATH = join14(homedir3(), ".claude", "settings.json");
20832
20932
  function projectRoot() {
20833
20933
  return gitToplevel() ?? process.cwd();
20834
20934
  }
@@ -20907,20 +21007,20 @@ function cursorConfigDirectory(env = process.env, home = homedir4(), platform2 =
20907
21007
  const explicit = env.CURSOR_CONFIG_DIR?.trim();
20908
21008
  if (explicit && isAbsolute6(explicit)) return explicit;
20909
21009
  const xdg = env.XDG_CONFIG_HOME?.trim();
20910
- if (platform2 !== "darwin" && xdg && isAbsolute6(xdg)) return join14(xdg, "cursor");
20911
- return join14(home, ".cursor");
21010
+ if (platform2 !== "darwin" && xdg && isAbsolute6(xdg)) return join15(xdg, "cursor");
21011
+ return join15(home, ".cursor");
20912
21012
  }
20913
21013
  function cursorHooksPath(scope, root = projectRoot()) {
20914
- return scope === "user" ? join14(cursorConfigDirectory(), "hooks.json") : join14(root, ".cursor", "hooks.json");
21014
+ return scope === "user" ? join15(cursorConfigDirectory(), "hooks.json") : join15(root, ".cursor", "hooks.json");
20915
21015
  }
20916
21016
  function plainObject(value) {
20917
21017
  return typeof value === "object" && value !== null && !Array.isArray(value);
20918
21018
  }
20919
21019
  function readCursorHooks(path) {
20920
- if (!existsSync10(path)) return {};
21020
+ if (!existsSync11(path)) return {};
20921
21021
  let parsed;
20922
21022
  try {
20923
- parsed = JSON.parse(readFileSync12(path, "utf8"));
21023
+ parsed = JSON.parse(readFileSync13(path, "utf8"));
20924
21024
  } catch {
20925
21025
  throw new Error(`invalid Cursor hooks JSON at ${path}`);
20926
21026
  }
@@ -25881,20 +25981,9 @@ import { platform } from "os";
25881
25981
  var AGENT_ENVELOPE_VERSION = 3;
25882
25982
 
25883
25983
  // src/hooks/redact.ts
25884
- import { existsSync as existsSync11, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
25885
- import { join as join15 } from "path";
25984
+ import { existsSync as existsSync12, readFileSync as readFileSync14, statSync as statSync5 } from "fs";
25985
+ import { join as join16 } from "path";
25886
25986
  import { Worker } from "worker_threads";
25887
-
25888
- // src/lib/ansi.ts
25889
- var MAX_HEALTH_ERROR_LENGTH = 240;
25890
- function boundedHealthError(value) {
25891
- if (!value) return void 0;
25892
- const clean = terminalSafeLine(value);
25893
- if (!clean) return void 0;
25894
- return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
25895
- }
25896
-
25897
- // src/hooks/redact.ts
25898
25987
  var USER_HOME_RULES = [
25899
25988
  {
25900
25989
  pattern: /\/Users\/[^/\\\s"'`]+/g,
@@ -25969,15 +26058,15 @@ function normalizedWorkspaceFlags(value) {
25969
26058
  return [...new Set(`g${requested}`)].sort().join("");
25970
26059
  }
25971
26060
  function loadWorkspaceRules(cwd) {
25972
- const path = join15(cwd, WORKSPACE_FILE2);
25973
- if (!existsSync11(path)) return [];
26061
+ const path = join16(cwd, WORKSPACE_FILE2);
26062
+ if (!existsSync12(path)) return [];
25974
26063
  let cfg;
25975
26064
  try {
25976
26065
  if (statSync5(path).size > MAX_WORKSPACE_CONFIG_BYTES) {
25977
26066
  debugRedaction(`ignoring oversized ${WORKSPACE_FILE2}`);
25978
26067
  return [];
25979
26068
  }
25980
- cfg = JSON.parse(readFileSync13(path, "utf-8"));
26069
+ cfg = JSON.parse(readFileSync14(path, "utf-8"));
25981
26070
  } catch {
25982
26071
  debugRedaction(`ignoring unparseable ${WORKSPACE_FILE2}`);
25983
26072
  return [];
@@ -26252,7 +26341,7 @@ function shouldFlushAfter(eventType, agent = "claude_code") {
26252
26341
  }
26253
26342
 
26254
26343
  // src/hooks/prim-hook.ts
26255
- var here = dirname11(fileURLToPath2(import.meta.url));
26344
+ var here = dirname12(fileURLToPath2(import.meta.url));
26256
26345
  var outputAttempted = false;
26257
26346
  function emitOutput(output, acknowledge) {
26258
26347
  outputAttempted = true;
@@ -26260,17 +26349,18 @@ function emitOutput(output, acknowledge) {
26260
26349
  }
26261
26350
  function resolveCliVersion() {
26262
26351
  try {
26263
- const pkg = JSON.parse(readFileSync14(join16(here, "..", "..", "package.json"), "utf-8"));
26352
+ const pkg = JSON.parse(readFileSync15(join17(here, "..", "..", "package.json"), "utf-8"));
26264
26353
  return pkg.version ?? "unknown";
26265
26354
  } catch {
26266
26355
  return "unknown";
26267
26356
  }
26268
26357
  }
26269
26358
  function spawnBackgroundFlush() {
26270
- const entry = join16(here, "..", "index.js");
26359
+ const entry = join17(here, "..", "index.js");
26271
26360
  spawn(process.execPath, [entry, "moves", "flush"], {
26272
26361
  detached: true,
26273
- stdio: "ignore"
26362
+ stdio: "ignore",
26363
+ env: unattendedEnv()
26274
26364
  }).unref();
26275
26365
  }
26276
26366
  async function main() {