@cpretzinger/boss-claude 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 (87) hide show
  1. package/README.md +304 -1
  2. package/bin/boss-claude.js +1138 -0
  3. package/bin/commands/mode.js +250 -0
  4. package/bin/onyx-guard.js +259 -0
  5. package/bin/onyx-guard.sh +251 -0
  6. package/bin/prompts.js +284 -0
  7. package/bin/rollback.js +85 -0
  8. package/bin/setup-wizard.js +492 -0
  9. package/config/.env.example +17 -0
  10. package/lib/README.md +83 -0
  11. package/lib/agent-logger.js +61 -0
  12. package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
  13. package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
  14. package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
  15. package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
  16. package/lib/agents/memory-supervisor.js +526 -0
  17. package/lib/agents/registry.js +135 -0
  18. package/lib/auto-monitor.js +131 -0
  19. package/lib/checkpoint-hook.js +112 -0
  20. package/lib/checkpoint.js +319 -0
  21. package/lib/commentator.js +213 -0
  22. package/lib/context-scribe.js +120 -0
  23. package/lib/delegation-strategies.js +326 -0
  24. package/lib/hierarchy-validator.js +643 -0
  25. package/lib/index.js +15 -0
  26. package/lib/init-with-mode.js +261 -0
  27. package/lib/init.js +44 -6
  28. package/lib/memory-result-aggregator.js +252 -0
  29. package/lib/memory.js +35 -7
  30. package/lib/mode-enforcer.js +473 -0
  31. package/lib/onyx-banner.js +169 -0
  32. package/lib/onyx-identity.js +214 -0
  33. package/lib/onyx-monitor.js +381 -0
  34. package/lib/onyx-reminder.js +188 -0
  35. package/lib/onyx-tool-interceptor.js +341 -0
  36. package/lib/onyx-wrapper.js +315 -0
  37. package/lib/orchestrator-gate.js +334 -0
  38. package/lib/output-formatter.js +296 -0
  39. package/lib/postgres.js +1 -1
  40. package/lib/prompt-injector.js +220 -0
  41. package/lib/prompts.js +532 -0
  42. package/lib/session.js +153 -6
  43. package/lib/setup/README.md +187 -0
  44. package/lib/setup/env-manager.js +785 -0
  45. package/lib/setup/error-recovery.js +630 -0
  46. package/lib/setup/explain-scopes.js +385 -0
  47. package/lib/setup/github-instructions.js +333 -0
  48. package/lib/setup/github-repo.js +254 -0
  49. package/lib/setup/import-credentials.js +498 -0
  50. package/lib/setup/index.js +62 -0
  51. package/lib/setup/init-postgres.js +785 -0
  52. package/lib/setup/init-redis.js +456 -0
  53. package/lib/setup/integration-test.js +652 -0
  54. package/lib/setup/progress.js +357 -0
  55. package/lib/setup/rollback.js +670 -0
  56. package/lib/setup/rollback.test.js +452 -0
  57. package/lib/setup/setup-with-rollback.example.js +351 -0
  58. package/lib/setup/summary.js +400 -0
  59. package/lib/setup/test-github-setup.js +10 -0
  60. package/lib/setup/test-postgres-init.js +98 -0
  61. package/lib/setup/verify-setup.js +102 -0
  62. package/lib/task-agent-worker.js +235 -0
  63. package/lib/token-monitor.js +466 -0
  64. package/lib/tool-wrapper-integration.js +369 -0
  65. package/lib/tool-wrapper.js +387 -0
  66. package/lib/validators/README.md +497 -0
  67. package/lib/validators/config.js +583 -0
  68. package/lib/validators/config.test.js +175 -0
  69. package/lib/validators/github.js +310 -0
  70. package/lib/validators/github.test.js +61 -0
  71. package/lib/validators/index.js +15 -0
  72. package/lib/validators/postgres.js +525 -0
  73. package/package.json +98 -13
  74. package/scripts/benchmark-memory.js +433 -0
  75. package/scripts/check-secrets.sh +12 -0
  76. package/scripts/fetch-todos.mjs +148 -0
  77. package/scripts/graceful-shutdown.sh +156 -0
  78. package/scripts/install-onyx-hooks.js +373 -0
  79. package/scripts/install.js +119 -18
  80. package/scripts/redis-monitor.js +284 -0
  81. package/scripts/redis-setup.js +412 -0
  82. package/scripts/test-memory-retrieval.js +201 -0
  83. package/scripts/validate-exports.js +68 -0
  84. package/scripts/validate-package.js +120 -0
  85. package/scripts/verify-onyx-deployment.js +309 -0
  86. package/scripts/verify-redis-deployment.js +354 -0
  87. package/scripts/verify-redis-init.js +219 -0
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * BOSS CLAUDE MODE COMMAND
5
+ *
6
+ * Usage:
7
+ * boss-claude mode # Show current mode
8
+ * boss-claude mode orchestrator # Switch to orchestrator mode
9
+ * boss-claude mode specialist # Switch to specialist mode
10
+ * boss-claude mode worker # Switch to worker mode
11
+ * boss-claude mode review # Switch to review mode
12
+ * boss-claude mode learning # Switch to learning mode
13
+ * boss-claude mode history # Show mode change history
14
+ * boss-claude mode stats # Show mode statistics
15
+ * boss-claude mode blocked # Show blocked actions
16
+ * boss-claude mode reset # Emergency reset to safe default
17
+ */
18
+
19
+ import { getEnforcer, MODES } from '../../lib/mode-enforcer.js';
20
+ import { getGate } from '../../lib/orchestrator-gate.js';
21
+ import chalk from 'chalk';
22
+
23
+ export async function modeCommand(args) {
24
+ const enforcer = getEnforcer();
25
+ const gate = getGate();
26
+
27
+ // No args - show current mode
28
+ if (!args || args.length === 0) {
29
+ await showCurrentMode(enforcer, gate);
30
+ return;
31
+ }
32
+
33
+ const subcommand = args[0].toLowerCase();
34
+
35
+ switch (subcommand) {
36
+ case 'orchestrator':
37
+ case 'specialist':
38
+ case 'worker':
39
+ case 'review':
40
+ case 'learning':
41
+ await switchMode(enforcer, subcommand, args);
42
+ break;
43
+
44
+ case 'history':
45
+ await showHistory(enforcer);
46
+ break;
47
+
48
+ case 'stats':
49
+ await showStats(enforcer);
50
+ break;
51
+
52
+ case 'blocked':
53
+ await showBlockedActions(enforcer);
54
+ break;
55
+
56
+ case 'reset':
57
+ await resetMode(enforcer);
58
+ break;
59
+
60
+ case 'status':
61
+ await gate.printStatus();
62
+ break;
63
+
64
+ default:
65
+ console.error(chalk.red(`Unknown mode command: ${subcommand}`));
66
+ console.log(chalk.yellow(`\nValid modes: orchestrator, specialist, worker, review, learning`));
67
+ console.log(chalk.yellow(`Valid commands: history, stats, blocked, reset, status`));
68
+ process.exit(1);
69
+ }
70
+ }
71
+
72
+ // ==========================================
73
+ // SHOW CURRENT MODE
74
+ // ==========================================
75
+ async function showCurrentMode(enforcer, gate) {
76
+ const currentMode = await enforcer.getCurrentMode();
77
+ const metadata = await enforcer.getModeMetadata();
78
+ const agentIdentity = await enforcer.getAgentIdentity();
79
+
80
+ console.log(chalk.bold.cyan(`\n╔══════════════════════════════════════════════════════════╗`));
81
+ console.log(chalk.bold.cyan(`║ BOSS CLAUDE - CURRENT MODE ║`));
82
+ console.log(chalk.bold.cyan(`╚══════════════════════════════════════════════════════════╝\n`));
83
+
84
+ console.log(chalk.bold(`Mode: ${chalk.green(currentMode.toUpperCase())}`));
85
+
86
+ if (metadata) {
87
+ console.log(chalk.gray(`Set by: ${metadata.setBy}`));
88
+ console.log(chalk.gray(`Set at: ${metadata.setAt}`));
89
+ console.log(chalk.gray(`Reason: ${metadata.reason}`));
90
+ if (metadata.sessionId) {
91
+ console.log(chalk.gray(`Session: ${metadata.sessionId}`));
92
+ }
93
+ console.log(chalk.gray(`TTL: ${metadata.ttl} seconds`));
94
+ }
95
+
96
+ if (agentIdentity) {
97
+ console.log(chalk.bold(`\nAgent: ${chalk.yellow(agentIdentity.agent)}`));
98
+ if (agentIdentity.domain) {
99
+ console.log(chalk.gray(`Domain: ${agentIdentity.domain}`));
100
+ }
101
+ console.log(chalk.gray(`Since: ${agentIdentity.setAt}`));
102
+ }
103
+
104
+ console.log(chalk.dim(`\nSwitch modes with: boss-claude mode [orchestrator|specialist|worker|review|learning]`));
105
+ console.log(chalk.dim(`View capabilities: boss-claude mode status\n`));
106
+ }
107
+
108
+ // ==========================================
109
+ // SWITCH MODE
110
+ // ==========================================
111
+ async function switchMode(enforcer, mode, args) {
112
+ const validModes = Object.values(MODES);
113
+
114
+ if (!validModes.includes(mode)) {
115
+ console.error(chalk.red(`Invalid mode: ${mode}`));
116
+ console.log(chalk.yellow(`Valid modes: ${validModes.join(', ')}`));
117
+ process.exit(1);
118
+ }
119
+
120
+ // Parse additional args
121
+ const reason = args[1] || 'manual-switch';
122
+ const agent = args[2] || 'boss-claude';
123
+
124
+ console.log(chalk.bold.cyan(`\n[MODE SWITCH] Switching to ${mode.toUpperCase()} mode...`));
125
+
126
+ try {
127
+ await enforcer.setMode(mode, {
128
+ agent,
129
+ reason,
130
+ sessionId: null
131
+ });
132
+
133
+ // Update agent identity if switching to specialist
134
+ if (mode === MODES.SPECIALIST && args[2]) {
135
+ const domain = args[3] || null;
136
+ await enforcer.setAgentIdentity(args[2], domain);
137
+ console.log(chalk.green(`✅ Agent identity set: ${args[2]} (${domain || 'no domain'})`));
138
+ }
139
+
140
+ console.log(chalk.bold.green(`✅ Mode switched to: ${mode.toUpperCase()}`));
141
+ console.log(chalk.gray(`Reason: ${reason}`));
142
+
143
+ // Show new status
144
+ const gate = getGate();
145
+ await gate.printStatus();
146
+
147
+ } catch (error) {
148
+ console.error(chalk.red(`❌ Failed to switch mode: ${error.message}`));
149
+ process.exit(1);
150
+ }
151
+ }
152
+
153
+ // ==========================================
154
+ // SHOW HISTORY
155
+ // ==========================================
156
+ async function showHistory(enforcer) {
157
+ console.log(chalk.bold.cyan(`\n╔══════════════════════════════════════════════════════════╗`));
158
+ console.log(chalk.bold.cyan(`║ MODE CHANGE HISTORY ║`));
159
+ console.log(chalk.bold.cyan(`╚══════════════════════════════════════════════════════════╝\n`));
160
+
161
+ const history = await enforcer.getModeHistory(20);
162
+
163
+ if (history.length === 0) {
164
+ console.log(chalk.yellow(`No mode changes recorded yet.`));
165
+ return;
166
+ }
167
+
168
+ history.forEach((entry, index) => {
169
+ const timestamp = new Date(entry.setAt).toLocaleString();
170
+ console.log(chalk.bold(`${index + 1}. ${chalk.green(entry.mode.toUpperCase())}`));
171
+ console.log(chalk.gray(` Time: ${timestamp}`));
172
+ console.log(chalk.gray(` By: ${entry.setBy}`));
173
+ console.log(chalk.gray(` Reason: ${entry.reason}`));
174
+ if (entry.sessionId) {
175
+ console.log(chalk.gray(` Session: ${entry.sessionId}`));
176
+ }
177
+ console.log('');
178
+ });
179
+
180
+ console.log(chalk.dim(`Showing last ${history.length} mode changes\n`));
181
+ }
182
+
183
+ // ==========================================
184
+ // SHOW STATS
185
+ // ==========================================
186
+ async function showStats(enforcer) {
187
+ console.log(chalk.bold.cyan(`\n╔══════════════════════════════════════════════════════════╗`));
188
+ console.log(chalk.bold.cyan(`║ MODE STATISTICS ║`));
189
+ console.log(chalk.bold.cyan(`╚══════════════════════════════════════════════════════════╝\n`));
190
+
191
+ const stats = await enforcer.getModeStats();
192
+
193
+ console.log(chalk.bold(`Actions per mode:\n`));
194
+
195
+ for (const [mode, count] of Object.entries(stats)) {
196
+ const bar = '█'.repeat(Math.min(count, 50));
197
+ console.log(chalk.bold(`${mode.padEnd(15)}: ${chalk.green(count.toString().padStart(6))} ${chalk.gray(bar)}`));
198
+ }
199
+
200
+ const total = Object.values(stats).reduce((sum, count) => sum + count, 0);
201
+ console.log(chalk.bold(`\nTotal actions: ${chalk.cyan(total)}\n`));
202
+ }
203
+
204
+ // ==========================================
205
+ // SHOW BLOCKED ACTIONS
206
+ // ==========================================
207
+ async function showBlockedActions(enforcer) {
208
+ console.log(chalk.bold.cyan(`\n╔══════════════════════════════════════════════════════════╗`));
209
+ console.log(chalk.bold.cyan(`║ BLOCKED ACTIONS (Security Audit) ║`));
210
+ console.log(chalk.bold.cyan(`╚══════════════════════════════════════════════════════════╝\n`));
211
+
212
+ const blocked = await enforcer.getBlockedActions(20);
213
+
214
+ if (blocked.length === 0) {
215
+ console.log(chalk.green(`✅ No blocked actions. All operations have been compliant.\n`));
216
+ return;
217
+ }
218
+
219
+ blocked.forEach((entry, index) => {
220
+ const timestamp = new Date(entry.timestamp).toLocaleString();
221
+ console.log(chalk.bold.red(`${index + 1}. BLOCKED ACTION`));
222
+ console.log(chalk.gray(` Time: ${timestamp}`));
223
+ console.log(chalk.gray(` Current mode: ${entry.currentMode}`));
224
+ console.log(chalk.gray(` Required mode: ${entry.requiredMode}`));
225
+ console.log(chalk.yellow(` Action: ${entry.action}`));
226
+ console.log('');
227
+ });
228
+
229
+ console.log(chalk.dim(`Showing last ${blocked.length} blocked actions`));
230
+ console.log(chalk.red(`⚠️ ${blocked.length} actions were blocked by mode enforcement\n`));
231
+ }
232
+
233
+ // ==========================================
234
+ // RESET MODE
235
+ // ==========================================
236
+ async function resetMode(enforcer) {
237
+ console.log(chalk.bold.yellow(`\n[WARNING] Resetting mode to safe default (WORKER mode)...`));
238
+
239
+ try {
240
+ await enforcer.resetMode();
241
+ console.log(chalk.bold.green(`✅ Mode reset complete`));
242
+
243
+ const gate = getGate();
244
+ await gate.printStatus();
245
+
246
+ } catch (error) {
247
+ console.error(chalk.red(`❌ Reset failed: ${error.message}`));
248
+ process.exit(1);
249
+ }
250
+ }
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ONYX GUARD CLI - Pre-Hook Tool Interceptor
4
+ *
5
+ * Executable Node.js wrapper for ONYX tool interception
6
+ * Can be invoked from command line or programmatically
7
+ *
8
+ * USAGE:
9
+ * npx onyx-guard --tool Read --file /path/to/file.js
10
+ * node onyx-guard.js check Bash
11
+ * onyx-guard report
12
+ */
13
+
14
+ import { Command } from 'commander';
15
+ import chalk from 'chalk';
16
+ import onyxInterceptor from '../lib/onyx-tool-interceptor.js';
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('onyx-guard')
22
+ .description('ONYX Tool Interceptor - Enforces delegation rules')
23
+ .version('1.0.0');
24
+
25
+ /**
26
+ * Command: check
27
+ * Check if a tool is allowed for ONYX without executing
28
+ */
29
+ program
30
+ .command('check <tool>')
31
+ .description('Check if tool is allowed for ONYX')
32
+ .action(async (tool) => {
33
+ console.log(chalk.blue(`\n🔍 Checking tool: ${tool}`));
34
+
35
+ const isForbidden = onyxInterceptor.forbiddenTools.includes(tool);
36
+ const isAllowed = onyxInterceptor.allowedTools.includes(tool);
37
+
38
+ if (isForbidden) {
39
+ console.log(chalk.red(`❌ FORBIDDEN: ${tool}`));
40
+ console.log(chalk.dim('ONYX must delegate to Task tool'));
41
+ process.exit(1);
42
+ } else if (isAllowed) {
43
+ console.log(chalk.green(`✅ ALLOWED: ${tool}`));
44
+ process.exit(0);
45
+ } else {
46
+ console.log(chalk.yellow(`⚠️ UNKNOWN: ${tool}`));
47
+ console.log(chalk.dim('Not in ALLOWED or FORBIDDEN lists'));
48
+ process.exit(2);
49
+ }
50
+ });
51
+
52
+ /**
53
+ * Command: intercept
54
+ * Intercept a tool call and apply delegation rules
55
+ */
56
+ program
57
+ .command('intercept <tool>')
58
+ .description('Intercept tool call and enforce delegation')
59
+ .option('-p, --params <json>', 'Tool parameters as JSON string', '{}')
60
+ .option('--auto-delegate', 'Auto-delegate without prompting', false)
61
+ .option('--silent', 'Silent mode - no prompts', false)
62
+ .action(async (tool, options) => {
63
+ let params = {};
64
+
65
+ try {
66
+ params = JSON.parse(options.params);
67
+ } catch (e) {
68
+ console.error(chalk.red('Error: Invalid JSON in --params'));
69
+ process.exit(1);
70
+ }
71
+
72
+ // If silent mode, disable prompts and auto-block
73
+ if (options.silent) {
74
+ const isForbidden = onyxInterceptor.forbiddenTools.includes(tool);
75
+
76
+ if (isForbidden) {
77
+ console.log(chalk.red(`❌ BLOCKED: ${tool} (forbidden for ONYX)`));
78
+ console.log(chalk.yellow('DELEGATE TO: Task tool'));
79
+ process.exit(1);
80
+ }
81
+
82
+ console.log(chalk.green(`✅ ALLOWED: ${tool}`));
83
+ process.exit(0);
84
+ }
85
+
86
+ // Full interactive mode
87
+ const result = await onyxInterceptor.interceptToolCall(tool, params);
88
+
89
+ if (!result.allowed) {
90
+ if (result.delegateTo) {
91
+ console.log(chalk.cyan(`\n📤 Delegation Required:`));
92
+ console.log(chalk.white(result.delegationMessage));
93
+ process.exit(10); // Exit code 10 = delegate
94
+ }
95
+
96
+ if (result.rejected) {
97
+ console.log(chalk.red('\n❌ Tool call rejected'));
98
+ process.exit(1);
99
+ }
100
+
101
+ console.log(chalk.red('\n❌ Tool call blocked'));
102
+ process.exit(1);
103
+ }
104
+
105
+ if (result.override) {
106
+ console.log(chalk.yellow('\n⚠️ Override granted - violation logged'));
107
+ process.exit(5); // Exit code 5 = override
108
+ }
109
+
110
+ console.log(chalk.green(`\n✅ Tool call allowed: ${tool}`));
111
+ process.exit(0);
112
+ });
113
+
114
+ /**
115
+ * Command: report
116
+ * Print interceptor statistics
117
+ */
118
+ program
119
+ .command('report')
120
+ .description('Print ONYX tool interceptor report')
121
+ .action(() => {
122
+ onyxInterceptor.printReport();
123
+ });
124
+
125
+ /**
126
+ * Command: reset
127
+ * Reset interceptor counters
128
+ */
129
+ program
130
+ .command('reset')
131
+ .description('Reset interceptor counters')
132
+ .action(() => {
133
+ onyxInterceptor.reset();
134
+ });
135
+
136
+ /**
137
+ * Command: list
138
+ * List forbidden/allowed tools
139
+ */
140
+ program
141
+ .command('list [type]')
142
+ .description('List tools (forbidden, allowed, or all)')
143
+ .action((type = 'all') => {
144
+ if (type === 'forbidden' || type === 'all') {
145
+ console.log(chalk.red('\n🚫 FORBIDDEN TOOLS FOR ONYX:'));
146
+ onyxInterceptor.forbiddenTools.forEach(tool => {
147
+ console.log(chalk.red(` ✗ ${tool}`));
148
+ });
149
+ }
150
+
151
+ if (type === 'allowed' || type === 'all') {
152
+ console.log(chalk.green('\n✅ ALLOWED TOOLS FOR ONYX:'));
153
+ onyxInterceptor.allowedTools.forEach(tool => {
154
+ console.log(chalk.green(` ✓ ${tool}`));
155
+ });
156
+ }
157
+
158
+ if (type === 'all') {
159
+ console.log(chalk.dim('\n📋 DELEGATION RULE:'));
160
+ console.log(chalk.dim(' ONYX is META-BOSS and NEVER touches files directly'));
161
+ console.log(chalk.dim(' ONYX delegates to Task tool → Task tool uses file operations'));
162
+ }
163
+ });
164
+
165
+ /**
166
+ * Command: enable/disable
167
+ * Enable or disable interceptor
168
+ */
169
+ program
170
+ .command('enable')
171
+ .description('Enable ONYX tool interceptor')
172
+ .action(() => {
173
+ onyxInterceptor.setEnabled(true);
174
+ });
175
+
176
+ program
177
+ .command('disable')
178
+ .description('Disable ONYX tool interceptor')
179
+ .action(() => {
180
+ onyxInterceptor.setEnabled(false);
181
+ });
182
+
183
+ /**
184
+ * Command: test
185
+ * Run test suite for interceptor
186
+ */
187
+ program
188
+ .command('test')
189
+ .description('Test ONYX tool interceptor')
190
+ .action(async () => {
191
+ console.log(chalk.blue('\n🧪 Running ONYX Tool Interceptor Tests\n'));
192
+
193
+ const tests = [
194
+ { tool: 'Read', shouldBlock: true },
195
+ { tool: 'Write', shouldBlock: true },
196
+ { tool: 'Edit', shouldBlock: true },
197
+ { tool: 'Bash', shouldBlock: true },
198
+ { tool: 'Grep', shouldBlock: true },
199
+ { tool: 'Glob', shouldBlock: true },
200
+ { tool: 'Task', shouldBlock: false },
201
+ { tool: 'WebFetch', shouldBlock: false },
202
+ { tool: 'WebSearch', shouldBlock: false }
203
+ ];
204
+
205
+ // Disable prompts for testing
206
+ const originalEnabled = onyxInterceptor.enabled;
207
+ onyxInterceptor.setEnabled(false);
208
+
209
+ let passed = 0;
210
+ let failed = 0;
211
+
212
+ tests.forEach(test => {
213
+ const isForbidden = onyxInterceptor.forbiddenTools.includes(test.tool);
214
+ const isBlocked = isForbidden;
215
+
216
+ const testPassed = isBlocked === test.shouldBlock;
217
+
218
+ if (testPassed) {
219
+ console.log(chalk.green(`✓ ${test.tool}: ${test.shouldBlock ? 'Blocked' : 'Allowed'}`));
220
+ passed++;
221
+ } else {
222
+ console.log(chalk.red(`✗ ${test.tool}: Expected ${test.shouldBlock ? 'block' : 'allow'}, got ${isBlocked ? 'block' : 'allow'}`));
223
+ failed++;
224
+ }
225
+ });
226
+
227
+ // Restore original state
228
+ onyxInterceptor.setEnabled(originalEnabled);
229
+
230
+ console.log(chalk.dim('\n─'.repeat(80)));
231
+ console.log(chalk.white(`Total Tests: ${tests.length}`));
232
+ console.log(chalk.green(`Passed: ${passed}`));
233
+ console.log(chalk.red(`Failed: ${failed}`));
234
+
235
+ if (failed === 0) {
236
+ console.log(chalk.green('\n✅ All tests passed!'));
237
+ process.exit(0);
238
+ } else {
239
+ console.log(chalk.red('\n❌ Some tests failed'));
240
+ process.exit(1);
241
+ }
242
+ });
243
+
244
+ // Parse arguments
245
+ program.parse(process.argv);
246
+
247
+ // If no command provided, show help
248
+ if (process.argv.length < 3) {
249
+ program.help();
250
+ }
251
+
252
+ /**
253
+ * EXIT CODES:
254
+ * 0 = Success / Tool allowed
255
+ * 1 = Error / Tool blocked
256
+ * 2 = Unknown tool
257
+ * 5 = Override granted
258
+ * 10 = Delegation required
259
+ */