@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,131 @@
1
+ /**
2
+ * AUTO-MONITOR
3
+ *
4
+ * Automatic token monitoring that wraps around Boss Claude operations.
5
+ * Can be used as a drop-in middleware to enforce delegation rules.
6
+ */
7
+
8
+ import tokenMonitor from './token-monitor.js';
9
+ import hierarchyValidator from './hierarchy-validator.js';
10
+ import chalk from 'chalk';
11
+
12
+ /**
13
+ * Auto-monitored operation wrapper
14
+ *
15
+ * Usage:
16
+ * const result = await autoMonitor.execute('Search codebase', async () => {
17
+ * // Your operation here
18
+ * return await searchCodebase();
19
+ * }, { estimatedTokens: 250 });
20
+ *
21
+ * @param {string} description - Operation description
22
+ * @param {Function} operation - Async operation to execute
23
+ * @param {Object} options - Options
24
+ * @param {number} options.estimatedTokens - Estimated token cost
25
+ * @param {boolean} options.forceDelegation - Force delegation check
26
+ * @param {string} options.agent - Agent performing operation (default: 'Boss Claude')
27
+ * @returns {Promise<Object>} Result with tokens used and operation ID
28
+ */
29
+ export async function execute(description, operation, options = {}) {
30
+ const {
31
+ estimatedTokens = 0,
32
+ forceDelegation = false,
33
+ agent = 'Boss Claude'
34
+ } = options;
35
+
36
+ // Start monitoring
37
+ const opId = tokenMonitor.startOperation(description, estimatedTokens);
38
+
39
+ try {
40
+ // Check if delegation is recommended
41
+ if (estimatedTokens > 100 || forceDelegation) {
42
+ const delegation = await hierarchyValidator.checkDelegation(
43
+ description,
44
+ estimatedTokens,
45
+ agent
46
+ );
47
+
48
+ if (delegation.shouldDelegate && !delegation.override) {
49
+ // User chose to delegate - record it
50
+ tokenMonitor.recordDelegation(opId, delegation.agent);
51
+
52
+ return {
53
+ delegated: true,
54
+ agent: delegation.agent,
55
+ opId,
56
+ message: `Operation delegated to ${delegation.agent}`
57
+ };
58
+ }
59
+
60
+ if (delegation.override) {
61
+ // User overrode delegation - will be monitored closely
62
+ console.log(chalk.yellow(`⚠️ Override logged - monitoring token usage`));
63
+ }
64
+ }
65
+
66
+ // Execute operation
67
+ const startTime = Date.now();
68
+ const result = await operation();
69
+ const duration = Date.now() - startTime;
70
+
71
+ // Estimate tokens used (rough heuristic: 1 token ≈ 2ms for I/O operations)
72
+ const estimatedActualTokens = Math.floor(duration / 2);
73
+
74
+ // Update monitor with actual tokens
75
+ tokenMonitor.addTokens(opId, estimatedActualTokens);
76
+
77
+ // Complete operation
78
+ tokenMonitor.completeOperation(opId);
79
+
80
+ return {
81
+ success: true,
82
+ result,
83
+ opId,
84
+ tokensUsed: estimatedActualTokens,
85
+ duration
86
+ };
87
+ } catch (error) {
88
+ // Log error and complete operation
89
+ console.error(chalk.red(`❌ Operation failed: ${error.message}`));
90
+ tokenMonitor.completeOperation(opId);
91
+
92
+ throw error;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Quick delegation check without execution
98
+ */
99
+ export function shouldDelegate(estimatedTokens) {
100
+ return tokenMonitor.shouldDelegate(estimatedTokens);
101
+ }
102
+
103
+ /**
104
+ * Display session summary
105
+ */
106
+ export function showSummary() {
107
+ tokenMonitor.displaySummary();
108
+ }
109
+
110
+ /**
111
+ * Reset session
112
+ */
113
+ export function reset() {
114
+ tokenMonitor.resetSession();
115
+ }
116
+
117
+ /**
118
+ * Get violation count for current session
119
+ */
120
+ export function getViolations() {
121
+ const summary = tokenMonitor.getSessionSummary();
122
+ return summary.violations;
123
+ }
124
+
125
+ export default {
126
+ execute,
127
+ shouldDelegate,
128
+ showSummary,
129
+ reset,
130
+ getViolations
131
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * CHECKPOINT AUTOMATION HOOK
3
+ * Automatically triggers checkpoint every 5 messages
4
+ * Integrated with Boss Claude session tracking
5
+ */
6
+
7
+ import { incrementMessage, formatCheckpointPrompt, getCheckpointStatus } from './checkpoint.js';
8
+
9
+ /**
10
+ * Pre-message hook - call before ONYX processes each message
11
+ * Returns checkpoint prompt if checkpoint needed
12
+ */
13
+ export async function preMessageHook() {
14
+ try {
15
+ const checkpoint = await incrementMessage();
16
+
17
+ if (checkpoint) {
18
+ // Checkpoint triggered - return prompt
19
+ return {
20
+ needsCheckpoint: true,
21
+ prompt: formatCheckpointPrompt(checkpoint),
22
+ checkpoint
23
+ };
24
+ }
25
+
26
+ return {
27
+ needsCheckpoint: false
28
+ };
29
+ } catch (error) {
30
+ console.error('Checkpoint hook error:', error.message);
31
+ return { needsCheckpoint: false };
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Post-message hook - track ONYX activity
37
+ * Can be used to auto-detect delegation vs direct execution
38
+ */
39
+ export async function postMessageHook(messageData) {
40
+ // Future: Auto-detect delegation from message patterns
41
+ // Look for keywords: "delegating to", "assigning to", specialist names
42
+ const specialists = [
43
+ 'n8n-workflow-architect',
44
+ 'postgres-n8n-specialist',
45
+ 'redis-architect',
46
+ 'github-expert',
47
+ 'automation-architect',
48
+ 'openai-streaming-expert',
49
+ 'data-science-pipeline'
50
+ ];
51
+
52
+ const message = messageData.content?.toLowerCase() || '';
53
+
54
+ const delegationDetected = specialists.some(s =>
55
+ message.includes(s.toLowerCase()) ||
56
+ message.includes('delegate') ||
57
+ message.includes('assigning to')
58
+ );
59
+
60
+ return {
61
+ delegation_detected: delegationDetected,
62
+ specialist: specialists.find(s => message.includes(s.toLowerCase()))
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Generate checkpoint reminder for ONYX
68
+ */
69
+ export function generateCheckpointReminder(messagesUntilCheckpoint) {
70
+ if (messagesUntilCheckpoint <= 2) {
71
+ return `\n⚠️ CHECKPOINT in ${messagesUntilCheckpoint} message${messagesUntilCheckpoint === 1 ? '' : 's'}\n`;
72
+ }
73
+ return '';
74
+ }
75
+
76
+ /**
77
+ * Validate checkpoint response
78
+ */
79
+ export function validateCheckpointResponse(response) {
80
+ const required = ['choice', 'tokens', 'justification'];
81
+ const missing = required.filter(field => !(field in response));
82
+
83
+ if (missing.length > 0) {
84
+ return {
85
+ valid: false,
86
+ error: `Missing required fields: ${missing.join(', ')}`
87
+ };
88
+ }
89
+
90
+ if (![1, 2].includes(response.choice)) {
91
+ return {
92
+ valid: false,
93
+ error: 'Choice must be 1 (delegated) or 2 (burned)'
94
+ };
95
+ }
96
+
97
+ if (typeof response.tokens !== 'number' || response.tokens < 0) {
98
+ return {
99
+ valid: false,
100
+ error: 'Tokens must be a positive number'
101
+ };
102
+ }
103
+
104
+ if (response.choice === 1 && !response.specialist) {
105
+ return {
106
+ valid: false,
107
+ error: 'Specialist required when delegation chosen'
108
+ };
109
+ }
110
+
111
+ return { valid: true };
112
+ }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * ONYX CHECKPOINT SYSTEM
3
+ * Pauses every 5 messages to enforce delegation accountability
4
+ * Rule: "Did you delegate or burn tokens?"
5
+ */
6
+
7
+ import Redis from 'ioredis';
8
+ import dotenv from 'dotenv';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname, join } from 'path';
11
+ import { existsSync } from 'fs';
12
+ import os from 'os';
13
+ import { exec } from 'child_process';
14
+ import { promisify } from 'util';
15
+
16
+ const execAsync = promisify(exec);
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ // Load environment variables from ~/.boss-claude/.env
22
+ const envPath = join(os.homedir(), '.boss-claude', '.env');
23
+ if (existsSync(envPath)) {
24
+ dotenv.config({ path: envPath });
25
+ }
26
+
27
+ let redis = null;
28
+
29
+ function getRedis() {
30
+ if (!redis) {
31
+ if (!process.env.REDIS_URL) {
32
+ throw new Error('REDIS_URL not found. Please run: boss-claude init');
33
+ }
34
+ redis = new Redis(process.env.REDIS_URL);
35
+ }
36
+ return redis;
37
+ }
38
+
39
+ async function getCurrentRepo() {
40
+ try {
41
+ const { stdout: repoPath } = await execAsync('git rev-parse --show-toplevel');
42
+ const { stdout: repoUrl } = await execAsync('git config --get remote.origin.url');
43
+ const repoName = repoUrl.trim().split('/').pop().replace('.git', '');
44
+
45
+ return {
46
+ name: repoName,
47
+ path: repoPath.trim(),
48
+ url: repoUrl.trim()
49
+ };
50
+ } catch (error) {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Initialize checkpoint tracking for current session
57
+ */
58
+ export async function initCheckpoint() {
59
+ const repo = await getCurrentRepo();
60
+ if (!repo) {
61
+ throw new Error('Not in a git repository');
62
+ }
63
+
64
+ const client = getRedis();
65
+ const checkpointKey = `boss:checkpoint:${repo.name}:current`;
66
+
67
+ const checkpoint = {
68
+ repo: repo.name,
69
+ started_at: new Date().toISOString(),
70
+ message_count: 0,
71
+ checkpoints_passed: 0,
72
+ delegation_count: 0,
73
+ token_burn_count: 0,
74
+ last_checkpoint: null,
75
+ total_tokens_saved: 0,
76
+ total_tokens_burned: 0
77
+ };
78
+
79
+ await client.set(checkpointKey, JSON.stringify(checkpoint));
80
+ return checkpoint;
81
+ }
82
+
83
+ /**
84
+ * Increment message counter and check if checkpoint needed
85
+ * Returns checkpoint data if checkpoint triggered, null otherwise
86
+ */
87
+ export async function incrementMessage() {
88
+ const repo = await getCurrentRepo();
89
+ if (!repo) {
90
+ return null;
91
+ }
92
+
93
+ const client = getRedis();
94
+ const checkpointKey = `boss:checkpoint:${repo.name}:current`;
95
+
96
+ let checkpoint = await client.get(checkpointKey);
97
+ if (!checkpoint) {
98
+ checkpoint = await initCheckpoint();
99
+ } else {
100
+ checkpoint = JSON.parse(checkpoint);
101
+ }
102
+
103
+ checkpoint.message_count++;
104
+
105
+ // Check if checkpoint needed (every 5 messages)
106
+ const needsCheckpoint = checkpoint.message_count % 5 === 0;
107
+
108
+ if (needsCheckpoint) {
109
+ checkpoint.last_checkpoint = new Date().toISOString();
110
+ checkpoint.awaiting_response = true;
111
+ }
112
+
113
+ await client.set(checkpointKey, JSON.stringify(checkpoint));
114
+
115
+ return needsCheckpoint ? checkpoint : null;
116
+ }
117
+
118
+ /**
119
+ * Record delegation decision at checkpoint
120
+ * @param {boolean} delegated - Did ONYX delegate the task?
121
+ * @param {number} tokensSaved - Estimated tokens saved by delegation (if delegated)
122
+ * @param {number} tokensBurned - Actual tokens used (if not delegated)
123
+ * @param {string} justification - Reason for decision
124
+ * @param {string} specialist - Which specialist was used (if delegated)
125
+ */
126
+ export async function recordCheckpoint(delegated, tokensSaved = 0, tokensBurned = 0, justification = '', specialist = '') {
127
+ const repo = await getCurrentRepo();
128
+ if (!repo) {
129
+ throw new Error('Not in a git repository');
130
+ }
131
+
132
+ const client = getRedis();
133
+ const checkpointKey = `boss:checkpoint:${repo.name}:current`;
134
+
135
+ let checkpoint = await client.get(checkpointKey);
136
+ if (!checkpoint) {
137
+ throw new Error('No active checkpoint found');
138
+ }
139
+
140
+ checkpoint = JSON.parse(checkpoint);
141
+
142
+ if (!checkpoint.awaiting_response) {
143
+ throw new Error('No checkpoint awaiting response');
144
+ }
145
+
146
+ // Update checkpoint stats
147
+ checkpoint.checkpoints_passed++;
148
+ checkpoint.awaiting_response = false;
149
+
150
+ if (delegated) {
151
+ checkpoint.delegation_count++;
152
+ checkpoint.total_tokens_saved += tokensSaved;
153
+ } else {
154
+ checkpoint.token_burn_count++;
155
+ checkpoint.total_tokens_burned += tokensBurned;
156
+ }
157
+
158
+ // Save checkpoint record to history
159
+ const historyKey = `boss:checkpoint:${repo.name}:history`;
160
+ const record = {
161
+ timestamp: checkpoint.last_checkpoint,
162
+ message_count: checkpoint.message_count,
163
+ delegated,
164
+ tokens_saved: tokensSaved,
165
+ tokens_burned: tokensBurned,
166
+ justification,
167
+ specialist
168
+ };
169
+
170
+ await client.lpush(historyKey, JSON.stringify(record));
171
+ await client.ltrim(historyKey, 0, 99); // Keep last 100 checkpoints
172
+
173
+ // Update current checkpoint
174
+ await client.set(checkpointKey, JSON.stringify(checkpoint));
175
+
176
+ return {
177
+ checkpoint,
178
+ record
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Get current checkpoint status
184
+ */
185
+ export async function getCheckpointStatus() {
186
+ const repo = await getCurrentRepo();
187
+ if (!repo) {
188
+ return null;
189
+ }
190
+
191
+ const client = getRedis();
192
+ const checkpointKey = `boss:checkpoint:${repo.name}:current`;
193
+
194
+ const checkpoint = await client.get(checkpointKey);
195
+ if (!checkpoint) {
196
+ return null;
197
+ }
198
+
199
+ return JSON.parse(checkpoint);
200
+ }
201
+
202
+ /**
203
+ * Get checkpoint history
204
+ * @param {number} limit - Number of records to retrieve
205
+ */
206
+ export async function getCheckpointHistory(limit = 20) {
207
+ const repo = await getCurrentRepo();
208
+ if (!repo) {
209
+ return [];
210
+ }
211
+
212
+ const client = getRedis();
213
+ const historyKey = `boss:checkpoint:${repo.name}:history`;
214
+
215
+ const records = await client.lrange(historyKey, 0, limit - 1);
216
+ return records.map(r => JSON.parse(r));
217
+ }
218
+
219
+ /**
220
+ * Calculate delegation efficiency
221
+ */
222
+ export async function getDelegationEfficiency() {
223
+ const checkpoint = await getCheckpointStatus();
224
+ if (!checkpoint || checkpoint.checkpoints_passed === 0) {
225
+ return {
226
+ delegation_rate: 0,
227
+ total_checkpoints: 0,
228
+ tokens_saved: 0,
229
+ tokens_burned: 0,
230
+ efficiency_score: 0
231
+ };
232
+ }
233
+
234
+ const delegation_rate = (checkpoint.delegation_count / checkpoint.checkpoints_passed) * 100;
235
+ const efficiency_score = checkpoint.total_tokens_saved > 0
236
+ ? ((checkpoint.total_tokens_saved - checkpoint.total_tokens_burned) / checkpoint.total_tokens_saved) * 100
237
+ : 0;
238
+
239
+ return {
240
+ delegation_rate: delegation_rate.toFixed(1),
241
+ total_checkpoints: checkpoint.checkpoints_passed,
242
+ delegations: checkpoint.delegation_count,
243
+ token_burns: checkpoint.token_burn_count,
244
+ tokens_saved: checkpoint.total_tokens_saved,
245
+ tokens_burned: checkpoint.total_tokens_burned,
246
+ net_savings: checkpoint.total_tokens_saved - checkpoint.total_tokens_burned,
247
+ efficiency_score: efficiency_score.toFixed(1)
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Format checkpoint prompt for ONYX
253
+ */
254
+ export function formatCheckpointPrompt(checkpoint) {
255
+ const efficiency = checkpoint.checkpoints_passed > 0
256
+ ? ((checkpoint.delegation_count / checkpoint.checkpoints_passed) * 100).toFixed(1)
257
+ : 0;
258
+
259
+ return `
260
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
261
+ ⚠️ CHECKPOINT #${checkpoint.checkpoints_passed + 1}
262
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
263
+
264
+ 📊 Session Stats:
265
+ Messages: ${checkpoint.message_count}
266
+ Checkpoints Passed: ${checkpoint.checkpoints_passed}
267
+ Delegation Rate: ${efficiency}%
268
+ Tokens Saved: ${checkpoint.total_tokens_saved.toLocaleString()}
269
+ Tokens Burned: ${checkpoint.total_tokens_burned.toLocaleString()}
270
+
271
+ 🔍 ACCOUNTABILITY QUESTION:
272
+
273
+ Did you delegate or burn tokens?
274
+
275
+ Options:
276
+ 1. DELEGATED - I assigned work to a specialist
277
+ 2. BURNED - I did it myself (justify why)
278
+
279
+ Respond with:
280
+ - Choice: [1 or 2]
281
+ - Tokens: [saved if delegated, burned if not]
282
+ - Specialist: [name if delegated]
283
+ - Justification: [brief reason]
284
+
285
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
286
+ `;
287
+ }
288
+
289
+ /**
290
+ * Reset checkpoint for new session
291
+ */
292
+ export async function resetCheckpoint() {
293
+ const repo = await getCurrentRepo();
294
+ if (!repo) {
295
+ throw new Error('Not in a git repository');
296
+ }
297
+
298
+ const client = getRedis();
299
+ const checkpointKey = `boss:checkpoint:${repo.name}:current`;
300
+ await client.del(checkpointKey);
301
+
302
+ return await initCheckpoint();
303
+ }
304
+
305
+ /**
306
+ * Export checkpoint data for analysis
307
+ */
308
+ export async function exportCheckpointData() {
309
+ const checkpoint = await getCheckpointStatus();
310
+ const history = await getCheckpointHistory(100);
311
+ const efficiency = await getDelegationEfficiency();
312
+
313
+ return {
314
+ current: checkpoint,
315
+ history,
316
+ efficiency,
317
+ exported_at: new Date().toISOString()
318
+ };
319
+ }