@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.
Files changed (3) hide show
  1. package/README.md +26 -1
  2. package/dist/index.js +104 -2
  3. 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
- An AI coding agent can drive the entire setup itself — see [`setup.md`](./setup.md).
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.24",
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",