fullcourtdefense-cli 1.5.3 → 1.6.1

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.
@@ -0,0 +1,10 @@
1
+ import { BotGuardConfig } from '../config';
2
+ import { ProtectAllArgs } from './mcpGateway';
3
+ export interface AutoProtectArgs extends ProtectAllArgs {
4
+ install?: string;
5
+ uninstall?: string;
6
+ status?: string;
7
+ intervalMinutes?: string;
8
+ onLogon?: string;
9
+ }
10
+ export declare function autoProtectCommand(args: AutoProtectArgs, config: BotGuardConfig): Promise<void>;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.autoProtectCommand = autoProtectCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ const mcpGateway_1 = require("./mcpGateway");
42
+ const COLOR = {
43
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
44
+ red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', cyan: '\x1b[36m', gray: '\x1b[90m',
45
+ };
46
+ const TASK_NAME = 'FullCourtDefense Auto-Protect';
47
+ const CRON_MARKER = '# FCD_AUTO_PROTECT';
48
+ function cliEntry() {
49
+ return path.resolve(process.argv[1] || path.join(__dirname, '..', 'index.js'));
50
+ }
51
+ function launcherDir() {
52
+ const base = process.platform === 'win32'
53
+ ? (process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'))
54
+ : path.join(os.homedir(), '.fullcourtdefense');
55
+ return path.join(base, 'FullCourtDefense');
56
+ }
57
+ /** On Windows, write a hidden VBS launcher so the scheduled run doesn't flash a console window. */
58
+ function ensureWindowsLauncher() {
59
+ const dir = launcherDir();
60
+ fs.mkdirSync(dir, { recursive: true });
61
+ const vbs = path.join(dir, 'auto-protect.vbs');
62
+ const node = process.execPath;
63
+ const cli = cliEntry();
64
+ // WScript.Shell.Run with window style 0 = hidden, wait = false.
65
+ const inner = `"${node}" "${cli}" protect-all`;
66
+ const content = `CreateObject("WScript.Shell").Run "cmd /c ${inner.replace(/"/g, '""')}", 0, False\n`;
67
+ fs.writeFileSync(vbs, content, 'utf8');
68
+ return vbs;
69
+ }
70
+ function installWindows(intervalMinutes, onLogon) {
71
+ const vbs = ensureWindowsLauncher();
72
+ const tr = `wscript.exe "${vbs}"`;
73
+ const baseArgs = ['/Create', '/TN', TASK_NAME, '/TR', tr, '/F', '/RL', 'LIMITED'];
74
+ const schedArgs = onLogon
75
+ ? ['/SC', 'ONLOGON']
76
+ : ['/SC', 'MINUTE', '/MO', String(intervalMinutes)];
77
+ const result = (0, child_process_1.spawnSync)('schtasks', [...baseArgs, ...schedArgs], { stdio: 'inherit' });
78
+ return result.status === 0;
79
+ }
80
+ function uninstallWindows() {
81
+ const result = (0, child_process_1.spawnSync)('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'inherit' });
82
+ const vbs = path.join(launcherDir(), 'auto-protect.vbs');
83
+ try {
84
+ if (fs.existsSync(vbs))
85
+ fs.unlinkSync(vbs);
86
+ }
87
+ catch { /* ignore */ }
88
+ return result.status === 0;
89
+ }
90
+ function statusWindows() {
91
+ (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', TASK_NAME, '/V', '/FO', 'LIST'], { stdio: 'inherit' });
92
+ }
93
+ function readCrontab() {
94
+ const result = (0, child_process_1.spawnSync)('crontab', ['-l'], { encoding: 'utf8' });
95
+ return result.status === 0 ? (result.stdout || '') : '';
96
+ }
97
+ function writeCrontab(content) {
98
+ const result = (0, child_process_1.spawnSync)('crontab', ['-'], { input: content.endsWith('\n') ? content : `${content}\n`, encoding: 'utf8' });
99
+ return result.status === 0;
100
+ }
101
+ function installUnix(intervalMinutes, onLogon) {
102
+ const node = process.execPath;
103
+ const cli = cliEntry();
104
+ const command = `"${node}" "${cli}" protect-all >/dev/null 2>&1`;
105
+ const schedule = onLogon ? '@reboot' : `*/${Math.max(1, intervalMinutes)} * * * *`;
106
+ const line = `${schedule} ${command} ${CRON_MARKER}`;
107
+ const existing = readCrontab().split('\n').filter(l => l && !l.includes(CRON_MARKER));
108
+ return writeCrontab([...existing, line].join('\n'));
109
+ }
110
+ function uninstallUnix() {
111
+ const existing = readCrontab().split('\n').filter(l => l && !l.includes(CRON_MARKER));
112
+ return writeCrontab(existing.join('\n'));
113
+ }
114
+ async function autoProtectCommand(args, config) {
115
+ const intervalMinutes = Number(args.intervalMinutes) > 0 ? Math.floor(Number(args.intervalMinutes)) : 60;
116
+ const onLogon = args.onLogon === 'true';
117
+ if (args.uninstall === 'true') {
118
+ const ok = process.platform === 'win32' ? uninstallWindows() : uninstallUnix();
119
+ console.log(ok
120
+ ? `${COLOR.green}Removed the FullCourtDefense auto-protect schedule.${COLOR.reset}`
121
+ : `${COLOR.yellow}No auto-protect schedule found (or removal failed).${COLOR.reset}`);
122
+ return;
123
+ }
124
+ if (args.status === 'true') {
125
+ if (process.platform === 'win32')
126
+ statusWindows();
127
+ else
128
+ console.log(readCrontab().split('\n').filter(l => l.includes(CRON_MARKER)).join('\n') || 'No auto-protect cron entry found.');
129
+ return;
130
+ }
131
+ // Default action is install.
132
+ console.log(`\n${COLOR.bold}${COLOR.cyan}FullCourtDefense — schedule self-healing auto-protect${COLOR.reset}`);
133
+ console.log(`${COLOR.gray}Re-runs \`protect-all\` ${onLogon ? 'at every logon' : `every ${intervalMinutes} min`} so any server that loses the proxy is automatically re-wrapped.${COLOR.reset}\n`);
134
+ // Protect everything once right now, then register the recurring task.
135
+ await (0, mcpGateway_1.protectAllCommand)({ ...args, install: undefined, uninstall: undefined, status: undefined }, config);
136
+ const ok = process.platform === 'win32'
137
+ ? installWindows(intervalMinutes, onLogon)
138
+ : installUnix(intervalMinutes, onLogon);
139
+ console.log('');
140
+ if (ok) {
141
+ console.log(`${COLOR.green}${COLOR.bold}Auto-protect scheduled.${COLOR.reset}`);
142
+ console.log(`${COLOR.gray}Trigger:${COLOR.reset} ${onLogon ? 'at logon' : `every ${intervalMinutes} minutes`}`);
143
+ console.log(`${COLOR.gray}Task:${COLOR.reset} ${process.platform === 'win32' ? `Scheduled Task "${TASK_NAME}"` : 'crontab entry'}`);
144
+ console.log(`${COLOR.gray}Status:${COLOR.reset} fullcourtdefense auto-protect --status true`);
145
+ console.log(`${COLOR.gray}Remove:${COLOR.reset} fullcourtdefense auto-protect --uninstall true`);
146
+ }
147
+ else {
148
+ console.log(`${COLOR.red}Could not register the schedule.${COLOR.reset} On Windows you may need to run the terminal as Administrator, or use --on-logon true.`);
149
+ }
150
+ }
@@ -1,12 +1,16 @@
1
1
  import { BotGuardConfig } from '../config';
2
- import { InstallMcpGatewayArgs } from './mcpGateway';
3
- export interface InstallAllArgs extends InstallMcpGatewayArgs {
2
+ import { ProtectAllArgs } from './mcpGateway';
3
+ export interface InstallAllArgs extends ProtectAllArgs {
4
+ apiKey?: string;
5
+ cursorProject?: string;
6
+ upload?: string;
4
7
  hooks?: string;
5
8
  discover?: string;
9
+ autoProtect?: string;
10
+ intervalMinutes?: string;
6
11
  }
7
12
  /**
8
- * One-click install: MCP gateway into every supported AI client, optional
9
- * Cursor hooks, optional fleet upload. Hooks are opt-in because Action Policies
10
- * should be the default enforcement path for MCP tools.
13
+ * One-click install: discover/wrap every existing MCP server, install Cursor
14
+ * hooks for built-in Cursor actions, optional self-heal and fleet upload.
11
15
  */
12
16
  export declare function installAllCommand(args: InstallAllArgs, config: BotGuardConfig): Promise<void>;
@@ -5,10 +5,10 @@ const config_1 = require("../config");
5
5
  const discover_1 = require("./discover");
6
6
  const installCursorHook_1 = require("./installCursorHook");
7
7
  const mcpGateway_1 = require("./mcpGateway");
8
+ const autoProtect_1 = require("./autoProtect");
8
9
  /**
9
- * One-click install: MCP gateway into every supported AI client, optional
10
- * Cursor hooks, optional fleet upload. Hooks are opt-in because Action Policies
11
- * should be the default enforcement path for MCP tools.
10
+ * One-click install: discover/wrap every existing MCP server, install Cursor
11
+ * hooks for built-in Cursor actions, optional self-heal and fleet upload.
12
12
  */
13
13
  async function installAllCommand(args, config) {
14
14
  const creds = (0, config_1.requireCliSetup)(config, {
@@ -23,15 +23,17 @@ async function installAllCommand(args, config) {
23
23
  clients: args.clients || 'all',
24
24
  };
25
25
  console.log('\n\x1b[1m\x1b[36mFullCourtDefense — install all AI clients\x1b[0m');
26
- console.log('\x1b[2mMCP gateway + optional fleet upload. Cursor hooks are opt-in with --hooks true.\x1b[0m\n');
27
- await (0, mcpGateway_1.installMcpGatewayCommand)(gatewayArgs, config);
28
- if (args.hooks === 'true') {
26
+ console.log('\x1b[2mAuto-protect existing MCP servers + Cursor action hooks. No --mcp-command or folder path needed.\x1b[0m\n');
27
+ await (0, mcpGateway_1.protectAllCommand)(gatewayArgs, config);
28
+ if (args.hooks !== 'false') {
29
29
  console.log('\n\x1b[1mInstalling Cursor runtime hooks…\x1b[0m');
30
30
  const hookArgs = {
31
31
  shieldId: creds.shieldId,
32
32
  shieldKey: creds.shieldKey,
33
33
  apiUrl: creds.apiUrl,
34
34
  project: args.cursorProject,
35
+ events: 'prompt,shell,mcp,file,read',
36
+ failClosed: 'true',
35
37
  };
36
38
  try {
37
39
  await (0, installCursorHook_1.installCursorHookCommand)(hookArgs, config);
@@ -40,6 +42,13 @@ async function installAllCommand(args, config) {
40
42
  console.log(`Cursor hooks skipped: ${error instanceof Error ? error.message : String(error)}`);
41
43
  }
42
44
  }
45
+ if (args.autoProtect === 'true') {
46
+ console.log('\n\x1b[1mScheduling self-healing MCP protection…\x1b[0m');
47
+ await (0, autoProtect_1.autoProtectCommand)({
48
+ ...gatewayArgs,
49
+ intervalMinutes: args.intervalMinutes || '60',
50
+ }, config);
51
+ }
43
52
  if (args.discover === 'true' || args.upload === 'true') {
44
53
  console.log('\n\x1b[1mUploading desktop posture to AI Fleet…\x1b[0m');
45
54
  const discoverArgs = {
@@ -52,5 +61,5 @@ async function installAllCommand(args, config) {
52
61
  };
53
62
  await (0, discover_1.discoverCommand)(discoverArgs, config);
54
63
  }
55
- console.log('\n\x1b[32mDone.\x1b[0m Restart each AI client you use, then run \x1b[1mfullcourtdefense discover --surface mcp\x1b[0m to verify gateways.');
64
+ console.log('\n\x1b[32mDone.\x1b[0m Restart each AI client you use, then run \x1b[1mfullcourtdefense protect-all --dry-run true\x1b[0m to verify gateways.');
56
65
  }
@@ -109,8 +109,8 @@ async function installCursorHookCommand(args, config) {
109
109
  const projectScope = args.project === 'true';
110
110
  const shadow = args.shadow === 'true';
111
111
  const file = hooksJsonPath(projectScope);
112
- const requested = (args.events || 'prompt,shell,mcp')
113
- .split(',').map((e) => e.trim().toLowerCase()).filter(Boolean);
112
+ const requested = (args.events || 'prompt,shell,mcp,file,read')
113
+ .split(/[\s,]+/).map((e) => e.trim().toLowerCase()).filter(Boolean);
114
114
  const events = requested.filter((e) => EVENT_MAP[e]);
115
115
  if (events.length === 0) {
116
116
  console.error(`${COLOR.red}No valid events. Choose from: ${Object.keys(EVENT_MAP).join(', ')}${COLOR.reset}`);
@@ -1279,33 +1279,48 @@ async function protectAllCommand(args, config) {
1279
1279
  : 'No MCP client config files found on this machine. Configure an MCP server in any client, then re-run.');
1280
1280
  return;
1281
1281
  }
1282
+ console.log(`\x1b[2mAuto-searching every known MCP client on this machine — ${files.length} config file(s) found.\x1b[0m\n`);
1282
1283
  let totalWrapped = 0;
1283
1284
  let totalManaged = 0;
1284
1285
  let totalRemote = 0;
1286
+ let totalEmpty = 0;
1287
+ const presentClientKeys = new Set();
1285
1288
  for (const file of files) {
1289
+ presentClientKeys.add(file.clientKey);
1286
1290
  const agentClient = CLIENT_KEY_TO_AGENT[file.clientKey] || 'cursor';
1287
1291
  const isToml = /\.toml$/i.test(file.path);
1288
1292
  const stats = isToml
1289
1293
  ? transformCodexToml(file.path, 'wrap', gatewayConfig, dryRun)
1290
1294
  : wrapJsonConfigFile(file.path, gatewayConfig, agentClient, dryRun);
1291
- if (!stats)
1292
- continue;
1293
- if (stats.wrapped.length === 0 && stats.skippedManaged.length === 0 && stats.skippedRemote.length === 0)
1295
+ console.log(`\x1b[1m${file.source}\x1b[0m \x1b[2m${file.path}\x1b[0m`);
1296
+ if (!stats) {
1297
+ console.log(' \x1b[33m• could not parse left unchanged\x1b[0m\n');
1294
1298
  continue;
1299
+ }
1300
+ const found = stats.wrapped.length + stats.skippedManaged.length + stats.skippedRemote.length;
1295
1301
  totalWrapped += stats.wrapped.length;
1296
1302
  totalManaged += stats.skippedManaged.length;
1297
1303
  totalRemote += stats.skippedRemote.length;
1298
- console.log(`\x1b[1m${file.source}\x1b[0m \x1b[2m${file.path}\x1b[0m`);
1299
1304
  if (stats.wrapped.length)
1300
- console.log(` \x1b[32m✓ wrapped:\x1b[0m ${stats.wrapped.join(', ')}`);
1305
+ console.log(` \x1b[32m✓ ${dryRun ? 'would update' : 'updated'} → now protected:\x1b[0m ${stats.wrapped.join(', ')}`);
1301
1306
  if (stats.skippedManaged.length)
1302
- console.log(` \x1b[2m• already protected:\x1b[0m ${stats.skippedManaged.join(', ')}`);
1307
+ console.log(` \x1b[2m• already protected (unchanged):\x1b[0m ${stats.skippedManaged.join(', ')}`);
1303
1308
  if (stats.skippedRemote.length)
1304
1309
  console.log(` \x1b[33m• skipped (remote/no command):\x1b[0m ${stats.skippedRemote.join(', ')}`);
1310
+ if (found === 0) {
1311
+ totalEmpty++;
1312
+ console.log(' \x1b[2m• no MCP servers configured — nothing to protect\x1b[0m');
1313
+ }
1305
1314
  console.log('');
1306
1315
  }
1316
+ // Show leading tools that have NO config file at all, so it's clear they were searched.
1317
+ const consideredClients = exports.ALL_GATEWAY_CLIENTS.filter(c => !allowedClientKeys || (AGENT_TO_CLIENT_KEYS[c] || []).some(k => allowedClientKeys.has(k)));
1318
+ const notConfigured = consideredClients.filter(c => !(AGENT_TO_CLIENT_KEYS[c] || []).some(k => presentClientKeys.has(k)));
1319
+ if (notConfigured.length) {
1320
+ console.log(`\x1b[2mNot configured (no config file found): ${notConfigured.map(c => CLIENT_LABELS[c]).join(', ')}\x1b[0m\n`);
1321
+ }
1307
1322
  console.log('\x1b[1mSummary\x1b[0m');
1308
- console.log(` ${dryRun ? 'would wrap' : 'wrapped'}: ${totalWrapped} already protected: ${totalManaged} skipped remote: ${totalRemote}`);
1323
+ console.log(` ${dryRun ? 'would update' : 'updated'}: ${totalWrapped} already protected: ${totalManaged} no servers: ${totalEmpty} skipped remote: ${totalRemote}`);
1309
1324
  console.log(` Each protected server enforces your org Action Policies (allow / block / wait-for-human-approval) on its tool calls.`);
1310
1325
  if (!dryRun && totalWrapped > 0)
1311
1326
  console.log(' Backups saved next to each edited file (*.fcd-backup-*). Restart each client to load the protected servers.');
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ const hook_1 = require("./commands/hook");
45
45
  const installCursorHook_1 = require("./commands/installCursorHook");
46
46
  const mcpGateway_1 = require("./commands/mcpGateway");
47
47
  const installAll_1 = require("./commands/installAll");
48
+ const autoProtect_1 = require("./commands/autoProtect");
48
49
  const fs = __importStar(require("fs"));
49
50
  const path = __importStar(require("path"));
50
51
  function readCliVersion() {
@@ -112,7 +113,9 @@ function printHelp() {
112
113
  configure Saves org API key, Organization ID, Shield ID, and Shield key to ~/.fullcourtdefense.yml.
113
114
  After setup, other commands read credentials automatically — no --shield-id/--api-key needed.
114
115
  setup Same as configure — validates credentials against the server before saving.
115
- install-all One-click: MCP gateway in every AI client. Cursor hooks are opt-in.
116
+ install-all One-click: protect existing MCP servers in every AI client and
117
+ install Cursor hooks for built-in Cursor actions. No folder path
118
+ or --mcp-command needed. Use --hooks false to skip Cursor hooks.
116
119
  install Alias for install-all.
117
120
  scan Runs the scan. Use --local for inside-organization scans.
118
121
  discover Finds MCP servers, local secrets, agent rules/skills, and machine
@@ -133,6 +136,10 @@ function printHelp() {
133
136
  unprotect-all
134
137
  Reverse protect-all: restore every server to its original command.
135
138
  Also supports --clients to target one tool, and --dry-run.
139
+ auto-protect
140
+ Self-healing: schedule protect-all to re-run automatically so any
141
+ server that loses the proxy is re-wrapped. --interval-minutes 60
142
+ (default) or --on-logon true. --uninstall true / --status true.
136
143
  install-mcp-gateway
137
144
  Install MCP gateway into selected clients (--clients all for every tool).
138
145
  install-cursor-mcp-gateway
@@ -169,8 +176,8 @@ function printHelp() {
169
176
  fullcourtdefense setup
170
177
  Saves to ~/.fullcourtdefense.yml — install, discover --upload, scan, gateway, and hooks use it automatically.
171
178
 
172
- 4. One-click protect every AI client on this machine with MCP Action Policies:
173
- fullcourtdefense install-all --mcp-command npm --mcp-args "run mcp"
179
+ 4. One-click protect every AI client on this machine with Action Policies:
180
+ fullcourtdefense install-all
174
181
  Verify installs:
175
182
  fullcourtdefense discover --surface mcp
176
183
  Or install one client: fullcourtdefense install-cursor-mcp-gateway ...
@@ -290,8 +297,8 @@ function printHelp() {
290
297
  $ fullcourtdefense install-claude-desktop-mcp-gateway --mcp-command npm --mcp-args "run mcp"
291
298
  $ fullcourtdefense mcp-gateway --mcp-command npm --mcp-args "run mcp"
292
299
  $ fullcourtdefense setup
293
- $ fullcourtdefense install-all --mcp-command npm --mcp-args "run mcp"
294
- $ fullcourtdefense install-all --mcp-command npm --mcp-args "run mcp" --upload
300
+ $ fullcourtdefense install-all
301
+ $ fullcourtdefense install-all --auto-protect true --upload
295
302
  $ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
296
303
  $ fullcourtdefense install-codex-mcp-gateway --mcp-command npm --mcp-args "run mcp"
297
304
  $ fullcourtdefense scan --system-prompt ./prompts/system.md --fail-threshold 80
@@ -500,6 +507,8 @@ async function main() {
500
507
  ...buildInstallArgs(),
501
508
  hooks: flags.hooks,
502
509
  discover: flags.discover,
510
+ autoProtect: flags['auto-protect'],
511
+ intervalMinutes: flags['interval-minutes'],
503
512
  };
504
513
  await (0, installAll_1.installAllCommand)(args, config);
505
514
  break;
@@ -530,6 +539,21 @@ async function main() {
530
539
  await (0, mcpGateway_1.unprotectAllCommand)(args, config);
531
540
  break;
532
541
  }
542
+ case 'auto-protect': {
543
+ const args = {
544
+ ...buildGatewayArgs(),
545
+ clients: flags.clients,
546
+ config: flags.config,
547
+ dryRun: flags['dry-run'],
548
+ install: flags.install,
549
+ uninstall: flags.uninstall,
550
+ status: flags.status,
551
+ intervalMinutes: flags['interval-minutes'],
552
+ onLogon: flags['on-logon'],
553
+ };
554
+ await (0, autoProtect_1.autoProtectCommand)(args, config);
555
+ break;
556
+ }
533
557
  case 'install-cursor-mcp-gateway': {
534
558
  const args = {
535
559
  ...buildGatewayArgs(),
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.5.3"
2
+ "version": "1.6.1"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.5.3",
3
+ "version": "1.6.1",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {