@seanmozeik/tripwire 0.6.6 → 0.7.0
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/LICENSE +21 -0
- package/README.md +152 -141
- package/dist/tripwire +0 -0
- package/dist/tripwire-pi.js +4 -0
- package/package.json +53 -19
- package/scripts/tripwire-cli +12 -0
- package/src/cli.ts +64 -57
- package/src/dispatch.ts +322 -114
- package/src/index.ts +1 -1
- package/src/lib/bash.ts +106 -62
- package/src/lib/config.ts +93 -46
- package/src/lib/cursor.ts +336 -0
- package/src/lib/diff.ts +5 -2
- package/src/lib/event.ts +20 -21
- package/src/lib/install.ts +508 -136
- package/src/lib/log.ts +2 -3
- package/src/lib/secrets.ts +151 -88
- package/src/main.ts +31 -0
- package/src/pi-extension.ts +337 -0
- package/src/rules/bash-deny.ts +13 -3
- package/src/rules/bash-git.ts +32 -45
- package/src/rules/bash-network-install.ts +6 -3
- package/src/rules/bash-redirect.ts +5 -5
- package/src/rules/bash-scoped-rm.ts +1 -1
- package/src/rules/bash-tar-explosion.ts +16 -15
- package/src/rules/config-custom.ts +21 -15
- package/src/rules/lazy-code.ts +1 -1
- package/src/rules/path-protect.ts +73 -10
- package/src/rules/post-secret-scrub.ts +17 -6
- package/src/rules/read-protect.ts +5 -15
- package/src/rules/tool-policy.ts +54 -0
- package/dist/tripwire-cli.js +0 -11
- package/dist/tripwire-cli.js.jsc +0 -0
- package/dist/tripwire.js +0 -96
- package/dist/tripwire.js.jsc +0 -0
- package/src/rules/bash-tool-policy.ts +0 -146
package/dist/tripwire.js.jsc
DELETED
|
Binary file
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { type Segment, hasBypass } from '../lib/bash';
|
|
2
|
-
import { type Decision, allow, deny, warn } from '../lib/decision';
|
|
3
|
-
|
|
4
|
-
// Opinionated tooling enforcement. Hard-deny on the package managers and
|
|
5
|
-
// Tools the user has explicitly replaced (npm/pip/patch-package); soft-warn
|
|
6
|
-
// Suggesting modern equivalents (find→fd, grep→rg).
|
|
7
|
-
//
|
|
8
|
-
// Hard-deny rationale (from the user's CLAUDE.md): TypeScript is bun-only,
|
|
9
|
-
// Python is uv-only. Slipping into npm or pip mid-session means the
|
|
10
|
-
// Agent forgot the toolchain and is about to install into the wrong
|
|
11
|
-
// Directory or make a lockfile bun can't read.
|
|
12
|
-
|
|
13
|
-
interface Policy {
|
|
14
|
-
readonly rule: string;
|
|
15
|
-
readonly action: 'deny' | 'warn';
|
|
16
|
-
readonly message: string;
|
|
17
|
-
readonly fires: (seg: Segment) => boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const POLICIES: readonly Policy[] = [
|
|
21
|
-
// ── HARD DENIES: wrong package manager ───────────────────────────────
|
|
22
|
-
{
|
|
23
|
-
rule: 'use-bun-not-npm',
|
|
24
|
-
action: 'deny',
|
|
25
|
-
message:
|
|
26
|
-
'Use `bun` instead of `npm`. Translations: `npm install` → `bun install`, `npm install <pkg>` → `bun add <pkg>`, `npm install -D <pkg>` → `bun add -d <pkg>`, `npm run X` → `bun run X` (or `bun X` for bin scripts), `npm test` → `bun test`. If you genuinely need npm (publishing to a registry that requires it, working in a different repo), append ` # tripwire-allow: <reason>` to the command.',
|
|
27
|
-
fires: (seg) => seg.head === 'npm',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
rule: 'use-bunx-not-npx',
|
|
31
|
-
action: 'deny',
|
|
32
|
-
message: 'Use `bunx` instead of `npx`. Same usage shape, faster, no implicit npm cache.',
|
|
33
|
-
fires: (seg) => seg.head === 'npx',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
rule: 'use-bun-not-pnpm',
|
|
37
|
-
action: 'deny',
|
|
38
|
-
message: 'Use `bun` instead of `pnpm`. The user is bun-only across their repos.',
|
|
39
|
-
fires: (seg) => seg.head === 'pnpm',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
rule: 'use-bun-not-yarn',
|
|
43
|
-
action: 'deny',
|
|
44
|
-
message: 'Use `bun` instead of `yarn`. The user is bun-only.',
|
|
45
|
-
fires: (seg) => seg.head === 'yarn',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
rule: 'use-uv-not-pip',
|
|
49
|
-
action: 'deny',
|
|
50
|
-
message:
|
|
51
|
-
'Use `uv` instead of `pip`. Translations: `pip install <pkg>` → `uv add <pkg>` (project dependency) or `uv pip install <pkg>` (env-only escape hatch). `pip freeze` → `uv pip freeze`. `pip list` → `uv pip list`. The user is uv-only across Python repos.',
|
|
52
|
-
fires: (seg) => seg.head === 'pip' || seg.head === 'pip3',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
rule: 'use-uv-sync-not-venv',
|
|
56
|
-
action: 'deny',
|
|
57
|
-
message:
|
|
58
|
-
'`python -m venv` creates a bare venv; use `uv sync` instead. uv sync creates the venv AND installs from pyproject.toml + uv.lock in one atomic step. To activate: `source .venv/bin/activate` after.',
|
|
59
|
-
fires: (seg) =>
|
|
60
|
-
(seg.head === 'python' || seg.head === 'python3') &&
|
|
61
|
-
seg.tokens.includes('-m') &&
|
|
62
|
-
seg.tokens.includes('venv'),
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
rule: 'uv-sync-over-uv-venv',
|
|
66
|
-
action: 'deny',
|
|
67
|
-
message:
|
|
68
|
-
'Use `uv sync` instead of `uv venv`. `uv venv` creates an empty venv that you then have to populate; `uv sync` creates the venv AND resolves+installs from pyproject.toml + uv.lock in one step. The only reason to use `uv venv` standalone is when there is no pyproject.toml — and in that case, `uv init` first.',
|
|
69
|
-
fires: (seg) => seg.head === 'uv' && seg.tokens[1] === 'venv',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
rule: 'use-bun-patch-not-patch-package',
|
|
73
|
-
action: 'deny',
|
|
74
|
-
message:
|
|
75
|
-
'Use `bun patch` instead of `patch-package`. Bun has built-in patch support that integrates with bun.lock; patch-package is npm-era and produces patches in a different format.',
|
|
76
|
-
fires: (seg) => seg.head === 'patch-package',
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
// ── SOFT WARNS: modern equivalents the user has installed ────────────────
|
|
80
|
-
{
|
|
81
|
-
rule: 'consider-fd',
|
|
82
|
-
action: 'warn',
|
|
83
|
-
message:
|
|
84
|
-
'Consider `fd` instead of `find`. Faster, simpler syntax, respects .gitignore by default. Examples: `find . -name "*.ts"` → `fd -e ts`, `find . -type f -name "X"` → `fd -t f X`, `find PATH ...` → `fd ... PATH`. The user has both installed; either works.',
|
|
85
|
-
fires: (seg) => seg.head === 'find',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
rule: 'consider-rg',
|
|
89
|
-
action: 'warn',
|
|
90
|
-
message:
|
|
91
|
-
'Consider `rg` (ripgrep) instead of `grep`. Faster; recurses by default; respects .gitignore by default (use `-u`/`-uu` to include ignored/hidden files); searches the CWD when no path is given. NOT a clean flag-for-flag swap; some flags differ or are unneeded. Carry over fine: `-i`, `-n`, `-v`, `-l`, `-c`. WATCH OUT: `-r` is NOT recursive in rg, it means `--replace` (rg already recurses), so `rg -rn "PATTERN" dir/` silently parses as `--replace=n` and rewrites every match to the literal "n" while exiting 0. Drop the `-r`: `grep -rn PATTERN .` → `rg -n PATTERN`. And `grep --include`/`--exclude` become `rg -g GLOB`/`-g !GLOB`.',
|
|
92
|
-
fires: (seg) => seg.head === 'grep' || seg.head === 'egrep' || seg.head === 'fgrep',
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
rule: 'rg-r-is-replace',
|
|
96
|
-
action: 'warn',
|
|
97
|
-
message:
|
|
98
|
-
'You almost certainly made a mistake: `-r` in rg means `--replace`, NOT recursive. ripgrep is always recursive by default — there is no `-r` flag for recursion. `-rn` silently parses as `--replace=n`, rewriting every match to the literal string "n" while exiting 0 with no error. If you want to search: drop the `-r` entirely (`rg -rn PATTERN` → `rg -n PATTERN`). If you genuinely want text substitution: use `--replace` explicitly so the intent is clear.',
|
|
99
|
-
fires: (seg) => {
|
|
100
|
-
if (seg.head !== 'rg') {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
return seg.flags.some((f) => f.startsWith('-') && !f.startsWith('--') && f.includes('r'));
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
rule: 'consider-btop',
|
|
108
|
-
action: 'warn',
|
|
109
|
-
message: 'Consider `btop` instead of `top`. Better UI, more info, modern.',
|
|
110
|
-
fires: (seg) => seg.head === 'top',
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
rule: 'consider-dust',
|
|
114
|
-
action: 'warn',
|
|
115
|
-
message: 'Consider `dust` instead of `du -sh`. Sorted, colorful, faster.',
|
|
116
|
-
fires: (seg) => seg.head === 'du',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
rule: 'consider-duf',
|
|
120
|
-
action: 'warn',
|
|
121
|
-
message: 'Consider `duf` instead of `df -h`. Better formatting, more readable.',
|
|
122
|
-
fires: (seg) => seg.head === 'df',
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
rule: 'consider-procs',
|
|
126
|
-
action: 'warn',
|
|
127
|
-
message: 'Consider `procs` instead of `ps aux`. Better filtering and output.',
|
|
128
|
-
fires: (seg) => seg.head === 'ps',
|
|
129
|
-
},
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
const bashToolPolicy = (segments: readonly Segment[], cmd: string): Decision => {
|
|
133
|
-
if (hasBypass(cmd)) {
|
|
134
|
-
return allow('bash-tool-policy');
|
|
135
|
-
}
|
|
136
|
-
for (const seg of segments) {
|
|
137
|
-
for (const p of POLICIES) {
|
|
138
|
-
if (p.fires(seg)) {
|
|
139
|
-
return p.action === 'deny' ? deny(p.rule, p.message) : warn(p.rule, p.message);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return allow('bash-tool-policy');
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export { bashToolPolicy };
|