@primitive.ai/prim 0.1.0-alpha.60 → 0.1.0-alpha.62

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.
@@ -140,14 +140,17 @@ import { join as join4 } from "path";
140
140
 
141
141
  // src/lib/git.ts
142
142
  import { execFileSync } from "child_process";
143
+ import { createHash as createHash2 } from "crypto";
143
144
  import { lstatSync, realpathSync } from "fs";
144
145
  import { basename, dirname as dirname3, isAbsolute, relative, resolve as resolve2, sep } from "path";
146
+ var GIT_TIMEOUT_MS = 1e3;
145
147
  function gitToplevel(cwd) {
146
148
  try {
147
149
  return execFileSync("git", ["rev-parse", "--show-toplevel"], {
148
150
  cwd,
149
151
  encoding: "utf-8",
150
- stdio: ["ignore", "pipe", "ignore"]
152
+ stdio: ["ignore", "pipe", "ignore"],
153
+ timeout: GIT_TIMEOUT_MS
151
154
  }).trim();
152
155
  } catch {
153
156
  return null;
@@ -156,6 +159,7 @@ function gitToplevel(cwd) {
156
159
 
157
160
  // src/lib/activation.ts
158
161
  var PRIM_ACTIVE_KEY = "prim.active";
162
+ var GIT_TIMEOUT_MS2 = 1e3;
159
163
  var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
160
164
  var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
161
165
  function repoActiveFlag(cwd) {
@@ -163,7 +167,8 @@ function repoActiveFlag(cwd) {
163
167
  const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
164
168
  cwd,
165
169
  encoding: "utf-8",
166
- stdio: ["ignore", "pipe", "ignore"]
170
+ stdio: ["ignore", "pipe", "ignore"],
171
+ timeout: GIT_TIMEOUT_MS2
167
172
  }).trim();
168
173
  if (value === "true") return "true";
169
174
  if (value === "false") return "false";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.60",
3
+ "version": "0.1.0-alpha.62",
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",
@@ -1,84 +0,0 @@
1
- // src/hooks/redact.ts
2
- import { existsSync, readFileSync } from "fs";
3
- import { join } from "path";
4
- var DEFAULT_RULES = [
5
- { pattern: /Bearer\s+[A-Za-z0-9._\-]+/g, reason: "bearer-token" },
6
- { pattern: /sk-[A-Za-z0-9]{32,}/g, reason: "sk-api-key" },
7
- {
8
- pattern: /-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]+?-----END [A-Z ]+PRIVATE KEY-----/g,
9
- reason: "private-key"
10
- },
11
- { pattern: /xox[abprs]-[A-Za-z0-9-]{10,}/g, reason: "slack-token" },
12
- { pattern: /ghp_[A-Za-z0-9]{36,}/g, reason: "github-pat" }
13
- ];
14
- var MAX_SCRUB_LEN = 256e3;
15
- var WORKSPACE_FILE = ".prim/redaction.json";
16
- function debugRedaction(message, err) {
17
- if (!process.env.PRIM_HOOK_DEBUG) {
18
- return;
19
- }
20
- const detail = err instanceof Error ? err.message : String(err);
21
- process.stderr.write(`[prim-hook] redaction: ${message}: ${detail}
22
- `);
23
- }
24
- function loadWorkspaceRules(cwd) {
25
- const path = join(cwd, WORKSPACE_FILE);
26
- if (!existsSync(path)) {
27
- return [];
28
- }
29
- let cfg;
30
- try {
31
- cfg = JSON.parse(readFileSync(path, "utf-8"));
32
- } catch (err) {
33
- debugRedaction(`ignoring unparseable ${WORKSPACE_FILE}`, err);
34
- return [];
35
- }
36
- if (!cfg.rules) {
37
- return [];
38
- }
39
- const compiled = [];
40
- for (const r of cfg.rules) {
41
- try {
42
- compiled.push({ pattern: new RegExp(r.pattern, r.flags ?? "g"), reason: r.reason });
43
- } catch (err) {
44
- debugRedaction(`skipping invalid redaction rule "${r.reason}"`, err);
45
- }
46
- }
47
- return compiled;
48
- }
49
- function applyRules(input, rules) {
50
- if (input.length > MAX_SCRUB_LEN) {
51
- return "<REDACTED:oversized>";
52
- }
53
- let out = input;
54
- for (const rule of rules) {
55
- out = out.replace(rule.pattern, `<REDACTED:${rule.reason}>`);
56
- }
57
- return out;
58
- }
59
- function scrub(value, rules = DEFAULT_RULES) {
60
- if (typeof value === "string") {
61
- return applyRules(value, rules);
62
- }
63
- if (Array.isArray(value)) {
64
- return value.map((v) => scrub(v, rules));
65
- }
66
- if (value && typeof value === "object") {
67
- const out = {};
68
- for (const [k, v] of Object.entries(value)) {
69
- out[k] = scrub(v, rules);
70
- }
71
- return out;
72
- }
73
- return value;
74
- }
75
- function scrubFromCwd(value, cwd) {
76
- const rules = [...DEFAULT_RULES, ...loadWorkspaceRules(cwd)];
77
- return scrub(value, rules);
78
- }
79
-
80
- export {
81
- DEFAULT_RULES,
82
- scrub,
83
- scrubFromCwd
84
- };
@@ -1,166 +0,0 @@
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
- import { lstatSync, realpathSync } from "fs";
10
- import { basename, dirname, isAbsolute, relative, resolve, sep } from "path";
11
- function gitToplevel(cwd) {
12
- try {
13
- return execFileSync("git", ["rev-parse", "--show-toplevel"], {
14
- cwd,
15
- encoding: "utf-8",
16
- stdio: ["ignore", "pipe", "ignore"]
17
- }).trim();
18
- } catch {
19
- return null;
20
- }
21
- }
22
- function gitValue(cwd, args) {
23
- try {
24
- const value = execFileSync("git", args, {
25
- cwd,
26
- encoding: "utf-8",
27
- stdio: ["ignore", "pipe", "ignore"]
28
- }).trim();
29
- return value || null;
30
- } catch {
31
- return null;
32
- }
33
- }
34
- function githubRepositoryFullName(cwd) {
35
- const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
36
- if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
37
- const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
38
- remote
39
- );
40
- const path = match?.[1] ?? match?.[2];
41
- if (!path) return null;
42
- const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
43
- if (parts.length !== 2 || parts.some((part) => !part || part !== part.trim())) return null;
44
- return `${parts[0]}/${parts[1]}`;
45
- }
46
- function canonicalGitRoot(cwd) {
47
- try {
48
- const top = gitToplevel(cwd);
49
- return top ? realpathSync.native(top) : null;
50
- } catch {
51
- return null;
52
- }
53
- }
54
- function canonicalRepositoryPath(filePath, cwd, repositoryRoot = canonicalGitRoot(cwd)) {
55
- if (!repositoryRoot || !filePath || [...filePath].some((char) => char < " " || char === "\x7F" || "~*?[]{}$`\\".includes(char)))
56
- return null;
57
- const absolute = resolve(cwd, filePath);
58
- let target;
59
- try {
60
- const stat = lstatSync(absolute);
61
- if (!stat.isFile() || stat.isSymbolicLink()) return null;
62
- target = realpathSync.native(absolute);
63
- } catch (error) {
64
- if (!(error instanceof Error) || error.code !== "ENOENT") {
65
- return null;
66
- }
67
- try {
68
- const parent = realpathSync.native(dirname(absolute));
69
- if (!lstatSync(parent).isDirectory()) return null;
70
- target = resolve(parent, basename(absolute));
71
- } catch {
72
- return null;
73
- }
74
- }
75
- const rel = relative(repositoryRoot, target);
76
- if (!rel || isAbsolute(rel) || rel === ".." || rel.startsWith(`..${sep}`)) {
77
- return null;
78
- }
79
- return sep === "/" ? rel : rel.split(sep).join("/");
80
- }
81
-
82
- // src/lib/activation.ts
83
- var PRIM_ACTIVE_KEY = "prim.active";
84
- var PRIM_REPO_SYNC_ID_KEY = "prim.repoSyncId";
85
- var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
86
- var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
87
- function repoActiveFlag(cwd) {
88
- try {
89
- const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
90
- cwd,
91
- encoding: "utf-8",
92
- stdio: ["ignore", "pipe", "ignore"]
93
- }).trim();
94
- if (value === "true") return "true";
95
- if (value === "false") return "false";
96
- return void 0;
97
- } catch {
98
- return void 0;
99
- }
100
- }
101
- function isRepoActive(cwd) {
102
- return repoActiveFlag(cwd) === "true";
103
- }
104
- function setRepoActive(cwd, active) {
105
- execFileSync2("git", ["config", "--local", PRIM_ACTIVE_KEY, active ? "true" : "false"], {
106
- cwd,
107
- stdio: ["ignore", "ignore", "pipe"]
108
- });
109
- }
110
- function repoSyncId(cwd) {
111
- try {
112
- return execFileSync2("git", ["config", "--local", "--get", PRIM_REPO_SYNC_ID_KEY], {
113
- cwd,
114
- encoding: "utf-8",
115
- stdio: ["ignore", "pipe", "ignore"]
116
- }).trim() || void 0;
117
- } catch {
118
- return void 0;
119
- }
120
- }
121
- function setRepoSyncId(cwd, value) {
122
- if (!value || value.includes("\0") || value.includes("\n")) {
123
- throw new Error("invalid repository binding returned by server");
124
- }
125
- execFileSync2("git", ["config", "--local", PRIM_REPO_SYNC_ID_KEY, value], {
126
- cwd,
127
- stdio: ["ignore", "ignore", "pipe"]
128
- });
129
- }
130
- function hasProjectPrimInstall(cwd) {
131
- const root = gitToplevel(cwd);
132
- if (root === null || root === homedir()) return false;
133
- for (const rel of PROJECT_INSTALL_FILES) {
134
- const path = join(root, rel);
135
- if (existsSync(path)) {
136
- try {
137
- const content = readFileSync(path, "utf-8");
138
- if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
139
- } catch {
140
- }
141
- }
142
- }
143
- return false;
144
- }
145
- function isRepoActiveForCapture(cwd) {
146
- const flag = repoActiveFlag(cwd);
147
- if (flag === "true") return true;
148
- if (flag === "false") return false;
149
- return hasProjectPrimInstall(cwd);
150
- }
151
- function decisionIngestionStatus(cwd) {
152
- return isRepoActiveForCapture(cwd) ? "enabled" : "disabled";
153
- }
154
-
155
- export {
156
- gitToplevel,
157
- githubRepositoryFullName,
158
- canonicalGitRoot,
159
- canonicalRepositoryPath,
160
- isRepoActive,
161
- setRepoActive,
162
- repoSyncId,
163
- setRepoSyncId,
164
- isRepoActiveForCapture,
165
- decisionIngestionStatus
166
- };