@primitive.ai/prim 0.1.0-alpha.62 → 0.1.0-alpha.63
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/dist/{chunk-I3HIPAIR.js → chunk-CPX3BUOC.js} +7 -4
- package/dist/{chunk-S4LFRVGD.js → chunk-HIMWM526.js} +1 -1
- package/dist/{chunk-R4CUX6FY.js → chunk-KHZNYEUV.js} +1 -1
- package/dist/{chunk-LOGW54W5.js → chunk-WNIY6Z6N.js} +1 -1
- package/dist/{chunk-YITJ2H3B.js → chunk-WS3MCNFD.js} +1 -1
- package/dist/daemon/server.js +343 -44
- package/dist/hooks/post-commit.js +2 -2
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +1 -1
- package/dist/hooks/prim-hook.js +3 -3
- package/dist/hooks/session-start.js +8 -4
- package/dist/index.js +186 -126
- package/dist/statusline-main.js +50 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
checkAffectedDecisions,
|
|
14
14
|
daemonOrDirectGet,
|
|
15
15
|
formatDecisionsWarning
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WS3MCNFD.js";
|
|
17
17
|
import {
|
|
18
18
|
JOURNAL_DIR,
|
|
19
19
|
SESSIONS_DIR,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
listFlushing,
|
|
23
23
|
pendingJournalStats,
|
|
24
24
|
readMovesFromPath
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HIMWM526.js";
|
|
26
26
|
import {
|
|
27
27
|
bindRepository,
|
|
28
28
|
compareSemver,
|
|
@@ -34,10 +34,10 @@ import {
|
|
|
34
34
|
registerSkillCommands,
|
|
35
35
|
uninstallPostCommitHookAtPath,
|
|
36
36
|
uninstallProjectPostCommitHook
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-WNIY6Z6N.js";
|
|
38
38
|
import {
|
|
39
39
|
fetchFeedbackCapability
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-KHZNYEUV.js";
|
|
41
41
|
import {
|
|
42
42
|
inspectWorkspaceId
|
|
43
43
|
} from "./chunk-UVIWZWM6.js";
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
resolveAuthCredential,
|
|
74
74
|
setStoredToken,
|
|
75
75
|
withFileLock
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-CPX3BUOC.js";
|
|
77
77
|
import {
|
|
78
78
|
daemonIsLive,
|
|
79
79
|
daemonRequest
|
|
@@ -104,6 +104,7 @@ async function applyActivation(active) {
|
|
|
104
104
|
bound = await bindRepository(root);
|
|
105
105
|
}
|
|
106
106
|
setRepoActive(root, active);
|
|
107
|
+
await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
|
|
107
108
|
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
108
109
|
`);
|
|
109
110
|
printJson({ active, repo: root, ...bound, ...postCommitHook ? { postCommitHook } : {} });
|
|
@@ -671,7 +672,7 @@ function normalizeApiUrl(url) {
|
|
|
671
672
|
|
|
672
673
|
// src/daemon/launchd.ts
|
|
673
674
|
var LAUNCHD_LABEL = "ai.getprimitive.prim-daemon";
|
|
674
|
-
var RUNTIME_SCHEMA_VERSION =
|
|
675
|
+
var RUNTIME_SCHEMA_VERSION = 3;
|
|
675
676
|
var RUNTIME_DIR_MODE = 448;
|
|
676
677
|
var RUNTIME_FILE_MODE = 384;
|
|
677
678
|
var RUNTIME_LAUNCHER_MODE = 448;
|
|
@@ -751,7 +752,7 @@ function findPackageVersion(entryFile) {
|
|
|
751
752
|
function readRuntimeManifest(path) {
|
|
752
753
|
try {
|
|
753
754
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
754
|
-
if (parsed.schemaVersion !== RUNTIME_SCHEMA_VERSION || typeof parsed.version !== "string" || typeof parsed.nodePath !== "string" || parsed.daemonFile !== "prim-daemon-server" || typeof parsed.daemonSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(parsed.daemonSha256)
|
|
755
|
+
if (parsed.schemaVersion !== RUNTIME_SCHEMA_VERSION || typeof parsed.version !== "string" || typeof parsed.nodePath !== "string" || parsed.daemonFile !== "prim-daemon-server" || typeof parsed.daemonSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(parsed.daemonSha256)) {
|
|
755
756
|
return null;
|
|
756
757
|
}
|
|
757
758
|
return parsed;
|
|
@@ -759,12 +760,12 @@ function readRuntimeManifest(path) {
|
|
|
759
760
|
return null;
|
|
760
761
|
}
|
|
761
762
|
}
|
|
762
|
-
function sameRuntime(current, desired, daemonPath
|
|
763
|
-
if (!current || current.version !== desired.version || current.nodePath !== desired.nodePath || current.daemonSha256 !== desired.daemonSha256
|
|
763
|
+
function sameRuntime(current, desired, daemonPath) {
|
|
764
|
+
if (!current || current.version !== desired.version || current.nodePath !== desired.nodePath || current.daemonSha256 !== desired.daemonSha256) {
|
|
764
765
|
return false;
|
|
765
766
|
}
|
|
766
767
|
try {
|
|
767
|
-
return sha256File(daemonPath) === desired.daemonSha256
|
|
768
|
+
return sha256File(daemonPath) === desired.daemonSha256;
|
|
768
769
|
} catch {
|
|
769
770
|
return false;
|
|
770
771
|
}
|
|
@@ -788,8 +789,6 @@ function stageRuntime(options = {}) {
|
|
|
788
789
|
if (!daemonSource || !existsSync2(daemonSource)) {
|
|
789
790
|
throw new Error("cannot stage runtime: prim-daemon-server bundle is unavailable");
|
|
790
791
|
}
|
|
791
|
-
const configuredStatusline = options.statuslineSource === void 0 ? binFile("prim-statusline") : options.statuslineSource;
|
|
792
|
-
const statuslineSource = configuredStatusline && existsSync2(configuredStatusline) ? configuredStatusline : null;
|
|
793
792
|
const nodePath = resolve(options.nodePath ?? process.execPath);
|
|
794
793
|
const version = options.version ?? findPackageVersion(daemonSource);
|
|
795
794
|
const desired = {
|
|
@@ -798,10 +797,6 @@ function stageRuntime(options = {}) {
|
|
|
798
797
|
nodePath,
|
|
799
798
|
daemonFile: "prim-daemon-server",
|
|
800
799
|
daemonSha256: sha256File(daemonSource),
|
|
801
|
-
...statuslineSource ? {
|
|
802
|
-
statuslineFile: "prim-statusline",
|
|
803
|
-
statuslineSha256: sha256File(statuslineSource)
|
|
804
|
-
} : {},
|
|
805
800
|
stagedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
806
801
|
};
|
|
807
802
|
let currentRelease = null;
|
|
@@ -811,19 +806,13 @@ function stageRuntime(options = {}) {
|
|
|
811
806
|
}
|
|
812
807
|
const current = currentRelease ? readRuntimeManifest(join(currentRelease, "manifest.json")) : null;
|
|
813
808
|
const currentDaemon = currentRelease ? join(currentRelease, desired.daemonFile) : "";
|
|
814
|
-
|
|
815
|
-
if (currentRelease && sameRuntime(current, desired, currentDaemon, currentStatusline)) {
|
|
809
|
+
if (currentRelease && sameRuntime(current, desired, currentDaemon)) {
|
|
816
810
|
const manifest = current;
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
RUNTIME_LAUNCHER_MODE
|
|
823
|
-
);
|
|
824
|
-
} else {
|
|
825
|
-
rmSync(paths.statuslineLauncher, { force: true });
|
|
826
|
-
}
|
|
811
|
+
atomicWrite(
|
|
812
|
+
paths.statuslineLauncher,
|
|
813
|
+
statuslineLauncherContent(manifest.version, options.homeDir),
|
|
814
|
+
RUNTIME_LAUNCHER_MODE
|
|
815
|
+
);
|
|
827
816
|
return {
|
|
828
817
|
changed: false,
|
|
829
818
|
manifest,
|
|
@@ -844,14 +833,6 @@ function stageRuntime(options = {}) {
|
|
|
844
833
|
if (sha256File(daemonTarget) !== desired.daemonSha256) {
|
|
845
834
|
throw new Error("cannot stage runtime: daemon bundle changed while it was copied");
|
|
846
835
|
}
|
|
847
|
-
if (statuslineSource && desired.statuslineFile) {
|
|
848
|
-
const statuslineTarget = join(stagingDir, desired.statuslineFile);
|
|
849
|
-
copyFileSync(statuslineSource, statuslineTarget);
|
|
850
|
-
chmodSync(statuslineTarget, RUNTIME_FILE_MODE);
|
|
851
|
-
if (sha256File(statuslineTarget) !== desired.statuslineSha256) {
|
|
852
|
-
throw new Error("cannot stage runtime: statusline bundle changed while it was copied");
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
836
|
const manifestTarget = join(stagingDir, "manifest.json");
|
|
856
837
|
writeFileSync(manifestTarget, `${JSON.stringify(desired, null, 2)}
|
|
857
838
|
`, {
|
|
@@ -859,23 +840,16 @@ function stageRuntime(options = {}) {
|
|
|
859
840
|
mode: RUNTIME_FILE_MODE,
|
|
860
841
|
flag: "wx"
|
|
861
842
|
});
|
|
862
|
-
const releaseName = `release-${createHash2("sha256").update(
|
|
863
|
-
`${version}\0${nodePath}\0${desired.statuslineFile ?? ""}\0${desired.stagedAt}\0${process.pid}\0${Math.random()}`
|
|
864
|
-
).digest("hex").slice(0, 16)}`;
|
|
843
|
+
const releaseName = `release-${createHash2("sha256").update(`${version}\0${nodePath}\0${desired.stagedAt}\0${process.pid}\0${Math.random()}`).digest("hex").slice(0, 16)}`;
|
|
865
844
|
releaseDir = join(paths.releasesDir, releaseName);
|
|
866
845
|
renameSync(stagingDir, releaseDir);
|
|
867
846
|
releaseDir = realpathSync(releaseDir);
|
|
868
847
|
atomicSymlink(join("releases", releaseName), paths.currentLink);
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
RUNTIME_LAUNCHER_MODE
|
|
875
|
-
);
|
|
876
|
-
} else {
|
|
877
|
-
rmSync(paths.statuslineLauncher, { force: true });
|
|
878
|
-
}
|
|
848
|
+
atomicWrite(
|
|
849
|
+
paths.statuslineLauncher,
|
|
850
|
+
statuslineLauncherContent(desired.version, options.homeDir),
|
|
851
|
+
RUNTIME_LAUNCHER_MODE
|
|
852
|
+
);
|
|
879
853
|
} catch (error) {
|
|
880
854
|
rmSync(stagingDir, { recursive: true, force: true });
|
|
881
855
|
throw error;
|
|
@@ -890,9 +864,20 @@ function stageRuntime(options = {}) {
|
|
|
890
864
|
function shellQuote(value) {
|
|
891
865
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
892
866
|
}
|
|
893
|
-
function statuslineLauncherContent(
|
|
867
|
+
function statuslineLauncherContent(version, homeDir = homedir()) {
|
|
868
|
+
const socketPath = join(homeDir, ".config", "prim", "sock");
|
|
869
|
+
const fallback = `primitive ${version} (daemon: down)`;
|
|
894
870
|
return `#!/bin/sh
|
|
895
|
-
|
|
871
|
+
response=$(
|
|
872
|
+
printf 'prim-statusline-v1\\000%s\\000%s\\000' "\${PWD:-/}" "\${PRIM_API_URL:-}" |
|
|
873
|
+
/usr/bin/nc -U -w 1 ${shellQuote(socketPath)} 2>/dev/null
|
|
874
|
+
)
|
|
875
|
+
status=$?
|
|
876
|
+
if [ "$status" -eq 0 ] && [ -n "$response" ]; then
|
|
877
|
+
printf '%s' "$response"
|
|
878
|
+
else
|
|
879
|
+
printf '%s' ${shellQuote(fallback)}
|
|
880
|
+
fi
|
|
896
881
|
`;
|
|
897
882
|
}
|
|
898
883
|
function runtimeStatuslineCommand(options = {}) {
|
|
@@ -1728,10 +1713,7 @@ function performInstall(scope, force) {
|
|
|
1728
1713
|
let statuslineCommand = STATUSLINE_COMMAND;
|
|
1729
1714
|
if (!blockedByUserStatusline && process.platform === "darwin" && (!before.statusLine || isPrimStatusLine(before))) {
|
|
1730
1715
|
try {
|
|
1731
|
-
|
|
1732
|
-
if (!runtime.manifest.statuslineFile) {
|
|
1733
|
-
throw new Error("standalone prim-statusline entry is unavailable");
|
|
1734
|
-
}
|
|
1716
|
+
stageRuntime();
|
|
1735
1717
|
statuslineCommand = runtimeStatuslineCommand();
|
|
1736
1718
|
} catch (error) {
|
|
1737
1719
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -2030,10 +2012,74 @@ ${line("project", result.project)}`);
|
|
|
2030
2012
|
}
|
|
2031
2013
|
|
|
2032
2014
|
// src/commands/daemon.ts
|
|
2033
|
-
import { spawn } from "child_process";
|
|
2015
|
+
import { spawn as spawn2 } from "child_process";
|
|
2034
2016
|
import { closeSync as closeSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync4, unlinkSync } from "fs";
|
|
2017
|
+
import { homedir as homedir5 } from "os";
|
|
2018
|
+
import { join as join5 } from "path";
|
|
2019
|
+
import { Option } from "commander";
|
|
2020
|
+
|
|
2021
|
+
// src/daemon/latest-bootstrap.ts
|
|
2022
|
+
import { spawn } from "child_process";
|
|
2035
2023
|
import { homedir as homedir4 } from "os";
|
|
2036
2024
|
import { join as join4 } from "path";
|
|
2025
|
+
var LATEST_BOOTSTRAP_TIMEOUT_MS = 45e3;
|
|
2026
|
+
var LATEST_BOOTSTRAP_LOCK_NAME = "daemon-latest.lock";
|
|
2027
|
+
async function runLatestEnsure(options) {
|
|
2028
|
+
const env = {
|
|
2029
|
+
...options.env ?? process.env,
|
|
2030
|
+
npm_config_prefer_online: "true",
|
|
2031
|
+
npm_config_fetch_retries: "1",
|
|
2032
|
+
npm_config_fetch_retry_mintimeout: "1000",
|
|
2033
|
+
npm_config_fetch_retry_maxtimeout: "5000",
|
|
2034
|
+
npm_config_fetch_timeout: "15000"
|
|
2035
|
+
};
|
|
2036
|
+
return await new Promise((resolve5) => {
|
|
2037
|
+
let settled = false;
|
|
2038
|
+
let child;
|
|
2039
|
+
try {
|
|
2040
|
+
child = (options.spawnProcess ?? spawn)(
|
|
2041
|
+
"npx",
|
|
2042
|
+
["--yes", "--prefer-online", "-p", "@primitive.ai/prim@latest", "prim", "daemon", "ensure"],
|
|
2043
|
+
{ env, stdio: "ignore" }
|
|
2044
|
+
);
|
|
2045
|
+
} catch {
|
|
2046
|
+
resolve5(false);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
const timer = setTimeout(() => {
|
|
2050
|
+
child.kill("SIGTERM");
|
|
2051
|
+
finish(false);
|
|
2052
|
+
}, options.timeoutMs ?? LATEST_BOOTSTRAP_TIMEOUT_MS);
|
|
2053
|
+
const finish = (result) => {
|
|
2054
|
+
if (settled) return;
|
|
2055
|
+
settled = true;
|
|
2056
|
+
clearTimeout(timer);
|
|
2057
|
+
resolve5(result);
|
|
2058
|
+
};
|
|
2059
|
+
timer.unref();
|
|
2060
|
+
child.once("error", () => finish(false));
|
|
2061
|
+
child.once("exit", (code) => finish(code === 0));
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
async function runLatestDaemonBootstrap(ensureCurrent, options = {}) {
|
|
2065
|
+
const lockPath = options.lockPath ?? join4(options.homeDir ?? homedir4(), ".config", "prim", LATEST_BOOTSTRAP_LOCK_NAME);
|
|
2066
|
+
try {
|
|
2067
|
+
return await withFileLock(
|
|
2068
|
+
lockPath,
|
|
2069
|
+
async () => {
|
|
2070
|
+
const current = await ensureCurrent();
|
|
2071
|
+
if (current.disabled) return true;
|
|
2072
|
+
return await runLatestEnsure(options);
|
|
2073
|
+
},
|
|
2074
|
+
// A concurrent SessionStart already owns the complete bootstrap. Do not
|
|
2075
|
+
// queue another npm lookup behind it; the short window only permits one
|
|
2076
|
+
// retry after recovering a stale lock.
|
|
2077
|
+
{ timeoutMs: 100, pollMs: 25 }
|
|
2078
|
+
);
|
|
2079
|
+
} catch {
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2037
2083
|
|
|
2038
2084
|
// src/lib/presence.ts
|
|
2039
2085
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
@@ -2084,10 +2130,10 @@ function formatTeammatesWithArea(teammates, cap) {
|
|
|
2084
2130
|
|
|
2085
2131
|
// src/commands/daemon.ts
|
|
2086
2132
|
var DAEMON_BIN = "prim-daemon-server";
|
|
2087
|
-
var CONFIG_DIR =
|
|
2088
|
-
var PID_PATH =
|
|
2089
|
-
var SOCK_PATH =
|
|
2090
|
-
var LOG_PATH =
|
|
2133
|
+
var CONFIG_DIR = join5(homedir5(), ".config", "prim");
|
|
2134
|
+
var PID_PATH = join5(CONFIG_DIR, "daemon.pid");
|
|
2135
|
+
var SOCK_PATH = join5(CONFIG_DIR, "sock");
|
|
2136
|
+
var LOG_PATH = join5(CONFIG_DIR, "daemon.log");
|
|
2091
2137
|
var CONFIG_DIR_MODE = 448;
|
|
2092
2138
|
var LOG_FILE_MODE = 384;
|
|
2093
2139
|
var STOP_TIMEOUT_MS = 5e3;
|
|
@@ -2158,13 +2204,13 @@ function sleep(ms) {
|
|
|
2158
2204
|
}
|
|
2159
2205
|
function spawnDaemon(options) {
|
|
2160
2206
|
const file = binFile(DAEMON_BIN);
|
|
2161
|
-
return file ?
|
|
2207
|
+
return file ? spawn2(process.execPath, [file], options) : spawn2(DAEMON_BIN, [], options);
|
|
2162
2208
|
}
|
|
2163
2209
|
function openDaemonLog(configDir = CONFIG_DIR) {
|
|
2164
2210
|
if (!existsSync4(configDir)) {
|
|
2165
2211
|
mkdirSync3(configDir, { recursive: true, mode: CONFIG_DIR_MODE });
|
|
2166
2212
|
}
|
|
2167
|
-
return openSync2(
|
|
2213
|
+
return openSync2(join5(configDir, "daemon.log"), "a", LOG_FILE_MODE);
|
|
2168
2214
|
}
|
|
2169
2215
|
async function waitForReady() {
|
|
2170
2216
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
@@ -2676,7 +2722,7 @@ async function daemonEnsure() {
|
|
|
2676
2722
|
process.stderr.write("[prim] daemon remains explicitly disabled\n");
|
|
2677
2723
|
console.log(JSON.stringify({ ensured: false, disabled: true, supervised: false }, null, 2));
|
|
2678
2724
|
}
|
|
2679
|
-
return;
|
|
2725
|
+
return { disabled };
|
|
2680
2726
|
}
|
|
2681
2727
|
const result = await ensureMacDaemon();
|
|
2682
2728
|
if (result.state === "disabled") {
|
|
@@ -2703,6 +2749,7 @@ async function daemonEnsure() {
|
|
|
2703
2749
|
2
|
|
2704
2750
|
)
|
|
2705
2751
|
);
|
|
2752
|
+
return { disabled: result.state === "disabled" };
|
|
2706
2753
|
}
|
|
2707
2754
|
function registerDaemonCommands(program2) {
|
|
2708
2755
|
const daemon = program2.command("daemon").description("Manage the prim companion daemon (latency unlock + presence + broadcast)");
|
|
@@ -2718,13 +2765,17 @@ function registerDaemonCommands(program2) {
|
|
|
2718
2765
|
daemon.command("restart").description("Restart the daemon and clear any explicit disable marker").option("--foreground", "Restart in the foreground").action(async (opts) => {
|
|
2719
2766
|
await daemonRestart(opts);
|
|
2720
2767
|
});
|
|
2721
|
-
daemon.command("ensure").description("Idempotently install, upgrade, and heal the daemon unless explicitly disabled").action(async () => {
|
|
2768
|
+
daemon.command("ensure").description("Idempotently install, upgrade, and heal the daemon unless explicitly disabled").addOption(new Option("--latest-bootstrap").hideHelp()).action(async (opts) => {
|
|
2769
|
+
if (opts.latestBootstrap) {
|
|
2770
|
+
await runLatestDaemonBootstrap(daemonEnsure);
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2722
2773
|
await daemonEnsure();
|
|
2723
2774
|
});
|
|
2724
2775
|
}
|
|
2725
2776
|
|
|
2726
2777
|
// src/commands/decisions.ts
|
|
2727
|
-
import { Option } from "commander";
|
|
2778
|
+
import { Option as Option2 } from "commander";
|
|
2728
2779
|
|
|
2729
2780
|
// src/decisions/cascade-renderer.ts
|
|
2730
2781
|
var DEPENDENTS_INLINE_LIMIT = 5;
|
|
@@ -3526,7 +3577,7 @@ function registerDecisionsCommands(program2) {
|
|
|
3526
3577
|
}
|
|
3527
3578
|
});
|
|
3528
3579
|
decisions.command("create").description("Record a decision directly with its explicit user or agent origin").requiredOption("--intent <text>", "What was decided (required)").addOption(
|
|
3529
|
-
new
|
|
3580
|
+
new Option2(
|
|
3530
3581
|
"--attribution <origin>",
|
|
3531
3582
|
"Who originated the exact choice: user | agent (required)"
|
|
3532
3583
|
).choices(["user", "agent"]).makeOptionMandatory()
|
|
@@ -4061,8 +4112,8 @@ import {
|
|
|
4061
4112
|
rmSync as rmSync2,
|
|
4062
4113
|
writeFileSync as writeFileSync3
|
|
4063
4114
|
} from "fs";
|
|
4064
|
-
import { homedir as
|
|
4065
|
-
import { dirname as dirname3, join as
|
|
4115
|
+
import { homedir as homedir6 } from "os";
|
|
4116
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
4066
4117
|
import { Document, parseDocument, stringify } from "yaml";
|
|
4067
4118
|
var CAPTURE_BIN3 = "prim-hook";
|
|
4068
4119
|
var GATE_BIN3 = "prim-pre-tool-use";
|
|
@@ -4087,13 +4138,13 @@ var PINNED_CASES = PRIM_BINS3.map((bin) => {
|
|
|
4087
4138
|
;;`;
|
|
4088
4139
|
}).join("\n");
|
|
4089
4140
|
function hermesHome() {
|
|
4090
|
-
return process.env.HERMES_HOME ??
|
|
4141
|
+
return process.env.HERMES_HOME ?? join6(homedir6(), ".hermes");
|
|
4091
4142
|
}
|
|
4092
4143
|
function configPath() {
|
|
4093
|
-
return
|
|
4144
|
+
return join6(hermesHome(), "config.yaml");
|
|
4094
4145
|
}
|
|
4095
4146
|
function shimPath() {
|
|
4096
|
-
return
|
|
4147
|
+
return join6(hermesHome(), "agent-hooks", "prim-shim.sh");
|
|
4097
4148
|
}
|
|
4098
4149
|
var SHIM_SCRIPT = `#!/bin/sh
|
|
4099
4150
|
# prim Hermes hook shim \u2014 managed by \`prim hermes install\`. Hermes runs hooks
|
|
@@ -4395,9 +4446,9 @@ function registerHermesCommands(program2) {
|
|
|
4395
4446
|
// src/commands/hooks.ts
|
|
4396
4447
|
import { execFileSync } from "child_process";
|
|
4397
4448
|
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
4398
|
-
import { homedir as
|
|
4399
|
-
import { dirname as dirname4, join as
|
|
4400
|
-
import { Option as
|
|
4449
|
+
import { homedir as homedir7 } from "os";
|
|
4450
|
+
import { dirname as dirname4, join as join7, resolve as resolve2 } from "path";
|
|
4451
|
+
import { Option as Option3 } from "commander";
|
|
4401
4452
|
var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
|
|
4402
4453
|
var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
|
|
4403
4454
|
var HOOKS = [PRE_COMMIT, POST_COMMIT];
|
|
@@ -4544,9 +4595,9 @@ function projectHooksDir(gitRoot) {
|
|
|
4544
4595
|
}
|
|
4545
4596
|
return resolve2(gitRoot, commonDir, "hooks");
|
|
4546
4597
|
}
|
|
4547
|
-
var PRIM_GIT_HOOKS_DIR =
|
|
4598
|
+
var PRIM_GIT_HOOKS_DIR = join7(homedir7(), ".config", "prim", "git-hooks");
|
|
4548
4599
|
function expandTilde(p) {
|
|
4549
|
-
return p.startsWith("~/") ?
|
|
4600
|
+
return p.startsWith("~/") ? join7(homedir7(), p.slice(2)) : p;
|
|
4550
4601
|
}
|
|
4551
4602
|
function isOurHooksDir(value) {
|
|
4552
4603
|
return value !== "" && expandTilde(value) === PRIM_GIT_HOOKS_DIR;
|
|
@@ -4739,12 +4790,12 @@ function registerHooksCommands(program2) {
|
|
|
4739
4790
|
hooks.command("install").description(
|
|
4740
4791
|
"Install the prim git hooks \u2014 pre-commit + post-commit (auto-detects Husky; use --target to override)"
|
|
4741
4792
|
).addOption(
|
|
4742
|
-
new
|
|
4793
|
+
new Option3("--target <where>", "install destination; bypasses Husky detection").choices([
|
|
4743
4794
|
"husky",
|
|
4744
4795
|
"git-hooks"
|
|
4745
4796
|
])
|
|
4746
4797
|
).addOption(
|
|
4747
|
-
new
|
|
4798
|
+
new Option3(
|
|
4748
4799
|
"--scope <scope>",
|
|
4749
4800
|
"project (default, this repo) or user (a global core.hooksPath capturing every repo)"
|
|
4750
4801
|
).choices(["project", "user"])
|
|
@@ -4784,7 +4835,7 @@ function registerHooksCommands(program2) {
|
|
|
4784
4835
|
hooks.command("uninstall").description(
|
|
4785
4836
|
"Remove the prim git hooks (.git/hooks, or the global core.hooksPath with --scope user)"
|
|
4786
4837
|
).addOption(
|
|
4787
|
-
new
|
|
4838
|
+
new Option3(
|
|
4788
4839
|
"--scope <scope>",
|
|
4789
4840
|
"project (default, this repo) or user (global core.hooksPath)"
|
|
4790
4841
|
).choices(["project", "user"])
|
|
@@ -4821,11 +4872,11 @@ function registerHooksCommands(program2) {
|
|
|
4821
4872
|
|
|
4822
4873
|
// src/commands/moves.ts
|
|
4823
4874
|
import { existsSync as existsSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4824
|
-
import { join as
|
|
4875
|
+
import { join as join9 } from "path";
|
|
4825
4876
|
|
|
4826
4877
|
// src/flusher.ts
|
|
4827
4878
|
import { createReadStream, renameSync as renameSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
4828
|
-
import { dirname as dirname5, join as
|
|
4879
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
4829
4880
|
import { createInterface } from "readline";
|
|
4830
4881
|
var BATCH_SIZE = 500;
|
|
4831
4882
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
@@ -4949,7 +5000,7 @@ async function flushOnce() {
|
|
|
4949
5000
|
return { flushed: total };
|
|
4950
5001
|
}
|
|
4951
5002
|
var flushInFlight;
|
|
4952
|
-
var FLUSH_LOCK_PATH =
|
|
5003
|
+
var FLUSH_LOCK_PATH = join8(dirname5(JOURNAL_DIR), ".flush.lock");
|
|
4953
5004
|
var FLUSH_LOCK_TIMEOUT_MS = 250;
|
|
4954
5005
|
function isFlushLockContended(error) {
|
|
4955
5006
|
return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
|
|
@@ -5066,18 +5117,18 @@ function registerMovesCommands(program2) {
|
|
|
5066
5117
|
}
|
|
5067
5118
|
});
|
|
5068
5119
|
moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
|
|
5069
|
-
const dir =
|
|
5120
|
+
const dir = join9(process.cwd(), ".prim");
|
|
5070
5121
|
if (!existsSync8(dir)) {
|
|
5071
5122
|
mkdirSync6(dir, { recursive: true, mode: DIR_MODE });
|
|
5072
5123
|
}
|
|
5073
|
-
const file =
|
|
5124
|
+
const file = join9(process.cwd(), WORKSPACE_FILE);
|
|
5074
5125
|
writeFileSync5(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
|
|
5075
5126
|
mode: FILE_MODE
|
|
5076
5127
|
});
|
|
5077
5128
|
console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
|
|
5078
5129
|
});
|
|
5079
5130
|
moves.command("drop").description("Remove the .prim/workspace.json binding from the cwd").action(() => {
|
|
5080
|
-
const file =
|
|
5131
|
+
const file = join9(process.cwd(), WORKSPACE_FILE);
|
|
5081
5132
|
if (!existsSync8(file)) {
|
|
5082
5133
|
console.log("[prim] no workspace binding in cwd");
|
|
5083
5134
|
return;
|
|
@@ -5176,7 +5227,7 @@ import {
|
|
|
5176
5227
|
unlinkSync as unlinkSync5,
|
|
5177
5228
|
writeFileSync as writeFileSync6
|
|
5178
5229
|
} from "fs";
|
|
5179
|
-
import { join as
|
|
5230
|
+
import { join as join10 } from "path";
|
|
5180
5231
|
var DIR_MODE2 = 448;
|
|
5181
5232
|
var FILE_MODE2 = 384;
|
|
5182
5233
|
function ensureDir() {
|
|
@@ -5185,7 +5236,7 @@ function ensureDir() {
|
|
|
5185
5236
|
}
|
|
5186
5237
|
}
|
|
5187
5238
|
function markerPath(sessionId) {
|
|
5188
|
-
return
|
|
5239
|
+
return join10(SESSIONS_DIR, `${sessionId}.json`);
|
|
5189
5240
|
}
|
|
5190
5241
|
function registerSessionCommands(program2) {
|
|
5191
5242
|
const session = program2.command("session").description("Decision Event Pipeline \u2014 session binding markers");
|
|
@@ -5213,7 +5264,7 @@ function registerSessionCommands(program2) {
|
|
|
5213
5264
|
for (const f of files) {
|
|
5214
5265
|
const sessionId = f.replace(/\.json$/, "");
|
|
5215
5266
|
try {
|
|
5216
|
-
const m = JSON.parse(readFileSync7(
|
|
5267
|
+
const m = JSON.parse(readFileSync7(join10(SESSIONS_DIR, f), "utf-8"));
|
|
5217
5268
|
console.log(`${sessionId} org=${m.orgId}`);
|
|
5218
5269
|
} catch {
|
|
5219
5270
|
}
|
|
@@ -5233,7 +5284,7 @@ function registerSessionCommands(program2) {
|
|
|
5233
5284
|
// src/commands/setup.ts
|
|
5234
5285
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
5235
5286
|
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
5236
|
-
import { join as
|
|
5287
|
+
import { join as join11 } from "path";
|
|
5237
5288
|
var EXIT_INCOMPLETE = 1;
|
|
5238
5289
|
var EXIT_USAGE3 = 2;
|
|
5239
5290
|
var SESSION_LABELS = {
|
|
@@ -5314,7 +5365,7 @@ function detectProjectConflicts(agent, run) {
|
|
|
5314
5365
|
}
|
|
5315
5366
|
try {
|
|
5316
5367
|
const root = gitToplevel();
|
|
5317
|
-
const preCommit = root &&
|
|
5368
|
+
const preCommit = root && join11(root, ".git", "hooks", "pre-commit");
|
|
5318
5369
|
if (preCommit && existsSync10(preCommit) && readFileSync8(preCommit, "utf-8").includes("prim-pre-commit")) {
|
|
5319
5370
|
conflicts.push(CONFLICT_HOOKS);
|
|
5320
5371
|
}
|
|
@@ -5473,14 +5524,55 @@ function registerSetupCommand(program2, dependencies = {}) {
|
|
|
5473
5524
|
import { readFileSync as readFileSync9 } from "fs";
|
|
5474
5525
|
import { dirname as dirname6, resolve as resolve3 } from "path";
|
|
5475
5526
|
import { fileURLToPath } from "url";
|
|
5476
|
-
|
|
5527
|
+
|
|
5528
|
+
// src/lib/statusline-render.ts
|
|
5477
5529
|
var STATUSLINE_NAME_CAP = 3;
|
|
5530
|
+
function formatStatusline(version, snapshot, resolveIngestionStatus) {
|
|
5531
|
+
if (!snapshot) {
|
|
5532
|
+
return `primitive ${version} (daemon: down)`;
|
|
5533
|
+
}
|
|
5534
|
+
if (snapshot.healthy === false) {
|
|
5535
|
+
if (snapshot.needsReauth) {
|
|
5536
|
+
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
|
|
5537
|
+
}
|
|
5538
|
+
if (snapshot.ingestion?.healthy === false) {
|
|
5539
|
+
const pending = snapshot.ingestion.pendingCount;
|
|
5540
|
+
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
5541
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
|
|
5542
|
+
}
|
|
5543
|
+
if (snapshot.heartbeat?.healthy === false) {
|
|
5544
|
+
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
|
|
5545
|
+
}
|
|
5546
|
+
return `primitive ${version} (daemon: starting)`;
|
|
5547
|
+
}
|
|
5548
|
+
const ingestionStatus = resolveIngestionStatus();
|
|
5549
|
+
if (snapshot.envMismatch) {
|
|
5550
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
|
|
5551
|
+
}
|
|
5552
|
+
if (snapshot.presenceStale) {
|
|
5553
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
|
|
5554
|
+
}
|
|
5555
|
+
let team;
|
|
5556
|
+
if (snapshot.onlineTeammates !== void 0) {
|
|
5557
|
+
team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
|
|
5558
|
+
} else if (snapshot.onlineNames !== void 0) {
|
|
5559
|
+
team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
|
|
5560
|
+
} else if (typeof snapshot.onlineCount === "number") {
|
|
5561
|
+
team = `team: ${String(snapshot.onlineCount)} online`;
|
|
5562
|
+
} else {
|
|
5563
|
+
team = "team: \u2014";
|
|
5564
|
+
}
|
|
5565
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
|
|
5566
|
+
}
|
|
5567
|
+
|
|
5568
|
+
// src/commands/statusline.ts
|
|
5569
|
+
var STATUSLINE_TIMEOUT_MS = 200;
|
|
5478
5570
|
function readPackageVersion() {
|
|
5479
5571
|
try {
|
|
5480
5572
|
const here = dirname6(fileURLToPath(import.meta.url));
|
|
5481
5573
|
const candidates = [
|
|
5482
|
-
//
|
|
5483
|
-
//
|
|
5574
|
+
// Schema-v2 runtimes staged a manifest beside this standalone bundle.
|
|
5575
|
+
// Keep resolving it while those legacy launchers transition to schema v3.
|
|
5484
5576
|
resolve3(here, "manifest.json"),
|
|
5485
5577
|
resolve3(here, "../../package.json"),
|
|
5486
5578
|
resolve3(here, "../package.json")
|
|
@@ -5515,40 +5607,8 @@ async function renderStatusline() {
|
|
|
5515
5607
|
);
|
|
5516
5608
|
if (!snapshot) {
|
|
5517
5609
|
debug("daemon snapshot missing");
|
|
5518
|
-
return `primitive ${version} (daemon: down)`;
|
|
5519
5610
|
}
|
|
5520
|
-
|
|
5521
|
-
if (snapshot.needsReauth) {
|
|
5522
|
-
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
|
|
5523
|
-
}
|
|
5524
|
-
if (snapshot.ingestion?.healthy === false) {
|
|
5525
|
-
const pending = snapshot.ingestion.pendingCount;
|
|
5526
|
-
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
5527
|
-
return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
|
|
5528
|
-
}
|
|
5529
|
-
if (snapshot.heartbeat?.healthy === false) {
|
|
5530
|
-
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
|
|
5531
|
-
}
|
|
5532
|
-
return `primitive ${version} (daemon: starting)`;
|
|
5533
|
-
}
|
|
5534
|
-
const ingestionStatus = decisionIngestionStatus(process.cwd());
|
|
5535
|
-
if (snapshot.envMismatch) {
|
|
5536
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
|
|
5537
|
-
}
|
|
5538
|
-
if (snapshot.presenceStale) {
|
|
5539
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
|
|
5540
|
-
}
|
|
5541
|
-
let team;
|
|
5542
|
-
if (snapshot.onlineTeammates !== void 0) {
|
|
5543
|
-
team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
|
|
5544
|
-
} else if (snapshot.onlineNames !== void 0) {
|
|
5545
|
-
team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
|
|
5546
|
-
} else if (typeof snapshot.onlineCount === "number") {
|
|
5547
|
-
team = `team: ${String(snapshot.onlineCount)} online`;
|
|
5548
|
-
} else {
|
|
5549
|
-
team = "team: \u2014";
|
|
5550
|
-
}
|
|
5551
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
|
|
5611
|
+
return formatStatusline(version, snapshot, () => decisionIngestionStatus(process.cwd()));
|
|
5552
5612
|
}
|
|
5553
5613
|
function registerStatuslineCommands(program2) {
|
|
5554
5614
|
program2.command("statusline").description("Render the Claude Code statusLine for the prim companion daemon").action(async () => {
|