@primitive.ai/prim 0.1.0-alpha.34 → 0.1.0-alpha.35

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 CHANGED
@@ -192,13 +192,22 @@ prim moves flush # Drain the local journals to the server (also runs fr
192
192
  ### Skill
193
193
 
194
194
  ```bash
195
- prim skill install # Install the decision-graph agent guide into your rules file
196
- prim skill uninstall # Remove the managed block
197
- prim skill status # Report whether the block is installed
198
- ```
199
-
200
- Writes a managed block teaching your agent how to work with the decision graph
201
- into the rules file it reads (CLAUDE.md, AGENTS.md, .cursor/rules, …).
195
+ prim skill install --agent claude # Install the decision-graph guide for Claude Code
196
+ prim skill install --agent codex # …or write the guide into another agent's rules file
197
+ prim skill uninstall --agent claude # Remove it
198
+ prim skill status --agent claude # Report whether it's installed
199
+ ```
200
+
201
+ Teaches your agent how to work with the decision graph. For **Claude Code**
202
+ (`--agent claude`) this installs a skills-directory plugin at
203
+ `<repo>/.claude/skills/prim/` (or `~/.claude/skills/prim/` with `--scope user`)
204
+ — a `.claude-plugin/plugin.json` + `SKILL.md` that auto-loads as the model-invoked
205
+ `prim@skills-dir` skill, no marketplace step; restart Claude Code or run
206
+ `/reload-plugins` after installing. For every other agent it writes a managed
207
+ block into the rules file that agent reads (`--agent codex` → AGENTS.md,
208
+ `--agent hermes` → .hermes.md, or an auto-detected .cursor/rules, …). A bare
209
+ `prim skill install` (no `--agent`) auto-detects a rules file and writes the
210
+ block; pass `--target <path>` for an explicit file.
202
211
 
203
212
  ## Development
204
213
 
@@ -54,6 +54,16 @@ function colorForArea(area) {
54
54
  }
55
55
  return AREA_COLORS[area] ?? "gray";
56
56
  }
57
+ function stripControlChars(text) {
58
+ let out = "";
59
+ for (const ch of text) {
60
+ const code = ch.codePointAt(0) ?? 0;
61
+ if (code > 31 && !(code >= 127 && code <= 159)) {
62
+ out += ch;
63
+ }
64
+ }
65
+ return out;
66
+ }
57
67
  function stripAnsi(text) {
58
68
  return text.replace(/\u001b\[[0-9;]*m/g, "");
59
69
  }
@@ -63,5 +73,6 @@ export {
63
73
  dim,
64
74
  bold,
65
75
  colorForArea,
76
+ stripControlChars,
66
77
  stripAnsi
67
78
  };
@@ -0,0 +1,78 @@
1
+ // src/lib/activation.ts
2
+ import { execFileSync as execFileSync2 } from "child_process";
3
+ import { existsSync, readFileSync } from "fs";
4
+ import { homedir } from "os";
5
+ import { join } from "path";
6
+
7
+ // src/lib/git.ts
8
+ import { execFileSync } from "child_process";
9
+ function gitToplevel(cwd) {
10
+ try {
11
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
12
+ cwd,
13
+ encoding: "utf-8",
14
+ stdio: ["ignore", "pipe", "ignore"]
15
+ }).trim();
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ // src/lib/activation.ts
22
+ var PRIM_ACTIVE_KEY = "prim.active";
23
+ var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
24
+ var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
25
+ function repoActiveFlag(cwd) {
26
+ try {
27
+ const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
28
+ cwd,
29
+ encoding: "utf-8",
30
+ stdio: ["ignore", "pipe", "ignore"]
31
+ }).trim();
32
+ if (value === "true") return "true";
33
+ if (value === "false") return "false";
34
+ return void 0;
35
+ } catch {
36
+ return void 0;
37
+ }
38
+ }
39
+ function setRepoActive(cwd, active) {
40
+ execFileSync2("git", ["config", "--local", PRIM_ACTIVE_KEY, active ? "true" : "false"], {
41
+ cwd,
42
+ stdio: ["ignore", "ignore", "pipe"]
43
+ });
44
+ }
45
+ function activateRepoBestEffort(cwd) {
46
+ try {
47
+ setRepoActive(cwd, true);
48
+ } catch {
49
+ }
50
+ }
51
+ function hasProjectPrimInstall(cwd) {
52
+ const root = gitToplevel(cwd);
53
+ if (root === null || root === homedir()) return false;
54
+ for (const rel of PROJECT_INSTALL_FILES) {
55
+ const path = join(root, rel);
56
+ if (existsSync(path)) {
57
+ try {
58
+ const content = readFileSync(path, "utf-8");
59
+ if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
60
+ } catch {
61
+ }
62
+ }
63
+ }
64
+ return false;
65
+ }
66
+ function isRepoActiveForCapture(cwd) {
67
+ const flag = repoActiveFlag(cwd);
68
+ if (flag === "true") return true;
69
+ if (flag === "false") return false;
70
+ return hasProjectPrimInstall(cwd);
71
+ }
72
+
73
+ export {
74
+ gitToplevel,
75
+ setRepoActive,
76
+ activateRepoBestEffort,
77
+ isRepoActiveForCapture
78
+ };
@@ -2,13 +2,16 @@
2
2
  import {
3
3
  bold,
4
4
  color
5
- } from "../chunk-4QJOQIY6.js";
5
+ } from "../chunk-AAGJFO7C.js";
6
6
  import {
7
7
  scrubFromCwd
8
8
  } from "../chunk-6LAQVM26.js";
9
9
  import {
10
10
  toMove
11
11
  } from "../chunk-S2O4P4A3.js";
12
+ import {
13
+ isRepoActiveForCapture
14
+ } from "../chunk-LUPD2JSH.js";
12
15
  import {
13
16
  getClient
14
17
  } from "../chunk-26VA3ADF.js";
@@ -128,6 +131,10 @@ async function main() {
128
131
  return;
129
132
  }
130
133
  const cwd = parsed.cwd ?? process.cwd();
134
+ if (!isRepoActiveForCapture(cwd)) {
135
+ emit();
136
+ return;
137
+ }
131
138
  const base = toMove(parsed, resolveCliVersion(), agent);
132
139
  const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
133
140
  try {
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ isRepoActiveForCapture
4
+ } from "../chunk-LUPD2JSH.js";
2
5
  import {
3
6
  getClient,
4
7
  getSiteUrl
@@ -295,6 +298,10 @@ async function main() {
295
298
  emit(failOpen());
296
299
  return;
297
300
  }
301
+ if (!isRepoActiveForCapture(cwd)) {
302
+ emit(failOpen());
303
+ return;
304
+ }
298
305
  let results;
299
306
  try {
300
307
  results = await Promise.all(files.map((f) => checkOneFile(f)));
@@ -10,6 +10,9 @@ import {
10
10
  shouldFlushAfter,
11
11
  toMove
12
12
  } from "../chunk-S2O4P4A3.js";
13
+ import {
14
+ isRepoActiveForCapture
15
+ } from "../chunk-LUPD2JSH.js";
13
16
  import "../chunk-26VA3ADF.js";
14
17
  import {
15
18
  normalizeEnvelope,
@@ -42,12 +45,14 @@ try {
42
45
  const raw = readFileSync(0, "utf-8");
43
46
  const parsed = normalizeEnvelope(JSON.parse(raw), agent);
44
47
  const cwd = parsed.cwd ?? process.cwd();
45
- const base = toMove(parsed, resolveCliVersion(), agent);
46
- const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
47
- const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd: move.env.cwd });
48
- appendMove(move, orgId);
49
- if (shouldFlushAfter(move.eventType)) {
50
- spawnBackgroundFlush();
48
+ if (isRepoActiveForCapture(cwd)) {
49
+ const base = toMove(parsed, resolveCliVersion(), agent);
50
+ const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
51
+ const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd: move.env.cwd });
52
+ appendMove(move, orgId);
53
+ if (shouldFlushAfter(move.eventType)) {
54
+ spawnBackgroundFlush();
55
+ }
51
56
  }
52
57
  } catch (err) {
53
58
  if (process.env.PRIM_HOOK_DEBUG) {