@primitive.ai/prim 0.1.0-alpha.23 → 0.1.0-alpha.24
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 +26 -1
- package/dist/index.js +104 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,15 @@ npx @primitive.ai/prim
|
|
|
24
24
|
|
|
25
25
|
## Quick Start
|
|
26
26
|
|
|
27
|
+
One command does the whole install — auth, session hooks, daemon, git hooks,
|
|
28
|
+
skill, and the welcome:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
prim setup # add --agent codex for Codex, --no-daemon to skip the daemon
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or run the steps individually:
|
|
35
|
+
|
|
27
36
|
```bash
|
|
28
37
|
# 1. Authenticate via browser (WorkOS OAuth)
|
|
29
38
|
prim auth login
|
|
@@ -38,10 +47,26 @@ prim daemon start
|
|
|
38
47
|
prim hooks install
|
|
39
48
|
```
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
`prim claude install` also writes a scoped `Bash(npx --yes @primitive.ai/prim@latest:*)`
|
|
51
|
+
allow-rule into `.claude/settings.json`, so an agent's prim calls don't stall on a
|
|
52
|
+
permission prompt.
|
|
53
|
+
|
|
54
|
+
An AI coding agent can drive the setup itself — see [`setup.md`](./setup.md).
|
|
42
55
|
|
|
43
56
|
## Commands
|
|
44
57
|
|
|
58
|
+
### Setup
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
prim setup # Run the whole install in one shot
|
|
62
|
+
prim setup --agent codex # Same, for OpenAI Codex
|
|
63
|
+
prim setup --no-daemon # Skip the companion daemon
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Orchestrates auth → session hooks → daemon → git hooks → skill → welcome,
|
|
67
|
+
re-running each underlying command so every step behaves exactly as if run by
|
|
68
|
+
hand (including the browser login). Idempotent — safe to re-run.
|
|
69
|
+
|
|
45
70
|
### Auth
|
|
46
71
|
|
|
47
72
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -401,6 +401,7 @@ var REGISTRATIONS = [
|
|
|
401
401
|
makeRegistration("SessionStart", "*", SESSION_START_BIN),
|
|
402
402
|
makeRegistration("SessionEnd", "*", SESSION_END_BIN)
|
|
403
403
|
];
|
|
404
|
+
var PRIM_PERMISSION_RULE = "Bash(npx --yes @primitive.ai/prim@latest:*)";
|
|
404
405
|
function settingsPathFor(scope) {
|
|
405
406
|
return scope === "user" ? USER_SCOPE_PATH : projectScopePath();
|
|
406
407
|
}
|
|
@@ -463,12 +464,36 @@ function applyStatusLine(settings) {
|
|
|
463
464
|
}
|
|
464
465
|
};
|
|
465
466
|
}
|
|
467
|
+
function applyPermissions(settings) {
|
|
468
|
+
const permissions = settings.permissions ?? {};
|
|
469
|
+
const allow = permissions.allow ?? [];
|
|
470
|
+
if (allow.includes(PRIM_PERMISSION_RULE)) {
|
|
471
|
+
return settings;
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
...settings,
|
|
475
|
+
permissions: { ...permissions, allow: [...allow, PRIM_PERMISSION_RULE] }
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function removePrimPermission(settings) {
|
|
479
|
+
const permissions = settings.permissions;
|
|
480
|
+
if (!permissions?.allow?.includes(PRIM_PERMISSION_RULE)) {
|
|
481
|
+
return settings;
|
|
482
|
+
}
|
|
483
|
+
const allow = permissions.allow.filter((rule) => rule !== PRIM_PERMISSION_RULE);
|
|
484
|
+
const nextPermissions = {
|
|
485
|
+
...permissions,
|
|
486
|
+
allow: allow.length > 0 ? allow : void 0
|
|
487
|
+
};
|
|
488
|
+
const hasOtherPerms = Object.values(nextPermissions).some((v) => v !== void 0);
|
|
489
|
+
return { ...settings, permissions: hasOtherPerms ? nextPermissions : void 0 };
|
|
490
|
+
}
|
|
466
491
|
function applyInstall(settings, options = {}) {
|
|
467
492
|
const hooks = { ...settings.hooks ?? {} };
|
|
468
493
|
for (const reg of REGISTRATIONS) {
|
|
469
494
|
hooks[reg.event] = ensureRegistration(hooks[reg.event] ?? [], reg, options.force ?? false);
|
|
470
495
|
}
|
|
471
|
-
return applyStatusLine({ ...settings, hooks });
|
|
496
|
+
return applyPermissions(applyStatusLine({ ...settings, hooks }));
|
|
472
497
|
}
|
|
473
498
|
function applyUninstall(settings) {
|
|
474
499
|
const source = settings.hooks ?? {};
|
|
@@ -486,7 +511,7 @@ function applyUninstall(settings) {
|
|
|
486
511
|
if (isPrimStatusLine(next)) {
|
|
487
512
|
next.statusLine = void 0;
|
|
488
513
|
}
|
|
489
|
-
return next;
|
|
514
|
+
return removePrimPermission(next);
|
|
490
515
|
}
|
|
491
516
|
function captureInstalled(settings) {
|
|
492
517
|
return CAPTURE_EVENTS.some(
|
|
@@ -2231,6 +2256,82 @@ function registerSessionCommands(program2) {
|
|
|
2231
2256
|
});
|
|
2232
2257
|
}
|
|
2233
2258
|
|
|
2259
|
+
// src/commands/setup.ts
|
|
2260
|
+
import { spawnSync } from "child_process";
|
|
2261
|
+
var EXIT_INCOMPLETE = 1;
|
|
2262
|
+
function planSetupSteps(opts) {
|
|
2263
|
+
const scopeArgs = opts.scope === "user" ? ["--scope", "user"] : [];
|
|
2264
|
+
const steps = [
|
|
2265
|
+
{
|
|
2266
|
+
key: "session",
|
|
2267
|
+
label: opts.agent === "codex" ? "Codex integration" : "Claude Code integration",
|
|
2268
|
+
args: [opts.agent, "install", ...scopeArgs],
|
|
2269
|
+
required: true
|
|
2270
|
+
}
|
|
2271
|
+
];
|
|
2272
|
+
if (opts.daemon) {
|
|
2273
|
+
steps.push({
|
|
2274
|
+
key: "daemon",
|
|
2275
|
+
label: "Companion daemon",
|
|
2276
|
+
args: ["daemon", "start"],
|
|
2277
|
+
required: false
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
steps.push({ key: "hooks", label: "Git hooks", args: ["hooks", "install"], required: true });
|
|
2281
|
+
steps.push({ key: "skill", label: "Agent skill", args: ["skill", "install"], required: true });
|
|
2282
|
+
return steps;
|
|
2283
|
+
}
|
|
2284
|
+
function registerSetupCommand(program2) {
|
|
2285
|
+
program2.command("setup").description(
|
|
2286
|
+
"Install everything in one shot (auth, session + git hooks, daemon, skill, welcome)"
|
|
2287
|
+
).option("--agent <agent>", "claude or codex", "claude").option("--scope <scope>", "project or user (session integration)", "project").option("--no-daemon", "skip starting the companion daemon").action((opts) => {
|
|
2288
|
+
const agent = opts.agent === "codex" ? "codex" : "claude";
|
|
2289
|
+
const scope = opts.scope === "user" ? "user" : "project";
|
|
2290
|
+
const self = process.argv[1];
|
|
2291
|
+
const run = (args, capture = false) => {
|
|
2292
|
+
const r = spawnSync(process.execPath, [self, ...args], {
|
|
2293
|
+
stdio: capture ? ["inherit", "pipe", "inherit"] : "inherit",
|
|
2294
|
+
encoding: "utf-8"
|
|
2295
|
+
});
|
|
2296
|
+
return { code: r.status ?? 1, stdout: capture ? r.stdout ?? "" : "" };
|
|
2297
|
+
};
|
|
2298
|
+
const results = {};
|
|
2299
|
+
const note = (msg) => {
|
|
2300
|
+
process.stderr.write(`[prim] ${msg}
|
|
2301
|
+
`);
|
|
2302
|
+
};
|
|
2303
|
+
const isAuthed = (json) => {
|
|
2304
|
+
try {
|
|
2305
|
+
return JSON.parse(json || "{}").authenticated === true;
|
|
2306
|
+
} catch {
|
|
2307
|
+
return false;
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
if (isAuthed(run(["auth", "status", "--json"], true).stdout)) {
|
|
2311
|
+
note("auth \xB7 already authenticated");
|
|
2312
|
+
results.auth = "ok";
|
|
2313
|
+
} else {
|
|
2314
|
+
note("auth \xB7 opening browser to authenticate\u2026");
|
|
2315
|
+
run(["auth", "login"]);
|
|
2316
|
+
results.auth = isAuthed(run(["auth", "status", "--json"], true).stdout) ? "ok" : "failed";
|
|
2317
|
+
}
|
|
2318
|
+
for (const step of planSetupSteps({ agent, daemon: opts.daemon, scope })) {
|
|
2319
|
+
note(`${step.label} \xB7 installing\u2026`);
|
|
2320
|
+
const { code } = run(step.args);
|
|
2321
|
+
results[step.key] = code === 0 ? "ok" : step.required ? "failed" : "skipped";
|
|
2322
|
+
}
|
|
2323
|
+
note("welcome");
|
|
2324
|
+
run(["welcome"]);
|
|
2325
|
+
results.welcome = "ok";
|
|
2326
|
+
const failed = Object.entries(results).filter(([, v]) => v === "failed").map(([k]) => k);
|
|
2327
|
+
const trail = Object.entries(results).map(([k, v]) => `${k}:${v}`).join(" \xB7 ");
|
|
2328
|
+
note(
|
|
2329
|
+
`setup ${failed.length === 0 ? "complete" : `incomplete (failed: ${failed.join(", ")})`} \u2014 ${trail}`
|
|
2330
|
+
);
|
|
2331
|
+
process.exit(failed.length === 0 ? 0 : EXIT_INCOMPLETE);
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2234
2335
|
// src/commands/skill.ts
|
|
2235
2336
|
import {
|
|
2236
2337
|
closeSync as closeSync2,
|
|
@@ -2583,6 +2684,7 @@ registerDaemonCommands(program);
|
|
|
2583
2684
|
registerReconcileCommands(program);
|
|
2584
2685
|
registerStatuslineCommands(program);
|
|
2585
2686
|
registerWelcomeCommand(program);
|
|
2687
|
+
registerSetupCommand(program);
|
|
2586
2688
|
process.on("unhandledRejection", (err) => {
|
|
2587
2689
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2588
2690
|
console.error(msg);
|
package/package.json
CHANGED