agentinel 1.0.4 → 1.0.5
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/README.md +30 -10
- package/dist/asen.js +100 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,7 +89,9 @@ Agentinel provides an opt-in **PATH shim**. By default, running `npx asen init`
|
|
|
89
89
|
npx asen init
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
This puts a tiny, fail-open wrapper script earlier in your `PATH`. When you type `npm install
|
|
92
|
+
This puts a tiny, fail-open wrapper script earlier in your `PATH`. When you type `npm install <pkg>`, the shim checks the package first. If it's safe, the real `npm` command runs instantly.
|
|
93
|
+
|
|
94
|
+
As a bonus, if you just run a plain `npm install` with no arguments, the shim instantly checks your unstaged `package.json` for any newly added dependencies, ensuring that packages you pasted in are scanned before they resolve!
|
|
93
95
|
|
|
94
96
|
**Terminal Example:**
|
|
95
97
|
```bash
|
|
@@ -166,16 +168,34 @@ Here are all the commands:
|
|
|
166
168
|
Wires up agent hooks and git hooks in the current repo, and installs the global PATH shim for human terminal protection.
|
|
167
169
|
```bash
|
|
168
170
|
$ npx asen init
|
|
169
|
-
wrote .agentinel.json
|
|
170
|
-
registered the Claude Code PreToolUse hook in .claude/settings.json
|
|
171
|
-
installed the git pre-commit hook in .git/hooks
|
|
172
|
-
wrote shims for npm, npx, pnpm, yarn, bun in /Users/user/.agentinel/bin
|
|
173
|
-
added the shims to PATH in /Users/user/.zshrc
|
|
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.
|
|
176
171
|
|
|
177
|
-
agentinel
|
|
178
|
-
|
|
172
|
+
╭─ agentinel ──────────────────────────────╮
|
|
173
|
+
│ agentinel setup complete │
|
|
174
|
+
│ │
|
|
175
|
+
│ New npm packages will be checked before │
|
|
176
|
+
│ they land. │
|
|
177
|
+
│ │
|
|
178
|
+
│ ✔ wrote .agentinel.json │
|
|
179
|
+
│ ✔ registered the Claude Code PreToolUse │
|
|
180
|
+
│ hook in .claude/settings.json │
|
|
181
|
+
│ ✔ installed the git pre-commit hook in │
|
|
182
|
+
│ .git/hooks │
|
|
183
|
+
│ ✔ wrote shims for npm, npx, pnpm, yarn, │
|
|
184
|
+
│ bun in /Users/user/.agentinel/bin │
|
|
185
|
+
│ ✔ added the shims to PATH in │
|
|
186
|
+
│ /Users/user/.zshrc │
|
|
187
|
+
│ ✔ Open a new terminal, or run `asen │
|
|
188
|
+
│ unshim` to undo this. │
|
|
189
|
+
│ │
|
|
190
|
+
│ Default mode is strict. Set "mode": │
|
|
191
|
+
│ "warn" in .agentinel.json to only warn │
|
|
192
|
+
│ instead. │
|
|
193
|
+
╰──────────────────────────────────────────╯
|
|
194
|
+
|
|
195
|
+
Performance Tip:
|
|
196
|
+
The hook runs on every command, and resolving through npx each time is slow.
|
|
197
|
+
For faster hooks, add it to the repo and run init again:
|
|
198
|
+
npm install --save-dev agentinel && npx asen init
|
|
179
199
|
```
|
|
180
200
|
|
|
181
201
|
When used with `--no-shim`, it wires up hooks but skips installing the global PATH shim.
|
package/dist/asen.js
CHANGED
|
@@ -862,7 +862,10 @@ function collectFromDependencies(lock, found) {
|
|
|
862
862
|
var RESET = "\x1B[0m";
|
|
863
863
|
var YELLOW = "\x1B[33m";
|
|
864
864
|
var RED = "\x1B[31m";
|
|
865
|
+
var GREEN = "\x1B[32m";
|
|
866
|
+
var CYAN = "\x1B[36m";
|
|
865
867
|
var DIM = "\x1B[2m";
|
|
868
|
+
var BOLD = "\x1B[1m";
|
|
866
869
|
var WIDTH = 46;
|
|
867
870
|
var TITLE = "agentinel";
|
|
868
871
|
function supportsColor(stream = process.stdout) {
|
|
@@ -952,6 +955,39 @@ function draw(b, paint) {
|
|
|
952
955
|
return b.action ? `${box}
|
|
953
956
|
${paint(DIM, b.action)}` : box;
|
|
954
957
|
}
|
|
958
|
+
function drawSuccessBox(heading, body, stream = process.stdout) {
|
|
959
|
+
const inner = WIDTH - 4;
|
|
960
|
+
const text = inner - 1;
|
|
961
|
+
const hasColor = supportsColor(stream);
|
|
962
|
+
const color = hasColor ? GREEN : "";
|
|
963
|
+
const paint = (code, str) => hasColor ? `${code}${str}${RESET}` : str;
|
|
964
|
+
const titlePart = hasColor ? `${BOLD}${TITLE}${RESET}${color}` : TITLE;
|
|
965
|
+
const top = paint(color, `\u256D\u2500 ${titlePart} ${"\u2500".repeat(WIDTH - TITLE.length - 5)}\u256E`);
|
|
966
|
+
const bottom = paint(color, `\u2570${"\u2500".repeat(WIDTH - 2)}\u256F`);
|
|
967
|
+
const rows = [];
|
|
968
|
+
const push = (line, styled) => {
|
|
969
|
+
const edge = paint(color, "\u2502");
|
|
970
|
+
const plainLine = (styled ?? line).replace(/\x1b\[[0-9;]*m/g, "");
|
|
971
|
+
const pad = " ".repeat(Math.max(0, inner - plainLine.length));
|
|
972
|
+
rows.push(`${edge} ${styled ?? line}${pad}${edge}`);
|
|
973
|
+
};
|
|
974
|
+
push(heading, paint(BOLD, heading));
|
|
975
|
+
push("");
|
|
976
|
+
for (const line of body) {
|
|
977
|
+
if (line === "") {
|
|
978
|
+
push("");
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (line.includes("\x1B")) {
|
|
982
|
+
push(line, line);
|
|
983
|
+
} else {
|
|
984
|
+
for (const wrapped of wrap(line, text)) {
|
|
985
|
+
push(wrapped);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return [top, ...rows, bottom].join("\n");
|
|
990
|
+
}
|
|
955
991
|
function wrap(line, width) {
|
|
956
992
|
const lines = [];
|
|
957
993
|
let current = "";
|
|
@@ -1107,7 +1143,7 @@ function startupFilePath(target) {
|
|
|
1107
1143
|
}
|
|
1108
1144
|
return join5(target.home, ".profile");
|
|
1109
1145
|
}
|
|
1110
|
-
function installShim(repoRoot, target = currentTarget()) {
|
|
1146
|
+
function installShim(repoRoot, target = currentTarget(), messages = []) {
|
|
1111
1147
|
const dir = shimDirectory(target.home);
|
|
1112
1148
|
mkdirSync(dir, { recursive: true });
|
|
1113
1149
|
for (const client of CLIENTS) {
|
|
@@ -1117,13 +1153,9 @@ function installShim(repoRoot, target = currentTarget()) {
|
|
|
1117
1153
|
chmodSync(path, 493);
|
|
1118
1154
|
}
|
|
1119
1155
|
}
|
|
1120
|
-
|
|
1121
|
-
addPathLine(target);
|
|
1122
|
-
|
|
1123
|
-
console.log(
|
|
1124
|
-
mode === "strict" ? "Mode is strict, so a risky package typed at the terminal will be blocked." : "Mode is warn, so a risky package typed at the terminal will be reported, not blocked."
|
|
1125
|
-
);
|
|
1126
|
-
console.log("Open a new terminal, or run `asen unshim` to undo this.");
|
|
1156
|
+
messages.push(`wrote shims for ${CLIENTS.join(", ")} in ${dir}`);
|
|
1157
|
+
addPathLine(target, messages);
|
|
1158
|
+
messages.push("Open a new terminal, or run `asen unshim` to undo this.");
|
|
1127
1159
|
return 0;
|
|
1128
1160
|
}
|
|
1129
1161
|
function removeShim(target = currentTarget()) {
|
|
@@ -1141,17 +1173,17 @@ function pathLine(target) {
|
|
|
1141
1173
|
return `export PATH="$HOME/.agentinel/bin:$PATH" ${RC_MARKER}
|
|
1142
1174
|
`;
|
|
1143
1175
|
}
|
|
1144
|
-
function addPathLine(target) {
|
|
1176
|
+
function addPathLine(target, messages) {
|
|
1145
1177
|
const path = startupFilePath(target);
|
|
1146
1178
|
const existing = existsSync4(path) ? readFileSync5(path, "utf8") : "";
|
|
1147
1179
|
if (existing.includes(RC_MARKER)) {
|
|
1148
|
-
|
|
1180
|
+
messages.push(`${path} already puts the shims on PATH, left alone`);
|
|
1149
1181
|
return;
|
|
1150
1182
|
}
|
|
1151
1183
|
mkdirSync(join5(path, ".."), { recursive: true });
|
|
1152
1184
|
const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
|
|
1153
1185
|
writeFileSync2(path, `${existing}${separator}${pathLine(target)}`, "utf8");
|
|
1154
|
-
|
|
1186
|
+
messages.push(`added the shims to PATH in ${path}`);
|
|
1155
1187
|
}
|
|
1156
1188
|
function removePathLine(target) {
|
|
1157
1189
|
const path = startupFilePath(target);
|
|
@@ -1268,12 +1300,15 @@ async function runCheckCommand(command) {
|
|
|
1268
1300
|
if (!command) {
|
|
1269
1301
|
return 0;
|
|
1270
1302
|
}
|
|
1271
|
-
const { installs, executes } = parseCommand(command);
|
|
1303
|
+
const { installs, executes, lockfile } = parseCommand(command);
|
|
1304
|
+
const repoRoot = repoRootOrCwd();
|
|
1272
1305
|
const names = [.../* @__PURE__ */ new Set([...installs, ...executes])];
|
|
1273
1306
|
if (names.length === 0) {
|
|
1274
|
-
return 0;
|
|
1307
|
+
if (!lockfile) return 0;
|
|
1308
|
+
const workingTree = newWorkingTreeDependencies(repoRoot);
|
|
1309
|
+
if (workingTree.length === 0) return 0;
|
|
1310
|
+
names.push(...workingTree);
|
|
1275
1311
|
}
|
|
1276
|
-
const repoRoot = repoRootOrCwd();
|
|
1277
1312
|
const config = loadConfig(repoRoot);
|
|
1278
1313
|
const verdicts = await checkPackages(names, config);
|
|
1279
1314
|
for (const verdict of verdicts) {
|
|
@@ -1297,21 +1332,30 @@ var HOOK_MARKER = "agentinel";
|
|
|
1297
1332
|
var HOOK_SUBCOMMAND = "hook claude-code";
|
|
1298
1333
|
function runInit(options = {}) {
|
|
1299
1334
|
const repoRoot = repoRootOrCwd();
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1335
|
+
const messages = [];
|
|
1336
|
+
writeConfig(repoRoot, messages);
|
|
1337
|
+
wireClaudeCodeHook(repoRoot, claudeCodeCommand(repoRoot), messages);
|
|
1338
|
+
wireOtherAgents(repoRoot, agentHookCommand(repoRoot), messages);
|
|
1339
|
+
wirePreCommitHook(repoRoot, gitHookCommand(repoRoot), messages);
|
|
1304
1340
|
if (options.shim) {
|
|
1305
|
-
installShim(repoRoot, options.shimTarget);
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1341
|
+
installShim(repoRoot, options.shimTarget, messages);
|
|
1342
|
+
}
|
|
1343
|
+
const successBox = drawSuccessBox("agentinel setup complete", [
|
|
1344
|
+
"New npm packages will be checked before they land.",
|
|
1345
|
+
"",
|
|
1346
|
+
...messages.map((m) => `\u2714 ${m}`),
|
|
1347
|
+
"",
|
|
1348
|
+
`Default mode is ${GREEN}strict${RESET}.`,
|
|
1349
|
+
'Set "mode": "warn" in .agentinel.json to only warn instead.'
|
|
1350
|
+
]);
|
|
1351
|
+
console.log(`
|
|
1352
|
+
${successBox}`);
|
|
1311
1353
|
if (!hasLocalInstall(repoRoot)) {
|
|
1312
|
-
console.log(
|
|
1313
|
-
|
|
1314
|
-
console.log(
|
|
1354
|
+
console.log(`
|
|
1355
|
+
${CYAN}Performance Tip:${RESET}`);
|
|
1356
|
+
console.log(`The hook runs on every command, and resolving through npx each time is slow.`);
|
|
1357
|
+
console.log(`For faster hooks, add it to the repo and run init again:`);
|
|
1358
|
+
console.log(` ${DIM}npm install --save-dev agentinel && npx asen init${RESET}`);
|
|
1315
1359
|
}
|
|
1316
1360
|
return 0;
|
|
1317
1361
|
}
|
|
@@ -1327,16 +1371,16 @@ function claudeCodeCommand(repoRoot) {
|
|
|
1327
1371
|
function gitHookCommand(repoRoot) {
|
|
1328
1372
|
return hasLocalInstall(repoRoot) ? "./node_modules/.bin/asen" : "npx agentinel";
|
|
1329
1373
|
}
|
|
1330
|
-
function writeConfig(repoRoot) {
|
|
1374
|
+
function writeConfig(repoRoot, messages) {
|
|
1331
1375
|
const path = configPath(repoRoot);
|
|
1332
1376
|
if (existsSync5(path)) {
|
|
1333
|
-
|
|
1377
|
+
messages.push(".agentinel.json already exists, left alone");
|
|
1334
1378
|
return;
|
|
1335
1379
|
}
|
|
1336
1380
|
saveConfig(repoRoot, defaultConfig());
|
|
1337
|
-
|
|
1381
|
+
messages.push("wrote .agentinel.json");
|
|
1338
1382
|
}
|
|
1339
|
-
function wireClaudeCodeHook(repoRoot, command) {
|
|
1383
|
+
function wireClaudeCodeHook(repoRoot, command, messages) {
|
|
1340
1384
|
const dir = join6(repoRoot, ".claude");
|
|
1341
1385
|
const path = join6(dir, "settings.json");
|
|
1342
1386
|
let settings = {};
|
|
@@ -1347,14 +1391,14 @@ function wireClaudeCodeHook(repoRoot, command) {
|
|
|
1347
1391
|
settings = parsed;
|
|
1348
1392
|
}
|
|
1349
1393
|
} catch {
|
|
1350
|
-
|
|
1394
|
+
messages.push(".claude/settings.json is not valid JSON, skipping the Claude Code hook");
|
|
1351
1395
|
return;
|
|
1352
1396
|
}
|
|
1353
1397
|
}
|
|
1354
1398
|
const hooks = asRecord2(settings.hooks) ?? {};
|
|
1355
1399
|
const preToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
1356
1400
|
if (alreadyRegistered(preToolUse)) {
|
|
1357
|
-
|
|
1401
|
+
messages.push("Claude Code hook already registered, left alone");
|
|
1358
1402
|
return;
|
|
1359
1403
|
}
|
|
1360
1404
|
preToolUse.push({
|
|
@@ -1365,7 +1409,7 @@ function wireClaudeCodeHook(repoRoot, command) {
|
|
|
1365
1409
|
settings.hooks = hooks;
|
|
1366
1410
|
mkdirSync2(dir, { recursive: true });
|
|
1367
1411
|
writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
1368
|
-
|
|
1412
|
+
messages.push("registered the Claude Code PreToolUse hook in .claude/settings.json");
|
|
1369
1413
|
}
|
|
1370
1414
|
function agentHookCommand(repoRoot) {
|
|
1371
1415
|
if (isWindows()) {
|
|
@@ -1373,31 +1417,31 @@ function agentHookCommand(repoRoot) {
|
|
|
1373
1417
|
}
|
|
1374
1418
|
return hasLocalInstall(repoRoot) ? '"$(git rev-parse --show-toplevel)"/node_modules/.bin/asen' : "npx agentinel";
|
|
1375
1419
|
}
|
|
1376
|
-
function wireOtherAgents(repoRoot, command) {
|
|
1420
|
+
function wireOtherAgents(repoRoot, command, messages) {
|
|
1377
1421
|
if (uses(repoRoot, ".codex")) {
|
|
1378
|
-
wireCodexHook(repoRoot, command);
|
|
1422
|
+
wireCodexHook(repoRoot, command, messages);
|
|
1379
1423
|
}
|
|
1380
1424
|
if (uses(repoRoot, ".copilot") || existsSync5(join6(repoRoot, ".github", "hooks"))) {
|
|
1381
|
-
wireCopilotHook(repoRoot, command);
|
|
1425
|
+
wireCopilotHook(repoRoot, command, messages);
|
|
1382
1426
|
}
|
|
1383
1427
|
if (uses(repoRoot, ".gemini")) {
|
|
1384
|
-
wireGeminiHook(repoRoot, command);
|
|
1428
|
+
wireGeminiHook(repoRoot, command, messages);
|
|
1385
1429
|
}
|
|
1386
1430
|
}
|
|
1387
1431
|
function uses(repoRoot, dir) {
|
|
1388
1432
|
return existsSync5(join6(repoRoot, dir)) || existsSync5(join6(homedir2(), dir));
|
|
1389
1433
|
}
|
|
1390
|
-
function wireCodexHook(repoRoot, command) {
|
|
1434
|
+
function wireCodexHook(repoRoot, command, messages) {
|
|
1391
1435
|
const path = join6(repoRoot, ".codex", "hooks.json");
|
|
1392
1436
|
const file = readJson(path);
|
|
1393
1437
|
if (file === null) {
|
|
1394
|
-
|
|
1438
|
+
messages.push(".codex/hooks.json is not valid JSON, skipping the Codex hook");
|
|
1395
1439
|
return;
|
|
1396
1440
|
}
|
|
1397
1441
|
const hooks = asRecord2(file.hooks) ?? {};
|
|
1398
1442
|
const preToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
1399
1443
|
if (registers(preToolUse, "codex")) {
|
|
1400
|
-
|
|
1444
|
+
messages.push("Codex hook already registered, left alone");
|
|
1401
1445
|
return;
|
|
1402
1446
|
}
|
|
1403
1447
|
preToolUse.push({
|
|
@@ -1407,19 +1451,19 @@ function wireCodexHook(repoRoot, command) {
|
|
|
1407
1451
|
hooks.PreToolUse = preToolUse;
|
|
1408
1452
|
file.hooks = hooks;
|
|
1409
1453
|
writeJson(path, file);
|
|
1410
|
-
|
|
1454
|
+
messages.push("registered the Codex PreToolUse hook in .codex/hooks.json");
|
|
1411
1455
|
}
|
|
1412
|
-
function wireCopilotHook(repoRoot, command) {
|
|
1456
|
+
function wireCopilotHook(repoRoot, command, messages) {
|
|
1413
1457
|
const path = join6(repoRoot, ".github", "hooks", "agentinel.json");
|
|
1414
1458
|
const file = readJson(path);
|
|
1415
1459
|
if (file === null) {
|
|
1416
|
-
|
|
1460
|
+
messages.push(".github/hooks/agentinel.json is not valid JSON, skipping the Copilot hook");
|
|
1417
1461
|
return;
|
|
1418
1462
|
}
|
|
1419
1463
|
const hooks = asRecord2(file.hooks) ?? {};
|
|
1420
1464
|
const preToolUse = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
|
|
1421
1465
|
if (registers(preToolUse, "copilot")) {
|
|
1422
|
-
|
|
1466
|
+
messages.push("Copilot hook already registered, left alone");
|
|
1423
1467
|
return;
|
|
1424
1468
|
}
|
|
1425
1469
|
preToolUse.push({ type: "command", matcher: "bash", bash: `${command} hook copilot` });
|
|
@@ -1427,19 +1471,19 @@ function wireCopilotHook(repoRoot, command) {
|
|
|
1427
1471
|
file.version = 1;
|
|
1428
1472
|
file.hooks = hooks;
|
|
1429
1473
|
writeJson(path, file);
|
|
1430
|
-
|
|
1474
|
+
messages.push("registered the Copilot preToolUse hook in .github/hooks/agentinel.json");
|
|
1431
1475
|
}
|
|
1432
|
-
function wireGeminiHook(repoRoot, command) {
|
|
1476
|
+
function wireGeminiHook(repoRoot, command, messages) {
|
|
1433
1477
|
const path = join6(repoRoot, ".gemini", "settings.json");
|
|
1434
1478
|
const file = readJson(path);
|
|
1435
1479
|
if (file === null) {
|
|
1436
|
-
|
|
1480
|
+
messages.push(".gemini/settings.json is not valid JSON, skipping the Gemini hook");
|
|
1437
1481
|
return;
|
|
1438
1482
|
}
|
|
1439
1483
|
const hooks = asRecord2(file.hooks) ?? {};
|
|
1440
1484
|
const beforeTool = Array.isArray(hooks.BeforeTool) ? hooks.BeforeTool : [];
|
|
1441
1485
|
if (registers(beforeTool, "gemini")) {
|
|
1442
|
-
|
|
1486
|
+
messages.push("Gemini hook already registered, left alone");
|
|
1443
1487
|
return;
|
|
1444
1488
|
}
|
|
1445
1489
|
beforeTool.push({
|
|
@@ -1449,7 +1493,7 @@ function wireGeminiHook(repoRoot, command) {
|
|
|
1449
1493
|
hooks.BeforeTool = beforeTool;
|
|
1450
1494
|
file.hooks = hooks;
|
|
1451
1495
|
writeJson(path, file);
|
|
1452
|
-
|
|
1496
|
+
messages.push("registered the Gemini BeforeTool hook in .gemini/settings.json");
|
|
1453
1497
|
}
|
|
1454
1498
|
function readJson(path) {
|
|
1455
1499
|
if (!existsSync5(path)) {
|
|
@@ -1490,9 +1534,9 @@ function hooksDirectory(repoRoot) {
|
|
|
1490
1534
|
}
|
|
1491
1535
|
return join6(repoRoot, ".git", "hooks");
|
|
1492
1536
|
}
|
|
1493
|
-
function wirePreCommitHook(repoRoot, command) {
|
|
1537
|
+
function wirePreCommitHook(repoRoot, command, messages) {
|
|
1494
1538
|
if (!existsSync5(join6(repoRoot, ".git"))) {
|
|
1495
|
-
|
|
1539
|
+
messages.push("not a git repo, skipping the pre-commit hook");
|
|
1496
1540
|
return;
|
|
1497
1541
|
}
|
|
1498
1542
|
const dir = hooksDirectory(repoRoot);
|
|
@@ -1504,18 +1548,18 @@ ${command} hook pre-commit
|
|
|
1504
1548
|
if (existsSync5(path)) {
|
|
1505
1549
|
const existing = readFileSync6(path, "utf8");
|
|
1506
1550
|
if (existing.includes(HOOK_MARKER)) {
|
|
1507
|
-
|
|
1551
|
+
messages.push("pre-commit hook already installed, left alone");
|
|
1508
1552
|
return;
|
|
1509
1553
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1554
|
+
messages.push("a pre-commit hook already exists, not overwriting it");
|
|
1555
|
+
messages.push(`add this line to ${path}:
|
|
1512
1556
|
${command} hook pre-commit`);
|
|
1513
1557
|
return;
|
|
1514
1558
|
}
|
|
1515
1559
|
mkdirSync2(dir, { recursive: true });
|
|
1516
1560
|
writeFileSync3(path, script, "utf8");
|
|
1517
1561
|
chmodSync2(path, 493);
|
|
1518
|
-
|
|
1562
|
+
messages.push(`installed the git pre-commit hook in ${dir}`);
|
|
1519
1563
|
}
|
|
1520
1564
|
function alreadyRegistered(preToolUse) {
|
|
1521
1565
|
return JSON.stringify(preToolUse).includes(`${HOOK_SUBCOMMAND}`);
|