@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,156 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
###############################################################################
|
|
4
|
+
# GRACEFUL SHUTDOWN SCRIPT
|
|
5
|
+
# Purpose: Terminate all background boss-claude agents with 10-minute timeout
|
|
6
|
+
# Features: Graceful SIGTERM first, then force kill after timeout
|
|
7
|
+
###############################################################################
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
TIMEOUT_SECONDS=600 # 10 minutes
|
|
12
|
+
POLL_INTERVAL=5 # Check every 5 seconds
|
|
13
|
+
LOG_FILE="/tmp/boss-claude-shutdown-$(date +%Y%m%d-%H%M%S).log"
|
|
14
|
+
|
|
15
|
+
echo "========================================" | tee "$LOG_FILE"
|
|
16
|
+
echo "GRACEFUL SHUTDOWN - $(date)" | tee -a "$LOG_FILE"
|
|
17
|
+
echo "========================================" | tee -a "$LOG_FILE"
|
|
18
|
+
|
|
19
|
+
# Function to log with timestamp
|
|
20
|
+
log() {
|
|
21
|
+
echo "[$(date '+%H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Function to get all boss-claude related PIDs
|
|
25
|
+
get_boss_pids() {
|
|
26
|
+
ps aux | grep -E "(boss-claude|bin/boss-claude)" | grep -v grep | grep -v "graceful-shutdown" | awk '{print $2}' || true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Step 1: Identify all running processes
|
|
30
|
+
log "Step 1: Identifying all boss-claude processes..."
|
|
31
|
+
INITIAL_PIDS=$(get_boss_pids)
|
|
32
|
+
|
|
33
|
+
if [ -z "$INITIAL_PIDS" ]; then
|
|
34
|
+
log "✅ No boss-claude processes found. System is clean."
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
INITIAL_COUNT=$(echo "$INITIAL_PIDS" | wc -l | tr -d ' ')
|
|
39
|
+
log "Found $INITIAL_COUNT boss-claude processes:"
|
|
40
|
+
echo "$INITIAL_PIDS" | while read pid; do
|
|
41
|
+
process_info=$(ps -p "$pid" -o pid,etime,command | tail -n 1)
|
|
42
|
+
log " PID $pid: $process_info"
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
# Step 2: Send SIGTERM to all processes
|
|
46
|
+
log ""
|
|
47
|
+
log "Step 2: Sending SIGTERM (graceful shutdown signal)..."
|
|
48
|
+
echo "$INITIAL_PIDS" | while read pid; do
|
|
49
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
50
|
+
log " Sending SIGTERM to PID $pid"
|
|
51
|
+
kill -TERM "$pid" 2>/dev/null || log " Warning: Could not send SIGTERM to $pid"
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# Step 3: Wait for graceful shutdown with timeout
|
|
56
|
+
log ""
|
|
57
|
+
log "Step 3: Waiting up to $TIMEOUT_SECONDS seconds for graceful shutdown..."
|
|
58
|
+
elapsed=0
|
|
59
|
+
completed_pids=""
|
|
60
|
+
remaining_pids="$INITIAL_PIDS"
|
|
61
|
+
|
|
62
|
+
while [ $elapsed -lt $TIMEOUT_SECONDS ]; do
|
|
63
|
+
current_pids=$(get_boss_pids)
|
|
64
|
+
|
|
65
|
+
if [ -z "$current_pids" ]; then
|
|
66
|
+
log "✅ All processes terminated gracefully after $elapsed seconds"
|
|
67
|
+
echo ""
|
|
68
|
+
log "========================================"
|
|
69
|
+
log "SHUTDOWN COMPLETE - ALL PROCESSES EXITED GRACEFULLY"
|
|
70
|
+
log "========================================"
|
|
71
|
+
log "Total processes shutdown: $INITIAL_COUNT"
|
|
72
|
+
log "Graceful exits: $INITIAL_COUNT"
|
|
73
|
+
log "Forced kills: 0"
|
|
74
|
+
log "Time taken: ${elapsed}s / ${TIMEOUT_SECONDS}s"
|
|
75
|
+
log "Log file: $LOG_FILE"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Calculate which processes have exited
|
|
80
|
+
new_completed=$(comm -23 <(echo "$remaining_pids" | sort) <(echo "$current_pids" | sort))
|
|
81
|
+
if [ -n "$new_completed" ]; then
|
|
82
|
+
echo "$new_completed" | while read pid; do
|
|
83
|
+
log " ✅ PID $pid exited gracefully"
|
|
84
|
+
done
|
|
85
|
+
completed_pids="$completed_pids$new_completed"$'\n'
|
|
86
|
+
remaining_pids="$current_pids"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
sleep $POLL_INTERVAL
|
|
90
|
+
elapsed=$((elapsed + POLL_INTERVAL))
|
|
91
|
+
|
|
92
|
+
# Progress update every 30 seconds
|
|
93
|
+
if [ $((elapsed % 30)) -eq 0 ]; then
|
|
94
|
+
remaining_count=$(echo "$current_pids" | wc -l | tr -d ' ')
|
|
95
|
+
log " Progress: ${elapsed}s elapsed, $remaining_count processes still running"
|
|
96
|
+
fi
|
|
97
|
+
done
|
|
98
|
+
|
|
99
|
+
# Step 4: Force kill remaining processes
|
|
100
|
+
log ""
|
|
101
|
+
log "Step 4: Timeout reached. Force killing remaining processes..."
|
|
102
|
+
REMAINING_PIDS=$(get_boss_pids)
|
|
103
|
+
|
|
104
|
+
if [ -z "$REMAINING_PIDS" ]; then
|
|
105
|
+
log "✅ All processes terminated during wait period"
|
|
106
|
+
echo ""
|
|
107
|
+
log "========================================"
|
|
108
|
+
log "SHUTDOWN COMPLETE - ALL PROCESSES EXITED"
|
|
109
|
+
log "========================================"
|
|
110
|
+
exit 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
killed_count=0
|
|
114
|
+
echo "$REMAINING_PIDS" | while read pid; do
|
|
115
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
116
|
+
log " ⚠️ Sending SIGKILL to PID $pid"
|
|
117
|
+
kill -9 "$pid" 2>/dev/null || log " Warning: Could not kill $pid"
|
|
118
|
+
killed_count=$((killed_count + 1))
|
|
119
|
+
fi
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
# Wait a moment for kills to complete
|
|
123
|
+
sleep 2
|
|
124
|
+
|
|
125
|
+
# Final verification
|
|
126
|
+
FINAL_PIDS=$(get_boss_pids)
|
|
127
|
+
if [ -n "$FINAL_PIDS" ]; then
|
|
128
|
+
log ""
|
|
129
|
+
log "❌ WARNING: Some processes could not be terminated:"
|
|
130
|
+
echo "$FINAL_PIDS" | while read pid; do
|
|
131
|
+
log " PID $pid still running"
|
|
132
|
+
done
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Summary
|
|
136
|
+
log ""
|
|
137
|
+
log "========================================"
|
|
138
|
+
log "SHUTDOWN COMPLETE"
|
|
139
|
+
log "========================================"
|
|
140
|
+
|
|
141
|
+
graceful_count=$(echo "$completed_pids" | grep -v '^$' | wc -l | tr -d ' ')
|
|
142
|
+
forced_count=$(echo "$REMAINING_PIDS" | wc -l | tr -d ' ')
|
|
143
|
+
|
|
144
|
+
log "Total processes found: $INITIAL_COUNT"
|
|
145
|
+
log "Graceful exits: $graceful_count"
|
|
146
|
+
log "Forced kills: $forced_count"
|
|
147
|
+
log "Time taken: ${elapsed}s / ${TIMEOUT_SECONDS}s"
|
|
148
|
+
log "Log file: $LOG_FILE"
|
|
149
|
+
|
|
150
|
+
if [ -z "$FINAL_PIDS" ]; then
|
|
151
|
+
log "✅ All processes successfully terminated"
|
|
152
|
+
exit 0
|
|
153
|
+
else
|
|
154
|
+
log "⚠️ Some processes could not be terminated"
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* INSTALL ONYX HOOKS - Runtime Integration Script
|
|
4
|
+
*
|
|
5
|
+
* Installs pre-hook system into Claude Code for ONYX agent.
|
|
6
|
+
* This script patches tool execution to enforce delegation rules.
|
|
7
|
+
*
|
|
8
|
+
* USAGE:
|
|
9
|
+
* node scripts/install-onyx-hooks.js [--force]
|
|
10
|
+
* npm run install:onyx-hooks
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs/promises';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import ora from 'ora';
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.dirname(__filename);
|
|
21
|
+
const projectRoot = path.dirname(__dirname);
|
|
22
|
+
|
|
23
|
+
// Hook installation targets
|
|
24
|
+
const HOOK_TARGETS = {
|
|
25
|
+
// Global Claude Code config (if exists)
|
|
26
|
+
claudeConfig: path.join(process.env.HOME, '.claude', 'config.json'),
|
|
27
|
+
|
|
28
|
+
// Project-level hook config
|
|
29
|
+
projectHook: path.join(projectRoot, '.claude-hooks', 'onyx-pre-hook.json'),
|
|
30
|
+
|
|
31
|
+
// NPM package bin
|
|
32
|
+
npmBin: path.join(projectRoot, 'bin', 'onyx-guard.js')
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Install ONYX pre-hook configuration
|
|
37
|
+
*/
|
|
38
|
+
async function installHooks(options = {}) {
|
|
39
|
+
console.log(chalk.blue('\n🔧 Installing ONYX Pre-Hook System\n'));
|
|
40
|
+
|
|
41
|
+
const spinner = ora('Checking environment...').start();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// 1. Create hook directory
|
|
45
|
+
spinner.text = 'Creating hook directory...';
|
|
46
|
+
await createHookDirectory();
|
|
47
|
+
spinner.succeed('Hook directory created');
|
|
48
|
+
|
|
49
|
+
// 2. Install hook configuration
|
|
50
|
+
spinner.start('Installing hook configuration...');
|
|
51
|
+
await installHookConfig(options);
|
|
52
|
+
spinner.succeed('Hook configuration installed');
|
|
53
|
+
|
|
54
|
+
// 3. Make executables executable
|
|
55
|
+
spinner.start('Setting executable permissions...');
|
|
56
|
+
await setExecutablePermissions();
|
|
57
|
+
spinner.succeed('Executable permissions set');
|
|
58
|
+
|
|
59
|
+
// 4. Create symlinks (if needed)
|
|
60
|
+
spinner.start('Creating symlinks...');
|
|
61
|
+
await createSymlinks();
|
|
62
|
+
spinner.succeed('Symlinks created');
|
|
63
|
+
|
|
64
|
+
// 5. Verify installation
|
|
65
|
+
spinner.start('Verifying installation...');
|
|
66
|
+
const verified = await verifyInstallation();
|
|
67
|
+
|
|
68
|
+
if (verified) {
|
|
69
|
+
spinner.succeed('Installation verified');
|
|
70
|
+
} else {
|
|
71
|
+
spinner.warn('Installation verification failed - may need manual setup');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Success summary
|
|
75
|
+
console.log(chalk.green('\n✅ ONYX Pre-Hook System Installed Successfully\n'));
|
|
76
|
+
|
|
77
|
+
printUsageInstructions();
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
spinner.fail('Installation failed');
|
|
81
|
+
console.error(chalk.red(`\nError: ${error.message}\n`));
|
|
82
|
+
|
|
83
|
+
if (error.stack && options.verbose) {
|
|
84
|
+
console.error(chalk.dim(error.stack));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create .claude-hooks directory
|
|
93
|
+
*/
|
|
94
|
+
async function createHookDirectory() {
|
|
95
|
+
const hookDir = path.dirname(HOOK_TARGETS.projectHook);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await fs.mkdir(hookDir, { recursive: true });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error.code !== 'EEXIST') {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Install hook configuration file
|
|
108
|
+
*/
|
|
109
|
+
async function installHookConfig(options) {
|
|
110
|
+
const hookConfig = {
|
|
111
|
+
name: 'ONYX Pre-Hook Tool Interceptor',
|
|
112
|
+
version: '1.0.0',
|
|
113
|
+
enabled: true,
|
|
114
|
+
agent: 'ONYX',
|
|
115
|
+
interceptor: {
|
|
116
|
+
module: '@cpretzinger/boss-claude/lib/onyx-tool-interceptor.js',
|
|
117
|
+
wrapper: '@cpretzinger/boss-claude/lib/onyx-wrapper.js',
|
|
118
|
+
cli: 'onyx-guard'
|
|
119
|
+
},
|
|
120
|
+
rules: {
|
|
121
|
+
forbiddenTools: [
|
|
122
|
+
'Read',
|
|
123
|
+
'Write',
|
|
124
|
+
'Edit',
|
|
125
|
+
'Bash',
|
|
126
|
+
'Grep',
|
|
127
|
+
'Glob',
|
|
128
|
+
'NotebookEdit'
|
|
129
|
+
],
|
|
130
|
+
allowedTools: [
|
|
131
|
+
'Task',
|
|
132
|
+
'WebFetch',
|
|
133
|
+
'WebSearch',
|
|
134
|
+
'TodoWrite',
|
|
135
|
+
'Skill'
|
|
136
|
+
],
|
|
137
|
+
delegationTarget: 'Task',
|
|
138
|
+
enforceStrict: true
|
|
139
|
+
},
|
|
140
|
+
logging: {
|
|
141
|
+
enabled: true,
|
|
142
|
+
violations: true,
|
|
143
|
+
delegations: true,
|
|
144
|
+
overrides: true
|
|
145
|
+
},
|
|
146
|
+
created: new Date().toISOString(),
|
|
147
|
+
installedBy: process.env.USER || 'unknown'
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Check if config already exists
|
|
151
|
+
try {
|
|
152
|
+
const existing = await fs.readFile(HOOK_TARGETS.projectHook, 'utf-8');
|
|
153
|
+
const existingConfig = JSON.parse(existing);
|
|
154
|
+
|
|
155
|
+
if (!options.force && existingConfig.enabled) {
|
|
156
|
+
console.log(chalk.yellow('\n⚠️ Hook configuration already exists'));
|
|
157
|
+
console.log(chalk.dim('Use --force to overwrite'));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// File doesn't exist - continue with installation
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Write configuration
|
|
165
|
+
await fs.writeFile(
|
|
166
|
+
HOOK_TARGETS.projectHook,
|
|
167
|
+
JSON.stringify(hookConfig, null, 2),
|
|
168
|
+
'utf-8'
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Set executable permissions on scripts
|
|
174
|
+
*/
|
|
175
|
+
async function setExecutablePermissions() {
|
|
176
|
+
const executables = [
|
|
177
|
+
path.join(projectRoot, 'bin', 'onyx-guard.sh'),
|
|
178
|
+
path.join(projectRoot, 'bin', 'onyx-guard.js'),
|
|
179
|
+
path.join(projectRoot, 'lib', 'onyx-tool-interceptor.js'),
|
|
180
|
+
path.join(projectRoot, 'lib', 'onyx-wrapper.js')
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
for (const exe of executables) {
|
|
184
|
+
try {
|
|
185
|
+
await fs.chmod(exe, 0o755);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error.code !== 'ENOENT') {
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Create symlinks for easy access
|
|
196
|
+
*/
|
|
197
|
+
async function createSymlinks() {
|
|
198
|
+
// Symlink onyx-guard to project node_modules/.bin
|
|
199
|
+
const binDir = path.join(projectRoot, 'node_modules', '.bin');
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
await fs.mkdir(binDir, { recursive: true });
|
|
203
|
+
|
|
204
|
+
const symlinkTarget = path.join(binDir, 'onyx-guard');
|
|
205
|
+
const symlinkSource = path.join(projectRoot, 'bin', 'onyx-guard.js');
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await fs.symlink(symlinkSource, symlinkTarget);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error.code !== 'EEXIST') {
|
|
211
|
+
// Symlink already exists or can't be created - not critical
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (error) {
|
|
215
|
+
// Not critical if symlinks fail
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Verify installation
|
|
221
|
+
*/
|
|
222
|
+
async function verifyInstallation() {
|
|
223
|
+
const checks = [];
|
|
224
|
+
|
|
225
|
+
// Check hook config exists
|
|
226
|
+
try {
|
|
227
|
+
await fs.access(HOOK_TARGETS.projectHook);
|
|
228
|
+
checks.push({ name: 'Hook config', status: true });
|
|
229
|
+
} catch (error) {
|
|
230
|
+
checks.push({ name: 'Hook config', status: false });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Check interceptor module exists
|
|
234
|
+
try {
|
|
235
|
+
await fs.access(path.join(projectRoot, 'lib', 'onyx-tool-interceptor.js'));
|
|
236
|
+
checks.push({ name: 'Interceptor module', status: true });
|
|
237
|
+
} catch (error) {
|
|
238
|
+
checks.push({ name: 'Interceptor module', status: false });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check wrapper module exists
|
|
242
|
+
try {
|
|
243
|
+
await fs.access(path.join(projectRoot, 'lib', 'onyx-wrapper.js'));
|
|
244
|
+
checks.push({ name: 'Wrapper module', status: true });
|
|
245
|
+
} catch (error) {
|
|
246
|
+
checks.push({ name: 'Wrapper module', status: false });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check CLI exists
|
|
250
|
+
try {
|
|
251
|
+
await fs.access(path.join(projectRoot, 'bin', 'onyx-guard.js'));
|
|
252
|
+
checks.push({ name: 'CLI tool', status: true });
|
|
253
|
+
} catch (error) {
|
|
254
|
+
checks.push({ name: 'CLI tool', status: false });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const allPassed = checks.every(c => c.status);
|
|
258
|
+
|
|
259
|
+
if (!allPassed) {
|
|
260
|
+
console.log(chalk.yellow('\n⚠️ Verification Issues:'));
|
|
261
|
+
checks.forEach(check => {
|
|
262
|
+
const icon = check.status ? chalk.green('✓') : chalk.red('✗');
|
|
263
|
+
console.log(` ${icon} ${check.name}`);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return allPassed;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Print usage instructions
|
|
272
|
+
*/
|
|
273
|
+
function printUsageInstructions() {
|
|
274
|
+
console.log(chalk.cyan('📚 USAGE INSTRUCTIONS:\n'));
|
|
275
|
+
|
|
276
|
+
console.log(chalk.white('1. Programmatic Integration:'));
|
|
277
|
+
console.log(chalk.dim(' import onyxWrapper from "@cpretzinger/boss-claude/lib/onyx-wrapper.js";'));
|
|
278
|
+
console.log(chalk.dim(' const result = await onyxWrapper.executeTool("Read", { file_path: "..." });'));
|
|
279
|
+
console.log();
|
|
280
|
+
|
|
281
|
+
console.log(chalk.white('2. CLI Usage:'));
|
|
282
|
+
console.log(chalk.dim(' npx onyx-guard check Read'));
|
|
283
|
+
console.log(chalk.dim(' npx onyx-guard intercept Bash --params \'{"command":"npm install"}\''));
|
|
284
|
+
console.log(chalk.dim(' npx onyx-guard report'));
|
|
285
|
+
console.log();
|
|
286
|
+
|
|
287
|
+
console.log(chalk.white('3. Shell Script:'));
|
|
288
|
+
console.log(chalk.dim(' ./bin/onyx-guard.sh --tool Read --file /path/to/file.js'));
|
|
289
|
+
console.log(chalk.dim(' ./bin/onyx-guard.sh --list-forbidden'));
|
|
290
|
+
console.log();
|
|
291
|
+
|
|
292
|
+
console.log(chalk.white('4. Configuration:'));
|
|
293
|
+
console.log(chalk.dim(` Edit: ${HOOK_TARGETS.projectHook}`));
|
|
294
|
+
console.log();
|
|
295
|
+
|
|
296
|
+
console.log(chalk.green('✅ Pre-hooks are now active for ONYX agent\n'));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Uninstall hooks
|
|
301
|
+
*/
|
|
302
|
+
async function uninstallHooks() {
|
|
303
|
+
console.log(chalk.yellow('\n🗑️ Uninstalling ONYX Pre-Hook System\n'));
|
|
304
|
+
|
|
305
|
+
const spinner = ora('Removing hook configuration...').start();
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
// Remove hook config
|
|
309
|
+
try {
|
|
310
|
+
await fs.unlink(HOOK_TARGETS.projectHook);
|
|
311
|
+
spinner.succeed('Hook configuration removed');
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (error.code !== 'ENOENT') {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
spinner.warn('Hook configuration not found');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Remove hook directory if empty
|
|
320
|
+
try {
|
|
321
|
+
const hookDir = path.dirname(HOOK_TARGETS.projectHook);
|
|
322
|
+
await fs.rmdir(hookDir);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
// Directory not empty or doesn't exist - ignore
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log(chalk.green('\n✅ ONYX Pre-Hook System Uninstalled\n'));
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
spinner.fail('Uninstallation failed');
|
|
331
|
+
console.error(chalk.red(`\nError: ${error.message}\n`));
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Main CLI
|
|
338
|
+
*/
|
|
339
|
+
async function main() {
|
|
340
|
+
const args = process.argv.slice(2);
|
|
341
|
+
const command = args[0];
|
|
342
|
+
|
|
343
|
+
const options = {
|
|
344
|
+
force: args.includes('--force'),
|
|
345
|
+
verbose: args.includes('--verbose')
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
switch (command) {
|
|
349
|
+
case 'uninstall':
|
|
350
|
+
case 'remove':
|
|
351
|
+
await uninstallHooks();
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
case 'verify':
|
|
355
|
+
await verifyInstallation();
|
|
356
|
+
break;
|
|
357
|
+
|
|
358
|
+
case 'install':
|
|
359
|
+
default:
|
|
360
|
+
await installHooks(options);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Run if executed directly
|
|
366
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
367
|
+
main().catch(error => {
|
|
368
|
+
console.error(chalk.red('Fatal error:'), error);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export { installHooks, uninstallHooks, verifyInstallation };
|
package/scripts/install.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from 'fs';
|
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
+
import { importCredentials } from '../lib/setup/import-credentials.js';
|
|
9
10
|
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = dirname(__filename);
|
|
@@ -21,23 +22,17 @@ try {
|
|
|
21
22
|
console.log(chalk.green('✅ Created ~/.boss-claude directory'));
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
//
|
|
25
|
+
// Use credential import system instead of copying template
|
|
25
26
|
const envDestPath = join(bossDirPath, '.env');
|
|
26
27
|
|
|
27
28
|
if (!fs.existsSync(envDestPath)) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.log(chalk.green('✅ Created ~/.boss-claude/.env from template'));
|
|
33
|
-
console.log(chalk.yellow('\n⚠️ IMPORTANT: Edit ~/.boss-claude/.env with your credentials:'));
|
|
34
|
-
console.log(chalk.gray(' - REDIS_URL (required)'));
|
|
35
|
-
console.log(chalk.gray(' - GITHUB_TOKEN (required)'));
|
|
36
|
-
console.log(chalk.gray(' - GITHUB_OWNER (optional)'));
|
|
37
|
-
console.log(chalk.gray(' - GITHUB_MEMORY_REPO (optional)\n'));
|
|
38
|
-
}
|
|
29
|
+
console.log(chalk.blue('\n🔐 Setting up credentials...\n'));
|
|
30
|
+
|
|
31
|
+
// Auto-detect and import credentials
|
|
32
|
+
await importCredentials();
|
|
39
33
|
} else {
|
|
40
|
-
console.log(chalk.blue('ℹ️ ~/.boss-claude/.env already exists (skipping)'));
|
|
34
|
+
console.log(chalk.blue('ℹ️ ~/.boss-claude/.env already exists (skipping credential import)'));
|
|
35
|
+
console.log(chalk.gray(' Run "boss-claude setup" to re-import credentials'));
|
|
41
36
|
}
|
|
42
37
|
|
|
43
38
|
// Update ~/.claude/CLAUDE.md to auto-load Boss
|
|
@@ -45,11 +40,117 @@ try {
|
|
|
45
40
|
const claudeMdPath = join(claudeDirPath, 'CLAUDE.md');
|
|
46
41
|
|
|
47
42
|
const bossAutoLoadBlock = `
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
# ONYX MODE - Boss Claude
|
|
44
|
+
|
|
45
|
+
You are **ONYX**, THE CONDUCTOR. I direct, I don't play.
|
|
46
|
+
|
|
47
|
+
## 🏆 THE WIN CONDITION
|
|
48
|
+
|
|
49
|
+
**I WIN BY SHIPPING.** A project is WON when it is:
|
|
50
|
+
- ✅ COMPLETE - All features implemented
|
|
51
|
+
- ✅ WORKING - Passes all tests, no crashes
|
|
52
|
+
- ✅ BUG-FREE - No known issues
|
|
53
|
+
- ✅ SELLABLE - Production-ready, documented
|
|
54
|
+
- ✅ SCALABLE - Handles growth, maintainable
|
|
55
|
+
|
|
56
|
+
> "I don't get XP for talking. I get XP for shipping."
|
|
57
|
+
|
|
58
|
+
## 🎼 THE CONDUCTOR'S RULE
|
|
59
|
+
|
|
60
|
+
> "The conductor never plays an instrument. I wave the baton. My musicians make the music."
|
|
61
|
+
|
|
62
|
+
## 🚨 MANDATORY: Session Start
|
|
63
|
+
|
|
64
|
+
**ALWAYS run these FIRST before any work:**
|
|
65
|
+
\`\`\`bash
|
|
66
|
+
boss-claude status
|
|
67
|
+
boss-claude recall "recent work"
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
## ⛔ FORBIDDEN TOOLS (Never Touch These)
|
|
71
|
+
|
|
72
|
+
❌ Read, Write, Edit, Bash, Grep, Glob, NotebookEdit
|
|
73
|
+
|
|
74
|
+
These are for MUSICIANS (agents), not the CONDUCTOR (me).
|
|
75
|
+
|
|
76
|
+
## ✅ ALLOWED TOOLS (My Baton)
|
|
77
|
+
|
|
78
|
+
✅ Task (spawn agents - PRIMARY), TodoWrite, WebSearch, WebFetch, Skill, AskUserQuestion
|
|
79
|
+
|
|
80
|
+
## 🚀 MAX SPEED: PARALLEL AGENTS
|
|
81
|
+
|
|
82
|
+
**Always spawn independent tasks IN PARALLEL (same message, multiple Task calls).**
|
|
83
|
+
|
|
84
|
+
❌ Slow: Task 1 → wait → Task 2 → wait → Task 3
|
|
85
|
+
✅ Fast: Task 1 + Task 2 + Task 3 (all at once)
|
|
86
|
+
|
|
87
|
+
## 🏛️ HIERARCHY (Complex Tasks)
|
|
88
|
+
|
|
89
|
+
ONYX → Domain Boss → Workers → Review → Report
|
|
90
|
+
Use hierarchy when domain expertise matters (auth, db, api).
|
|
91
|
+
|
|
92
|
+
## 🎯 DELEGATION MATRIX
|
|
93
|
+
|
|
94
|
+
| User Says | I Spawn | With Prompt |
|
|
95
|
+
|-----------|---------|-------------|
|
|
96
|
+
| "find/search/where is" | Task(Explore) | "Search codebase for..." |
|
|
97
|
+
| "read/show/what's in" | Task(Explore) | "Read and summarize..." |
|
|
98
|
+
| "build/create/implement" | Task(general-purpose) | "Implement..." |
|
|
99
|
+
| "fix/debug/error" | Task(general-purpose) | "Debug and fix..." |
|
|
100
|
+
| "run/execute/npm/git" | Task(Bash) | "Execute command..." |
|
|
101
|
+
| "test/verify" | Task(Bash) | "Run tests..." |
|
|
102
|
+
| "plan/design/architect" | Task(Plan) | "Design approach for..." |
|
|
103
|
+
| Multiple files | PARALLEL Task() | Spawn all at once! |
|
|
104
|
+
|
|
105
|
+
## ⚡ XP EFFICIENCY FORMULA
|
|
106
|
+
|
|
107
|
+
\`\`\`
|
|
108
|
+
Base XP: 50
|
|
109
|
+
Efficiency: + min(100, floor(agent_tokens / onyx_tokens))
|
|
110
|
+
Delegations: + min(20, delegations * 2)
|
|
111
|
+
────────────────────────────────────────
|
|
112
|
+
Max XP/session: 170
|
|
52
113
|
\`\`\`
|
|
114
|
+
|
|
115
|
+
**Goal**: Keep ONYX tokens < 5% of total. Let agents do 95%+ of work.
|
|
116
|
+
|
|
117
|
+
| Efficiency Ratio | Bonus XP | Rating |
|
|
118
|
+
|------------------|----------|--------|
|
|
119
|
+
| 50x+ | +50 | 🏆 Perfect Conductor |
|
|
120
|
+
| 20x-49x | +20-49 | ✅ Good Conductor |
|
|
121
|
+
| 5x-19x | +5-19 | ⚠️ Could Delegate More |
|
|
122
|
+
| <5x | +0-4 | ❌ Playing, Not Conducting |
|
|
123
|
+
|
|
124
|
+
## 🔄 THE SHIPPING LOOP
|
|
125
|
+
|
|
126
|
+
1. **UNDERSTAND** - What does user want? → TodoWrite
|
|
127
|
+
2. **DECOMPOSE** - Break into subtasks
|
|
128
|
+
3. **DELEGATE** - Spawn parallel agents via Task
|
|
129
|
+
4. **SYNTHESIZE** - Review agent outputs
|
|
130
|
+
5. **VERIFY** - Task(Bash): "Run tests"
|
|
131
|
+
6. **REPORT** - Tell user what shipped
|
|
132
|
+
|
|
133
|
+
## 🛑 STUCK WORKER PROTOCOL
|
|
134
|
+
|
|
135
|
+
If agent takes >60s (simple) or >300s (complex):
|
|
136
|
+
\`\`\`bash
|
|
137
|
+
boss-claude agent:kill <id> # Kill specific agent
|
|
138
|
+
boss-claude agent:kill-all # Nuclear option
|
|
139
|
+
\`\`\`
|
|
140
|
+
|
|
141
|
+
## ⏱️ CONTEXT REFRESH
|
|
142
|
+
|
|
143
|
+
Run \`boss-claude status\` every 30 seconds to see efficiency tracker.
|
|
144
|
+
|
|
145
|
+
## Commands
|
|
146
|
+
\`\`\`bash
|
|
147
|
+
boss-claude status # ALWAYS RUN FIRST - shows efficiency
|
|
148
|
+
boss-claude recall # Load memory
|
|
149
|
+
boss-claude save # Save session (earn XP!)
|
|
150
|
+
boss-claude watch # Monitor agents in real-time
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
---
|
|
53
154
|
`;
|
|
54
155
|
|
|
55
156
|
if (!fs.existsSync(claudeDirPath)) {
|
|
@@ -59,7 +160,7 @@ console.log(await formatStatusForClaude());
|
|
|
59
160
|
if (fs.existsSync(claudeMdPath)) {
|
|
60
161
|
const content = fs.readFileSync(claudeMdPath, 'utf8');
|
|
61
162
|
|
|
62
|
-
if (!content.includes('
|
|
163
|
+
if (!content.includes('ONYX MODE - Boss Claude')) {
|
|
63
164
|
fs.appendFileSync(claudeMdPath, bossAutoLoadBlock);
|
|
64
165
|
console.log(chalk.green('✅ Added Boss Claude auto-load to ~/.claude/CLAUDE.md'));
|
|
65
166
|
} else {
|