@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,235 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * TASK AGENT WORKER
4
+ *
5
+ * Executes delegated operations via Task tool.
6
+ * Handles the actual execution of auto-delegated tool calls.
7
+ */
8
+
9
+ import chalk from 'chalk';
10
+
11
+ /**
12
+ * Task Agent Worker Class
13
+ *
14
+ * Processes delegated tool operations by calling Task tool
15
+ */
16
+ export class TaskAgentWorker {
17
+ constructor(config = {}) {
18
+ this.taskExecutor = config.taskExecutor || null;
19
+ this.executionLog = [];
20
+ this.successCount = 0;
21
+ this.failureCount = 0;
22
+ }
23
+
24
+ /**
25
+ * Execute delegated tool call via Task
26
+ *
27
+ * @param {Object} delegation - Delegation object from tool-wrapper
28
+ * @returns {Promise<Object>} Execution result
29
+ */
30
+ async executeDelegation(delegation) {
31
+ const startTime = Date.now();
32
+
33
+ console.log(chalk.cyan('\n📤 Task Agent Processing Delegation'));
34
+ console.log(chalk.dim('─'.repeat(60)));
35
+ console.log(chalk.white(`Tool: ${chalk.bold(delegation.originalTool)}`));
36
+ console.log(chalk.white(`Description: ${delegation.description}`));
37
+ console.log(chalk.dim(`Strategy: ${delegation.strategy}`));
38
+ console.log(chalk.dim('─'.repeat(60)));
39
+
40
+ try {
41
+ // Execute via Task tool
42
+ const result = await this._executeViaTask(delegation);
43
+
44
+ this.successCount++;
45
+ const executionTime = Date.now() - startTime;
46
+
47
+ const logEntry = {
48
+ timestamp: new Date().toISOString(),
49
+ delegation,
50
+ result,
51
+ success: true,
52
+ executionTime
53
+ };
54
+
55
+ this.executionLog.push(logEntry);
56
+
57
+ console.log(chalk.green(`✅ Delegation completed (${executionTime}ms)`));
58
+
59
+ return {
60
+ success: true,
61
+ delegated: true,
62
+ result,
63
+ executionTime,
64
+ originalTool: delegation.originalTool,
65
+ strategy: delegation.strategy
66
+ };
67
+
68
+ } catch (error) {
69
+ this.failureCount++;
70
+ const executionTime = Date.now() - startTime;
71
+
72
+ const logEntry = {
73
+ timestamp: new Date().toISOString(),
74
+ delegation,
75
+ error: error.message,
76
+ success: false,
77
+ executionTime
78
+ };
79
+
80
+ this.executionLog.push(logEntry);
81
+
82
+ console.log(chalk.red(`❌ Delegation failed: ${error.message}`));
83
+
84
+ return {
85
+ success: false,
86
+ delegated: true,
87
+ error: error.message,
88
+ executionTime,
89
+ originalTool: delegation.originalTool,
90
+ strategy: delegation.strategy
91
+ };
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Execute via Task tool
97
+ */
98
+ async _executeViaTask(delegation) {
99
+ if (!this.taskExecutor) {
100
+ // No task executor provided - return mock result
101
+ console.log(chalk.yellow('⚠️ No task executor configured - returning mock result'));
102
+
103
+ return {
104
+ mock: true,
105
+ message: 'Task would be executed here',
106
+ delegation
107
+ };
108
+ }
109
+
110
+ // Call provided task executor
111
+ if (typeof this.taskExecutor === 'function') {
112
+ const taskParams = {
113
+ description: delegation.description,
114
+ context: {
115
+ originalTool: delegation.originalTool,
116
+ originalParams: delegation.originalParams,
117
+ delegatedBy: 'ToolWrapper',
118
+ strategy: delegation.strategy,
119
+ details: delegation.details
120
+ }
121
+ };
122
+
123
+ return await this.taskExecutor(taskParams);
124
+ }
125
+
126
+ throw new Error('Invalid task executor - must be a function');
127
+ }
128
+
129
+ /**
130
+ * Set task executor function
131
+ */
132
+ setTaskExecutor(executor) {
133
+ if (typeof executor !== 'function') {
134
+ throw new Error('Task executor must be a function');
135
+ }
136
+ this.taskExecutor = executor;
137
+ console.log(chalk.green('✅ Task executor configured'));
138
+ }
139
+
140
+ /**
141
+ * Get execution statistics
142
+ */
143
+ getStats() {
144
+ const total = this.successCount + this.failureCount;
145
+ const successRate = total > 0
146
+ ? (this.successCount / total * 100).toFixed(2)
147
+ : 0;
148
+
149
+ const avgExecutionTime = this.executionLog.length > 0
150
+ ? this.executionLog.reduce((sum, entry) => sum + entry.executionTime, 0) / this.executionLog.length
151
+ : 0;
152
+
153
+ return {
154
+ total,
155
+ successCount: this.successCount,
156
+ failureCount: this.failureCount,
157
+ successRate: parseFloat(successRate),
158
+ avgExecutionTime: Math.round(avgExecutionTime),
159
+ recentExecutions: this.executionLog.slice(-10)
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Print execution report
165
+ */
166
+ printReport() {
167
+ const stats = this.getStats();
168
+
169
+ console.log(chalk.blue('\n📊 TASK AGENT WORKER REPORT'));
170
+ console.log(chalk.dim('═'.repeat(60)));
171
+ console.log(chalk.white(`Total Delegations: ${chalk.magenta(stats.total)}`));
172
+ console.log(chalk.white(`Successful: ${chalk.green(stats.successCount)}`));
173
+ console.log(chalk.white(`Failed: ${chalk.red(stats.failureCount)}`));
174
+ console.log(chalk.white(`Success Rate: ${chalk.cyan(stats.successRate + '%')}`));
175
+ console.log(chalk.white(`Avg Execution Time: ${chalk.yellow(stats.avgExecutionTime + 'ms')}`));
176
+ console.log(chalk.dim('═'.repeat(60)));
177
+
178
+ if (stats.recentExecutions.length > 0) {
179
+ console.log(chalk.white('\nRecent Delegations:'));
180
+ stats.recentExecutions.forEach((exec, i) => {
181
+ const status = exec.success ? chalk.green('✓') : chalk.red('✗');
182
+ const time = chalk.dim(`${exec.executionTime}ms`);
183
+ const tool = exec.delegation.originalTool;
184
+ console.log(` ${status} ${tool} - ${time}`);
185
+ });
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Get execution log
191
+ */
192
+ getLog(limit = 20) {
193
+ return this.executionLog.slice(-limit);
194
+ }
195
+
196
+ /**
197
+ * Reset worker state
198
+ */
199
+ reset() {
200
+ this.executionLog = [];
201
+ this.successCount = 0;
202
+ this.failureCount = 0;
203
+ console.log(chalk.green('✅ Task Agent Worker reset'));
204
+ }
205
+ }
206
+
207
+ // Singleton instance
208
+ const taskWorker = new TaskAgentWorker();
209
+
210
+ export default taskWorker;
211
+
212
+ /**
213
+ * INTEGRATION EXAMPLE:
214
+ *
215
+ * import taskWorker from '@cpretzinger/boss-claude/lib/task-agent-worker.js';
216
+ *
217
+ * // Configure task executor
218
+ * taskWorker.setTaskExecutor(async (taskParams) => {
219
+ * // Your Task tool implementation
220
+ * return await executeTaskTool(taskParams);
221
+ * });
222
+ *
223
+ * // Execute delegation
224
+ * const delegation = {
225
+ * description: 'Read config file',
226
+ * originalTool: 'Read',
227
+ * originalParams: { file_path: '/path/to/config.json' },
228
+ * strategy: 'ONYX_AUTO_DELEGATE'
229
+ * };
230
+ *
231
+ * const result = await taskWorker.executeDelegation(delegation);
232
+ *
233
+ * // Print report
234
+ * taskWorker.printReport();
235
+ */
@@ -0,0 +1,466 @@
1
+ import chalk from 'chalk';
2
+ import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import os from 'os';
5
+
6
+ const BOSS_DIR = join(os.homedir(), '.boss-claude');
7
+ const MONITOR_LOG = join(BOSS_DIR, 'delegation-violations.log');
8
+ const SESSION_FILE = join(BOSS_DIR, 'current-session.json');
9
+
10
+ /**
11
+ * REAL-TIME TOKEN MONITOR
12
+ *
13
+ * Screams "DELEGATION VIOLATION" if ONYX (Boss Claude) burns >100 tokens
14
+ * without using Task tool delegation.
15
+ *
16
+ * RULES:
17
+ * - Boss Claude (ONYX) must delegate tasks >100 tokens to Task tool
18
+ * - Task tool is the primary worker agent for comprehensive searches
19
+ * - Violations are logged and displayed in real-time
20
+ * - Session tracking persists across conversation turns
21
+ */
22
+
23
+ export class TokenMonitor {
24
+ constructor() {
25
+ this.threshold = 100;
26
+ this.sessionTokens = 0;
27
+ this.lastDelegation = null;
28
+ this.violations = [];
29
+ this.operations = [];
30
+ this.sessionStartTime = new Date();
31
+
32
+ // Load existing session if available
33
+ this._loadSession();
34
+ }
35
+
36
+ /**
37
+ * Start tracking an operation
38
+ * @param {string} operation - Operation description
39
+ * @param {number} estimatedTokens - Estimated token cost
40
+ * @returns {string} Operation ID
41
+ */
42
+ startOperation(operation, estimatedTokens = 0) {
43
+ const opId = `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
44
+
45
+ const op = {
46
+ id: opId,
47
+ operation,
48
+ estimatedTokens,
49
+ startTime: new Date().toISOString(),
50
+ actualTokens: 0,
51
+ delegated: false,
52
+ delegationTool: null,
53
+ completed: false
54
+ };
55
+
56
+ this.operations.push(op);
57
+
58
+ console.log(chalk.cyan(`\n📊 TOKEN MONITOR: Starting operation "${operation}"`));
59
+ console.log(chalk.dim(`Estimated tokens: ${estimatedTokens}`));
60
+
61
+ if (estimatedTokens > this.threshold) {
62
+ console.log(chalk.yellow(`⚠️ WARNING: Operation exceeds ${this.threshold} token threshold`));
63
+ console.log(chalk.yellow(`💡 RECOMMENDATION: Delegate to Task tool for comprehensive work`));
64
+ }
65
+
66
+ return opId;
67
+ }
68
+
69
+ /**
70
+ * Record delegation to Task tool or other agents
71
+ * @param {string} opId - Operation ID
72
+ * @param {string} tool - Tool name (e.g., 'Task', 'Agent', etc.)
73
+ */
74
+ recordDelegation(opId, tool = 'Task') {
75
+ const op = this.operations.find(o => o.id === opId);
76
+
77
+ if (!op) {
78
+ console.error(chalk.red(`❌ Operation ${opId} not found`));
79
+ return;
80
+ }
81
+
82
+ op.delegated = true;
83
+ op.delegationTool = tool;
84
+ op.delegationTime = new Date().toISOString();
85
+ this.lastDelegation = new Date();
86
+
87
+ console.log(chalk.green(`✅ Delegation recorded: ${tool}`));
88
+ this._saveSession();
89
+ }
90
+
91
+ /**
92
+ * Update actual token usage for an operation
93
+ * @param {string} opId - Operation ID
94
+ * @param {number} tokensUsed - Actual tokens consumed
95
+ */
96
+ addTokens(opId, tokensUsed) {
97
+ const op = this.operations.find(o => o.id === opId);
98
+
99
+ if (!op) {
100
+ console.error(chalk.red(`❌ Operation ${opId} not found`));
101
+ return;
102
+ }
103
+
104
+ op.actualTokens += tokensUsed;
105
+ this.sessionTokens += tokensUsed;
106
+
107
+ // Check for violation
108
+ if (!op.delegated && op.actualTokens > this.threshold) {
109
+ this._screamViolation(op);
110
+ }
111
+
112
+ this._saveSession();
113
+ this._displayStatus(op);
114
+ }
115
+
116
+ /**
117
+ * Complete an operation
118
+ * @param {string} opId - Operation ID
119
+ */
120
+ completeOperation(opId) {
121
+ const op = this.operations.find(o => o.id === opId);
122
+
123
+ if (!op) {
124
+ console.error(chalk.red(`❌ Operation ${opId} not found`));
125
+ return;
126
+ }
127
+
128
+ op.completed = true;
129
+ op.endTime = new Date().toISOString();
130
+
131
+ // Final violation check
132
+ if (!op.delegated && op.actualTokens > this.threshold) {
133
+ this._screamViolation(op);
134
+ } else if (op.delegated) {
135
+ console.log(chalk.green(`\n✅ Operation completed with proper delegation`));
136
+ console.log(chalk.dim(`Tokens used: ${op.actualTokens} | Tool: ${op.delegationTool}`));
137
+ }
138
+
139
+ this._saveSession();
140
+ }
141
+
142
+ /**
143
+ * SCREAM DELEGATION VIOLATION
144
+ * @param {Object} op - Operation object
145
+ */
146
+ _screamViolation(op) {
147
+ const violation = {
148
+ timestamp: new Date().toISOString(),
149
+ operation: op.operation,
150
+ tokensUsed: op.actualTokens,
151
+ threshold: this.threshold,
152
+ excess: op.actualTokens - this.threshold,
153
+ estimatedTokens: op.estimatedTokens,
154
+ severity: this._calculateSeverity(op.actualTokens)
155
+ };
156
+
157
+ this.violations.push(violation);
158
+
159
+ // SCREAM TO CONSOLE
160
+ console.log('\n' + chalk.bgRed.white.bold('═'.repeat(80)));
161
+ console.log(chalk.bgRed.white.bold('║' + ' '.repeat(78) + '║'));
162
+ console.log(chalk.bgRed.white.bold('║' + ' '.repeat(25) + '🚨 DELEGATION VIOLATION 🚨' + ' '.repeat(26) + '║'));
163
+ console.log(chalk.bgRed.white.bold('║' + ' '.repeat(78) + '║'));
164
+ console.log(chalk.bgRed.white.bold('═'.repeat(80)));
165
+
166
+ console.log(chalk.red.bold('\n⚠️ ONYX BURNED TOKENS WITHOUT DELEGATION'));
167
+ console.log(chalk.dim('─'.repeat(80)));
168
+ console.log(chalk.white(`Operation: ${chalk.yellow(op.operation)}`));
169
+ console.log(chalk.white(`Tokens Used: ${chalk.red.bold(op.actualTokens)} (Threshold: ${this.threshold})`));
170
+ console.log(chalk.white(`Excess: ${chalk.red.bold('+' + violation.excess + ' tokens')}`));
171
+ console.log(chalk.white(`Severity: ${this._getSeverityColor(violation.severity)}`));
172
+
173
+ if (op.estimatedTokens > 0) {
174
+ console.log(chalk.white(`Estimated: ${op.estimatedTokens} | Actual: ${op.actualTokens}`));
175
+ const accuracy = Math.abs(100 - (op.actualTokens / op.estimatedTokens * 100));
176
+ if (accuracy > 20) {
177
+ console.log(chalk.yellow(`⚠️ Estimation was off by ${accuracy.toFixed(1)}%`));
178
+ }
179
+ }
180
+
181
+ console.log(chalk.dim('\n' + '─'.repeat(80)));
182
+ console.log(chalk.red.bold('CANONICAL PROTOCOL VIOLATED:'));
183
+ console.log(chalk.white(' Rule: ONYX must delegate operations >100 tokens to Task tool'));
184
+ console.log(chalk.white(' Why: Task tool provides comprehensive search & analysis'));
185
+ console.log(chalk.white(' Fix: Use Task tool for multi-step operations'));
186
+
187
+ console.log(chalk.dim('\n' + '─'.repeat(80)));
188
+ console.log(chalk.yellow('RECOMMENDED ACTION:'));
189
+ console.log(chalk.white(' 1. Stop current operation'));
190
+ console.log(chalk.white(' 2. Invoke Task tool with clear instructions'));
191
+ console.log(chalk.white(' 3. Let Task tool handle comprehensive work'));
192
+ console.log(chalk.dim('─'.repeat(80)) + '\n');
193
+
194
+ // Log to file
195
+ this._logViolation(violation);
196
+ this._saveSession();
197
+ }
198
+
199
+ /**
200
+ * Display current status for an operation
201
+ */
202
+ _displayStatus(op) {
203
+ const pct = (op.actualTokens / this.threshold * 100).toFixed(0);
204
+ const color = pct < 50 ? chalk.green : pct < 80 ? chalk.yellow : chalk.red;
205
+
206
+ console.log(color(`Token usage: ${op.actualTokens}/${this.threshold} (${pct}%)`));
207
+
208
+ if (!op.delegated && op.actualTokens > this.threshold * 0.8) {
209
+ console.log(chalk.yellow(`⚠️ Approaching threshold! Consider delegation.`));
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Calculate severity of violation
215
+ */
216
+ _calculateSeverity(tokensUsed) {
217
+ const excess = tokensUsed - this.threshold;
218
+
219
+ if (excess < 100) return 'LOW';
220
+ if (excess < 500) return 'MEDIUM';
221
+ if (excess < 1000) return 'HIGH';
222
+ return 'CRITICAL';
223
+ }
224
+
225
+ /**
226
+ * Get colored severity indicator
227
+ */
228
+ _getSeverityColor(severity) {
229
+ const colors = {
230
+ 'LOW': chalk.yellow('⚠️ LOW'),
231
+ 'MEDIUM': chalk.orange('⚠️ MEDIUM'),
232
+ 'HIGH': chalk.red('🚨 HIGH'),
233
+ 'CRITICAL': chalk.bgRed.white.bold('💀 CRITICAL')
234
+ };
235
+
236
+ return colors[severity] || severity;
237
+ }
238
+
239
+ /**
240
+ * Log violation to file
241
+ */
242
+ _logViolation(violation) {
243
+ const logEntry = [
244
+ `[${violation.timestamp}] VIOLATION`,
245
+ `Operation: ${violation.operation}`,
246
+ `Tokens: ${violation.tokensUsed} (excess: +${violation.excess})`,
247
+ `Severity: ${violation.severity}`,
248
+ `---`
249
+ ].join('\n') + '\n';
250
+
251
+ try {
252
+ appendFileSync(MONITOR_LOG, logEntry, 'utf8');
253
+ } catch (err) {
254
+ console.error(chalk.dim(`Failed to write violation log: ${err.message}`));
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Save session state to disk
260
+ */
261
+ _saveSession() {
262
+ const session = {
263
+ startTime: this.sessionStartTime.toISOString(),
264
+ sessionTokens: this.sessionTokens,
265
+ operations: this.operations,
266
+ violations: this.violations,
267
+ lastUpdated: new Date().toISOString()
268
+ };
269
+
270
+ try {
271
+ writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2), 'utf8');
272
+ } catch (err) {
273
+ console.error(chalk.dim(`Failed to save session: ${err.message}`));
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Load existing session from disk
279
+ */
280
+ _loadSession() {
281
+ if (!existsSync(SESSION_FILE)) {
282
+ return;
283
+ }
284
+
285
+ try {
286
+ const data = readFileSync(SESSION_FILE, 'utf8');
287
+ const session = JSON.parse(data);
288
+
289
+ this.sessionStartTime = new Date(session.startTime);
290
+ this.sessionTokens = session.sessionTokens || 0;
291
+ this.operations = session.operations || [];
292
+ this.violations = session.violations || [];
293
+
294
+ console.log(chalk.dim(`\n📊 Resumed session from ${this.sessionStartTime.toLocaleString()}`));
295
+ console.log(chalk.dim(`Session tokens: ${this.sessionTokens}`));
296
+
297
+ if (this.violations.length > 0) {
298
+ console.log(chalk.yellow(`⚠️ ${this.violations.length} violation(s) in this session`));
299
+ }
300
+ } catch (err) {
301
+ console.error(chalk.dim(`Failed to load session: ${err.message}`));
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Get session summary
307
+ */
308
+ getSessionSummary() {
309
+ const completedOps = this.operations.filter(o => o.completed);
310
+ const delegatedOps = this.operations.filter(o => o.delegated);
311
+ const totalTokens = this.sessionTokens;
312
+ const violationCount = this.violations.length;
313
+
314
+ return {
315
+ duration: Date.now() - this.sessionStartTime.getTime(),
316
+ operations: {
317
+ total: this.operations.length,
318
+ completed: completedOps.length,
319
+ delegated: delegatedOps.length,
320
+ delegationRate: (delegatedOps.length / this.operations.length * 100).toFixed(1) + '%'
321
+ },
322
+ tokens: {
323
+ total: totalTokens,
324
+ average: totalTokens / this.operations.length || 0
325
+ },
326
+ violations: {
327
+ count: violationCount,
328
+ severity: this._getViolationBreakdown()
329
+ },
330
+ compliance: violationCount === 0 ? '✅ COMPLIANT' : `❌ ${violationCount} VIOLATION(S)`
331
+ };
332
+ }
333
+
334
+ /**
335
+ * Display formatted session summary
336
+ */
337
+ displaySummary() {
338
+ const summary = this.getSessionSummary();
339
+
340
+ console.log(chalk.blue('\n═'.repeat(80)));
341
+ console.log(chalk.blue.bold(' TOKEN MONITOR SESSION SUMMARY'));
342
+ console.log(chalk.blue('═'.repeat(80)));
343
+
344
+ console.log(chalk.white('\n📊 OPERATIONS:'));
345
+ console.log(chalk.dim(` Total: ${summary.operations.total}`));
346
+ console.log(chalk.dim(` Completed: ${summary.operations.completed}`));
347
+ console.log(chalk.dim(` Delegated: ${summary.operations.delegated} (${summary.operations.delegationRate})`));
348
+
349
+ console.log(chalk.white('\n💰 TOKENS:'));
350
+ console.log(chalk.dim(` Total: ${summary.tokens.total.toLocaleString()}`));
351
+ console.log(chalk.dim(` Average: ${summary.tokens.average.toFixed(0)} per operation`));
352
+
353
+ console.log(chalk.white('\n🚨 VIOLATIONS:'));
354
+ if (summary.violations.count === 0) {
355
+ console.log(chalk.green(' ✅ No violations - COMPLIANT'));
356
+ } else {
357
+ console.log(chalk.red(` ❌ Total: ${summary.violations.count}`));
358
+ Object.entries(summary.violations.severity).forEach(([severity, count]) => {
359
+ console.log(chalk.dim(` ${severity}: ${count}`));
360
+ });
361
+ }
362
+
363
+ console.log(chalk.white('\n🎯 COMPLIANCE:'));
364
+ console.log(summary.violations.count === 0 ?
365
+ chalk.green.bold(' ✅ FULLY COMPLIANT') :
366
+ chalk.red.bold(` ❌ ${summary.violations.count} VIOLATION(S) LOGGED`)
367
+ );
368
+
369
+ console.log(chalk.blue('\n═'.repeat(80)) + '\n');
370
+ }
371
+
372
+ /**
373
+ * Get breakdown of violations by severity
374
+ */
375
+ _getViolationBreakdown() {
376
+ const breakdown = { LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 };
377
+
378
+ this.violations.forEach(v => {
379
+ breakdown[v.severity]++;
380
+ });
381
+
382
+ return breakdown;
383
+ }
384
+
385
+ /**
386
+ * Reset session (start fresh)
387
+ */
388
+ resetSession() {
389
+ this.sessionTokens = 0;
390
+ this.operations = [];
391
+ this.violations = [];
392
+ this.sessionStartTime = new Date();
393
+ this.lastDelegation = null;
394
+
395
+ try {
396
+ if (existsSync(SESSION_FILE)) {
397
+ writeFileSync(SESSION_FILE, JSON.stringify({
398
+ startTime: this.sessionStartTime.toISOString(),
399
+ sessionTokens: 0,
400
+ operations: [],
401
+ violations: [],
402
+ lastUpdated: new Date().toISOString()
403
+ }, null, 2), 'utf8');
404
+ }
405
+ } catch (err) {
406
+ console.error(chalk.dim(`Failed to reset session: ${err.message}`));
407
+ }
408
+
409
+ console.log(chalk.green('\n✅ Session reset - starting fresh'));
410
+ }
411
+
412
+ /**
413
+ * Quick check: Should this operation be delegated?
414
+ * @param {number} estimatedTokens - Estimated token cost
415
+ * @returns {boolean}
416
+ */
417
+ shouldDelegate(estimatedTokens) {
418
+ return estimatedTokens > this.threshold;
419
+ }
420
+ }
421
+
422
+ // Singleton instance
423
+ const monitor = new TokenMonitor();
424
+
425
+ export default monitor;
426
+
427
+ /**
428
+ * USAGE EXAMPLES:
429
+ *
430
+ * 1. Start an operation:
431
+ * const opId = tokenMonitor.startOperation('Search codebase for patterns', 150);
432
+ *
433
+ * 2. Record delegation (if using Task tool):
434
+ * tokenMonitor.recordDelegation(opId, 'Task');
435
+ *
436
+ * 3. Update tokens as work progresses:
437
+ * tokenMonitor.addTokens(opId, 50);
438
+ * tokenMonitor.addTokens(opId, 75);
439
+ *
440
+ * 4. Complete operation:
441
+ * tokenMonitor.completeOperation(opId);
442
+ *
443
+ * 5. Display session summary:
444
+ * tokenMonitor.displaySummary();
445
+ *
446
+ * 6. Check if delegation needed:
447
+ * if (tokenMonitor.shouldDelegate(200)) {
448
+ * // Use Task tool
449
+ * }
450
+ *
451
+ * INTEGRATION WITH HIERARCHY VALIDATOR:
452
+ *
453
+ * import tokenMonitor from './token-monitor.js';
454
+ * import hierarchyValidator from './hierarchy-validator.js';
455
+ *
456
+ * // Before starting work
457
+ * const opId = tokenMonitor.startOperation('Complex analysis', 500);
458
+ *
459
+ * if (tokenMonitor.shouldDelegate(500)) {
460
+ * const delegation = await hierarchyValidator.checkDelegation('Complex analysis', 500);
461
+ * if (delegation.shouldDelegate) {
462
+ * tokenMonitor.recordDelegation(opId, delegation.agent);
463
+ * // Delegate to specialist
464
+ * }
465
+ * }
466
+ */