@primitive.ai/prim 0.1.0-alpha.56 → 0.1.0-alpha.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/SKILL.md +18 -28
- package/dist/{chunk-6LAQVM26.js → chunk-GUXBUBQ7.js} +2 -0
- package/dist/{chunk-DWDEQRWN.js → chunk-I3HIPAIR.js} +12 -14
- package/dist/chunk-LDQZASIF.js +166 -0
- package/dist/{chunk-WELBNODJ.js → chunk-R4CUX6FY.js} +1 -1
- package/dist/{chunk-BOTKPLTI.js → chunk-S4LFRVGD.js} +1 -1
- package/dist/{chunk-ROL7SDXX.js → chunk-UBEBEBLM.js} +2 -2
- package/dist/{chunk-F5QCFBJ6.js → chunk-YDBZTFW5.js} +21 -22
- package/dist/{chunk-HSZPTVKU.js → chunk-YIMUIPMD.js} +20 -15
- package/dist/{chunk-VDLAAUWV.js → chunk-YITJ2H3B.js} +1 -1
- package/dist/chunk-YWJHWGXH.js +438 -0
- package/dist/daemon/server.js +0 -11
- package/dist/hooks/post-commit.js +4 -4
- package/dist/hooks/post-tool-use.js +29 -11
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +84 -240
- package/dist/hooks/prim-hook.js +12 -10
- package/dist/hooks/session-start.js +5 -5
- package/dist/index.js +203 -111
- package/dist/statusline-main.js +10 -8
- package/package.json +2 -1
- package/dist/chunk-R5ZJRSLV.js +0 -82
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
checkAffectedDecisions,
|
|
13
13
|
daemonOrDirectGet,
|
|
14
14
|
formatDecisionsWarning
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YITJ2H3B.js";
|
|
16
16
|
import {
|
|
17
17
|
JOURNAL_DIR,
|
|
18
18
|
SESSIONS_DIR,
|
|
@@ -21,14 +21,14 @@ import {
|
|
|
21
21
|
listFlushing,
|
|
22
22
|
pendingJournalStats,
|
|
23
23
|
readMovesFromPath
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-S4LFRVGD.js";
|
|
25
25
|
import {
|
|
26
26
|
printJson,
|
|
27
27
|
registerSkillCommands
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-UBEBEBLM.js";
|
|
29
29
|
import {
|
|
30
30
|
fetchFeedbackCapability
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-R4CUX6FY.js";
|
|
32
32
|
import {
|
|
33
33
|
inspectWorkspaceId
|
|
34
34
|
} from "./chunk-IMAIBPUC.js";
|
|
@@ -36,16 +36,21 @@ import {
|
|
|
36
36
|
binFile,
|
|
37
37
|
commandMatchesBin,
|
|
38
38
|
detachedHookShimCommand,
|
|
39
|
-
|
|
39
|
+
packageVersion,
|
|
40
|
+
pinnedHookCommand,
|
|
40
41
|
warmBinCache
|
|
41
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-YIMUIPMD.js";
|
|
42
43
|
import {
|
|
43
|
-
|
|
44
|
+
canonicalGitRoot,
|
|
45
|
+
canonicalRepositoryPath,
|
|
44
46
|
decisionIngestionStatus,
|
|
45
47
|
gitToplevel,
|
|
48
|
+
githubRepositoryFullName,
|
|
46
49
|
isRepoActiveForCapture,
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
repoSyncId,
|
|
51
|
+
setRepoActive,
|
|
52
|
+
setRepoSyncId
|
|
53
|
+
} from "./chunk-LDQZASIF.js";
|
|
49
54
|
import {
|
|
50
55
|
HttpError,
|
|
51
56
|
REFRESH_TOKEN_PATH,
|
|
@@ -60,7 +65,7 @@ import {
|
|
|
60
65
|
resolveAuthCredential,
|
|
61
66
|
setStoredToken,
|
|
62
67
|
withFileLock
|
|
63
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-I3HIPAIR.js";
|
|
64
69
|
import {
|
|
65
70
|
daemonIsLive,
|
|
66
71
|
daemonRequest
|
|
@@ -74,7 +79,7 @@ import { Command } from "commander";
|
|
|
74
79
|
import updateNotifier from "update-notifier";
|
|
75
80
|
|
|
76
81
|
// src/commands/activation.ts
|
|
77
|
-
function applyActivation(active) {
|
|
82
|
+
async function applyActivation(active) {
|
|
78
83
|
const root = gitToplevel();
|
|
79
84
|
if (!root) {
|
|
80
85
|
process.stderr.write(
|
|
@@ -84,16 +89,33 @@ function applyActivation(active) {
|
|
|
84
89
|
process.exit(1);
|
|
85
90
|
}
|
|
86
91
|
try {
|
|
87
|
-
|
|
92
|
+
let bound;
|
|
93
|
+
if (active) {
|
|
94
|
+
const repositoryFullName = githubRepositoryFullName(root);
|
|
95
|
+
if (!repositoryFullName) {
|
|
96
|
+
throw new Error("origin must be a GitHub HTTPS/SSH remote in owner/name form");
|
|
97
|
+
}
|
|
98
|
+
const response = await getClient().post("/api/cli/repositories/bind", {
|
|
99
|
+
repositoryFullName
|
|
100
|
+
});
|
|
101
|
+
if (typeof response.repoSyncId !== "string" || response.repoSyncId.length === 0) {
|
|
102
|
+
throw new Error("server returned no repository binding");
|
|
103
|
+
}
|
|
104
|
+
setRepoSyncId(root, response.repoSyncId);
|
|
105
|
+
bound = { repoSyncId: response.repoSyncId, repositoryFullName };
|
|
106
|
+
}
|
|
107
|
+
setRepoActive(root, active);
|
|
108
|
+
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
109
|
+
`);
|
|
110
|
+
printJson({ active, repo: root, ...bound });
|
|
88
111
|
} catch (err) {
|
|
89
112
|
const detail = err instanceof Error ? err.message : String(err);
|
|
90
|
-
process.stderr.write(
|
|
91
|
-
`
|
|
113
|
+
process.stderr.write(
|
|
114
|
+
`[prim] failed to ${active ? "bind and enable" : "disable"} prim: ${detail}
|
|
115
|
+
`
|
|
116
|
+
);
|
|
92
117
|
process.exit(1);
|
|
93
118
|
}
|
|
94
|
-
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
95
|
-
`);
|
|
96
|
-
printJson({ active, repo: root });
|
|
97
119
|
}
|
|
98
120
|
function registerActivationCommands(program2) {
|
|
99
121
|
program2.command("enable").description("Activate prim's hooks in this repo (git config prim.active=true)").action(() => applyActivation(true));
|
|
@@ -1068,7 +1090,7 @@ var SESSION_START_BIN = "prim-session-start";
|
|
|
1068
1090
|
var SESSION_END_BIN = "prim-session-end";
|
|
1069
1091
|
var STATUSLINE_BIN = "prim";
|
|
1070
1092
|
var STATUSLINE_ARGS = "statusline";
|
|
1071
|
-
var STATUSLINE_COMMAND =
|
|
1093
|
+
var STATUSLINE_COMMAND = pinnedHookCommand(STATUSLINE_BIN, STATUSLINE_ARGS);
|
|
1072
1094
|
var STATUSLINE_REFRESH_SECONDS = 5;
|
|
1073
1095
|
var PRIM_BINS = [
|
|
1074
1096
|
CAPTURE_BIN,
|
|
@@ -1092,8 +1114,13 @@ var CAPTURE_EVENTS = [
|
|
|
1092
1114
|
"SessionEnd",
|
|
1093
1115
|
"SubagentStop"
|
|
1094
1116
|
];
|
|
1095
|
-
function makeRegistration(event, matcher, bin, args = ""
|
|
1096
|
-
return {
|
|
1117
|
+
function makeRegistration(event, matcher, bin, args = "") {
|
|
1118
|
+
return {
|
|
1119
|
+
event,
|
|
1120
|
+
matcher,
|
|
1121
|
+
bin,
|
|
1122
|
+
command: pinnedHookCommand(bin, args)
|
|
1123
|
+
};
|
|
1097
1124
|
}
|
|
1098
1125
|
function makeDetachedRegistration(event, matcher, bin, args = "") {
|
|
1099
1126
|
return { event, matcher, bin, command: detachedHookShimCommand(bin, args) };
|
|
@@ -1102,11 +1129,9 @@ var REGISTRATIONS = [
|
|
|
1102
1129
|
...CAPTURE_EVENTS.map(
|
|
1103
1130
|
(event) => event === "SessionEnd" ? makeDetachedRegistration(event, "*", CAPTURE_BIN) : makeRegistration(event, "*", CAPTURE_BIN)
|
|
1104
1131
|
),
|
|
1105
|
-
makeRegistration("PreToolUse", "Edit|Write|MultiEdit", GATE_BIN),
|
|
1106
|
-
makeRegistration("PostToolUse", "Edit|Write|MultiEdit", POST_TOOL_USE_BIN),
|
|
1107
|
-
|
|
1108
|
-
// to refresh the bin cache the other hooks read. See lib/bin-cache.ts.
|
|
1109
|
-
makeRegistration("SessionStart", "*", SESSION_START_BIN, "", { cacheRead: false }),
|
|
1132
|
+
makeRegistration("PreToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", GATE_BIN),
|
|
1133
|
+
makeRegistration("PostToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", POST_TOOL_USE_BIN),
|
|
1134
|
+
makeRegistration("SessionStart", "*", SESSION_START_BIN),
|
|
1110
1135
|
makeDetachedRegistration("SessionEnd", "*", SESSION_END_BIN)
|
|
1111
1136
|
];
|
|
1112
1137
|
var PRIM_PERMISSION_RULE = "Bash(npx --yes @primitive.ai/prim*)";
|
|
@@ -1347,9 +1372,6 @@ function performInstall(scope, force) {
|
|
|
1347
1372
|
if (changed) {
|
|
1348
1373
|
atomicWrite2(path, after);
|
|
1349
1374
|
}
|
|
1350
|
-
if (scope === "project") {
|
|
1351
|
-
activateRepoBestEffort(process.cwd());
|
|
1352
|
-
}
|
|
1353
1375
|
return {
|
|
1354
1376
|
scope,
|
|
1355
1377
|
path,
|
|
@@ -1507,9 +1529,7 @@ var CODEX_REGISTRATIONS = [
|
|
|
1507
1529
|
...CODEX_CAPTURE_EVENTS.map((event) => makeRegistration(event, "*", CAPTURE_BIN2, CODEX_ARGS)),
|
|
1508
1530
|
makeRegistration("PreToolUse", "apply_patch", GATE_BIN2, CODEX_ARGS),
|
|
1509
1531
|
makeRegistration("PostToolUse", "apply_patch", POST_TOOL_USE_BIN2, CODEX_ARGS),
|
|
1510
|
-
|
|
1511
|
-
// refresh the bin cache the other hooks read. See lib/bin-cache.ts.
|
|
1512
|
-
makeRegistration("SessionStart", "*", SESSION_START_BIN2, CODEX_ARGS, { cacheRead: false })
|
|
1532
|
+
makeRegistration("SessionStart", "*", SESSION_START_BIN2, CODEX_ARGS)
|
|
1513
1533
|
];
|
|
1514
1534
|
var USER_SCOPE_PATH2 = join3(homedir3(), ".codex", "hooks.json");
|
|
1515
1535
|
var projectScopePath2 = () => join3(projectRoot(), ".codex", "hooks.json");
|
|
@@ -1562,9 +1582,6 @@ function performInstall2(scope, force) {
|
|
|
1562
1582
|
if (changed) {
|
|
1563
1583
|
atomicWrite2(path, after);
|
|
1564
1584
|
}
|
|
1565
|
-
if (scope === "project") {
|
|
1566
|
-
activateRepoBestEffort(process.cwd());
|
|
1567
|
-
}
|
|
1568
1585
|
return resultFor(scope, path, after, changed);
|
|
1569
1586
|
}
|
|
1570
1587
|
function performUninstall2(scope) {
|
|
@@ -2775,7 +2792,9 @@ function toRequestBody(request) {
|
|
|
2775
2792
|
alternatives: request.alternatives,
|
|
2776
2793
|
confidence: request.confidence,
|
|
2777
2794
|
reversibility: request.reversibility,
|
|
2778
|
-
files: request.files
|
|
2795
|
+
files: request.files,
|
|
2796
|
+
protocolVersion: request.protocolVersion,
|
|
2797
|
+
repoSyncId: request.repoSyncId
|
|
2779
2798
|
};
|
|
2780
2799
|
const body = {};
|
|
2781
2800
|
for (const [key, value] of Object.entries(candidate)) {
|
|
@@ -3035,14 +3054,16 @@ var CREATE_INACTIVE_PROMPT = "[prim] Decision ingestion is disabled here. Create
|
|
|
3035
3054
|
var CREATE_INACTIVE_APPROVED = "[prim] one-time Decision creation approved; passive ingestion remains disabled here";
|
|
3036
3055
|
var CREATE_INACTIVE_REJECTED = "[prim] decision not created: Decision ingestion is disabled here; rerun with prim's --yes to approve this one Decision, or run `prim enable` in a Git project";
|
|
3037
3056
|
var splitList = (value) => (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
3057
|
+
var collectItem = (value, previous) => previous.concat(value);
|
|
3058
|
+
var collectPaths = (value, previous = []) => previous.concat(splitList(value));
|
|
3038
3059
|
function registerDecisionsCommands(program2) {
|
|
3039
3060
|
const decisions = program2.command("decisions").description("Inspect the project Decision Graph");
|
|
3040
3061
|
decisions.command("check").description("Look up active decisions that reference one or more file paths").requiredOption(
|
|
3041
3062
|
"--files <files>",
|
|
3042
|
-
"Comma-separated file paths to check against the Decision Graph"
|
|
3063
|
+
"Comma-separated file paths to check against the Decision Graph (repeatable)",
|
|
3064
|
+
collectPaths
|
|
3043
3065
|
).action(async (opts) => {
|
|
3044
|
-
const
|
|
3045
|
-
const result = await checkAffectedDecisions(filePaths);
|
|
3066
|
+
const result = await checkAffectedDecisions(opts.files);
|
|
3046
3067
|
const warning = formatDecisionsWarning(result);
|
|
3047
3068
|
if (warning) {
|
|
3048
3069
|
console.error(warning);
|
|
@@ -3115,10 +3136,43 @@ function registerDecisionsCommands(program2) {
|
|
|
3115
3136
|
).option("--kind <kind>", "change | exploration | task_execution | unclear (default change)").option("--rationale <text>", "Why the decision was made").option(
|
|
3116
3137
|
"--area <area>",
|
|
3117
3138
|
"Functional area (auth, data, infra, ui, api, billing, mobile, docs, testing)"
|
|
3118
|
-
).option(
|
|
3139
|
+
).option(
|
|
3140
|
+
"--decided <item>",
|
|
3141
|
+
"One adopted constraint, verbatim; repeat the flag for each bullet",
|
|
3142
|
+
collectItem,
|
|
3143
|
+
[]
|
|
3144
|
+
).option(
|
|
3145
|
+
"--alternatives <item>",
|
|
3146
|
+
"One rejected option, verbatim; repeat the flag for each",
|
|
3147
|
+
collectItem,
|
|
3148
|
+
[]
|
|
3149
|
+
).option("--confidence <level>", "high | medium | low (default high)").option("--reversibility <level>", "high | low (default high)").option(
|
|
3119
3150
|
"--files <paths>",
|
|
3120
|
-
"Comma-separated repo-relative paths this decision governs (
|
|
3151
|
+
"Comma-separated exact repo-relative paths this decision governs (repeatable)",
|
|
3152
|
+
collectPaths
|
|
3121
3153
|
).action(async (opts, command) => {
|
|
3154
|
+
const requestedFiles = opts.files ?? [];
|
|
3155
|
+
let explicitScope = {};
|
|
3156
|
+
if (requestedFiles.length > 0) {
|
|
3157
|
+
const binding = repoSyncId(process.cwd());
|
|
3158
|
+
const root = canonicalGitRoot(process.cwd());
|
|
3159
|
+
const canonical = requestedFiles.map(
|
|
3160
|
+
(path) => canonicalRepositoryPath(path, root ?? process.cwd(), root)
|
|
3161
|
+
);
|
|
3162
|
+
if (!binding || canonical.some((path) => !path)) {
|
|
3163
|
+
console.error(
|
|
3164
|
+
"[prim] scoped decision rejected: run `prim enable` and provide exact in-repository file paths"
|
|
3165
|
+
);
|
|
3166
|
+
console.log(JSON.stringify({ ok: false, error: "invalid_repository_scope" }, null, 2));
|
|
3167
|
+
process.exitCode = EXIT_USAGE;
|
|
3168
|
+
return;
|
|
3169
|
+
}
|
|
3170
|
+
explicitScope = {
|
|
3171
|
+
protocolVersion: 3,
|
|
3172
|
+
repoSyncId: binding,
|
|
3173
|
+
files: canonical
|
|
3174
|
+
};
|
|
3175
|
+
}
|
|
3122
3176
|
if (!isRepoActiveForCapture(process.cwd())) {
|
|
3123
3177
|
const globals = command.optsWithGlobals();
|
|
3124
3178
|
const nonInteractive = isNonInteractive(globals);
|
|
@@ -3137,11 +3191,11 @@ function registerDecisionsCommands(program2) {
|
|
|
3137
3191
|
kind: opts.kind,
|
|
3138
3192
|
rationale: opts.rationale,
|
|
3139
3193
|
area: opts.area,
|
|
3140
|
-
decided:
|
|
3141
|
-
alternatives:
|
|
3194
|
+
decided: opts.decided,
|
|
3195
|
+
alternatives: opts.alternatives,
|
|
3142
3196
|
confidence: opts.confidence,
|
|
3143
3197
|
reversibility: opts.reversibility,
|
|
3144
|
-
|
|
3198
|
+
...explicitScope
|
|
3145
3199
|
};
|
|
3146
3200
|
try {
|
|
3147
3201
|
const outcome = await fetchCreate(request);
|
|
@@ -3562,13 +3616,19 @@ var HERMES_ARGS = "--agent hermes";
|
|
|
3562
3616
|
var EDIT_MATCHER = "write_file|patch";
|
|
3563
3617
|
var JSON_INDENT3 = 2;
|
|
3564
3618
|
var SHIM_MODE = 493;
|
|
3565
|
-
var PRIM_BINS3 = [
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3619
|
+
var PRIM_BINS3 = [CAPTURE_BIN3, GATE_BIN3, POST_TOOL_USE_BIN3, SESSION_START_BIN3, SESSION_END_BIN2];
|
|
3620
|
+
var PINNED_VERSION = packageVersion();
|
|
3621
|
+
if (!PINNED_VERSION) throw new Error("cannot determine Primitive package version");
|
|
3622
|
+
var sh = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
3623
|
+
var PINNED_NODE = sh(process.execPath);
|
|
3624
|
+
var PINNED_CASES = PRIM_BINS3.map((bin) => {
|
|
3625
|
+
const file = binFile(bin);
|
|
3626
|
+
if (!file) throw new Error(`cannot locate Primitive hook binary: ${bin}`);
|
|
3627
|
+
return ` ${bin})
|
|
3628
|
+
if [ -x ${PINNED_NODE} ] && [ -f ${sh(file)} ]; then exec ${PINNED_NODE} ${sh(file)} "$@"; fi
|
|
3629
|
+
exec npx --yes -p @primitive.ai/prim@${PINNED_VERSION} "$bin" "$@"
|
|
3630
|
+
;;`;
|
|
3631
|
+
}).join("\n");
|
|
3572
3632
|
function hermesHome() {
|
|
3573
3633
|
return process.env.HERMES_HOME ?? join5(homedir5(), ".hermes");
|
|
3574
3634
|
}
|
|
@@ -3580,17 +3640,13 @@ function shimPath() {
|
|
|
3580
3640
|
}
|
|
3581
3641
|
var SHIM_SCRIPT = `#!/bin/sh
|
|
3582
3642
|
# prim Hermes hook shim \u2014 managed by \`prim hermes install\`. Hermes runs hooks
|
|
3583
|
-
# with shell=False, so
|
|
3584
|
-
# shell does for the Claude Code / Codex installs lives here instead.
|
|
3643
|
+
# with shell=False, so exact package entrypoints are selected here.
|
|
3585
3644
|
bin="$1"
|
|
3586
3645
|
shift
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
exec "./node_modules/.bin/$bin" "$@"
|
|
3592
|
-
fi
|
|
3593
|
-
exec npx --yes -p @primitive.ai/prim@latest "$bin" "$@"
|
|
3646
|
+
case "$bin" in
|
|
3647
|
+
${PINNED_CASES}
|
|
3648
|
+
esac
|
|
3649
|
+
exit 64
|
|
3594
3650
|
`;
|
|
3595
3651
|
function commandFor(bin) {
|
|
3596
3652
|
return `"${shimPath()}" ${bin} ${HERMES_ARGS}`;
|
|
@@ -3899,13 +3955,7 @@ var PRIM_BLOCK_END = blockMarkers(PRE_COMMIT).end;
|
|
|
3899
3955
|
var PRIM_MANAGED_MARK = "prim-managed-hook";
|
|
3900
3956
|
var PRIM_CREATED_MARK = "prim-created-hook";
|
|
3901
3957
|
function hookShim(binName) {
|
|
3902
|
-
return `
|
|
3903
|
-
${binName} || true
|
|
3904
|
-
elif [ -f "./node_modules/.bin/${binName}" ]; then
|
|
3905
|
-
./node_modules/.bin/${binName} || true
|
|
3906
|
-
else
|
|
3907
|
-
npx --yes -p @primitive.ai/prim ${binName} 2>/dev/null || true
|
|
3908
|
-
fi`;
|
|
3958
|
+
return `{ ${pinnedHookCommand(binName)}; } || true`;
|
|
3909
3959
|
}
|
|
3910
3960
|
function gatedShim(binName) {
|
|
3911
3961
|
return `if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
@@ -4170,7 +4220,6 @@ function installHooks(gitRoot, target) {
|
|
|
4170
4220
|
installToDotGit(gitRoot, spec);
|
|
4171
4221
|
}
|
|
4172
4222
|
}
|
|
4173
|
-
activateRepoBestEffort(gitRoot);
|
|
4174
4223
|
}
|
|
4175
4224
|
function registerHooksCommands(program2) {
|
|
4176
4225
|
const hooks = program2.command("hooks").description("Manage git hooks");
|
|
@@ -4588,9 +4637,7 @@ async function performReconcile(idOrShortId, opts = {}) {
|
|
|
4588
4637
|
process.exitCode = EXIT_SERVER;
|
|
4589
4638
|
}
|
|
4590
4639
|
function registerReconcileCommands(program2) {
|
|
4591
|
-
program2.command("reconcile <idOrShortId>").description(
|
|
4592
|
-
"Issue a single-use bypass for a decision flagged by Conflict Gates Enforcement (not currently enabled)"
|
|
4593
|
-
).option(
|
|
4640
|
+
program2.command("reconcile <idOrShortId>").description("Issue a single-use bypass for a decision flagged by Conflict Gates Enforcement").option(
|
|
4594
4641
|
"--flag <conflictFlagId>",
|
|
4595
4642
|
"Specific flag id to bind the bypass to (default: the decision's latest unack'd flag)"
|
|
4596
4643
|
).option("--json", "(reserved; STDOUT is always JSON)").action(async (idOrShortId, opts) => {
|
|
@@ -4712,9 +4759,12 @@ function planSetupSteps(opts) {
|
|
|
4712
4759
|
});
|
|
4713
4760
|
const skillArgs = ["skill", "install", "--agent", opts.agent, ...scopeArgs];
|
|
4714
4761
|
steps.push({ key: "skill", label: "Agent skill", args: skillArgs, required: true });
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4762
|
+
steps.push({
|
|
4763
|
+
key: "enable",
|
|
4764
|
+
label: "Bind and activate this repo",
|
|
4765
|
+
args: ["enable"],
|
|
4766
|
+
required: true
|
|
4767
|
+
});
|
|
4718
4768
|
return steps;
|
|
4719
4769
|
}
|
|
4720
4770
|
var CONFLICT_SESSION = "session";
|
|
@@ -4870,7 +4920,7 @@ function registerSetupCommand(program2, dependencies = {}) {
|
|
|
4870
4920
|
}
|
|
4871
4921
|
}
|
|
4872
4922
|
note("welcome");
|
|
4873
|
-
results.welcome = run(["welcome"]).code === 0 ? "ok" : "failed";
|
|
4923
|
+
results.welcome = run(["welcome", "--agent", agent]).code === 0 ? "ok" : "failed";
|
|
4874
4924
|
const failed = Object.entries(results).filter(([, v]) => v === "failed").map(([k]) => k);
|
|
4875
4925
|
const trail = Object.entries(results).map(([k, v]) => `${k}:${v}`).join(" \xB7 ");
|
|
4876
4926
|
note(
|
|
@@ -4976,28 +5026,58 @@ function registerStatuslineCommands(program2) {
|
|
|
4976
5026
|
// src/commands/welcome.ts
|
|
4977
5027
|
var CMD_GUTTER = 38;
|
|
4978
5028
|
var RECENT_LIMIT = 5;
|
|
4979
|
-
var
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5029
|
+
var WELCOME_AGENTS = ["claude", "codex", "hermes"];
|
|
5030
|
+
function resolveWelcomeAgent(agentFlag, env) {
|
|
5031
|
+
if (agentFlag !== void 0 && WELCOME_AGENTS.includes(agentFlag)) {
|
|
5032
|
+
return agentFlag;
|
|
5033
|
+
}
|
|
5034
|
+
if (agentFlag === void 0 && env.HERMES_INTERACTIVE) {
|
|
5035
|
+
return "hermes";
|
|
5036
|
+
}
|
|
5037
|
+
return void 0;
|
|
5038
|
+
}
|
|
5039
|
+
var CAPTURE_LINES = {
|
|
5040
|
+
claude: ["Capture is automatic \u2014 keep coding; your decisions are recorded for you."],
|
|
5041
|
+
codex: [
|
|
5042
|
+
"Capture starts once you trust the prim hooks \u2014 run /hooks in Codex;",
|
|
5043
|
+
"until then nothing is recorded."
|
|
5044
|
+
],
|
|
5045
|
+
hermes: [
|
|
5046
|
+
"Capture starts once you approve the prim hooks when Hermes prompts;",
|
|
5047
|
+
"until then nothing is recorded."
|
|
5048
|
+
],
|
|
5049
|
+
generic: [
|
|
5050
|
+
"Capture is automatic once your agent's prim hooks are active \u2014 keep",
|
|
5051
|
+
"coding; your decisions are recorded for you."
|
|
5052
|
+
]
|
|
5053
|
+
};
|
|
5054
|
+
var PASSIVE_LINES = {
|
|
5055
|
+
claude: ["Otherwise Primitive captures decisions passively while you work."],
|
|
5056
|
+
codex: [
|
|
5057
|
+
"Once you trust the prim hooks (/hooks in Codex), Primitive captures",
|
|
5058
|
+
"decisions passively while you work."
|
|
5059
|
+
],
|
|
5060
|
+
hermes: [
|
|
5061
|
+
"Once you approve the prim hooks, Primitive captures decisions",
|
|
5062
|
+
"passively while you work."
|
|
5063
|
+
],
|
|
5064
|
+
generic: [
|
|
5065
|
+
"Once your agent's prim hooks are active, Primitive captures decisions",
|
|
5066
|
+
"passively while you work."
|
|
5067
|
+
]
|
|
5068
|
+
};
|
|
5069
|
+
var GUIDANCE_PASSIVE = {
|
|
5070
|
+
claude: "Otherwise, Primitive passively captures decisions in the background while you work.",
|
|
5071
|
+
codex: "Otherwise, once you trust the prim hooks (run /hooks in Codex), Primitive passively captures decisions in the background while you work.",
|
|
5072
|
+
hermes: "Otherwise, once you approve the prim hooks when Hermes prompts, Primitive passively captures decisions in the background while you work.",
|
|
5073
|
+
generic: "Otherwise, once your agent's prim hooks are active, Primitive passively captures decisions in the background while you work."
|
|
5074
|
+
};
|
|
5075
|
+
function seedGuidanceFor(agent) {
|
|
4996
5076
|
return [
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
];
|
|
5077
|
+
"You can tell your agent to add any decision to your Primitive decision graph at any time.",
|
|
5078
|
+
GUIDANCE_PASSIVE[agent ?? "generic"],
|
|
5079
|
+
"To capture decisions in other repositories, enable Primitive there too: run `prim enable` in each one."
|
|
5080
|
+
].join(" ");
|
|
5001
5081
|
}
|
|
5002
5082
|
function welcomeStateFromRecent(result) {
|
|
5003
5083
|
if (result.unavailable !== void 0) {
|
|
@@ -5010,9 +5090,14 @@ function welcomeStateFromRecent(result) {
|
|
|
5010
5090
|
}
|
|
5011
5091
|
return { org: "active", recent };
|
|
5012
5092
|
}
|
|
5013
|
-
function formatWelcome(state) {
|
|
5093
|
+
function formatWelcome(state, agent) {
|
|
5014
5094
|
const cmd = (command, desc) => ` ${dim(command.padEnd(CMD_GUTTER))}${desc}`;
|
|
5015
5095
|
const bullet = (text) => ` ${color("\u2022", "green")} ${text}`;
|
|
5096
|
+
const asBullet = ([first, ...rest]) => [
|
|
5097
|
+
bullet(first),
|
|
5098
|
+
...rest.map((line) => ` ${line}`)
|
|
5099
|
+
];
|
|
5100
|
+
const copyKey = agent ?? "generic";
|
|
5016
5101
|
const head = [
|
|
5017
5102
|
bold(color("Welcome to Primitive", "green")),
|
|
5018
5103
|
"",
|
|
@@ -5020,10 +5105,9 @@ function formatWelcome(state) {
|
|
|
5020
5105
|
"shared graph \u2014 automatically, as you work.",
|
|
5021
5106
|
"",
|
|
5022
5107
|
bold("How it works"),
|
|
5023
|
-
|
|
5024
|
-
bullet("Conflict Gates
|
|
5025
|
-
" with a load-bearing decision
|
|
5026
|
-
" support@getprimitive.ai to turn them on for your team.",
|
|
5108
|
+
...asBullet(CAPTURE_LINES[copyKey]),
|
|
5109
|
+
bullet("Conflict Gates can warn, ask, or block edits that conflict"),
|
|
5110
|
+
" with a load-bearing decision when Enforcement is enabled.",
|
|
5027
5111
|
bullet('Occasional yes/no prompts confirm the "why" behind a decision \u2014'),
|
|
5028
5112
|
" answering keeps the graph trustworthy.",
|
|
5029
5113
|
""
|
|
@@ -5043,10 +5127,16 @@ function formatWelcome(state) {
|
|
|
5043
5127
|
body = [
|
|
5044
5128
|
...teamContext,
|
|
5045
5129
|
bold("Let's seed your decision graph"),
|
|
5046
|
-
"You haven't recorded a decision yet
|
|
5047
|
-
"
|
|
5130
|
+
"You haven't recorded a decision yet. Your agent will look through",
|
|
5131
|
+
"what you've told it and this repo's shared memory files, and propose",
|
|
5132
|
+
"any decisions it finds \u2014 approve, revise, or reject each one, and",
|
|
5133
|
+
'share the "why" when it asks. Approved decisions become part of',
|
|
5134
|
+
"your team's shared graph, visible to any teammates.",
|
|
5048
5135
|
"",
|
|
5049
|
-
|
|
5136
|
+
bold("From here on"),
|
|
5137
|
+
bullet("Tell your agent to add any decision to Primitive, any time."),
|
|
5138
|
+
...asBullet(PASSIVE_LINES[copyKey]),
|
|
5139
|
+
bullet("Run `prim enable` in each additional repo you want captured.")
|
|
5050
5140
|
];
|
|
5051
5141
|
} else {
|
|
5052
5142
|
body = [
|
|
@@ -5056,10 +5146,9 @@ function formatWelcome(state) {
|
|
|
5056
5146
|
cmd("prim --help", "everything else")
|
|
5057
5147
|
];
|
|
5058
5148
|
}
|
|
5059
|
-
|
|
5060
|
-
return [...head, ...body, ...footer].join("\n");
|
|
5149
|
+
return [...head, ...body, "", dim("App: https://app.getprimitive.ai")].join("\n");
|
|
5061
5150
|
}
|
|
5062
|
-
function welcomeJson(state) {
|
|
5151
|
+
function welcomeJson(state, agent) {
|
|
5063
5152
|
if (state.org === "active") {
|
|
5064
5153
|
return { welcomed: true, org: "active", recent: state.recent };
|
|
5065
5154
|
}
|
|
@@ -5067,20 +5156,23 @@ function welcomeJson(state) {
|
|
|
5067
5156
|
return {
|
|
5068
5157
|
welcomed: true,
|
|
5069
5158
|
org: "seed",
|
|
5070
|
-
|
|
5071
|
-
reversePromptTemplate: REVERSE_PROMPT_TEMPLATE,
|
|
5159
|
+
seedGuidance: seedGuidanceFor(agent),
|
|
5072
5160
|
recent: state.recent
|
|
5073
5161
|
};
|
|
5074
5162
|
}
|
|
5075
5163
|
return { welcomed: true, org: "unknown" };
|
|
5076
5164
|
}
|
|
5077
5165
|
function registerWelcomeCommand(program2, deps = { getClient }) {
|
|
5078
|
-
program2.command("welcome").description("Print a brief orientation to Primitive's decision graph").
|
|
5166
|
+
program2.command("welcome").description("Print a brief orientation to Primitive's decision graph").option(
|
|
5167
|
+
"--agent <agent>",
|
|
5168
|
+
"claude, codex, or hermes \u2014 tailors the capture guidance (Hermes auto-detected)"
|
|
5169
|
+
).action(async (options) => {
|
|
5170
|
+
const agent = resolveWelcomeAgent(options.agent, process.env);
|
|
5079
5171
|
const result = await fetchRecent({ limit: RECENT_LIMIT }, deps);
|
|
5080
5172
|
const state = welcomeStateFromRecent(result);
|
|
5081
|
-
process.stderr.write(`${formatWelcome(state)}
|
|
5173
|
+
process.stderr.write(`${formatWelcome(state, agent)}
|
|
5082
5174
|
`);
|
|
5083
|
-
printJson(welcomeJson(state));
|
|
5175
|
+
printJson(welcomeJson(state, agent));
|
|
5084
5176
|
});
|
|
5085
5177
|
}
|
|
5086
5178
|
|
package/dist/statusline-main.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/statusline.ts
|
|
4
4
|
import { readFileSync as readFileSync5 } from "fs";
|
|
5
|
-
import { dirname as
|
|
5
|
+
import { dirname as dirname5, resolve as resolve3 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/client.ts
|
|
@@ -63,7 +63,7 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
63
63
|
const id = nextRequestId++;
|
|
64
64
|
const request = { id, method, params };
|
|
65
65
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
66
|
-
return new Promise((
|
|
66
|
+
return new Promise((resolve4) => {
|
|
67
67
|
let settled = false;
|
|
68
68
|
let socket;
|
|
69
69
|
let buffer = "";
|
|
@@ -78,7 +78,7 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
78
78
|
} catch {
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
resolve4(value);
|
|
82
82
|
};
|
|
83
83
|
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
84
84
|
timer.unref();
|
|
@@ -140,6 +140,8 @@ import { join as join4 } from "path";
|
|
|
140
140
|
|
|
141
141
|
// src/lib/git.ts
|
|
142
142
|
import { execFileSync } from "child_process";
|
|
143
|
+
import { lstatSync, realpathSync } from "fs";
|
|
144
|
+
import { basename, dirname as dirname3, isAbsolute, relative, resolve as resolve2, sep } from "path";
|
|
143
145
|
function gitToplevel(cwd) {
|
|
144
146
|
try {
|
|
145
147
|
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -202,7 +204,7 @@ import { join as join6 } from "path";
|
|
|
202
204
|
|
|
203
205
|
// src/lib/bin-path.ts
|
|
204
206
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
205
|
-
import { dirname as
|
|
207
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "path";
|
|
206
208
|
import { fileURLToPath } from "url";
|
|
207
209
|
var PKG_NAME = "@primitive.ai/prim";
|
|
208
210
|
var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
|
|
@@ -271,13 +273,13 @@ var STATUSLINE_TIMEOUT_MS = 200;
|
|
|
271
273
|
var STATUSLINE_NAME_CAP = 3;
|
|
272
274
|
function readPackageVersion() {
|
|
273
275
|
try {
|
|
274
|
-
const here =
|
|
276
|
+
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
275
277
|
const candidates = [
|
|
276
278
|
// The supervised runtime stages a tiny manifest beside the standalone
|
|
277
279
|
// statusline bundle, so it remains versioned without loading the package.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
280
|
+
resolve3(here, "manifest.json"),
|
|
281
|
+
resolve3(here, "../../package.json"),
|
|
282
|
+
resolve3(here, "../package.json")
|
|
281
283
|
];
|
|
282
284
|
for (const path of candidates) {
|
|
283
285
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitive.ai/prim",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.58",
|
|
4
4
|
"description": "CLI for Primitive's decision graph — passive decision capture, conflict gate, and team presence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"commander": "^12.1.0",
|
|
64
64
|
"diff": "^5.2.0",
|
|
65
|
+
"unbash": "4.0.3",
|
|
65
66
|
"update-notifier": "^7.3.1",
|
|
66
67
|
"yaml": "^2.9.0"
|
|
67
68
|
},
|