@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.
- package/README.md +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- 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
|
+
*/
|