@primitive.ai/prim 0.1.0-alpha.23 → 0.1.0-alpha.25
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 +27 -1
- package/dist/index.js +119 -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,27 @@ 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:*)`
|
|
51
|
+
allow-rule into `.claude/settings.json` (covering both the `@latest` onboarding form
|
|
52
|
+
and the bare day-to-day form), so an agent's prim calls don't stall on a permission
|
|
53
|
+
prompt.
|
|
54
|
+
|
|
55
|
+
An AI coding agent can drive the setup itself — see [`setup.md`](./setup.md).
|
|
42
56
|
|
|
43
57
|
## Commands
|
|
44
58
|
|
|
59
|
+
### Setup
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
prim setup # Run the whole install in one shot
|
|
63
|
+
prim setup --agent codex # Same, for OpenAI Codex
|
|
64
|
+
prim setup --no-daemon # Skip the companion daemon
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Orchestrates auth → session hooks → daemon → git hooks → skill → welcome,
|
|
68
|
+
re-running each underlying command so every step behaves exactly as if run by
|
|
69
|
+
hand (including the browser login). Idempotent — safe to re-run.
|
|
70
|
+
|
|
45
71
|
### Auth
|
|
46
72
|
|
|
47
73
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -401,6 +401,12 @@ 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:*)";
|
|
405
|
+
var LEGACY_PRIM_PERMISSION_RULES = ["Bash(npx --yes @primitive.ai/prim@latest:*)"];
|
|
406
|
+
var ALL_PRIM_PERMISSION_RULES = /* @__PURE__ */ new Set([
|
|
407
|
+
PRIM_PERMISSION_RULE,
|
|
408
|
+
...LEGACY_PRIM_PERMISSION_RULES
|
|
409
|
+
]);
|
|
404
410
|
function settingsPathFor(scope) {
|
|
405
411
|
return scope === "user" ? USER_SCOPE_PATH : projectScopePath();
|
|
406
412
|
}
|
|
@@ -463,12 +469,36 @@ function applyStatusLine(settings) {
|
|
|
463
469
|
}
|
|
464
470
|
};
|
|
465
471
|
}
|
|
472
|
+
function applyPermissions(settings) {
|
|
473
|
+
const permissions = settings.permissions ?? {};
|
|
474
|
+
const allow = permissions.allow ?? [];
|
|
475
|
+
const withoutPrim = allow.filter((rule) => !ALL_PRIM_PERMISSION_RULES.has(rule));
|
|
476
|
+
const nextAllow = [...withoutPrim, PRIM_PERMISSION_RULE];
|
|
477
|
+
const unchanged = allow.length === nextAllow.length && allow.every((rule, i) => rule === nextAllow[i]);
|
|
478
|
+
if (unchanged) {
|
|
479
|
+
return settings;
|
|
480
|
+
}
|
|
481
|
+
return { ...settings, permissions: { ...permissions, allow: nextAllow } };
|
|
482
|
+
}
|
|
483
|
+
function removePrimPermission(settings) {
|
|
484
|
+
const permissions = settings.permissions;
|
|
485
|
+
if (!permissions?.allow?.some((rule) => ALL_PRIM_PERMISSION_RULES.has(rule))) {
|
|
486
|
+
return settings;
|
|
487
|
+
}
|
|
488
|
+
const allow = permissions.allow.filter((rule) => !ALL_PRIM_PERMISSION_RULES.has(rule));
|
|
489
|
+
const nextPermissions = {
|
|
490
|
+
...permissions,
|
|
491
|
+
allow: allow.length > 0 ? allow : void 0
|
|
492
|
+
};
|
|
493
|
+
const hasOtherPerms = Object.values(nextPermissions).some((v) => v !== void 0);
|
|
494
|
+
return { ...settings, permissions: hasOtherPerms ? nextPermissions : void 0 };
|
|
495
|
+
}
|
|
466
496
|
function applyInstall(settings, options = {}) {
|
|
467
497
|
const hooks = { ...settings.hooks ?? {} };
|
|
468
498
|
for (const reg of REGISTRATIONS) {
|
|
469
499
|
hooks[reg.event] = ensureRegistration(hooks[reg.event] ?? [], reg, options.force ?? false);
|
|
470
500
|
}
|
|
471
|
-
return applyStatusLine({ ...settings, hooks });
|
|
501
|
+
return applyPermissions(applyStatusLine({ ...settings, hooks }));
|
|
472
502
|
}
|
|
473
503
|
function applyUninstall(settings) {
|
|
474
504
|
const source = settings.hooks ?? {};
|
|
@@ -486,7 +516,7 @@ function applyUninstall(settings) {
|
|
|
486
516
|
if (isPrimStatusLine(next)) {
|
|
487
517
|
next.statusLine = void 0;
|
|
488
518
|
}
|
|
489
|
-
return next;
|
|
519
|
+
return removePrimPermission(next);
|
|
490
520
|
}
|
|
491
521
|
function captureInstalled(settings) {
|
|
492
522
|
return CAPTURE_EVENTS.some(
|
|
@@ -2231,6 +2261,92 @@ function registerSessionCommands(program2) {
|
|
|
2231
2261
|
});
|
|
2232
2262
|
}
|
|
2233
2263
|
|
|
2264
|
+
// src/commands/setup.ts
|
|
2265
|
+
import { spawnSync } from "child_process";
|
|
2266
|
+
var EXIT_INCOMPLETE = 1;
|
|
2267
|
+
var EXIT_USAGE3 = 2;
|
|
2268
|
+
function planSetupSteps(opts) {
|
|
2269
|
+
const scopeArgs = opts.scope === "user" ? ["--scope", "user"] : [];
|
|
2270
|
+
const steps = [
|
|
2271
|
+
{
|
|
2272
|
+
key: "session",
|
|
2273
|
+
label: opts.agent === "codex" ? "Codex integration" : "Claude Code integration",
|
|
2274
|
+
args: [opts.agent, "install", ...scopeArgs],
|
|
2275
|
+
required: true
|
|
2276
|
+
}
|
|
2277
|
+
];
|
|
2278
|
+
if (opts.daemon) {
|
|
2279
|
+
steps.push({
|
|
2280
|
+
key: "daemon",
|
|
2281
|
+
label: "Companion daemon",
|
|
2282
|
+
args: ["daemon", "start"],
|
|
2283
|
+
required: false
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
steps.push({ key: "hooks", label: "Git hooks", args: ["hooks", "install"], required: true });
|
|
2287
|
+
steps.push({ key: "skill", label: "Agent skill", args: ["skill", "install"], required: true });
|
|
2288
|
+
return steps;
|
|
2289
|
+
}
|
|
2290
|
+
function registerSetupCommand(program2) {
|
|
2291
|
+
program2.command("setup").description(
|
|
2292
|
+
"Install everything in one shot (auth, session + git hooks, daemon, skill, welcome)"
|
|
2293
|
+
).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) => {
|
|
2294
|
+
if (opts.agent !== "claude" && opts.agent !== "codex") {
|
|
2295
|
+
process.stderr.write(`[prim] unknown --agent "${opts.agent}" (expected claude or codex)
|
|
2296
|
+
`);
|
|
2297
|
+
process.exit(EXIT_USAGE3);
|
|
2298
|
+
}
|
|
2299
|
+
if (opts.scope !== "project" && opts.scope !== "user") {
|
|
2300
|
+
process.stderr.write(`[prim] unknown --scope "${opts.scope}" (expected project or user)
|
|
2301
|
+
`);
|
|
2302
|
+
process.exit(EXIT_USAGE3);
|
|
2303
|
+
}
|
|
2304
|
+
const agent = opts.agent;
|
|
2305
|
+
const scope = opts.scope;
|
|
2306
|
+
const self = process.argv[1];
|
|
2307
|
+
const run = (args, capture = false) => {
|
|
2308
|
+
const r = spawnSync(process.execPath, [self, ...args], {
|
|
2309
|
+
stdio: capture ? ["inherit", "pipe", "inherit"] : "inherit",
|
|
2310
|
+
encoding: "utf-8"
|
|
2311
|
+
});
|
|
2312
|
+
return { code: r.status ?? 1, stdout: capture ? r.stdout ?? "" : "" };
|
|
2313
|
+
};
|
|
2314
|
+
const results = {};
|
|
2315
|
+
const note = (msg) => {
|
|
2316
|
+
process.stderr.write(`[prim] ${msg}
|
|
2317
|
+
`);
|
|
2318
|
+
};
|
|
2319
|
+
const isAuthed = (json) => {
|
|
2320
|
+
try {
|
|
2321
|
+
return JSON.parse(json || "{}").authenticated === true;
|
|
2322
|
+
} catch {
|
|
2323
|
+
return false;
|
|
2324
|
+
}
|
|
2325
|
+
};
|
|
2326
|
+
if (isAuthed(run(["auth", "status", "--json"], true).stdout)) {
|
|
2327
|
+
note("auth \xB7 already authenticated");
|
|
2328
|
+
results.auth = "ok";
|
|
2329
|
+
} else {
|
|
2330
|
+
note("auth \xB7 opening browser to authenticate\u2026");
|
|
2331
|
+
run(["auth", "login"]);
|
|
2332
|
+
results.auth = isAuthed(run(["auth", "status", "--json"], true).stdout) ? "ok" : "failed";
|
|
2333
|
+
}
|
|
2334
|
+
for (const step of planSetupSteps({ agent, daemon: opts.daemon, scope })) {
|
|
2335
|
+
note(`${step.label} \xB7 installing\u2026`);
|
|
2336
|
+
const { code } = run(step.args);
|
|
2337
|
+
results[step.key] = code === 0 ? "ok" : step.required ? "failed" : "skipped";
|
|
2338
|
+
}
|
|
2339
|
+
note("welcome");
|
|
2340
|
+
results.welcome = run(["welcome"]).code === 0 ? "ok" : "failed";
|
|
2341
|
+
const failed = Object.entries(results).filter(([, v]) => v === "failed").map(([k]) => k);
|
|
2342
|
+
const trail = Object.entries(results).map(([k, v]) => `${k}:${v}`).join(" \xB7 ");
|
|
2343
|
+
note(
|
|
2344
|
+
`setup ${failed.length === 0 ? "complete" : `incomplete (failed: ${failed.join(", ")})`} \u2014 ${trail}`
|
|
2345
|
+
);
|
|
2346
|
+
process.exit(failed.length === 0 ? 0 : EXIT_INCOMPLETE);
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2234
2350
|
// src/commands/skill.ts
|
|
2235
2351
|
import {
|
|
2236
2352
|
closeSync as closeSync2,
|
|
@@ -2583,6 +2699,7 @@ registerDaemonCommands(program);
|
|
|
2583
2699
|
registerReconcileCommands(program);
|
|
2584
2700
|
registerStatuslineCommands(program);
|
|
2585
2701
|
registerWelcomeCommand(program);
|
|
2702
|
+
registerSetupCommand(program);
|
|
2586
2703
|
process.on("unhandledRejection", (err) => {
|
|
2587
2704
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2588
2705
|
console.error(msg);
|
package/package.json
CHANGED