prae-cli 0.10.0 → 0.10.2
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/lib/bin.js +1 -1
- package/lib/{checkpoint-BqJ8xwky.js → checkpoint-D7d6ZGXR.js} +1 -1
- package/lib/{cli-DPldZ2ND.js → cli-BTVEk0Fh.js} +402 -41
- package/lib/cli-DYKKNlDI.js +7 -0
- package/lib/docs/pilot-claude-code.md +22 -21
- package/lib/facts-DkTRO2p0.js +89 -0
- package/lib/{hook-Gll2spTP.js → hook-C7LGEIs2.js} +4 -91
- package/lib/index.js +5 -6
- package/lib/{init-DRd-IRR6.js → init-DvzCuemh.js} +10 -4
- package/lib/{instructions-DihPiiC2.js → instructions-Bcs2n2O6.js} +2 -2
- package/lib/{mcp-D521EoP6.js → mcp-C2gadkb2.js} +2 -2
- package/lib/playground.js +713 -109
- package/lib/prae-baseline.prae.json +144 -1
- package/lib/{receipt-check-CUuEjGHL.js → receipt-check-BtAJqyrX.js} +1 -1
- package/lib/{src-wG36kT6K.js → src-2iJy5MZc.js} +1 -1
- package/lib/src-CZYvvEn5.js +1691 -0
- package/lib/{src-DMoYsdfo.js → src-DIuEg7gi.js} +1 -1
- package/lib/{test-run-C8dHjIE2.js → test-run-DCprBW44.js} +1 -1
- package/lib/{views-CUcixte0.js → views-endiZu1Z.js} +1 -1
- package/package.json +5 -5
- package/lib/cli-BcAJlB9B.js +0 -8
- package/lib/evaluate-DhoGQw0Z.js +0 -521
- package/lib/frameworks-BgZHGKiH.js +0 -118
- package/lib/pack-h5n43S0F.js +0 -448
- /package/lib/{ocsf-DF9cMiQJ.js → ocsf-DwExfPqP.js} +0 -0
- /package/lib/{src-BuaOiL2v.js → src-9phPLfAL.js} +0 -0
- /package/lib/{src-Cy-Acrgj.js → src-CuiWC80o.js} +0 -0
- /package/lib/{src-BlmsTGdY.js → src-DV83J9G2.js} +0 -0
- /package/lib/{src-CDYyZyeU.js → src-DxJIOaP5.js} +0 -0
|
@@ -26,23 +26,21 @@ guide in a temporary project:
|
|
|
26
26
|
`claude -p` refused them. Nothing protected changed on disk.
|
|
27
27
|
- **Record.** `prae verify` passed the chain, with 25 of 25 signatures.
|
|
28
28
|
|
|
29
|
-
## 1. Install
|
|
29
|
+
## 1. Install
|
|
30
30
|
|
|
31
|
-
You need Node 22.19 or later
|
|
32
|
-
|
|
33
|
-
run it from a checkout of this repository, built once:
|
|
31
|
+
You need Node 22.19 or later. Install the CLI globally, then preview the
|
|
32
|
+
change and apply it:
|
|
34
33
|
|
|
35
34
|
```bash
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
npm install -g prae-cli # prae-cli 0.10.0 or later carries the hook
|
|
36
|
+
prae init claude-code --scope user # prints the settings diff, writes nothing
|
|
37
|
+
prae init claude-code --scope user --apply # installs, shadow mode
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
$PRAE init claude-code --scope user --apply # installs, shadow mode
|
|
45
|
-
```
|
|
40
|
+
Install globally rather than with `npx prae-cli init …`: the settings entry
|
|
41
|
+
runs the `prae` it was installed from, and an `npx` copy lives in npm's
|
|
42
|
+
temporary cache, which npm may clear — the hook would then stop running, and
|
|
43
|
+
Claude Code 2.1.233 lets every call through when a hook cannot start.
|
|
46
44
|
|
|
47
45
|
You must name a target. `--scope user` is `~/.claude/settings.json`, which
|
|
48
46
|
covers every project. `--scope project` is `./.claude/settings.json`.
|
|
@@ -55,19 +53,22 @@ hand-formatted file shows layout changes in the diff. Its values do not change.
|
|
|
55
53
|
|
|
56
54
|
```json
|
|
57
55
|
"PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "timeout": 10,
|
|
58
|
-
"command": "'/opt/homebrew/bin/node' '…/
|
|
56
|
+
"command": "'/opt/homebrew/bin/node' '…/lib/node_modules/prae-cli/lib/bin.js' hook claude-code --shadow --pack '~/.prae/claude-code/prae-baseline.prae.json' --ledger '~/.prae/claude-code/ledger.jsonl' --key '~/.prae/claude-code/prae-signing.pem' --settings '~/.claude/settings.json'" }] }]
|
|
59
57
|
```
|
|
60
58
|
|
|
61
59
|
The real file holds absolute paths. The command names the node binary and
|
|
62
60
|
PRAE's own script by absolute path, so it does not depend on `PATH`. If you
|
|
63
|
-
|
|
61
|
+
upgrade or reinstall prae-cli, or switch Node versions, run `init` again. Claude Code reloads settings on its own,
|
|
64
62
|
so new tool calls go through PRAE without a restart.
|
|
65
63
|
|
|
66
64
|
Options:
|
|
67
65
|
|
|
68
66
|
- `--pack <file>`: use your own pack. The default copies the built-in baseline
|
|
69
|
-
(
|
|
70
|
-
credential files, writes outside the workspace, force-push, publishes
|
|
67
|
+
(13 rules: download-and-execute, `rm -rf`, privilege escalation, block devices,
|
|
68
|
+
credential files, writes outside the workspace, force-push, publishes, and
|
|
69
|
+
credential stores — the macOS Keychain through `security` and its files,
|
|
70
|
+
`.netrc`, `.git-credentials`, `.npmrc`, Docker, kube, GnuPG, gh and gcloud
|
|
71
|
+
config — read directly or named in a shell command).
|
|
71
72
|
`prae check <file>` summarises a pack.
|
|
72
73
|
- `--ledger <file>`: write the ledger somewhere else.
|
|
73
74
|
- `--dir <dir>`: keep PRAE's state somewhere other than `~/.prae/claude-code/`.
|
|
@@ -95,9 +96,9 @@ Read the record:
|
|
|
95
96
|
|
|
96
97
|
```bash
|
|
97
98
|
P=~/.prae/claude-code
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
prae verify $P/ledger.jsonl --key $P/prae-signing.pub.pem
|
|
100
|
+
prae report $P/ledger.jsonl --pack $P/prae-baseline.prae.json --out report.html
|
|
101
|
+
prae console $P/ledger.jsonl --pack $P/prae-baseline.prae.json --out console/
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
In the report and the console, a shadow verdict is marked `· SHADOW`. It is
|
|
@@ -106,7 +107,7 @@ never counted as vetoed or held.
|
|
|
106
107
|
## 3. From shadow to enforce
|
|
107
108
|
|
|
108
109
|
```bash
|
|
109
|
-
|
|
110
|
+
prae init claude-code --scope user --enforce --apply
|
|
110
111
|
```
|
|
111
112
|
|
|
112
113
|
This replaces PRAE's entry. It does not add a second one. In enforce mode:
|
|
@@ -124,7 +125,7 @@ warning and records a `PRAE-ERROR` row marked `shadow`.
|
|
|
124
125
|
## 4. Uninstall
|
|
125
126
|
|
|
126
127
|
```bash
|
|
127
|
-
|
|
128
|
+
prae init claude-code --scope user --remove --apply
|
|
128
129
|
```
|
|
129
130
|
|
|
130
131
|
This removes PRAE's hook entry and nothing else. The ledger and the key stay
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { a as PATH_ARGS, s as relativeTo } from "./src-CZYvvEn5.js";
|
|
2
|
+
import { capabilityForTool } from "capmark";
|
|
3
|
+
|
|
4
|
+
//#region ../dsh/src/facts.ts
|
|
5
|
+
function resolveArgs(args, resolvePath, cwd) {
|
|
6
|
+
if (resolvePath === void 0 && cwd === void 0) return args;
|
|
7
|
+
const out = { ...args };
|
|
8
|
+
for (const key of PATH_ARGS) {
|
|
9
|
+
const value = out[key];
|
|
10
|
+
if (typeof value !== "string") continue;
|
|
11
|
+
let path = value;
|
|
12
|
+
if (resolvePath !== void 0) try {
|
|
13
|
+
path = resolvePath(path);
|
|
14
|
+
} catch {}
|
|
15
|
+
out[key] = cwd === void 0 ? path : relativeTo(path, cwd);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Claude Code names its built-in tools in PascalCase (`Read`, `Bash`, …),
|
|
21
|
+
* which capmark's catalogue does not list. Each takes the capability of its
|
|
22
|
+
* lowercase counterpart; without this a Claude Code call carries no
|
|
23
|
+
* capability, and the rules scoped by one (paths, egress, the shell) never
|
|
24
|
+
* see it. An MCP tool, `mcp__<server>__<tool>`, takes its tool's.
|
|
25
|
+
*/
|
|
26
|
+
const CLAUDE_CODE = {
|
|
27
|
+
Read: "read",
|
|
28
|
+
Write: "write",
|
|
29
|
+
Edit: "edit",
|
|
30
|
+
MultiEdit: "edit",
|
|
31
|
+
NotebookEdit: "edit",
|
|
32
|
+
Bash: "bash",
|
|
33
|
+
PowerShell: "bash",
|
|
34
|
+
Grep: "grep",
|
|
35
|
+
Glob: "glob",
|
|
36
|
+
LS: "glob",
|
|
37
|
+
WebFetch: "web_fetch",
|
|
38
|
+
WebSearch: "web_search"
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* MCP tools whose names capmark's catalogue does not list, from the
|
|
42
|
+
* reference servers an agent most often mounts (modelcontextprotocol
|
|
43
|
+
* `filesystem` and `fetch`) and `prae mcp`'s own. Applied only after an
|
|
44
|
+
* `mcp__<server>__` prefix: without it, a Claude Code session reading
|
|
45
|
+
* `.env` through `mcp__filesystem__read_file` carries no capability, and
|
|
46
|
+
* no `fs:read` rule sees it.
|
|
47
|
+
*/
|
|
48
|
+
const MCP_TOOLS = {
|
|
49
|
+
read_file: "read",
|
|
50
|
+
read_text_file: "read",
|
|
51
|
+
read_media_file: "read",
|
|
52
|
+
list_directory: "glob",
|
|
53
|
+
list_dir: "glob",
|
|
54
|
+
directory_tree: "glob",
|
|
55
|
+
search_files: "glob",
|
|
56
|
+
get_file_info: "glob",
|
|
57
|
+
write_file: "write",
|
|
58
|
+
edit_file: "edit",
|
|
59
|
+
create_directory: "write",
|
|
60
|
+
fetch: "web_fetch"
|
|
61
|
+
};
|
|
62
|
+
function capabilityOf(tool) {
|
|
63
|
+
const direct = capabilityForTool(tool)?.id;
|
|
64
|
+
if (direct !== void 0) return direct;
|
|
65
|
+
const alias = CLAUDE_CODE[tool];
|
|
66
|
+
if (alias !== void 0) return capabilityForTool(alias)?.id;
|
|
67
|
+
const mcp = tool.startsWith("mcp__") ? tool.lastIndexOf("__") : -1;
|
|
68
|
+
if (mcp <= 5) return void 0;
|
|
69
|
+
const name = tool.slice(mcp + 2);
|
|
70
|
+
return capabilityOf(MCP_TOOLS[name] ?? name);
|
|
71
|
+
}
|
|
72
|
+
function factsFor(exec, options = {}) {
|
|
73
|
+
const args = exec.arguments ?? {};
|
|
74
|
+
const capability = capabilityOf(exec.name);
|
|
75
|
+
const plugin = options.owners?.get(exec.name);
|
|
76
|
+
const id = exec.agent?.id;
|
|
77
|
+
const agent = typeof id === "string" ? id : void 0;
|
|
78
|
+
return {
|
|
79
|
+
tool: exec.name,
|
|
80
|
+
...capability === void 0 ? {} : { capability },
|
|
81
|
+
...plugin === void 0 ? {} : { plugin },
|
|
82
|
+
...agent === void 0 ? {} : { agent },
|
|
83
|
+
args: resolveArgs(args, options.resolvePath, options.cwd),
|
|
84
|
+
...options.counts === void 0 ? {} : { counts: options.counts }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
export { factsFor as n, capabilityOf as t };
|
|
@@ -1,98 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "./
|
|
3
|
-
import { t as
|
|
4
|
-
import { n as
|
|
5
|
-
import { a as scan, n as MAX_SCAN_BYTES } from "./src-BuaOiL2v.js";
|
|
1
|
+
import { a as PATH_ARGS, i as evaluate, l as segments, s as relativeTo, t as parsePack } from "./src-CZYvvEn5.js";
|
|
2
|
+
import { n as Ledger } from "./src-CuiWC80o.js";
|
|
3
|
+
import { n as factsFor, t as capabilityOf } from "./facts-DkTRO2p0.js";
|
|
4
|
+
import { a as scan, n as MAX_SCAN_BYTES } from "./src-9phPLfAL.js";
|
|
6
5
|
import { lstatSync, readFileSync, readlinkSync, realpathSync, statSync } from "node:fs";
|
|
7
6
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
8
7
|
import { createHash } from "node:crypto";
|
|
9
|
-
import { capabilityForTool } from "capmark";
|
|
10
8
|
|
|
11
|
-
//#region ../dsh/src/facts.ts
|
|
12
|
-
function resolveArgs(args, resolvePath, cwd) {
|
|
13
|
-
if (resolvePath === void 0 && cwd === void 0) return args;
|
|
14
|
-
const out = { ...args };
|
|
15
|
-
for (const key of PATH_ARGS) {
|
|
16
|
-
const value = out[key];
|
|
17
|
-
if (typeof value !== "string") continue;
|
|
18
|
-
let path = value;
|
|
19
|
-
if (resolvePath !== void 0) try {
|
|
20
|
-
path = resolvePath(path);
|
|
21
|
-
} catch {}
|
|
22
|
-
out[key] = cwd === void 0 ? path : relativeTo(path, cwd);
|
|
23
|
-
}
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Claude Code names its built-in tools in PascalCase (`Read`, `Bash`, …),
|
|
28
|
-
* which capmark's catalogue does not list. Each takes the capability of its
|
|
29
|
-
* lowercase counterpart; without this a Claude Code call carries no
|
|
30
|
-
* capability, and the rules scoped by one (paths, egress, the shell) never
|
|
31
|
-
* see it. An MCP tool, `mcp__<server>__<tool>`, takes its tool's.
|
|
32
|
-
*/
|
|
33
|
-
const CLAUDE_CODE = {
|
|
34
|
-
Read: "read",
|
|
35
|
-
Write: "write",
|
|
36
|
-
Edit: "edit",
|
|
37
|
-
MultiEdit: "edit",
|
|
38
|
-
NotebookEdit: "edit",
|
|
39
|
-
Bash: "bash",
|
|
40
|
-
PowerShell: "bash",
|
|
41
|
-
Grep: "grep",
|
|
42
|
-
Glob: "glob",
|
|
43
|
-
LS: "glob",
|
|
44
|
-
WebFetch: "web_fetch",
|
|
45
|
-
WebSearch: "web_search"
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* MCP tools whose names capmark's catalogue does not list, from the
|
|
49
|
-
* reference servers an agent most often mounts (modelcontextprotocol
|
|
50
|
-
* `filesystem` and `fetch`) and `prae mcp`'s own. Applied only after an
|
|
51
|
-
* `mcp__<server>__` prefix: without it, a Claude Code session reading
|
|
52
|
-
* `.env` through `mcp__filesystem__read_file` carries no capability, and
|
|
53
|
-
* no `fs:read` rule sees it.
|
|
54
|
-
*/
|
|
55
|
-
const MCP_TOOLS = {
|
|
56
|
-
read_file: "read",
|
|
57
|
-
read_text_file: "read",
|
|
58
|
-
read_media_file: "read",
|
|
59
|
-
list_directory: "glob",
|
|
60
|
-
list_dir: "glob",
|
|
61
|
-
directory_tree: "glob",
|
|
62
|
-
search_files: "glob",
|
|
63
|
-
get_file_info: "glob",
|
|
64
|
-
write_file: "write",
|
|
65
|
-
edit_file: "edit",
|
|
66
|
-
create_directory: "write",
|
|
67
|
-
fetch: "web_fetch"
|
|
68
|
-
};
|
|
69
|
-
function capabilityOf(tool) {
|
|
70
|
-
const direct = capabilityForTool(tool)?.id;
|
|
71
|
-
if (direct !== void 0) return direct;
|
|
72
|
-
const alias = CLAUDE_CODE[tool];
|
|
73
|
-
if (alias !== void 0) return capabilityForTool(alias)?.id;
|
|
74
|
-
const mcp = tool.startsWith("mcp__") ? tool.lastIndexOf("__") : -1;
|
|
75
|
-
if (mcp <= 5) return void 0;
|
|
76
|
-
const name = tool.slice(mcp + 2);
|
|
77
|
-
return capabilityOf(MCP_TOOLS[name] ?? name);
|
|
78
|
-
}
|
|
79
|
-
function factsFor(exec, options = {}) {
|
|
80
|
-
const args = exec.arguments ?? {};
|
|
81
|
-
const capability = capabilityOf(exec.name);
|
|
82
|
-
const plugin = options.owners?.get(exec.name);
|
|
83
|
-
const id = exec.agent?.id;
|
|
84
|
-
const agent = typeof id === "string" ? id : void 0;
|
|
85
|
-
return {
|
|
86
|
-
tool: exec.name,
|
|
87
|
-
...capability === void 0 ? {} : { capability },
|
|
88
|
-
...plugin === void 0 ? {} : { plugin },
|
|
89
|
-
...agent === void 0 ? {} : { agent },
|
|
90
|
-
args: resolveArgs(args, options.resolvePath, options.cwd),
|
|
91
|
-
...options.counts === void 0 ? {} : { counts: options.counts }
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
//#endregion
|
|
96
9
|
//#region ../dsh/src/judge.ts
|
|
97
10
|
/**
|
|
98
11
|
* Judge one call against a pack, without a harness anywhere near it.
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import "./
|
|
3
|
-
import "./
|
|
4
|
-
import "./
|
|
5
|
-
import
|
|
6
|
-
import "./views-CUcixte0.js";
|
|
1
|
+
import "./src-CZYvvEn5.js";
|
|
2
|
+
import "./src-CuiWC80o.js";
|
|
3
|
+
import { a as planWrite, i as dshHome, n as applyWrite, o as praeRow, r as defaultProfile, s as profileAt, t as main } from "./cli-BTVEk0Fh.js";
|
|
4
|
+
import "./facts-DkTRO2p0.js";
|
|
5
|
+
import "./views-endiZu1Z.js";
|
|
7
6
|
|
|
8
7
|
export { applyWrite, defaultProfile, dshHome, main, planWrite, praeRow, profileAt };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import {
|
|
3
|
-
import { d as generateKeypair, u as fingerprint } from "./src-Cy-Acrgj.js";
|
|
1
|
+
import { t as parsePack } from "./src-CZYvvEn5.js";
|
|
2
|
+
import { d as generateKeypair, u as fingerprint } from "./src-CuiWC80o.js";
|
|
4
3
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
5
4
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
6
5
|
import { homedir } from "node:os";
|
|
@@ -228,10 +227,17 @@ function applyInit(plan) {
|
|
|
228
227
|
}
|
|
229
228
|
return print === void 0 ? {} : { fingerprint: print };
|
|
230
229
|
}
|
|
230
|
+
/** Whether a script path is inside npm's temporary npx cache (`…/_npx/<hash>/…`). */
|
|
231
|
+
function fromNpxCache(bin) {
|
|
232
|
+
return /[\\/]_npx[\\/]/.test(bin);
|
|
233
|
+
}
|
|
231
234
|
/** `prae init claude-code …`: print the diff; with --apply, write it. */
|
|
232
235
|
function initClaudeCode(argv, context) {
|
|
233
236
|
const plan = planInit(argv, context);
|
|
234
237
|
const removing = argv.includes("--remove");
|
|
238
|
+
if (!removing && fromNpxCache(context.bin)) console.error(`prae: warning: this prae runs from npm's temporary npx cache (${context.bin}).\n The hook entry would point there, and npm may clear that cache: the hook would
|
|
239
|
+
stop running, and Claude Code lets a call through when its hook cannot start.
|
|
240
|
+
Install it to stay: npm install -g prae-cli, then run prae init claude-code again.`);
|
|
235
241
|
console.log(`settings ${plan.settings}`);
|
|
236
242
|
if (!removing) {
|
|
237
243
|
console.log(`mode ${argv.includes("--enforce") ? "enforce · deny and ask are returned to Claude Code; a gate error refuses the call" : "shadow · every verdict sealed, every call proceeds"}`);
|
|
@@ -263,4 +269,4 @@ function initClaudeCode(argv, context) {
|
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
//#endregion
|
|
266
|
-
export { HOOK_TIMEOUT, MARKER, applyInit, initClaudeCode, lineDiff, planInit, quote, settingsTarget, stableNode, withPrae, withoutPrae };
|
|
272
|
+
export { HOOK_TIMEOUT, MARKER, applyInit, fromNpxCache, initClaudeCode, lineDiff, planInit, quote, settingsTarget, stableNode, withPrae, withoutPrae };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import { c as renderNav, l as renderShell, s as esc, t as navFor } from "./views-
|
|
1
|
+
import "./src-CZYvvEn5.js";
|
|
2
|
+
import { c as renderNav, l as renderShell, s as esc, t as navFor } from "./views-endiZu1Z.js";
|
|
3
3
|
|
|
4
4
|
//#region src/console/instructions.ts
|
|
5
5
|
const STYLE = `
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as scan, n as MAX_SCAN_BYTES } from "./src-
|
|
1
|
+
import { i as evaluate } from "./src-CZYvvEn5.js";
|
|
2
|
+
import { a as scan, n as MAX_SCAN_BYTES } from "./src-9phPLfAL.js";
|
|
3
3
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
4
4
|
import { resolve, sep } from "node:path";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|