agentinel 1.0.0 → 1.0.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.
Files changed (3) hide show
  1. package/README.md +17 -10
  2. package/dist/asen.js +118 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -77,10 +77,10 @@ The AI reads this, realizes the package is fake or malicious, and intelligently
77
77
 
78
78
  What about installs that never go through an agent? (e.g., You typing `npm install` manually).
79
79
 
80
- Agentinel provides an opt-in **PATH shim**.
80
+ Agentinel provides an opt-in **PATH shim**. By default, running `npx asen init` installs this shim automatically.
81
81
 
82
82
  ```sh
83
- npx asen init --shim
83
+ npx asen init
84
84
  ```
85
85
 
86
86
  This puts a tiny, fail-open wrapper script earlier in your `PATH`. When you type `npm install`, the shim checks the package first. If it's safe, the real `npm` command runs instantly.
@@ -153,28 +153,28 @@ Agentinel hard-blocks the install and returns an error payload to the agent. The
153
153
 
154
154
  Here are all the commands you can run via `npx asen <command>`:
155
155
 
156
- ### `npx asen init [--shim]`
157
- Wires up agent hooks and git hooks in the current repo.
156
+ ### `npx asen init [--no-shim]`
157
+ Wires up agent hooks and git hooks in the current repo, and installs the global PATH shim for human terminal protection.
158
158
  ```bash
159
159
  $ npx asen init
160
160
  wrote .agentinel.json
161
161
  registered the Claude Code PreToolUse hook in .claude/settings.json
162
162
  installed the git pre-commit hook in .git/hooks
163
+ wrote shims for npm, npx, pnpm, yarn, bun in /Users/user/.agentinel/bin
164
+ added the shims to PATH in /Users/user/.zshrc
165
+ Mode is warn, so a risky package typed at the terminal will be reported, not blocked.
166
+ Open a new terminal, or run \`asen unshim\` to undo this.
163
167
 
164
168
  agentinel is set up. New npm packages will be checked before they land.
165
169
  Default mode is warn. Set "mode": "strict" in .agentinel.json to block instead.
166
170
  ```
167
171
 
168
- When used with `--shim`, it wires up hooks AND installs the global PATH shim for human terminal protection.
172
+ When used with `--no-shim`, it wires up hooks but skips installing the global PATH shim.
169
173
  ```bash
170
- $ npx asen init --shim
174
+ $ npx asen init --no-shim
171
175
  wrote .agentinel.json
172
176
  registered the Claude Code PreToolUse hook in .claude/settings.json
173
177
  installed the git pre-commit hook in .git/hooks
174
- wrote shims for npm, npx, pnpm, yarn, bun in /Users/user/.agentinel/bin
175
- added the shims to PATH in /Users/user/.zshrc
176
- Mode is warn, so a risky package typed at the terminal will be reported, not blocked.
177
- Open a new terminal, or run \`asen unshim\` to undo this.
178
178
 
179
179
  agentinel is set up. New npm packages will be checked before they land.
180
180
  Default mode is warn. Set "mode": "strict" in .agentinel.json to block instead.
@@ -208,6 +208,13 @@ $ npx asen mode strict
208
208
  set mode to strict in .agentinel.json
209
209
  ```
210
210
 
211
+ ### `npx asen uninstall`
212
+ Completely removes all Agentinel hooks from your repository config files (`.claude`, `.gemini`, `.github`, etc.) and removes global shims.
213
+ ```bash
214
+ $ npx asen uninstall
215
+ agentinel has been completely uninstalled from this repository.
216
+ ```
217
+
211
218
  ### `npx asen unshim`
212
219
  Removes the global PATH shim.
213
220
  ```bash
package/dist/asen.js CHANGED
@@ -1496,9 +1496,102 @@ function runMode(targetMode) {
1496
1496
  return 0;
1497
1497
  }
1498
1498
 
1499
- // src/hooks/claude-code.ts
1500
- import { existsSync as existsSync6 } from "fs";
1499
+ // src/commands/uninstall.ts
1500
+ import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
1501
1501
  import { join as join6 } from "path";
1502
+ var HOOK_MARKER2 = "agentinel";
1503
+ var HOOK_SUBCOMMAND2 = "hook claude-code";
1504
+ function runUninstall() {
1505
+ const repoRoot = repoRootOrCwd();
1506
+ unwireClaudeCodeHook(repoRoot);
1507
+ unwireCodexHook(repoRoot);
1508
+ unwireCopilotHook(repoRoot);
1509
+ unwireGeminiHook(repoRoot);
1510
+ unwirePreCommitHook(repoRoot);
1511
+ removeShim();
1512
+ console.log("agentinel has been completely uninstalled from this repository.");
1513
+ return 0;
1514
+ }
1515
+ function unwireClaudeCodeHook(repoRoot) {
1516
+ const path = join6(repoRoot, ".claude", "settings.json");
1517
+ if (!existsSync6(path)) return;
1518
+ const file = readJson2(path);
1519
+ if (file === null) return;
1520
+ const hooks = asRecord3(file.hooks);
1521
+ if (!hooks || !Array.isArray(hooks.PreToolUse)) return;
1522
+ const preToolUse = hooks.PreToolUse;
1523
+ hooks.PreToolUse = preToolUse.filter((hook) => !JSON.stringify(hook).includes(HOOK_SUBCOMMAND2));
1524
+ if (hooks.PreToolUse.length === 0) {
1525
+ delete hooks.PreToolUse;
1526
+ }
1527
+ if (Object.keys(hooks).length === 0) {
1528
+ delete file.hooks;
1529
+ }
1530
+ writeJson2(path, file);
1531
+ console.log("removed Claude Code hook");
1532
+ }
1533
+ function unwireCodexHook(repoRoot) {
1534
+ const path = join6(repoRoot, ".codex", "hooks.json");
1535
+ if (!existsSync6(path)) return;
1536
+ const file = readJson2(path);
1537
+ if (file === null) return;
1538
+ const hooks = asRecord3(file.hooks);
1539
+ if (!hooks || !Array.isArray(hooks.PreToolUse)) return;
1540
+ const preToolUse = hooks.PreToolUse;
1541
+ hooks.PreToolUse = preToolUse.filter((hook) => !JSON.stringify(hook).includes("hook codex"));
1542
+ writeJson2(path, file);
1543
+ console.log("removed Codex hook");
1544
+ }
1545
+ function unwireCopilotHook(repoRoot) {
1546
+ const path = join6(repoRoot, ".github", "hooks", "agentinel.json");
1547
+ if (!existsSync6(path)) return;
1548
+ const file = readJson2(path);
1549
+ if (file === null) return;
1550
+ const hooks = asRecord3(file.hooks);
1551
+ if (!hooks || !Array.isArray(hooks.preToolUse)) return;
1552
+ const preToolUse = hooks.preToolUse;
1553
+ hooks.preToolUse = preToolUse.filter((hook) => !JSON.stringify(hook).includes("hook copilot"));
1554
+ writeJson2(path, file);
1555
+ console.log("removed Copilot hook");
1556
+ }
1557
+ function unwireGeminiHook(repoRoot) {
1558
+ const path = join6(repoRoot, ".gemini", "settings.json");
1559
+ if (!existsSync6(path)) return;
1560
+ const file = readJson2(path);
1561
+ if (file === null) return;
1562
+ const hooks = asRecord3(file.hooks);
1563
+ if (!hooks || !Array.isArray(hooks.BeforeTool)) return;
1564
+ const beforeTool = hooks.BeforeTool;
1565
+ hooks.BeforeTool = beforeTool.filter((hook) => !JSON.stringify(hook).includes("hook gemini"));
1566
+ writeJson2(path, file);
1567
+ console.log("removed Gemini hook");
1568
+ }
1569
+ function unwirePreCommitHook(repoRoot) {
1570
+ if (!existsSync6(join6(repoRoot, ".git"))) return;
1571
+ const path = join6(hooksDirectory(repoRoot), "pre-commit");
1572
+ if (!existsSync6(path)) return;
1573
+ const existing = readFileSync6(path, "utf8");
1574
+ if (!existing.includes(HOOK_MARKER2)) return;
1575
+ rmSync2(path);
1576
+ console.log("removed git pre-commit hook");
1577
+ }
1578
+ function readJson2(path) {
1579
+ try {
1580
+ return asRecord3(JSON.parse(readFileSync6(path, "utf8"))) ?? {};
1581
+ } catch {
1582
+ return null;
1583
+ }
1584
+ }
1585
+ function writeJson2(path, value) {
1586
+ writeFileSync4(path, JSON.stringify(value, null, 2) + "\n", "utf8");
1587
+ }
1588
+ function asRecord3(value) {
1589
+ return typeof value === "object" && value !== null ? value : null;
1590
+ }
1591
+
1592
+ // src/hooks/claude-code.ts
1593
+ import { existsSync as existsSync7 } from "fs";
1594
+ import { join as join7 } from "path";
1502
1595
 
1503
1596
  // src/checks/package-guard/resolve.ts
1504
1597
  import { execFileSync as execFileSync4 } from "child_process";
@@ -1582,7 +1675,7 @@ function candidatesFor(command, repoRoot) {
1582
1675
  }
1583
1676
  function isLocalTool(repoRoot, name) {
1584
1677
  const binary = name.includes("/") ? name.split("/").pop() : name;
1585
- return existsSync6(join6(repoRoot, "node_modules", ".bin", binary));
1678
+ return existsSync7(join7(repoRoot, "node_modules", ".bin", binary));
1586
1679
  }
1587
1680
  function warn(verdicts) {
1588
1681
  const summary = plainSummary(verdicts);
@@ -1796,25 +1889,41 @@ async function runPreCommitHook() {
1796
1889
  }
1797
1890
 
1798
1891
  // bin/asen.ts
1892
+ import { readFileSync as readFileSync7 } from "fs";
1893
+ import { dirname as dirname2, join as join8 } from "path";
1894
+ import { fileURLToPath as fileURLToPath2 } from "url";
1895
+ function getVersion() {
1896
+ try {
1897
+ const file = fileURLToPath2(import.meta.url);
1898
+ const pkgPath = join8(dirname2(file), "..", "package.json");
1899
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
1900
+ return typeof pkg.version === "string" ? pkg.version : "unknown";
1901
+ } catch {
1902
+ return "unknown";
1903
+ }
1904
+ }
1799
1905
  var USAGE = `asen, a guard for AI coding agent workflows
1800
1906
 
1801
- asen init [--shim] set up the hooks and config in this repo
1907
+ asen init [--no-shim] set up the hooks and config in this repo
1802
1908
  asen check [pkg...] check staged dependencies, or specific packages
1803
1909
  asen allow <pkg> --reason "..." allowlist a flagged package, with a logged reason
1804
1910
  asen mode <warn|strict> switch Agentinel's operating mode
1911
+ asen uninstall remove all agent hooks from this repository
1805
1912
  asen unshim remove the PATH shims that --shim installed
1806
1913
  `;
1807
1914
  async function main(argv2) {
1808
1915
  const [command, ...rest] = argv2;
1809
1916
  switch (command) {
1810
1917
  case "init":
1811
- return runInit({ shim: rest.includes("--shim") });
1918
+ return runInit({ shim: !rest.includes("--no-shim") });
1812
1919
  case "check":
1813
1920
  return runCheck(positionals(rest));
1814
1921
  case "allow":
1815
1922
  return runAllow(positionals(rest)[0], readFlag(rest, "--reason"));
1816
1923
  case "mode":
1817
1924
  return runMode(positionals(rest)[0]);
1925
+ case "uninstall":
1926
+ return runUninstall();
1818
1927
  case "unshim":
1819
1928
  return removeShim();
1820
1929
  // Not documented in the usage text on purpose. These are what the installed hooks call.
@@ -1823,6 +1932,10 @@ async function main(argv2) {
1823
1932
  // What the PATH shims call, with the whole command line as one argument.
1824
1933
  case "check-command":
1825
1934
  return runCheckCommand(rest[0]);
1935
+ case "--version":
1936
+ case "-v":
1937
+ console.log(`agentinel v${getVersion()}`);
1938
+ return 0;
1826
1939
  case "--help":
1827
1940
  case "-h":
1828
1941
  case void 0:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentinel",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Catches AI hallucinated npm packages before an agent installs them",
5
5
  "license": "MIT",
6
6
  "author": "Aman Janwani",