@primitive.ai/prim 0.1.0-alpha.61 → 0.1.0-alpha.62
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-DA4BMHEB.js +77 -0
- package/dist/{chunk-B4SHJYIN.js → chunk-GF5QII7T.js} +83 -110
- package/dist/{chunk-YIMUIPMD.js → chunk-KBFFVYBC.js} +7 -1
- package/dist/chunk-LOGW54W5.js +1244 -0
- package/dist/chunk-QGVHLP5J.js +117 -0
- package/dist/{chunk-IMAIBPUC.js → chunk-UVIWZWM6.js} +1 -0
- package/dist/chunk-XTNMSS4C.js +283 -0
- package/dist/hooks/post-commit.js +82 -16
- package/dist/hooks/post-tool-use.js +40 -24
- package/dist/hooks/pre-tool-use.js +103 -11
- package/dist/hooks/prim-hook.js +30 -9
- package/dist/hooks/session-start.js +22 -5
- package/dist/index.js +176 -30
- package/dist/statusline-main.js +7 -2
- package/package.json +1 -1
- package/dist/chunk-GUXBUBQ7.js +0 -84
- package/dist/chunk-LDQZASIF.js +0 -166
- package/dist/chunk-N4R7EXZT.js +0 -620
- package/dist/chunk-YDBZTFW5.js +0 -67
package/dist/index.js
CHANGED
|
@@ -26,15 +26,21 @@ import {
|
|
|
26
26
|
import {
|
|
27
27
|
bindRepository,
|
|
28
28
|
compareSemver,
|
|
29
|
+
ensureEffectivePostCommitHook,
|
|
30
|
+
ensurePostCommitHookAtPath,
|
|
31
|
+
inspectEffectivePostCommitHook,
|
|
32
|
+
postCommitHookBlock,
|
|
29
33
|
printJson,
|
|
30
|
-
registerSkillCommands
|
|
31
|
-
|
|
34
|
+
registerSkillCommands,
|
|
35
|
+
uninstallPostCommitHookAtPath,
|
|
36
|
+
uninstallProjectPostCommitHook
|
|
37
|
+
} from "./chunk-LOGW54W5.js";
|
|
32
38
|
import {
|
|
33
39
|
fetchFeedbackCapability
|
|
34
40
|
} from "./chunk-R4CUX6FY.js";
|
|
35
41
|
import {
|
|
36
42
|
inspectWorkspaceId
|
|
37
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-UVIWZWM6.js";
|
|
38
44
|
import {
|
|
39
45
|
binFile,
|
|
40
46
|
commandMatchesBin,
|
|
@@ -42,16 +48,17 @@ import {
|
|
|
42
48
|
packageVersion,
|
|
43
49
|
pinnedHookCommand,
|
|
44
50
|
warmBinCache
|
|
45
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-KBFFVYBC.js";
|
|
46
52
|
import {
|
|
47
53
|
canonicalGitRoot,
|
|
48
54
|
canonicalRepositoryPath,
|
|
49
55
|
decisionIngestionStatus,
|
|
50
56
|
gitToplevel,
|
|
51
57
|
isRepoActiveForCapture,
|
|
58
|
+
isValidRepoSyncId,
|
|
52
59
|
repoSyncId,
|
|
53
60
|
setRepoActive
|
|
54
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-XTNMSS4C.js";
|
|
55
62
|
import {
|
|
56
63
|
HttpError,
|
|
57
64
|
REFRESH_TOKEN_PATH,
|
|
@@ -91,17 +98,19 @@ async function applyActivation(active) {
|
|
|
91
98
|
}
|
|
92
99
|
try {
|
|
93
100
|
let bound;
|
|
101
|
+
let postCommitHook;
|
|
94
102
|
if (active) {
|
|
103
|
+
postCommitHook = ensureEffectivePostCommitHook(root).path;
|
|
95
104
|
bound = await bindRepository(root);
|
|
96
105
|
}
|
|
97
106
|
setRepoActive(root, active);
|
|
98
107
|
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
99
108
|
`);
|
|
100
|
-
printJson({ active, repo: root, ...bound });
|
|
109
|
+
printJson({ active, repo: root, ...bound, ...postCommitHook ? { postCommitHook } : {} });
|
|
101
110
|
} catch (err) {
|
|
102
111
|
const detail = err instanceof Error ? err.message : String(err);
|
|
103
112
|
process.stderr.write(
|
|
104
|
-
`[prim] failed to ${active ? "bind and enable" : "disable"} prim: ${detail}
|
|
113
|
+
`[prim] failed to ${active ? "cover, bind, and enable" : "disable"} prim: ${detail}
|
|
105
114
|
`
|
|
106
115
|
);
|
|
107
116
|
process.exit(1);
|
|
@@ -3848,6 +3857,46 @@ function checkWorkspaceIdentity() {
|
|
|
3848
3857
|
};
|
|
3849
3858
|
}
|
|
3850
3859
|
}
|
|
3860
|
+
function classifyRepositoryBinding(value) {
|
|
3861
|
+
return isValidRepoSyncId(value) ? {
|
|
3862
|
+
name: "repo-binding",
|
|
3863
|
+
status: "ok",
|
|
3864
|
+
detail: "valid local repository binding ready"
|
|
3865
|
+
} : {
|
|
3866
|
+
name: "repo-binding",
|
|
3867
|
+
status: "fail",
|
|
3868
|
+
detail: "missing or invalid local prim.repoSyncId \u2014 run `prim enable`"
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
function checkRepositoryBinding() {
|
|
3872
|
+
return classifyRepositoryBinding(repoSyncId(process.cwd()));
|
|
3873
|
+
}
|
|
3874
|
+
function classifyPostCommitHook(inspection) {
|
|
3875
|
+
if (inspection.covered) {
|
|
3876
|
+
return {
|
|
3877
|
+
name: "post-commit",
|
|
3878
|
+
status: "ok",
|
|
3879
|
+
detail: `effective and executable \xB7 ${inspection.kind} \xB7 ${inspection.hookPath}`
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
return {
|
|
3883
|
+
name: "post-commit",
|
|
3884
|
+
status: "fail",
|
|
3885
|
+
detail: `${inspection.reason ?? "uncovered"} \xB7 ${inspection.hookPath}`
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3888
|
+
function checkPostCommitHook() {
|
|
3889
|
+
try {
|
|
3890
|
+
return classifyPostCommitHook(inspectEffectivePostCommitHook());
|
|
3891
|
+
} catch (error) {
|
|
3892
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3893
|
+
return {
|
|
3894
|
+
name: "post-commit",
|
|
3895
|
+
status: /not a git repository/iu.test(detail) ? "warn" : "fail",
|
|
3896
|
+
detail: detail.slice(0, 120)
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3851
3900
|
function checkFeedbackHooks() {
|
|
3852
3901
|
try {
|
|
3853
3902
|
const status = performStatus();
|
|
@@ -3877,6 +3926,9 @@ function parseMovesStatus(value) {
|
|
|
3877
3926
|
if (status.captureState !== "enabled" && status.captureState !== "disabled" || !nullableNumber(status.latestIngestAt) || !nullableNumber(status.latestClassificationAt) || !nullableNumber(status.highWaterMark) || typeof status.pendingSessionCount !== "number" || !Number.isInteger(status.pendingSessionCount) || status.pendingSessionCount < 0 || typeof status.sampled !== "boolean") {
|
|
3878
3927
|
throw new Error("moves status returned an invalid response");
|
|
3879
3928
|
}
|
|
3929
|
+
if (status.oldestPendingAt !== void 0 && !nullableNumber(status.oldestPendingAt) || status.oldestPendingAgeMs !== void 0 && !nullableNumber(status.oldestPendingAgeMs) || typeof status.oldestPendingAgeMs === "number" && status.oldestPendingAgeMs < 0 || status.pendingCommitCorrelationCount !== void 0 && (!Number.isInteger(status.pendingCommitCorrelationCount) || status.pendingCommitCorrelationCount < 0)) {
|
|
3930
|
+
throw new Error("moves status returned invalid pending-age fields");
|
|
3931
|
+
}
|
|
3880
3932
|
return status;
|
|
3881
3933
|
}
|
|
3882
3934
|
function classifyMovesStatus(status) {
|
|
@@ -3887,10 +3939,11 @@ function classifyMovesStatus(status) {
|
|
|
3887
3939
|
};
|
|
3888
3940
|
let classification;
|
|
3889
3941
|
if (status.pendingSessionCount > 0) {
|
|
3942
|
+
const oldest = typeof status.oldestPendingAgeMs === "number" ? ` \xB7 oldest ${String(Math.round(status.oldestPendingAgeMs / MS_PER_SECOND))}s` : typeof status.oldestPendingAt === "number" ? ` \xB7 oldest ${String(Math.max(0, Math.round((Date.now() - status.oldestPendingAt) / MS_PER_SECOND)))}s` : "";
|
|
3890
3943
|
classification = {
|
|
3891
3944
|
name: "classification",
|
|
3892
3945
|
status: "warn",
|
|
3893
|
-
detail: `${String(status.pendingSessionCount)} session(s) pending${status.sampled ? " in a bounded sample" : ""}`
|
|
3946
|
+
detail: `${String(status.pendingSessionCount)} session(s) pending${oldest}${status.sampled ? " in a bounded sample" : ""}`
|
|
3894
3947
|
};
|
|
3895
3948
|
} else if (status.sampled) {
|
|
3896
3949
|
classification = {
|
|
@@ -3905,7 +3958,16 @@ function classifyMovesStatus(status) {
|
|
|
3905
3958
|
detail: status.latestClassificationAt === null ? "no sessions awaiting classification" : `caught up \xB7 last ${new Date(status.latestClassificationAt).toISOString()}`
|
|
3906
3959
|
};
|
|
3907
3960
|
}
|
|
3908
|
-
|
|
3961
|
+
const correlation = status.pendingCommitCorrelationCount === void 0 ? void 0 : status.pendingCommitCorrelationCount > 0 ? {
|
|
3962
|
+
name: "commit-correlation",
|
|
3963
|
+
status: "warn",
|
|
3964
|
+
detail: `${String(status.pendingCommitCorrelationCount)} commit(s) awaiting evidence correlation`
|
|
3965
|
+
} : {
|
|
3966
|
+
name: "commit-correlation",
|
|
3967
|
+
status: "ok",
|
|
3968
|
+
detail: "caught up"
|
|
3969
|
+
};
|
|
3970
|
+
return correlation ? [capture, classification, correlation] : [capture, classification];
|
|
3909
3971
|
}
|
|
3910
3972
|
async function checkBackend() {
|
|
3911
3973
|
try {
|
|
@@ -3954,6 +4016,8 @@ async function collectChecks() {
|
|
|
3954
4016
|
checkStranded(),
|
|
3955
4017
|
checkFeedbackHooks(),
|
|
3956
4018
|
checkWorkspaceIdentity(),
|
|
4019
|
+
checkRepositoryBinding(),
|
|
4020
|
+
checkPostCommitHook(),
|
|
3957
4021
|
...backend,
|
|
3958
4022
|
await checkFeedbackCapability()
|
|
3959
4023
|
];
|
|
@@ -4337,6 +4401,7 @@ import { Option as Option2 } from "commander";
|
|
|
4337
4401
|
var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
|
|
4338
4402
|
var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
|
|
4339
4403
|
var HOOKS = [PRE_COMMIT, POST_COMMIT];
|
|
4404
|
+
var GIT_TIMEOUT_MS = 1e3;
|
|
4340
4405
|
function blockMarkers(spec) {
|
|
4341
4406
|
return {
|
|
4342
4407
|
start: `# >>> prim ${spec.hookName} hook >>>`,
|
|
@@ -4363,12 +4428,14 @@ ${hookShim(spec.binName)}
|
|
|
4363
4428
|
`;
|
|
4364
4429
|
}
|
|
4365
4430
|
function huskyBlock(spec) {
|
|
4431
|
+
if (spec.hookName === POST_COMMIT.hookName) return postCommitHookBlock();
|
|
4366
4432
|
const { start, end } = blockMarkers(spec);
|
|
4367
4433
|
return `${start}
|
|
4368
4434
|
${hookShim(spec.binName)}
|
|
4369
4435
|
${end}`;
|
|
4370
4436
|
}
|
|
4371
4437
|
function gatedBlock(spec) {
|
|
4438
|
+
if (spec.hookName === POST_COMMIT.hookName) return postCommitHookBlock();
|
|
4372
4439
|
const { start, end } = blockMarkers(spec);
|
|
4373
4440
|
return `${start}
|
|
4374
4441
|
${gatedShim(spec.binName)}
|
|
@@ -4377,6 +4444,21 @@ ${end}`;
|
|
|
4377
4444
|
function mergePrimBlock(hookPath, block, binName) {
|
|
4378
4445
|
if (existsSync7(hookPath)) {
|
|
4379
4446
|
const existing = readFileSync6(hookPath, "utf-8");
|
|
4447
|
+
const start = block.slice(0, block.indexOf("\n"));
|
|
4448
|
+
const end = block.slice(block.lastIndexOf("\n") + 1);
|
|
4449
|
+
const starts = existing.split(start).length - 1;
|
|
4450
|
+
const ends = existing.split(end).length - 1;
|
|
4451
|
+
if (starts !== 0 || ends !== 0) {
|
|
4452
|
+
if (starts !== 1 || ends !== 1 || existing.indexOf(end) < existing.indexOf(start)) {
|
|
4453
|
+
throw new Error(`malformed Prim hook markers in ${hookPath}`);
|
|
4454
|
+
}
|
|
4455
|
+
const from = existing.indexOf(start);
|
|
4456
|
+
const through = existing.indexOf(end) + end.length;
|
|
4457
|
+
const refreshed = existing.slice(0, from) + block + existing.slice(through);
|
|
4458
|
+
if (refreshed === existing) return false;
|
|
4459
|
+
writeFileSync4(hookPath, refreshed, { mode: 493 });
|
|
4460
|
+
return true;
|
|
4461
|
+
}
|
|
4380
4462
|
if (containsPrimHook(existing, binName)) return false;
|
|
4381
4463
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
4382
4464
|
writeFileSync4(hookPath, `${existing}${separator}${block}
|
|
@@ -4432,7 +4514,7 @@ function installToHusky(gitRoot, spec = PRE_COMMIT) {
|
|
|
4432
4514
|
}
|
|
4433
4515
|
}
|
|
4434
4516
|
function installToDotGit(gitRoot, spec = PRE_COMMIT) {
|
|
4435
|
-
const hooksDir =
|
|
4517
|
+
const hooksDir = projectHooksDir(gitRoot);
|
|
4436
4518
|
const hookPath = resolve2(hooksDir, spec.hookName);
|
|
4437
4519
|
if (!existsSync7(hooksDir)) {
|
|
4438
4520
|
mkdirSync5(hooksDir, { recursive: true });
|
|
@@ -4450,6 +4532,18 @@ function installToDotGit(gitRoot, spec = PRE_COMMIT) {
|
|
|
4450
4532
|
writeFileSync4(hookPath, dotGitScript(spec), { mode: 493 });
|
|
4451
4533
|
console.log(`Installed ${spec.hookName} hook at ${hookPath}`);
|
|
4452
4534
|
}
|
|
4535
|
+
function projectHooksDir(gitRoot) {
|
|
4536
|
+
const commonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], {
|
|
4537
|
+
cwd: gitRoot,
|
|
4538
|
+
encoding: "utf-8",
|
|
4539
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4540
|
+
timeout: GIT_TIMEOUT_MS
|
|
4541
|
+
}).trim();
|
|
4542
|
+
if (commonDir === "") {
|
|
4543
|
+
throw new Error("git returned an empty common directory");
|
|
4544
|
+
}
|
|
4545
|
+
return resolve2(gitRoot, commonDir, "hooks");
|
|
4546
|
+
}
|
|
4453
4547
|
var PRIM_GIT_HOOKS_DIR = join6(homedir6(), ".config", "prim", "git-hooks");
|
|
4454
4548
|
function expandTilde(p) {
|
|
4455
4549
|
return p.startsWith("~/") ? join6(homedir6(), p.slice(2)) : p;
|
|
@@ -4461,7 +4555,8 @@ function gitConfigGet(level) {
|
|
|
4461
4555
|
try {
|
|
4462
4556
|
return execFileSync("git", ["config", level, "--get", "core.hooksPath"], {
|
|
4463
4557
|
encoding: "utf-8",
|
|
4464
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
4558
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4559
|
+
timeout: GIT_TIMEOUT_MS
|
|
4465
4560
|
}).trim();
|
|
4466
4561
|
} catch {
|
|
4467
4562
|
return "";
|
|
@@ -4489,14 +4584,18 @@ var PASSTHROUGH_HOOKS = [
|
|
|
4489
4584
|
];
|
|
4490
4585
|
function globalHookScript(spec) {
|
|
4491
4586
|
const chainExit = spec.hookName === "pre-commit" ? "|| exit $?" : "|| true";
|
|
4587
|
+
const invocation = spec.hookName === POST_COMMIT.hookName ? postCommitHookBlock() : gatedShim(spec.binName);
|
|
4588
|
+
const beforeComments = spec.hookName === POST_COMMIT.hookName ? `${invocation}
|
|
4589
|
+
` : "";
|
|
4590
|
+
const afterComments = spec.hookName === POST_COMMIT.hookName ? "" : `${invocation}
|
|
4591
|
+
`;
|
|
4492
4592
|
return `#!/bin/sh
|
|
4493
|
-
# prim global ${spec.hookName} hook (core.hooksPath) \u2014 managed by prim; do not edit.
|
|
4593
|
+
${beforeComments}# prim global ${spec.hookName} hook (core.hooksPath) \u2014 managed by prim; do not edit.
|
|
4494
4594
|
# Install/uninstall: prim hooks install|uninstall --scope user
|
|
4495
4595
|
# Runs prim only where activated \u2014 'prim enable' (this repo) or
|
|
4496
4596
|
# 'git config --global prim.active true' (every repo). Chains to the repo's own
|
|
4497
4597
|
# hook regardless, so inactive repos are unaffected.
|
|
4498
|
-
${
|
|
4499
|
-
common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0
|
|
4598
|
+
${afterComments}common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0
|
|
4500
4599
|
repo_hook="$common_dir/hooks/${spec.hookName}"
|
|
4501
4600
|
if [ -x "$repo_hook" ] && ! grep -q '${PRIM_MANAGED_MARK}' "$repo_hook" 2>/dev/null; then
|
|
4502
4601
|
"$repo_hook" "$@" ${chainExit}
|
|
@@ -4521,18 +4620,29 @@ function writeOwnHooks() {
|
|
|
4521
4620
|
mkdirSync5(PRIM_GIT_HOOKS_DIR, { recursive: true });
|
|
4522
4621
|
}
|
|
4523
4622
|
for (const spec of HOOKS) {
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4623
|
+
const path = resolve2(PRIM_GIT_HOOKS_DIR, spec.hookName);
|
|
4624
|
+
if (spec.hookName === POST_COMMIT.hookName) {
|
|
4625
|
+
ensurePostCommitHookAtPath(path, globalHookScript(spec));
|
|
4626
|
+
} else {
|
|
4627
|
+
writeFileSync4(path, globalHookScript(spec), { mode: 493 });
|
|
4628
|
+
}
|
|
4527
4629
|
}
|
|
4528
4630
|
for (const name of PASSTHROUGH_HOOKS) {
|
|
4529
4631
|
writeFileSync4(resolve2(PRIM_GIT_HOOKS_DIR, name), passThroughScript(name), { mode: 493 });
|
|
4530
4632
|
}
|
|
4531
4633
|
}
|
|
4532
4634
|
function appendPrimBlock(hookPath, spec) {
|
|
4635
|
+
if (spec.hookName === POST_COMMIT.hookName) {
|
|
4636
|
+
ensurePostCommitHookAtPath(hookPath);
|
|
4637
|
+
return;
|
|
4638
|
+
}
|
|
4533
4639
|
mergePrimBlock(hookPath, gatedBlock(spec), spec.binName);
|
|
4534
4640
|
}
|
|
4535
4641
|
function stripPrimBlock(hookPath, spec) {
|
|
4642
|
+
if (spec.hookName === POST_COMMIT.hookName) {
|
|
4643
|
+
uninstallPostCommitHookAtPath(hookPath);
|
|
4644
|
+
return;
|
|
4645
|
+
}
|
|
4536
4646
|
if (!existsSync7(hookPath)) return;
|
|
4537
4647
|
const existing = readFileSync6(hookPath, "utf-8");
|
|
4538
4648
|
const primCreated = existing.includes(PRIM_CREATED_MARK);
|
|
@@ -4564,7 +4674,10 @@ function installGlobalHooks(opts = {}) {
|
|
|
4564
4674
|
);
|
|
4565
4675
|
}
|
|
4566
4676
|
writeOwnHooks();
|
|
4567
|
-
execFileSync("git", ["config", "--global", "core.hooksPath", PRIM_GIT_HOOKS_DIR]
|
|
4677
|
+
execFileSync("git", ["config", "--global", "core.hooksPath", PRIM_GIT_HOOKS_DIR], {
|
|
4678
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
4679
|
+
timeout: GIT_TIMEOUT_MS
|
|
4680
|
+
});
|
|
4568
4681
|
console.log(
|
|
4569
4682
|
`Installed prim global git hooks; set core.hooksPath to ${PRIM_GIT_HOOKS_DIR}. Repos are opt-in: run \`prim enable\` in each repo to capture, or \`git config --global prim.active true\` for all.`
|
|
4570
4683
|
);
|
|
@@ -4591,7 +4704,10 @@ function uninstallGlobalHooks() {
|
|
|
4591
4704
|
const p = resolve2(PRIM_GIT_HOOKS_DIR, name);
|
|
4592
4705
|
if (existsSync7(p)) unlinkSync2(p);
|
|
4593
4706
|
}
|
|
4594
|
-
execFileSync("git", ["config", "--global", "--unset", "core.hooksPath"]
|
|
4707
|
+
execFileSync("git", ["config", "--global", "--unset", "core.hooksPath"], {
|
|
4708
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
4709
|
+
timeout: GIT_TIMEOUT_MS
|
|
4710
|
+
});
|
|
4595
4711
|
console.log("Removed prim global git hooks and unset core.hooksPath.");
|
|
4596
4712
|
return;
|
|
4597
4713
|
}
|
|
@@ -4606,13 +4722,17 @@ function uninstallGlobalHooks() {
|
|
|
4606
4722
|
console.log("No prim global git hooks found.");
|
|
4607
4723
|
}
|
|
4608
4724
|
function installHooks(gitRoot, target) {
|
|
4609
|
-
for (const spec of HOOKS) {
|
|
4725
|
+
for (const spec of HOOKS.filter((candidate) => candidate !== POST_COMMIT)) {
|
|
4610
4726
|
if (target === "husky") {
|
|
4611
4727
|
installToHusky(gitRoot, spec);
|
|
4612
4728
|
} else {
|
|
4613
4729
|
installToDotGit(gitRoot, spec);
|
|
4614
4730
|
}
|
|
4615
4731
|
}
|
|
4732
|
+
const postCommit = ensureEffectivePostCommitHook(gitRoot);
|
|
4733
|
+
console.log(
|
|
4734
|
+
`${postCommit.changed ? "Installed" : "Refreshed"} effective post-commit hook at ${postCommit.path}.`
|
|
4735
|
+
);
|
|
4616
4736
|
}
|
|
4617
4737
|
function registerHooksCommands(program2) {
|
|
4618
4738
|
const hooks = program2.command("hooks").description("Manage git hooks");
|
|
@@ -4674,8 +4794,9 @@ function registerHooksCommands(program2) {
|
|
|
4674
4794
|
return;
|
|
4675
4795
|
}
|
|
4676
4796
|
const gitRoot = getGitRoot();
|
|
4677
|
-
|
|
4678
|
-
|
|
4797
|
+
const hooksDir = projectHooksDir(gitRoot);
|
|
4798
|
+
for (const spec of HOOKS.filter((candidate) => candidate !== POST_COMMIT)) {
|
|
4799
|
+
const hookPath = resolve2(hooksDir, spec.hookName);
|
|
4679
4800
|
if (!existsSync7(hookPath)) {
|
|
4680
4801
|
console.log(`No ${spec.hookName} hook found.`);
|
|
4681
4802
|
continue;
|
|
@@ -4687,6 +4808,14 @@ function registerHooksCommands(program2) {
|
|
|
4687
4808
|
console.log(`Left ${spec.hookName} hook at ${hookPath} untouched (not a prim hook).`);
|
|
4688
4809
|
}
|
|
4689
4810
|
}
|
|
4811
|
+
const postCommit = uninstallProjectPostCommitHook(gitRoot);
|
|
4812
|
+
if (!postCommit.changed) {
|
|
4813
|
+
console.log(`No Prim post-commit block found at ${postCommit.path}.`);
|
|
4814
|
+
} else if (postCommit.removedFile) {
|
|
4815
|
+
console.log(`Removed Prim-created post-commit hook at ${postCommit.path}.`);
|
|
4816
|
+
} else {
|
|
4817
|
+
console.log(`Removed the Prim post-commit block from ${postCommit.path}.`);
|
|
4818
|
+
}
|
|
4690
4819
|
});
|
|
4691
4820
|
}
|
|
4692
4821
|
|
|
@@ -5130,12 +5259,6 @@ function planSetupSteps(opts) {
|
|
|
5130
5259
|
args: ["daemon", "start"],
|
|
5131
5260
|
required: true
|
|
5132
5261
|
});
|
|
5133
|
-
steps.push({
|
|
5134
|
-
key: "health",
|
|
5135
|
-
label: "Capture health",
|
|
5136
|
-
args: ["doctor"],
|
|
5137
|
-
required: true
|
|
5138
|
-
});
|
|
5139
5262
|
} else {
|
|
5140
5263
|
steps.push({
|
|
5141
5264
|
key: "daemon-opt-out",
|
|
@@ -5154,10 +5277,18 @@ function planSetupSteps(opts) {
|
|
|
5154
5277
|
steps.push({ key: "skill", label: "Agent skill", args: skillArgs, required: true });
|
|
5155
5278
|
steps.push({
|
|
5156
5279
|
key: "enable",
|
|
5157
|
-
label: "
|
|
5280
|
+
label: "Activate this repo",
|
|
5158
5281
|
args: ["enable"],
|
|
5159
5282
|
required: true
|
|
5160
5283
|
});
|
|
5284
|
+
if (opts.daemon) {
|
|
5285
|
+
steps.push({
|
|
5286
|
+
key: "health",
|
|
5287
|
+
label: "Capture health",
|
|
5288
|
+
args: ["doctor"],
|
|
5289
|
+
required: true
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5161
5292
|
return steps;
|
|
5162
5293
|
}
|
|
5163
5294
|
var CONFLICT_SESSION = "session";
|
|
@@ -5295,7 +5426,10 @@ function registerSetupCommand(program2, dependencies = {}) {
|
|
|
5295
5426
|
note("pre-authorize \xB7 writing prim allow-rule (user scope)\u2026");
|
|
5296
5427
|
results.preauth = run(["claude", "preauth", "--scope", "user"]).code === 0 ? "ok" : "skipped";
|
|
5297
5428
|
}
|
|
5298
|
-
|
|
5429
|
+
const setupSteps = planSetupSteps({ agent, daemon: opts.daemon, scope });
|
|
5430
|
+
for (const step of setupSteps.filter(
|
|
5431
|
+
(candidate) => candidate.key !== "enable" && candidate.key !== "health"
|
|
5432
|
+
)) {
|
|
5299
5433
|
note(`${step.label} \xB7 installing\u2026`);
|
|
5300
5434
|
const { code } = run(step.args);
|
|
5301
5435
|
results[step.key] = code === 0 ? "ok" : step.required ? "failed" : "skipped";
|
|
@@ -5312,6 +5446,18 @@ function registerSetupCommand(program2, dependencies = {}) {
|
|
|
5312
5446
|
);
|
|
5313
5447
|
}
|
|
5314
5448
|
}
|
|
5449
|
+
const enableStep = setupSteps.find((candidate) => candidate.key === "enable");
|
|
5450
|
+
if (enableStep) {
|
|
5451
|
+
note(`${enableStep.label} \xB7 installing\u2026`);
|
|
5452
|
+
const { code } = run(enableStep.args);
|
|
5453
|
+
results[enableStep.key] = code === 0 ? "ok" : enableStep.required ? "failed" : "skipped";
|
|
5454
|
+
}
|
|
5455
|
+
const healthStep = setupSteps.find((candidate) => candidate.key === "health");
|
|
5456
|
+
if (healthStep) {
|
|
5457
|
+
note(`${healthStep.label} \xB7 verifying\u2026`);
|
|
5458
|
+
const { code } = run(healthStep.args);
|
|
5459
|
+
results[healthStep.key] = code === 0 ? "ok" : healthStep.required ? "failed" : "skipped";
|
|
5460
|
+
}
|
|
5315
5461
|
note("welcome");
|
|
5316
5462
|
results.welcome = run(["welcome", "--agent", agent]).code === 0 ? "ok" : "failed";
|
|
5317
5463
|
const failed = Object.entries(results).filter(([, v]) => v === "failed").map(([k]) => k);
|
package/dist/statusline-main.js
CHANGED
|
@@ -140,14 +140,17 @@ import { join as join4 } from "path";
|
|
|
140
140
|
|
|
141
141
|
// src/lib/git.ts
|
|
142
142
|
import { execFileSync } from "child_process";
|
|
143
|
+
import { createHash as createHash2 } from "crypto";
|
|
143
144
|
import { lstatSync, realpathSync } from "fs";
|
|
144
145
|
import { basename, dirname as dirname3, isAbsolute, relative, resolve as resolve2, sep } from "path";
|
|
146
|
+
var GIT_TIMEOUT_MS = 1e3;
|
|
145
147
|
function gitToplevel(cwd) {
|
|
146
148
|
try {
|
|
147
149
|
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
148
150
|
cwd,
|
|
149
151
|
encoding: "utf-8",
|
|
150
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
152
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
153
|
+
timeout: GIT_TIMEOUT_MS
|
|
151
154
|
}).trim();
|
|
152
155
|
} catch {
|
|
153
156
|
return null;
|
|
@@ -156,6 +159,7 @@ function gitToplevel(cwd) {
|
|
|
156
159
|
|
|
157
160
|
// src/lib/activation.ts
|
|
158
161
|
var PRIM_ACTIVE_KEY = "prim.active";
|
|
162
|
+
var GIT_TIMEOUT_MS2 = 1e3;
|
|
159
163
|
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
|
|
160
164
|
var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
|
|
161
165
|
function repoActiveFlag(cwd) {
|
|
@@ -163,7 +167,8 @@ function repoActiveFlag(cwd) {
|
|
|
163
167
|
const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
|
|
164
168
|
cwd,
|
|
165
169
|
encoding: "utf-8",
|
|
166
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
170
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
171
|
+
timeout: GIT_TIMEOUT_MS2
|
|
167
172
|
}).trim();
|
|
168
173
|
if (value === "true") return "true";
|
|
169
174
|
if (value === "false") return "false";
|
package/package.json
CHANGED
package/dist/chunk-GUXBUBQ7.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// src/hooks/redact.ts
|
|
2
|
-
import { existsSync, readFileSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
var DEFAULT_RULES = [
|
|
5
|
-
{ pattern: /Bearer\s+[A-Za-z0-9._\-]+/g, reason: "bearer-token" },
|
|
6
|
-
{ pattern: /sk-[A-Za-z0-9]{32,}/g, reason: "sk-api-key" },
|
|
7
|
-
{
|
|
8
|
-
pattern: /-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]+?-----END [A-Z ]+PRIVATE KEY-----/g,
|
|
9
|
-
reason: "private-key"
|
|
10
|
-
},
|
|
11
|
-
{ pattern: /xox[abprs]-[A-Za-z0-9-]{10,}/g, reason: "slack-token" },
|
|
12
|
-
{ pattern: /ghp_[A-Za-z0-9]{36,}/g, reason: "github-pat" }
|
|
13
|
-
];
|
|
14
|
-
var MAX_SCRUB_LEN = 256e3;
|
|
15
|
-
var WORKSPACE_FILE = ".prim/redaction.json";
|
|
16
|
-
function debugRedaction(message, err) {
|
|
17
|
-
if (!process.env.PRIM_HOOK_DEBUG) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
21
|
-
process.stderr.write(`[prim-hook] redaction: ${message}: ${detail}
|
|
22
|
-
`);
|
|
23
|
-
}
|
|
24
|
-
function loadWorkspaceRules(cwd) {
|
|
25
|
-
const path = join(cwd, WORKSPACE_FILE);
|
|
26
|
-
if (!existsSync(path)) {
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
let cfg;
|
|
30
|
-
try {
|
|
31
|
-
cfg = JSON.parse(readFileSync(path, "utf-8"));
|
|
32
|
-
} catch (err) {
|
|
33
|
-
debugRedaction(`ignoring unparseable ${WORKSPACE_FILE}`, err);
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
if (!cfg.rules) {
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
const compiled = [];
|
|
40
|
-
for (const r of cfg.rules) {
|
|
41
|
-
try {
|
|
42
|
-
compiled.push({ pattern: new RegExp(r.pattern, r.flags ?? "g"), reason: r.reason });
|
|
43
|
-
} catch (err) {
|
|
44
|
-
debugRedaction(`skipping invalid redaction rule "${r.reason}"`, err);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return compiled;
|
|
48
|
-
}
|
|
49
|
-
function applyRules(input, rules) {
|
|
50
|
-
if (input.length > MAX_SCRUB_LEN) {
|
|
51
|
-
return "<REDACTED:oversized>";
|
|
52
|
-
}
|
|
53
|
-
let out = input;
|
|
54
|
-
for (const rule of rules) {
|
|
55
|
-
out = out.replace(rule.pattern, `<REDACTED:${rule.reason}>`);
|
|
56
|
-
}
|
|
57
|
-
return out;
|
|
58
|
-
}
|
|
59
|
-
function scrub(value, rules = DEFAULT_RULES) {
|
|
60
|
-
if (typeof value === "string") {
|
|
61
|
-
return applyRules(value, rules);
|
|
62
|
-
}
|
|
63
|
-
if (Array.isArray(value)) {
|
|
64
|
-
return value.map((v) => scrub(v, rules));
|
|
65
|
-
}
|
|
66
|
-
if (value && typeof value === "object") {
|
|
67
|
-
const out = {};
|
|
68
|
-
for (const [k, v] of Object.entries(value)) {
|
|
69
|
-
out[k] = scrub(v, rules);
|
|
70
|
-
}
|
|
71
|
-
return out;
|
|
72
|
-
}
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
function scrubFromCwd(value, cwd) {
|
|
76
|
-
const rules = [...DEFAULT_RULES, ...loadWorkspaceRules(cwd)];
|
|
77
|
-
return scrub(value, rules);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
DEFAULT_RULES,
|
|
82
|
-
scrub,
|
|
83
|
-
scrubFromCwd
|
|
84
|
-
};
|