proof-of-commitment 1.21.1 → 1.22.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.
Files changed (2) hide show
  1. package/index.js +205 -83
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Usage: npx proof-of-commitment [packages...] [options]
6
6
  */
7
7
 
8
- const API = 'https://poc-backend.amdal-dev.workers.dev/api/audit';
8
+ const API = process.env.COMMIT_API_URL || 'https://poc-backend.amdal-dev.workers.dev/api/audit';
9
9
  const KEYS_API = 'https://poc-backend.amdal-dev.workers.dev/api/keys';
10
10
  const WATCHLIST_API = 'https://poc-backend.amdal-dev.workers.dev/api/watchlist';
11
11
  const WEB = 'https://getcommit.dev/audit';
@@ -506,9 +506,11 @@ ${clr(c.bold, 'Reports:')}
506
506
  Saves audit-report.html to cwd + prints Markdown for GitHub issues
507
507
 
508
508
  ${clr(c.bold, 'IDE Hooks:')}
509
- poc hook Install Cursor beforeShellExecution hook (blocks CRITICAL packages)
510
- poc hook --global Install for all Cursor projects (~/.cursor/hooks.json)
511
- poc hook --uninstall Remove the hook
509
+ poc hook Install supply chain gate for Cursor + Claude Code (blocks CRITICAL packages)
510
+ poc hook --cursor Install only the Cursor beforeShellExecution hook
511
+ poc hook --claude-code Install only the Claude Code PreToolUse hook
512
+ poc hook --global Install for the current user (~/.cursor + ~/.claude)
513
+ poc hook --uninstall Remove the hook from both Cursor and Claude Code
512
514
 
513
515
  ${clr(c.bold, 'Account:')}
514
516
  poc login [key] Save and validate your API key (interactive or direct)
@@ -1132,10 +1134,18 @@ async function cmdLogout() {
1132
1134
  }
1133
1135
 
1134
1136
  /**
1135
- * poc hook [--cursor] [--global] [--uninstall]
1136
- * Install a beforeShellExecution hook for Cursor that scores packages before install.
1137
- * Writes a standalone Node.js hook script to ~/.commit/cursor-hook.js and
1138
- * configures .cursor/hooks.json (project) or ~/.cursor/hooks.json (--global).
1137
+ * poc hook [--cursor] [--claude-code] [--global] [--uninstall]
1138
+ * Install a supply chain gate hook for Cursor (beforeShellExecution) and/or
1139
+ * Claude Code (PreToolUse) that scores packages before install.
1140
+ *
1141
+ * Writes a single hook script to ~/.commit/cursor-hook.js (the filename is
1142
+ * kept for backward compatibility with v1.21.x installs; the same script
1143
+ * now auto-detects whether stdin is in Cursor or Claude Code format and
1144
+ * emits the matching response shape).
1145
+ *
1146
+ * Default installs both Cursor + Claude Code configs. Pass --cursor or
1147
+ * --claude-code to install only one. --global writes to ~/.cursor and
1148
+ * ~/.claude; default writes to ./.cursor and ./.claude.
1139
1149
  */
1140
1150
  async function cmdHook(args) {
1141
1151
  const os = await import('os');
@@ -1144,18 +1154,27 @@ async function cmdHook(args) {
1144
1154
 
1145
1155
  const isGlobal = args.includes('--global') || args.includes('-g');
1146
1156
  const uninstall = args.includes('--uninstall') || args.includes('--remove');
1157
+ const onlyCursor = args.includes('--cursor');
1158
+ const onlyClaude = args.includes('--claude-code') || args.includes('--claude');
1159
+ // Default (no client flag) = install both. --cursor and --claude-code narrow scope.
1160
+ const installCursor = !onlyClaude;
1161
+ const installClaude = !onlyCursor;
1147
1162
 
1148
1163
  // ── Hook script (plain Node.js, no external deps) ─────────────────────
1164
+ // Single script serves BOTH Cursor (beforeShellExecution) and Claude Code
1165
+ // (PreToolUse). It auto-detects which client called it by inspecting the
1166
+ // stdin JSON and emits the matching response format.
1149
1167
  const hookScript = `#!/usr/bin/env node
1150
1168
  /**
1151
- * Commit supply chain hook for Cursor (auto-generated by \`poc hook\`)
1169
+ * Commit supply chain hook for Cursor + Claude Code (auto-generated by \`poc hook\`)
1152
1170
  * Intercepts npm/pip/cargo/go install commands and scores packages
1153
1171
  * against getcommit.dev before they run.
1154
1172
  *
1155
1173
  * CRITICAL packages are blocked. HIGH packages trigger confirmation.
1174
+ * Auto-detects Cursor vs Claude Code stdin format and replies in kind.
1156
1175
  * Docs: https://getcommit.dev/docs/cursor-hook
1157
1176
  */
1158
- const API = 'https://poc-backend.amdal-dev.workers.dev/api/audit';
1177
+ const API = process.env.COMMIT_API_URL || 'https://poc-backend.amdal-dev.workers.dev/api/audit';
1159
1178
  const fs = require('fs');
1160
1179
  const path = require('path');
1161
1180
 
@@ -1169,7 +1188,7 @@ function readKey() {
1169
1188
  }
1170
1189
 
1171
1190
  function parseInstall(cmd) {
1172
- const t = cmd.trim();
1191
+ const t = (cmd || '').trim();
1173
1192
  let m;
1174
1193
  // npm / pnpm / yarn
1175
1194
  m = t.match(/^(?:npm\\s+(?:i|install|add)|pnpm\\s+(?:i|install|add)|yarn\\s+add)\\s+(.+)/);
@@ -1186,11 +1205,51 @@ function parseInstall(cmd) {
1186
1205
  return null;
1187
1206
  }
1188
1207
 
1208
+ // Detect which client called us and how to extract the command.
1209
+ // Cursor: stdin = { command: 'npm install ...', workingDirectory? }
1210
+ // Claude Code: stdin = { tool_name: 'Bash', tool_input: { command: '...' }, hook_event_name: 'PreToolUse', ... }
1211
+ function detectClient(input) {
1212
+ if (input && input.tool_input && typeof input.tool_input.command === 'string') {
1213
+ return { client: 'claude-code', cmd: input.tool_input.command };
1214
+ }
1215
+ if (input && typeof input.command === 'string') {
1216
+ return { client: 'cursor', cmd: input.command };
1217
+ }
1218
+ return { client: 'cursor', cmd: '' };
1219
+ }
1220
+
1221
+ // Emit the appropriate "no decision" / "allow" output for the detected client.
1222
+ function emitAllow(client) {
1223
+ if (client === 'claude-code') {
1224
+ // No stdout + exit 0 = defer to normal permission flow.
1225
+ return;
1226
+ }
1227
+ process.stdout.write(JSON.stringify({ permission: 'allow' }));
1228
+ }
1229
+
1230
+ // Emit deny / ask in the matching format.
1231
+ function emit(client, decision, userMsg, agentMsg) {
1232
+ if (client === 'claude-code') {
1233
+ process.stdout.write(JSON.stringify({
1234
+ hookSpecificOutput: {
1235
+ hookEventName: 'PreToolUse',
1236
+ permissionDecision: decision,
1237
+ permissionDecisionReason: userMsg,
1238
+ },
1239
+ }));
1240
+ return;
1241
+ }
1242
+ const body = { permission: decision, user_message: userMsg };
1243
+ if (agentMsg) body.agent_message = agentMsg;
1244
+ process.stdout.write(JSON.stringify(body));
1245
+ }
1246
+
1189
1247
  async function main() {
1190
1248
  let input;
1191
- try { input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8')); } catch { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
1192
- const parsed = parseInstall(input.command || '');
1193
- if (!parsed || parsed.pkgs.length === 0) { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
1249
+ try { input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8')); } catch { emitAllow('cursor'); return; }
1250
+ const { client, cmd } = detectClient(input);
1251
+ const parsed = parseInstall(cmd);
1252
+ if (!parsed || parsed.pkgs.length === 0) { emitAllow(client); return; }
1194
1253
 
1195
1254
  const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
1196
1255
  const key = readKey();
@@ -1201,7 +1260,7 @@ async function main() {
1201
1260
  const timer = setTimeout(() => ctrl.abort(), 4000);
1202
1261
  const res = await fetch(API, { method: 'POST', headers, body: JSON.stringify({ packages: parsed.pkgs, ecosystem: parsed.eco }), signal: ctrl.signal });
1203
1262
  clearTimeout(timer);
1204
- if (!res.ok && res.status !== 429) { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
1263
+ if (!res.ok && res.status !== 429) { emitAllow(client); return; }
1205
1264
  const data = await res.json();
1206
1265
  const results = data.results || data.packages_already_scored || [];
1207
1266
 
@@ -1209,12 +1268,12 @@ async function main() {
1209
1268
  const high = results.filter(r => (r.riskFlags || []).some(f => f.startsWith('HIGH')));
1210
1269
  const url = 'https://getcommit.dev/audit?packages=' + parsed.pkgs.join(',') + '&ecosystem=' + parsed.eco;
1211
1270
 
1212
- // v1.21.1: detect rate-limit hit and surface signup CTA + unscored-package warning.
1213
- // Without this, hook silently allowed unscored packages on 429 (false sense of security)
1214
- // and the conversion driver (signup URL in 429 body) never reached the user.
1271
+ // Detect rate-limit hit and surface signup CTA + unscored-package warning.
1272
+ // Without this, hook would silently allow unscored packages on 429 (false sense of security).
1215
1273
  const rateLimited = res.status === 429;
1216
- // Force cursor-hook attribution backend default is audit-cli-429 which misattributes.
1217
- const rlUrl = rateLimited ? 'https://getcommit.dev/get-started?ref=cursor-hook-429&utm_source=cli' : '';
1274
+ // Per-client attribution so /api/keys/create source counters split traffic cleanly.
1275
+ const refTag = client === 'claude-code' ? 'claude-code-hook-429' : 'cursor-hook-429';
1276
+ const rlUrl = rateLimited ? 'https://getcommit.dev/get-started?ref=' + refTag + '&utm_source=cli' : '';
1218
1277
  const unscored = rateLimited ? Math.max(0, parsed.pkgs.length - results.length) : 0;
1219
1278
  const rlNote = rateLimited
1220
1279
  ? '\\n\\n\\u26A0 Commit free limit reached'
@@ -1224,19 +1283,17 @@ async function main() {
1224
1283
 
1225
1284
  if (critical.length > 0) {
1226
1285
  const lines = critical.map(r => ' \\u{1F534} ' + r.name + ' (score ' + (r.score||'?') + ') \\u2014 ' + (r.riskFlags||[]).slice(0,1).join(', '));
1227
- process.stdout.write(JSON.stringify({
1228
- permission: 'deny',
1229
- user_message: '\\u{1F534} Commit blocked: ' + critical.map(r=>r.name).join(', ') + ' flagged CRITICAL\\n\\n' + lines.join('\\n') + '\\n\\n\\u2192 ' + url + rlNote,
1230
- agent_message: 'Package install blocked by Commit. CRITICAL = sole publisher + high downloads (attack surface of axios/node-ipc incidents). ' + critical.map(r=>r.name).join(', ') + '. Report: ' + url,
1231
- }));
1286
+ emit(client, 'deny',
1287
+ '\\u{1F534} Commit blocked: ' + critical.map(r=>r.name).join(', ') + ' flagged CRITICAL\\n\\n' + lines.join('\\n') + '\\n\\n\\u2192 ' + url + rlNote,
1288
+ 'Package install blocked by Commit. CRITICAL = sole publisher + high downloads (attack surface of axios/node-ipc incidents). ' + critical.map(r=>r.name).join(', ') + '. Report: ' + url
1289
+ );
1232
1290
  return;
1233
1291
  }
1234
1292
  if (high.length > 0) {
1235
1293
  const lines = high.map(r => ' \\u{1F7E1} ' + r.name + ' (score ' + (r.score||'?') + ') \\u2014 ' + (r.riskFlags||[]).slice(0,1).join(', '));
1236
- process.stdout.write(JSON.stringify({
1237
- permission: 'ask',
1238
- user_message: '\\u{1F7E1} Commit: ' + high.map(r=>r.name).join(', ') + ' scored HIGH risk\\n\\n' + lines.join('\\n') + '\\n\\nProceed? \\u2192 ' + url + rlNote,
1239
- }));
1294
+ emit(client, 'ask',
1295
+ '\\u{1F7E1} Commit: ' + high.map(r=>r.name).join(', ') + ' scored HIGH risk\\n\\n' + lines.join('\\n') + '\\n\\nProceed? \\u2192 ' + url + rlNote
1296
+ );
1240
1297
  return;
1241
1298
  }
1242
1299
  // Rate-limited with no critical/high in the scored partial: still alert user.
@@ -1246,14 +1303,11 @@ async function main() {
1246
1303
  const head = unscored > 0
1247
1304
  ? '\\u26A0 Commit free limit reached \\u2014 ' + unscored + ' of ' + parsed.pkgs.length + ' package(s) NOT audited'
1248
1305
  : '\\u2713 ' + parsed.pkgs.join(', ') + ' look clean (free-tier audit)';
1249
- process.stdout.write(JSON.stringify({
1250
- permission: 'ask',
1251
- user_message: head + '\\n\\nFree API key (200/day, no card, 30s):\\n ' + rlUrl + '\\n\\nProceed anyway?',
1252
- }));
1306
+ emit(client, 'ask', head + '\\n\\nFree API key (200/day, no card, 30s):\\n ' + rlUrl + '\\n\\nProceed anyway?');
1253
1307
  return;
1254
1308
  }
1255
- process.stdout.write(JSON.stringify({ permission: 'allow' }));
1256
- } catch { process.stdout.write(JSON.stringify({ permission: 'allow' })); }
1309
+ emitAllow(client);
1310
+ } catch { emitAllow(client); }
1257
1311
  }
1258
1312
  main();
1259
1313
  `;
@@ -1261,35 +1315,119 @@ main();
1261
1315
  const commitDir = path.join(os.homedir(), '.commit');
1262
1316
  const hookPath = path.join(commitDir, 'cursor-hook.js');
1263
1317
 
1318
+ // ── Cursor config helpers ─────────────────────────────────────────────
1319
+ function cursorHooksFile(global) {
1320
+ const dir = global ? path.join(os.homedir(), '.cursor') : path.join(process.cwd(), '.cursor');
1321
+ return { dir, file: path.join(dir, 'hooks.json') };
1322
+ }
1323
+
1324
+ function installCursorHook(global) {
1325
+ const { dir, file } = cursorHooksFile(global);
1326
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1327
+ let cfg = { version: 1, hooks: {} };
1328
+ if (fs.existsSync(file)) {
1329
+ try { cfg = JSON.parse(fs.readFileSync(file, 'utf-8')); } catch {}
1330
+ }
1331
+ if (!cfg.hooks) cfg.hooks = {};
1332
+ if (!cfg.hooks.beforeShellExecution) cfg.hooks.beforeShellExecution = [];
1333
+ const existing = cfg.hooks.beforeShellExecution.some(h => h.command?.includes('cursor-hook.js'));
1334
+ if (!existing) cfg.hooks.beforeShellExecution.push({ command: `node ${hookPath}` });
1335
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n');
1336
+ return file;
1337
+ }
1338
+
1339
+ function uninstallCursorHook(global) {
1340
+ const { file } = cursorHooksFile(global);
1341
+ if (!fs.existsSync(file)) return false;
1342
+ try {
1343
+ const cfg = JSON.parse(fs.readFileSync(file, 'utf-8'));
1344
+ const hooks = cfg.hooks?.beforeShellExecution || [];
1345
+ const filtered = hooks.filter(h => !h.command?.includes('cursor-hook.js'));
1346
+ if (filtered.length === hooks.length) return false;
1347
+ cfg.hooks.beforeShellExecution = filtered;
1348
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n');
1349
+ return true;
1350
+ } catch { return false; }
1351
+ }
1352
+
1353
+ // ── Claude Code config helpers ────────────────────────────────────────
1354
+ function claudeSettingsFile(global) {
1355
+ const dir = global ? path.join(os.homedir(), '.claude') : path.join(process.cwd(), '.claude');
1356
+ return { dir, file: path.join(dir, 'settings.json') };
1357
+ }
1358
+
1359
+ function installClaudeHook(global) {
1360
+ const { dir, file } = claudeSettingsFile(global);
1361
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1362
+ let cfg = {};
1363
+ if (fs.existsSync(file)) {
1364
+ try { cfg = JSON.parse(fs.readFileSync(file, 'utf-8')); } catch {}
1365
+ }
1366
+ if (!cfg.hooks) cfg.hooks = {};
1367
+ if (!Array.isArray(cfg.hooks.PreToolUse)) cfg.hooks.PreToolUse = [];
1368
+
1369
+ // Find or create the Bash matcher entry.
1370
+ let bashEntry = cfg.hooks.PreToolUse.find(e => e.matcher === 'Bash');
1371
+ if (!bashEntry) {
1372
+ bashEntry = { matcher: 'Bash', hooks: [] };
1373
+ cfg.hooks.PreToolUse.push(bashEntry);
1374
+ }
1375
+ if (!Array.isArray(bashEntry.hooks)) bashEntry.hooks = [];
1376
+ const existing = bashEntry.hooks.some(h => h.command?.includes('cursor-hook.js'));
1377
+ if (!existing) {
1378
+ bashEntry.hooks.push({
1379
+ type: 'command',
1380
+ command: `node ${hookPath}`,
1381
+ timeout: 10,
1382
+ });
1383
+ }
1384
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n');
1385
+ return file;
1386
+ }
1387
+
1388
+ function uninstallClaudeHook(global) {
1389
+ const { file } = claudeSettingsFile(global);
1390
+ if (!fs.existsSync(file)) return false;
1391
+ try {
1392
+ const cfg = JSON.parse(fs.readFileSync(file, 'utf-8'));
1393
+ const pre = cfg.hooks?.PreToolUse || [];
1394
+ let changed = false;
1395
+ for (const entry of pre) {
1396
+ if (!Array.isArray(entry.hooks)) continue;
1397
+ const before = entry.hooks.length;
1398
+ entry.hooks = entry.hooks.filter(h => !h.command?.includes('cursor-hook.js'));
1399
+ if (entry.hooks.length !== before) changed = true;
1400
+ }
1401
+ // Drop empty Bash entries so we don't leave a dangling matcher.
1402
+ cfg.hooks.PreToolUse = pre.filter(e => !(e.matcher === 'Bash' && (!e.hooks || e.hooks.length === 0)));
1403
+ if (!changed) return false;
1404
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n');
1405
+ return true;
1406
+ } catch { return false; }
1407
+ }
1408
+
1264
1409
  // ── Uninstall ──────────────────────────────────────────────────────────
1265
1410
  if (uninstall) {
1266
1411
  let removed = false;
1267
- if (fs.existsSync(hookPath)) {
1268
- fs.unlinkSync(hookPath);
1269
- removed = true;
1412
+ if (fs.existsSync(hookPath)) { fs.unlinkSync(hookPath); removed = true; }
1413
+
1414
+ // Cursor: clean both project and global, regardless of flags — be thorough.
1415
+ for (const g of [false, true]) {
1416
+ if (uninstallCursorHook(g)) {
1417
+ removed = true;
1418
+ console.log(clr(c.dim, ` Updated: ${cursorHooksFile(g).file}`));
1419
+ }
1270
1420
  }
1271
- // Remove from hooks.json
1272
- for (const hooksDir of [
1273
- path.join(process.cwd(), '.cursor'),
1274
- path.join(os.homedir(), '.cursor'),
1275
- ]) {
1276
- const hooksFile = path.join(hooksDir, 'hooks.json');
1277
- if (fs.existsSync(hooksFile)) {
1278
- try {
1279
- const cfg = JSON.parse(fs.readFileSync(hooksFile, 'utf-8'));
1280
- const hooks = cfg.hooks?.beforeShellExecution || [];
1281
- const filtered = hooks.filter(h => !h.command?.includes('cursor-hook.js'));
1282
- if (filtered.length !== hooks.length) {
1283
- cfg.hooks.beforeShellExecution = filtered;
1284
- fs.writeFileSync(hooksFile, JSON.stringify(cfg, null, 2) + '\n');
1285
- removed = true;
1286
- console.log(clr(c.dim, ` Updated: ${hooksFile}`));
1287
- }
1288
- } catch {}
1421
+ // Claude Code: same.
1422
+ for (const g of [false, true]) {
1423
+ if (uninstallClaudeHook(g)) {
1424
+ removed = true;
1425
+ console.log(clr(c.dim, ` Updated: ${claudeSettingsFile(g).file}`));
1289
1426
  }
1290
1427
  }
1428
+
1291
1429
  if (removed) {
1292
- console.log(clr(c.green, '\n ✓ Cursor hook uninstalled.'));
1430
+ console.log(clr(c.green, '\n ✓ Commit hook uninstalled (Cursor + Claude Code).'));
1293
1431
  } else {
1294
1432
  console.log(clr(c.dim, '\n No hook found to remove.'));
1295
1433
  }
@@ -1302,49 +1440,33 @@ main();
1302
1440
  if (!fs.existsSync(commitDir)) fs.mkdirSync(commitDir, { recursive: true });
1303
1441
  fs.writeFileSync(hookPath, hookScript, { mode: 0o755 });
1304
1442
 
1305
- // 2. Configure hooks.json
1306
- const hooksDir = isGlobal
1307
- ? path.join(os.homedir(), '.cursor')
1308
- : path.join(process.cwd(), '.cursor');
1309
- if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
1310
-
1311
- const hooksFile = path.join(hooksDir, 'hooks.json');
1312
- let cfg = { version: 1, hooks: {} };
1313
- if (fs.existsSync(hooksFile)) {
1314
- try { cfg = JSON.parse(fs.readFileSync(hooksFile, 'utf-8')); } catch {}
1315
- }
1316
- if (!cfg.hooks) cfg.hooks = {};
1317
- if (!cfg.hooks.beforeShellExecution) cfg.hooks.beforeShellExecution = [];
1318
-
1319
- // Avoid duplicates
1320
- const existing = cfg.hooks.beforeShellExecution.some(h => h.command?.includes('cursor-hook.js'));
1321
- if (!existing) {
1322
- cfg.hooks.beforeShellExecution.push({
1323
- command: `node ${hookPath}`,
1324
- });
1325
- }
1326
- fs.writeFileSync(hooksFile, JSON.stringify(cfg, null, 2) + '\n');
1443
+ const writtenFiles = [];
1444
+ if (installCursor) writtenFiles.push({ client: 'Cursor', file: installCursorHook(isGlobal) });
1445
+ if (installClaude) writtenFiles.push({ client: 'Claude Code', file: installClaudeHook(isGlobal) });
1327
1446
 
1328
1447
  // 3. Report
1329
- console.log(clr(c.green, '\n ✓ Cursor supply chain hook installed'));
1448
+ const clientList = writtenFiles.map(w => w.client).join(' + ');
1449
+ console.log(clr(c.green, `\n ✓ Commit supply chain hook installed (${clientList})`));
1330
1450
  console.log();
1331
1451
  console.log(clr(c.bold, ' What happens now:'));
1332
1452
  console.log(clr(c.dim, ' Every ') + clr(c.cyan, 'npm install') + clr(c.dim, ', ') +
1333
1453
  clr(c.cyan, 'pip install') + clr(c.dim, ', ') + clr(c.cyan, 'cargo add') + clr(c.dim, ', and ') +
1334
- clr(c.cyan, 'go get') + clr(c.dim, ' in Cursor'));
1454
+ clr(c.cyan, 'go get') + clr(c.dim, ` in ${clientList}`));
1335
1455
  console.log(clr(c.dim, ' is scored against Commit before it runs.'));
1336
1456
  console.log(clr(c.dim, ' CRITICAL packages are blocked. HIGH packages ask for confirmation.'));
1337
1457
  console.log();
1338
1458
  console.log(clr(c.bold, ' Files:'));
1339
1459
  console.log(clr(c.dim, ` Hook script: ${hookPath}`));
1340
- console.log(clr(c.dim, ` Config: ${hooksFile}`));
1460
+ for (const w of writtenFiles) {
1461
+ console.log(clr(c.dim, ` ${w.client.padEnd(11)} ${w.file}`));
1462
+ }
1341
1463
  console.log();
1342
1464
 
1343
1465
  const key = await readApiKey();
1344
1466
  if (!key) {
1345
1467
  console.log(clr(c.yellow, ' ⚠ No API key found.') + clr(c.dim, ' Anonymous limit: 15 audits/day.'));
1346
1468
  console.log(clr(c.dim, ' Get a free key (200/day): ') + clr(c.cyan, 'poc login'));
1347
- console.log(clr(c.dim, ' Or: ') + clr(c.cyan, 'https://getcommit.dev/get-started?ref=cursor-hook&utm_source=cli'));
1469
+ console.log(clr(c.dim, ' Or: ') + clr(c.cyan, 'https://getcommit.dev/get-started?ref=poc-hook&utm_source=cli'));
1348
1470
  console.log();
1349
1471
  }
1350
1472
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.21.1",
3
+ "version": "1.22.0",
4
4
  "mcpName": "io.github.piiiico/proof-of-commitment",
5
5
  "description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
6
6
  "type": "module",