agentinel 1.0.3 → 1.0.4

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 -8
  2. package/dist/asen.js +120 -74
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -42,11 +42,17 @@ Every other tool in this space guards your terminal. **Agentinel guards your age
42
42
 
43
43
  ## 🚀 Installation
44
44
 
45
- Install it as a dev-dependency in your repository:
45
+ For the best experience across all your projects and to use the short `asen` alias, install Agentinel globally:
46
46
 
47
+ ```sh
48
+ npm install -g agentinel
49
+ asen init
50
+ ```
51
+
52
+ Alternatively, you can install it as a dev-dependency per-project:
47
53
  ```sh
48
54
  npm install --save-dev agentinel
49
- npx asen init
55
+ npx agentinel init
50
56
  ```
51
57
  *(No account, no server, no complex configuration.)*
52
58
 
@@ -151,7 +157,10 @@ Agentinel hard-blocks the install and returns an error payload to the agent. The
151
157
 
152
158
  ## 🧰 Command Reference
153
159
 
154
- Here are all the commands you can run via `npx asen <command>`:
160
+ You can run Agentinel using `npx agentinel <command>`.
161
+ If you have installed `agentinel` globally (`npm install -g agentinel`) or locally in your project, you can use the shorter alias: `npx asen <command>` (or just `asen <command>` if global).
162
+
163
+ Here are all the commands:
155
164
 
156
165
  ### `npx asen init [--no-shim]`
157
166
  Wires up agent hooks and git hooks in the current repo, and installs the global PATH shim for human terminal protection.
@@ -162,11 +171,11 @@ registered the Claude Code PreToolUse hook in .claude/settings.json
162
171
  installed the git pre-commit hook in .git/hooks
163
172
  wrote shims for npm, npx, pnpm, yarn, bun in /Users/user/.agentinel/bin
164
173
  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.
174
+ Mode is strict, so a risky package typed at the terminal will be blocked.
175
+ Open a new terminal, or run `asen unshim` to undo this.
167
176
 
168
177
  agentinel is set up. New npm packages will be checked before they land.
169
- Default mode is warn. Set "mode": "strict" in .agentinel.json to block instead.
178
+ Default mode is strict. Set "mode": "warn" in .agentinel.json to only warn instead.
170
179
  ```
171
180
 
172
181
  When used with `--no-shim`, it wires up hooks but skips installing the global PATH shim.
@@ -177,11 +186,11 @@ registered the Claude Code PreToolUse hook in .claude/settings.json
177
186
  installed the git pre-commit hook in .git/hooks
178
187
 
179
188
  agentinel is set up. New npm packages will be checked before they land.
180
- Default mode is warn. Set "mode": "strict" in .agentinel.json to block instead.
189
+ Default mode is strict. Set "mode": "warn" in .agentinel.json to only warn instead.
181
190
  ```
182
191
 
183
192
  ### `npx asen check [pkg...]`
184
- Scans the currently staged dependencies in your lockfile. Exits non-zero if flagged (great for CI/CD pipelines).
193
+ Scans the unstaged (or newly added) dependencies in your working tree, including the lockfile. Exits non-zero if flagged. (The Git pre-commit hook uses a strictly staged version of this check).
185
194
  ```bash
186
195
  $ npx asen check
187
196
  checked 142 package(s), nothing suspicious
package/dist/asen.js CHANGED
@@ -27,27 +27,68 @@ function readFlag(args, flag) {
27
27
 
28
28
  // src/checks/package-guard/staged-deps.ts
29
29
  import { execFileSync } from "child_process";
30
+ import { readFileSync, lstatSync } from "fs";
31
+ import { join, resolve, sep } from "path";
30
32
  var DEP_FIELDS = ["dependencies", "devDependencies", "optionalDependencies"];
31
33
  function newStagedDependencies(repoRoot) {
32
34
  const names = [];
33
- for (const path of stagedManifestPaths(repoRoot)) {
35
+ for (const path of changedManifestPaths(repoRoot, [
36
+ "diff",
37
+ "--cached",
38
+ "--name-only",
39
+ "-z",
40
+ "--diff-filter=ACMR"
41
+ ]) ?? []) {
34
42
  const staged = gitJson(repoRoot, `:${path}`);
35
- if (!staged) {
36
- continue;
37
- }
43
+ if (!staged) continue;
38
44
  const before = collectNames(gitJson(repoRoot, `HEAD:${path}`) ?? {});
39
45
  for (const name of collectNames(staged)) {
40
- if (!before.has(name) && !names.includes(name)) {
41
- names.push(name);
46
+ if (!before.has(name) && !names.includes(name)) names.push(name);
47
+ }
48
+ }
49
+ return names;
50
+ }
51
+ function newWorkingTreeDependencies(repoRoot) {
52
+ const names = [];
53
+ let paths = changedManifestPaths(repoRoot, [
54
+ "diff",
55
+ "HEAD",
56
+ "--name-only",
57
+ "-z",
58
+ "--diff-filter=ACMR"
59
+ ]);
60
+ if (paths === null) {
61
+ paths = changedManifestPaths(repoRoot, ["ls-files", "-z", "-c", "-o", "--exclude-standard"]) ?? [];
62
+ } else {
63
+ const untracked = changedManifestPaths(repoRoot, ["ls-files", "-z", "-o", "--exclude-standard"]) ?? [];
64
+ paths = Array.from(/* @__PURE__ */ new Set([...paths, ...untracked]));
65
+ }
66
+ for (const path of paths) {
67
+ let onDisk = null;
68
+ try {
69
+ const fullPath = resolve(join(repoRoot, path));
70
+ if (!fullPath.startsWith(resolve(repoRoot) + sep) && fullPath !== resolve(repoRoot)) {
71
+ continue;
42
72
  }
73
+ if (lstatSync(fullPath).isSymbolicLink()) {
74
+ continue;
75
+ }
76
+ onDisk = JSON.parse(readFileSync(fullPath, "utf8"));
77
+ } catch {
78
+ continue;
79
+ }
80
+ if (!onDisk) continue;
81
+ const before = collectNames(gitJson(repoRoot, `HEAD:${path}`) ?? {});
82
+ for (const name of collectNames(onDisk)) {
83
+ if (!before.has(name) && !names.includes(name)) names.push(name);
43
84
  }
44
85
  }
45
86
  return names;
46
87
  }
47
- function stagedManifestPaths(repoRoot) {
48
- const output = git(repoRoot, ["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"]);
88
+ function changedManifestPaths(repoRoot, gitArgs) {
89
+ const output = git(repoRoot, gitArgs);
49
90
  if (output === null) {
50
- return [];
91
+ return null;
51
92
  }
52
93
  return output.split("\0").filter((path) => path === "package.json" || path.endsWith("/package.json")).filter((path) => !isInsideDependencies(path));
53
94
  }
@@ -96,14 +137,14 @@ function git(cwd, args) {
96
137
  }
97
138
 
98
139
  // src/config/load.ts
99
- import { readFileSync, writeFileSync, existsSync } from "fs";
100
- import { join } from "path";
140
+ import { readFileSync as readFileSync2, writeFileSync, existsSync } from "fs";
141
+ import { join as join2 } from "path";
101
142
 
102
143
  // src/config/schema.ts
103
144
  var CONFIG_FILENAME = ".agentinel.json";
104
145
  function defaultConfig() {
105
146
  return {
106
- mode: "warn",
147
+ mode: "strict",
107
148
  allow: [
108
149
  {
109
150
  name: "asen",
@@ -129,7 +170,7 @@ function parseConfig(raw, warn2 = () => {
129
170
  config.mode = source.mode;
130
171
  } else if (source.mode !== void 0) {
131
172
  warn2(
132
- `unknown mode ${JSON.stringify(source.mode)} in ${CONFIG_FILENAME}, falling back to "warn". Valid modes are "warn" and "strict".`
173
+ `unknown mode ${JSON.stringify(source.mode)} in ${CONFIG_FILENAME}, falling back to "strict". Valid modes are "warn" and "strict".`
133
174
  );
134
175
  }
135
176
  if (Array.isArray(source.allow)) {
@@ -155,7 +196,7 @@ function isAllowlisted(config, name) {
155
196
  var ConfigError = class extends Error {
156
197
  };
157
198
  function configPath(repoRoot) {
158
- return join(repoRoot, CONFIG_FILENAME);
199
+ return join2(repoRoot, CONFIG_FILENAME);
159
200
  }
160
201
  function loadConfig(repoRoot) {
161
202
  const path = configPath(repoRoot);
@@ -164,7 +205,7 @@ function loadConfig(repoRoot) {
164
205
  }
165
206
  let text;
166
207
  try {
167
- text = readFileSync(path, "utf8");
208
+ text = readFileSync2(path, "utf8");
168
209
  } catch (error) {
169
210
  throw new ConfigError(`could not read ${CONFIG_FILENAME}: ${describe(error)}`);
170
211
  }
@@ -304,8 +345,8 @@ function describeFailure(error) {
304
345
  }
305
346
 
306
347
  // src/checks/package-guard/malware.ts
307
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
308
- import { dirname, join as join2 } from "path";
348
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
349
+ import { dirname, join as join3 } from "path";
309
350
  import { fileURLToPath } from "url";
310
351
  import { gunzipSync } from "zlib";
311
352
  var cache = null;
@@ -331,7 +372,7 @@ function load() {
331
372
  return {};
332
373
  }
333
374
  try {
334
- const raw = gunzipSync(readFileSync2(path)).toString("utf8");
375
+ const raw = gunzipSync(readFileSync3(path)).toString("utf8");
335
376
  const parsed = JSON.parse(raw);
336
377
  return typeof parsed === "object" && parsed !== null ? parsed : {};
337
378
  } catch {
@@ -341,9 +382,9 @@ function load() {
341
382
  function listPath() {
342
383
  const here = dirname(fileURLToPath(import.meta.url));
343
384
  const candidates = [
344
- join2(here, "..", "data", "malware-names.json.gz"),
345
- join2(here, "..", "..", "data", "malware-names.json.gz"),
346
- join2(here, "..", "..", "..", "data", "malware-names.json.gz")
385
+ join3(here, "..", "data", "malware-names.json.gz"),
386
+ join3(here, "..", "..", "data", "malware-names.json.gz"),
387
+ join3(here, "..", "..", "..", "data", "malware-names.json.gz")
347
388
  ];
348
389
  return candidates.find((path) => existsSync2(path)) ?? null;
349
390
  }
@@ -735,15 +776,18 @@ function scanForKnownMalware(tree, config) {
735
776
 
736
777
  // src/checks/package-guard/lockfile.ts
737
778
  import { execFileSync as execFileSync2 } from "child_process";
738
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
739
- import { join as join3 } from "path";
779
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
780
+ import { join as join4 } from "path";
740
781
  function packagesInLockfile(repoRoot) {
741
- const path = join3(repoRoot, "package-lock.json");
782
+ const path = join4(repoRoot, "package-lock.json");
742
783
  if (!existsSync3(path)) {
743
784
  return [];
744
785
  }
745
786
  return packagesInLockText(readFileOrNull(path));
746
787
  }
788
+ function workingTreeLockfilePackages(repoRoot) {
789
+ return packagesInLockfile(repoRoot);
790
+ }
747
791
  function stagedLockfilePackages(repoRoot) {
748
792
  let text = null;
749
793
  try {
@@ -778,7 +822,7 @@ function packagesInLockText(text) {
778
822
  }
779
823
  function readFileOrNull(path) {
780
824
  try {
781
- return readFileSync3(path, "utf8");
825
+ return readFileSync4(path, "utf8");
782
826
  } catch {
783
827
  return null;
784
828
  }
@@ -981,8 +1025,8 @@ async function runCheck(names) {
981
1025
  named = names;
982
1026
  tree = [];
983
1027
  } else {
984
- named = newStagedDependencies(repoRoot);
985
- tree = stagedLockfilePackages(repoRoot).filter((entry) => !named.includes(entry.name));
1028
+ named = newWorkingTreeDependencies(repoRoot);
1029
+ tree = workingTreeLockfilePackages(repoRoot).filter((entry) => !named.includes(entry.name));
986
1030
  }
987
1031
  if (named.length === 0 && tree.length === 0) {
988
1032
  console.log("no new packages to check");
@@ -1020,9 +1064,9 @@ async function runCheck(names) {
1020
1064
 
1021
1065
  // src/commands/init.ts
1022
1066
  import { execFileSync as execFileSync3 } from "child_process";
1023
- import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
1067
+ import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
1024
1068
  import { homedir as homedir2 } from "os";
1025
- import { isAbsolute, join as join5 } from "path";
1069
+ import { isAbsolute, join as join6 } from "path";
1026
1070
 
1027
1071
  // src/platform.ts
1028
1072
  function isWindows() {
@@ -1033,9 +1077,9 @@ function npmCommand() {
1033
1077
  }
1034
1078
 
1035
1079
  // src/commands/shim.ts
1036
- import { chmodSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "fs";
1080
+ import { chmodSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync2 } from "fs";
1037
1081
  import { homedir } from "os";
1038
- import { join as join4 } from "path";
1082
+ import { join as join5 } from "path";
1039
1083
  var CLIENTS = ["npm", "npx", "pnpm", "yarn", "bun"];
1040
1084
  var BLOCK_EXIT_CODE = 2;
1041
1085
  var RC_MARKER = "# agentinel";
@@ -1043,7 +1087,7 @@ function currentTarget() {
1043
1087
  return { home: homedir(), shell: process.env.SHELL ?? "", platform: process.platform };
1044
1088
  }
1045
1089
  function shimDirectory(home) {
1046
- return join4(home, ".agentinel", "bin");
1090
+ return join5(home, ".agentinel", "bin");
1047
1091
  }
1048
1092
  function onWindows(target) {
1049
1093
  return target.platform === "win32";
@@ -1053,21 +1097,21 @@ function shimFileName(client, target) {
1053
1097
  }
1054
1098
  function startupFilePath(target) {
1055
1099
  if (onWindows(target)) {
1056
- return join4(target.home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1");
1100
+ return join5(target.home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1");
1057
1101
  }
1058
1102
  if (target.shell.includes("zsh")) {
1059
- return join4(target.home, ".zshrc");
1103
+ return join5(target.home, ".zshrc");
1060
1104
  }
1061
1105
  if (target.shell.includes("bash")) {
1062
- return join4(target.home, ".bashrc");
1106
+ return join5(target.home, ".bashrc");
1063
1107
  }
1064
- return join4(target.home, ".profile");
1108
+ return join5(target.home, ".profile");
1065
1109
  }
1066
1110
  function installShim(repoRoot, target = currentTarget()) {
1067
1111
  const dir = shimDirectory(target.home);
1068
1112
  mkdirSync(dir, { recursive: true });
1069
1113
  for (const client of CLIENTS) {
1070
- const path = join4(dir, shimFileName(client, target));
1114
+ const path = join5(dir, shimFileName(client, target));
1071
1115
  writeFileSync2(path, shimScript(client, target), "utf8");
1072
1116
  if (!onWindows(target)) {
1073
1117
  chmodSync(path, 493);
@@ -1099,12 +1143,12 @@ function pathLine(target) {
1099
1143
  }
1100
1144
  function addPathLine(target) {
1101
1145
  const path = startupFilePath(target);
1102
- const existing = existsSync4(path) ? readFileSync4(path, "utf8") : "";
1146
+ const existing = existsSync4(path) ? readFileSync5(path, "utf8") : "";
1103
1147
  if (existing.includes(RC_MARKER)) {
1104
1148
  console.log(`${path} already puts the shims on PATH, left alone`);
1105
1149
  return;
1106
1150
  }
1107
- mkdirSync(join4(path, ".."), { recursive: true });
1151
+ mkdirSync(join5(path, ".."), { recursive: true });
1108
1152
  const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
1109
1153
  writeFileSync2(path, `${existing}${separator}${pathLine(target)}`, "utf8");
1110
1154
  console.log(`added the shims to PATH in ${path}`);
@@ -1114,7 +1158,7 @@ function removePathLine(target) {
1114
1158
  if (!existsSync4(path)) {
1115
1159
  return;
1116
1160
  }
1117
- const lines = readFileSync4(path, "utf8").split("\n");
1161
+ const lines = readFileSync5(path, "utf8").split("\n");
1118
1162
  const kept = lines.filter((line) => !line.includes(RC_MARKER));
1119
1163
  if (kept.length === lines.length) {
1120
1164
  return;
@@ -1261,7 +1305,9 @@ function runInit(options = {}) {
1261
1305
  installShim(repoRoot, options.shimTarget);
1262
1306
  }
1263
1307
  console.log("\nagentinel is set up. New npm packages will be checked before they land.");
1264
- console.log('Default mode is warn. Set "mode": "strict" in .agentinel.json to block instead.');
1308
+ console.log(
1309
+ 'Default mode is strict. Set "mode": "warn" in .agentinel.json to only warn instead.'
1310
+ );
1265
1311
  if (!hasLocalInstall(repoRoot)) {
1266
1312
  console.log("\nThe hook runs on every command, and going through npx each time is slow.");
1267
1313
  console.log("For faster hooks, add it to the repo and run init again:");
@@ -1270,7 +1316,7 @@ function runInit(options = {}) {
1270
1316
  return 0;
1271
1317
  }
1272
1318
  function hasLocalInstall(repoRoot) {
1273
- return existsSync5(join5(repoRoot, "node_modules", ".bin", "asen"));
1319
+ return existsSync5(join6(repoRoot, "node_modules", ".bin", "asen"));
1274
1320
  }
1275
1321
  function claudeCodeCommand(repoRoot) {
1276
1322
  if (isWindows()) {
@@ -1291,12 +1337,12 @@ function writeConfig(repoRoot) {
1291
1337
  console.log("wrote .agentinel.json");
1292
1338
  }
1293
1339
  function wireClaudeCodeHook(repoRoot, command) {
1294
- const dir = join5(repoRoot, ".claude");
1295
- const path = join5(dir, "settings.json");
1340
+ const dir = join6(repoRoot, ".claude");
1341
+ const path = join6(dir, "settings.json");
1296
1342
  let settings = {};
1297
1343
  if (existsSync5(path)) {
1298
1344
  try {
1299
- const parsed = JSON.parse(readFileSync5(path, "utf8"));
1345
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
1300
1346
  if (typeof parsed === "object" && parsed !== null) {
1301
1347
  settings = parsed;
1302
1348
  }
@@ -1331,7 +1377,7 @@ function wireOtherAgents(repoRoot, command) {
1331
1377
  if (uses(repoRoot, ".codex")) {
1332
1378
  wireCodexHook(repoRoot, command);
1333
1379
  }
1334
- if (uses(repoRoot, ".copilot") || existsSync5(join5(repoRoot, ".github", "hooks"))) {
1380
+ if (uses(repoRoot, ".copilot") || existsSync5(join6(repoRoot, ".github", "hooks"))) {
1335
1381
  wireCopilotHook(repoRoot, command);
1336
1382
  }
1337
1383
  if (uses(repoRoot, ".gemini")) {
@@ -1339,10 +1385,10 @@ function wireOtherAgents(repoRoot, command) {
1339
1385
  }
1340
1386
  }
1341
1387
  function uses(repoRoot, dir) {
1342
- return existsSync5(join5(repoRoot, dir)) || existsSync5(join5(homedir2(), dir));
1388
+ return existsSync5(join6(repoRoot, dir)) || existsSync5(join6(homedir2(), dir));
1343
1389
  }
1344
1390
  function wireCodexHook(repoRoot, command) {
1345
- const path = join5(repoRoot, ".codex", "hooks.json");
1391
+ const path = join6(repoRoot, ".codex", "hooks.json");
1346
1392
  const file = readJson(path);
1347
1393
  if (file === null) {
1348
1394
  console.log(".codex/hooks.json is not valid JSON, skipping the Codex hook");
@@ -1364,7 +1410,7 @@ function wireCodexHook(repoRoot, command) {
1364
1410
  console.log("registered the Codex PreToolUse hook in .codex/hooks.json");
1365
1411
  }
1366
1412
  function wireCopilotHook(repoRoot, command) {
1367
- const path = join5(repoRoot, ".github", "hooks", "agentinel.json");
1413
+ const path = join6(repoRoot, ".github", "hooks", "agentinel.json");
1368
1414
  const file = readJson(path);
1369
1415
  if (file === null) {
1370
1416
  console.log(".github/hooks/agentinel.json is not valid JSON, skipping the Copilot hook");
@@ -1384,7 +1430,7 @@ function wireCopilotHook(repoRoot, command) {
1384
1430
  console.log("registered the Copilot preToolUse hook in .github/hooks/agentinel.json");
1385
1431
  }
1386
1432
  function wireGeminiHook(repoRoot, command) {
1387
- const path = join5(repoRoot, ".gemini", "settings.json");
1433
+ const path = join6(repoRoot, ".gemini", "settings.json");
1388
1434
  const file = readJson(path);
1389
1435
  if (file === null) {
1390
1436
  console.log(".gemini/settings.json is not valid JSON, skipping the Gemini hook");
@@ -1410,13 +1456,13 @@ function readJson(path) {
1410
1456
  return {};
1411
1457
  }
1412
1458
  try {
1413
- return asRecord2(JSON.parse(readFileSync5(path, "utf8"))) ?? {};
1459
+ return asRecord2(JSON.parse(readFileSync6(path, "utf8"))) ?? {};
1414
1460
  } catch {
1415
1461
  return null;
1416
1462
  }
1417
1463
  }
1418
1464
  function writeJson(path, value) {
1419
- mkdirSync2(join5(path, ".."), { recursive: true });
1465
+ mkdirSync2(join6(path, ".."), { recursive: true });
1420
1466
  writeFileSync3(path, JSON.stringify(value, null, 2) + "\n", "utf8");
1421
1467
  }
1422
1468
  function registers(entries, kind) {
@@ -1436,27 +1482,27 @@ function git2(repoRoot, args) {
1436
1482
  function hooksDirectory(repoRoot) {
1437
1483
  const configured = git2(repoRoot, ["config", "--get", "core.hooksPath"]);
1438
1484
  if (configured) {
1439
- return isAbsolute(configured) ? configured : join5(repoRoot, configured);
1485
+ return isAbsolute(configured) ? configured : join6(repoRoot, configured);
1440
1486
  }
1441
1487
  const path = git2(repoRoot, ["rev-parse", "--git-path", "hooks"]);
1442
1488
  if (path) {
1443
- return isAbsolute(path) ? path : join5(repoRoot, path);
1489
+ return isAbsolute(path) ? path : join6(repoRoot, path);
1444
1490
  }
1445
- return join5(repoRoot, ".git", "hooks");
1491
+ return join6(repoRoot, ".git", "hooks");
1446
1492
  }
1447
1493
  function wirePreCommitHook(repoRoot, command) {
1448
- if (!existsSync5(join5(repoRoot, ".git"))) {
1494
+ if (!existsSync5(join6(repoRoot, ".git"))) {
1449
1495
  console.log("not a git repo, skipping the pre-commit hook");
1450
1496
  return;
1451
1497
  }
1452
1498
  const dir = hooksDirectory(repoRoot);
1453
- const path = join5(dir, "pre-commit");
1499
+ const path = join6(dir, "pre-commit");
1454
1500
  const script = `#!/bin/sh
1455
1501
  # ${HOOK_MARKER}: check newly added npm packages before they get committed
1456
1502
  ${command} hook pre-commit
1457
1503
  `;
1458
1504
  if (existsSync5(path)) {
1459
- const existing = readFileSync5(path, "utf8");
1505
+ const existing = readFileSync6(path, "utf8");
1460
1506
  if (existing.includes(HOOK_MARKER)) {
1461
1507
  console.log("pre-commit hook already installed, left alone");
1462
1508
  return;
@@ -1497,8 +1543,8 @@ function runMode(targetMode) {
1497
1543
  }
1498
1544
 
1499
1545
  // src/commands/uninstall.ts
1500
- import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
1501
- import { join as join6 } from "path";
1546
+ import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
1547
+ import { join as join7 } from "path";
1502
1548
  var HOOK_MARKER2 = "agentinel";
1503
1549
  var HOOK_SUBCOMMAND2 = "hook claude-code";
1504
1550
  function runUninstall() {
@@ -1513,7 +1559,7 @@ function runUninstall() {
1513
1559
  return 0;
1514
1560
  }
1515
1561
  function unwireClaudeCodeHook(repoRoot) {
1516
- const path = join6(repoRoot, ".claude", "settings.json");
1562
+ const path = join7(repoRoot, ".claude", "settings.json");
1517
1563
  if (!existsSync6(path)) return;
1518
1564
  const file = readJson2(path);
1519
1565
  if (file === null) return;
@@ -1531,7 +1577,7 @@ function unwireClaudeCodeHook(repoRoot) {
1531
1577
  console.log("removed Claude Code hook");
1532
1578
  }
1533
1579
  function unwireCodexHook(repoRoot) {
1534
- const path = join6(repoRoot, ".codex", "hooks.json");
1580
+ const path = join7(repoRoot, ".codex", "hooks.json");
1535
1581
  if (!existsSync6(path)) return;
1536
1582
  const file = readJson2(path);
1537
1583
  if (file === null) return;
@@ -1543,7 +1589,7 @@ function unwireCodexHook(repoRoot) {
1543
1589
  console.log("removed Codex hook");
1544
1590
  }
1545
1591
  function unwireCopilotHook(repoRoot) {
1546
- const path = join6(repoRoot, ".github", "hooks", "agentinel.json");
1592
+ const path = join7(repoRoot, ".github", "hooks", "agentinel.json");
1547
1593
  if (!existsSync6(path)) return;
1548
1594
  const file = readJson2(path);
1549
1595
  if (file === null) return;
@@ -1555,7 +1601,7 @@ function unwireCopilotHook(repoRoot) {
1555
1601
  console.log("removed Copilot hook");
1556
1602
  }
1557
1603
  function unwireGeminiHook(repoRoot) {
1558
- const path = join6(repoRoot, ".gemini", "settings.json");
1604
+ const path = join7(repoRoot, ".gemini", "settings.json");
1559
1605
  if (!existsSync6(path)) return;
1560
1606
  const file = readJson2(path);
1561
1607
  if (file === null) return;
@@ -1567,17 +1613,17 @@ function unwireGeminiHook(repoRoot) {
1567
1613
  console.log("removed Gemini hook");
1568
1614
  }
1569
1615
  function unwirePreCommitHook(repoRoot) {
1570
- if (!existsSync6(join6(repoRoot, ".git"))) return;
1571
- const path = join6(hooksDirectory(repoRoot), "pre-commit");
1616
+ if (!existsSync6(join7(repoRoot, ".git"))) return;
1617
+ const path = join7(hooksDirectory(repoRoot), "pre-commit");
1572
1618
  if (!existsSync6(path)) return;
1573
- const existing = readFileSync6(path, "utf8");
1619
+ const existing = readFileSync7(path, "utf8");
1574
1620
  if (!existing.includes(HOOK_MARKER2)) return;
1575
1621
  rmSync2(path);
1576
1622
  console.log("removed git pre-commit hook");
1577
1623
  }
1578
1624
  function readJson2(path) {
1579
1625
  try {
1580
- return asRecord3(JSON.parse(readFileSync6(path, "utf8"))) ?? {};
1626
+ return asRecord3(JSON.parse(readFileSync7(path, "utf8"))) ?? {};
1581
1627
  } catch {
1582
1628
  return null;
1583
1629
  }
@@ -1591,7 +1637,7 @@ function asRecord3(value) {
1591
1637
 
1592
1638
  // src/hooks/claude-code.ts
1593
1639
  import { existsSync as existsSync7 } from "fs";
1594
- import { join as join7 } from "path";
1640
+ import { join as join8 } from "path";
1595
1641
 
1596
1642
  // src/checks/package-guard/resolve.ts
1597
1643
  import { execFileSync as execFileSync4 } from "child_process";
@@ -1675,7 +1721,7 @@ function candidatesFor(command, repoRoot) {
1675
1721
  }
1676
1722
  function isLocalTool(repoRoot, name) {
1677
1723
  const binary = name.includes("/") ? name.split("/").pop() : name;
1678
- return existsSync7(join7(repoRoot, "node_modules", ".bin", binary));
1724
+ return existsSync7(join8(repoRoot, "node_modules", ".bin", binary));
1679
1725
  }
1680
1726
  function warn(verdicts) {
1681
1727
  const summary = plainSummary(verdicts);
@@ -1889,14 +1935,14 @@ async function runPreCommitHook() {
1889
1935
  }
1890
1936
 
1891
1937
  // bin/asen.ts
1892
- import { readFileSync as readFileSync7 } from "fs";
1893
- import { dirname as dirname2, join as join8 } from "path";
1938
+ import { readFileSync as readFileSync8 } from "fs";
1939
+ import { dirname as dirname2, join as join9 } from "path";
1894
1940
  import { fileURLToPath as fileURLToPath2 } from "url";
1895
1941
  function getVersion() {
1896
1942
  try {
1897
1943
  const file = fileURLToPath2(import.meta.url);
1898
- const pkgPath = join8(dirname2(file), "..", "package.json");
1899
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
1944
+ const pkgPath = join9(dirname2(file), "..", "package.json");
1945
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
1900
1946
  return typeof pkg.version === "string" ? pkg.version : "unknown";
1901
1947
  } catch {
1902
1948
  return "unknown";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentinel",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Catches AI hallucinated npm packages before an agent installs them",
5
5
  "license": "MIT",
6
6
  "author": "Aman Janwani",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "homepage": "https://github.com/aman-janwani/agentinel#readme",
16
16
  "bin": {
17
- "asen": "./dist/asen.js"
17
+ "asen": "./dist/asen.js",
18
+ "agentinel": "./dist/asen.js"
18
19
  },
19
20
  "files": [
20
21
  "dist",