@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,452 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BOSS CLAUDE - Rollback System Tests
4
+ * Demonstrates rollback functionality and validates operations
5
+ */
6
+
7
+ import { RollbackManager, SetupState, recordAction } from './rollback.js';
8
+ import chalk from 'chalk';
9
+ import fs from 'fs/promises';
10
+ import { existsSync } from 'fs';
11
+ import path from 'path';
12
+ import os from 'os';
13
+
14
+ const TEST_DIR = path.join(os.tmpdir(), 'boss-claude-rollback-test');
15
+ const TEST_STATE = path.join(TEST_DIR, 'test-state.json');
16
+
17
+ /**
18
+ * Test utilities
19
+ */
20
+ class TestRunner {
21
+ constructor() {
22
+ this.tests = [];
23
+ this.passed = 0;
24
+ this.failed = 0;
25
+ }
26
+
27
+ async test(name, fn) {
28
+ try {
29
+ console.log(chalk.blue(`\n▶ ${name}`));
30
+ await fn();
31
+ console.log(chalk.green(` ✓ PASSED`));
32
+ this.passed++;
33
+ } catch (error) {
34
+ console.log(chalk.red(` ✗ FAILED: ${error.message}`));
35
+ if (error.stack) {
36
+ console.log(chalk.gray(error.stack));
37
+ }
38
+ this.failed++;
39
+ }
40
+ }
41
+
42
+ summary() {
43
+ console.log(chalk.bold('\n' + '='.repeat(70)));
44
+ console.log(chalk.bold('TEST SUMMARY'));
45
+ console.log('='.repeat(70));
46
+ console.log(chalk.green(` Passed: ${this.passed}`));
47
+ if (this.failed > 0) {
48
+ console.log(chalk.red(` Failed: ${this.failed}`));
49
+ }
50
+ console.log(chalk.white(` Total: ${this.passed + this.failed}`));
51
+ console.log('='.repeat(70) + '\n');
52
+
53
+ return this.failed === 0;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Setup test environment
59
+ */
60
+ async function setupTestEnv() {
61
+ if (existsSync(TEST_DIR)) {
62
+ await fs.rm(TEST_DIR, { recursive: true, force: true });
63
+ }
64
+ await fs.mkdir(TEST_DIR, { recursive: true });
65
+ }
66
+
67
+ /**
68
+ * Cleanup test environment
69
+ */
70
+ async function cleanupTestEnv() {
71
+ if (existsSync(TEST_DIR)) {
72
+ await fs.rm(TEST_DIR, { recursive: true, force: true });
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Test setup state tracking
78
+ */
79
+ async function testSetupState(runner) {
80
+ await runner.test('SetupState - Save and Load', async () => {
81
+ const state = new SetupState();
82
+ state.state.actions.push({
83
+ type: 'test',
84
+ data: { foo: 'bar' },
85
+ timestamp: new Date().toISOString()
86
+ });
87
+
88
+ // Override state file path for testing
89
+ const originalStateFile = path.join(os.homedir(), '.boss-claude', 'setup-state.json');
90
+ const testStateFile = TEST_STATE;
91
+
92
+ // Save to test location
93
+ await fs.writeFile(testStateFile, JSON.stringify(state.state, null, 2));
94
+
95
+ // Load from test location
96
+ const loaded = JSON.parse(await fs.readFile(testStateFile, 'utf8'));
97
+
98
+ if (loaded.actions.length !== 1) {
99
+ throw new Error('Failed to save/load state');
100
+ }
101
+
102
+ if (loaded.actions[0].type !== 'test') {
103
+ throw new Error('State data mismatch');
104
+ }
105
+ });
106
+
107
+ await runner.test('SetupState - Record Action', async () => {
108
+ const state = new SetupState();
109
+ state.state.actions = [];
110
+
111
+ // Record multiple actions
112
+ state.state.actions.push({
113
+ type: 'github_repo',
114
+ data: { owner: 'test', repo: 'test-repo' },
115
+ timestamp: new Date().toISOString()
116
+ });
117
+
118
+ state.state.actions.push({
119
+ type: 'env_vars',
120
+ data: { keys: ['TEST_KEY'] },
121
+ timestamp: new Date().toISOString()
122
+ });
123
+
124
+ if (state.state.actions.length !== 2) {
125
+ throw new Error('Failed to record actions');
126
+ }
127
+ });
128
+
129
+ await runner.test('SetupState - Mark Completed', async () => {
130
+ const state = new SetupState();
131
+ state.state.completed = false;
132
+
133
+ state.state.completed = true;
134
+
135
+ if (!state.state.completed) {
136
+ throw new Error('Failed to mark completed');
137
+ }
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Test file rollback
143
+ */
144
+ async function testFileRollback(runner) {
145
+ await runner.test('File Rollback - Single File', async () => {
146
+ const testFile = path.join(TEST_DIR, 'test-file.txt');
147
+ await fs.writeFile(testFile, 'test content');
148
+
149
+ const manager = new RollbackManager({ verbose: false });
150
+ const result = await manager.rollbackFiles({
151
+ data: { files: [testFile] }
152
+ });
153
+
154
+ if (!result.success) {
155
+ throw new Error('File rollback failed');
156
+ }
157
+
158
+ if (existsSync(testFile)) {
159
+ throw new Error('File was not deleted');
160
+ }
161
+ });
162
+
163
+ await runner.test('File Rollback - Multiple Files', async () => {
164
+ const files = [
165
+ path.join(TEST_DIR, 'file1.txt'),
166
+ path.join(TEST_DIR, 'file2.txt'),
167
+ path.join(TEST_DIR, 'file3.txt')
168
+ ];
169
+
170
+ for (const file of files) {
171
+ await fs.writeFile(file, 'test');
172
+ }
173
+
174
+ const manager = new RollbackManager({ verbose: false });
175
+ const result = await manager.rollbackFiles({
176
+ data: { files }
177
+ });
178
+
179
+ if (!result.success) {
180
+ throw new Error('Multiple file rollback failed');
181
+ }
182
+
183
+ if (result.removed !== 3) {
184
+ throw new Error(`Expected 3 files removed, got ${result.removed}`);
185
+ }
186
+
187
+ for (const file of files) {
188
+ if (existsSync(file)) {
189
+ throw new Error(`File ${file} was not deleted`);
190
+ }
191
+ }
192
+ });
193
+
194
+ await runner.test('File Rollback - Missing File (Should Succeed)', async () => {
195
+ const nonExistentFile = path.join(TEST_DIR, 'does-not-exist.txt');
196
+
197
+ const manager = new RollbackManager({ verbose: false });
198
+ const result = await manager.rollbackFiles({
199
+ data: { files: [nonExistentFile] }
200
+ });
201
+
202
+ if (!result.success) {
203
+ throw new Error('Should succeed for missing files');
204
+ }
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Test directory rollback
210
+ */
211
+ async function testDirectoryRollback(runner) {
212
+ await runner.test('Directory Rollback - Single Directory', async () => {
213
+ const testDir = path.join(TEST_DIR, 'test-subdir');
214
+ await fs.mkdir(testDir, { recursive: true });
215
+ await fs.writeFile(path.join(testDir, 'file.txt'), 'test');
216
+
217
+ const manager = new RollbackManager({ verbose: false });
218
+ const result = await manager.rollbackDirectories({
219
+ data: { directories: [testDir] }
220
+ });
221
+
222
+ if (!result.success) {
223
+ throw new Error('Directory rollback failed');
224
+ }
225
+
226
+ if (existsSync(testDir)) {
227
+ throw new Error('Directory was not deleted');
228
+ }
229
+ });
230
+
231
+ await runner.test('Directory Rollback - Nested Directories', async () => {
232
+ const baseDir = path.join(TEST_DIR, 'nested');
233
+ const subDir = path.join(baseDir, 'level1', 'level2');
234
+ await fs.mkdir(subDir, { recursive: true });
235
+ await fs.writeFile(path.join(subDir, 'deep-file.txt'), 'test');
236
+
237
+ const manager = new RollbackManager({ verbose: false });
238
+ const result = await manager.rollbackDirectories({
239
+ data: { directories: [baseDir] }
240
+ });
241
+
242
+ if (!result.success) {
243
+ throw new Error('Nested directory rollback failed');
244
+ }
245
+
246
+ if (existsSync(baseDir)) {
247
+ throw new Error('Base directory was not deleted');
248
+ }
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Test dry run mode
254
+ */
255
+ async function testDryRun(runner) {
256
+ await runner.test('Dry Run - Files Not Deleted', async () => {
257
+ const testFile = path.join(TEST_DIR, 'dry-run-test.txt');
258
+ await fs.writeFile(testFile, 'should not be deleted');
259
+
260
+ const manager = new RollbackManager({ verbose: false, dryRun: true });
261
+ const result = await manager.rollbackFiles({
262
+ data: { files: [testFile] }
263
+ });
264
+
265
+ if (!result.dryRun) {
266
+ throw new Error('Dry run flag not set in result');
267
+ }
268
+
269
+ if (!existsSync(testFile)) {
270
+ throw new Error('File was deleted in dry run mode!');
271
+ }
272
+
273
+ // Cleanup
274
+ await fs.unlink(testFile);
275
+ });
276
+
277
+ await runner.test('Dry Run - Directories Not Deleted', async () => {
278
+ const testDir = path.join(TEST_DIR, 'dry-run-dir');
279
+ await fs.mkdir(testDir, { recursive: true });
280
+
281
+ const manager = new RollbackManager({ verbose: false, dryRun: true });
282
+ const result = await manager.rollbackDirectories({
283
+ data: { directories: [testDir] }
284
+ });
285
+
286
+ if (!result.dryRun) {
287
+ throw new Error('Dry run flag not set in result');
288
+ }
289
+
290
+ if (!existsSync(testDir)) {
291
+ throw new Error('Directory was deleted in dry run mode!');
292
+ }
293
+
294
+ // Cleanup
295
+ await fs.rm(testDir, { recursive: true });
296
+ });
297
+ }
298
+
299
+ /**
300
+ * Test full rollback workflow
301
+ */
302
+ async function testFullRollback(runner) {
303
+ await runner.test('Full Rollback - Multiple Action Types', async () => {
304
+ // Create test state
305
+ const state = {
306
+ timestamp: new Date().toISOString(),
307
+ completed: false,
308
+ actions: [
309
+ {
310
+ type: 'files',
311
+ data: {
312
+ files: [
313
+ path.join(TEST_DIR, 'rollback-file1.txt'),
314
+ path.join(TEST_DIR, 'rollback-file2.txt')
315
+ ]
316
+ },
317
+ timestamp: new Date().toISOString()
318
+ },
319
+ {
320
+ type: 'directories',
321
+ data: {
322
+ directories: [path.join(TEST_DIR, 'rollback-dir')]
323
+ },
324
+ timestamp: new Date().toISOString()
325
+ }
326
+ ]
327
+ };
328
+
329
+ // Create test files and directories
330
+ for (const file of state.actions[0].data.files) {
331
+ await fs.writeFile(file, 'test');
332
+ }
333
+ await fs.mkdir(state.actions[1].data.directories[0], { recursive: true });
334
+
335
+ // Save state
336
+ await fs.writeFile(TEST_STATE, JSON.stringify(state, null, 2));
337
+
338
+ // Execute rollback
339
+ const manager = new RollbackManager({ verbose: false });
340
+ const result = await manager.rollback(TEST_STATE);
341
+
342
+ if (!result.success) {
343
+ throw new Error('Full rollback failed');
344
+ }
345
+
346
+ if (result.total !== 2) {
347
+ throw new Error(`Expected 2 actions, got ${result.total}`);
348
+ }
349
+
350
+ // Verify cleanup
351
+ for (const file of state.actions[0].data.files) {
352
+ if (existsSync(file)) {
353
+ throw new Error(`File ${file} was not deleted`);
354
+ }
355
+ }
356
+
357
+ for (const dir of state.actions[1].data.directories) {
358
+ if (existsSync(dir)) {
359
+ throw new Error(`Directory ${dir} was not deleted`);
360
+ }
361
+ }
362
+ });
363
+ }
364
+
365
+ /**
366
+ * Test error handling
367
+ */
368
+ async function testErrorHandling(runner) {
369
+ await runner.test('Error Handling - Invalid Action Type', async () => {
370
+ const manager = new RollbackManager({ verbose: false });
371
+ const result = await manager.rollbackAction({
372
+ type: 'invalid_type',
373
+ data: {},
374
+ timestamp: new Date().toISOString()
375
+ });
376
+
377
+ if (result.success) {
378
+ throw new Error('Should fail for invalid action type');
379
+ }
380
+ });
381
+
382
+ await runner.test('Error Handling - Permission Denied', async () => {
383
+ // This test is platform-specific and may not work everywhere
384
+ // Skip on systems where we can't test permissions
385
+ const testFile = path.join(TEST_DIR, 'permission-test.txt');
386
+ await fs.writeFile(testFile, 'test');
387
+
388
+ try {
389
+ await fs.chmod(testFile, 0o000); // Remove all permissions
390
+
391
+ const manager = new RollbackManager({ verbose: false });
392
+ const result = await manager.rollbackFiles({
393
+ data: { files: [testFile] }
394
+ });
395
+
396
+ // Result may succeed or fail depending on permissions model
397
+ // Just verify it handles the case without crashing
398
+
399
+ // Cleanup - restore permissions first
400
+ await fs.chmod(testFile, 0o644);
401
+ await fs.unlink(testFile);
402
+ } catch (error) {
403
+ // Permission tests may not work on all systems
404
+ console.log(chalk.gray(` Skipped: ${error.message}`));
405
+ }
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Main test execution
411
+ */
412
+ async function runTests() {
413
+ console.log(chalk.bold.cyan('\n🧪 BOSS CLAUDE ROLLBACK TESTS\n'));
414
+
415
+ const runner = new TestRunner();
416
+
417
+ try {
418
+ // Setup
419
+ await setupTestEnv();
420
+
421
+ // Run test suites
422
+ await testSetupState(runner);
423
+ await testFileRollback(runner);
424
+ await testDirectoryRollback(runner);
425
+ await testDryRun(runner);
426
+ await testFullRollback(runner);
427
+ await testErrorHandling(runner);
428
+
429
+ // Cleanup
430
+ await cleanupTestEnv();
431
+
432
+ // Summary
433
+ const success = runner.summary();
434
+
435
+ process.exit(success ? 0 : 1);
436
+
437
+ } catch (error) {
438
+ console.error(chalk.red.bold('\n✗ Test execution failed\n'));
439
+ console.error(chalk.red(error.message));
440
+ console.error(chalk.gray(error.stack));
441
+
442
+ await cleanupTestEnv();
443
+ process.exit(1);
444
+ }
445
+ }
446
+
447
+ // Run tests if executed directly
448
+ if (import.meta.url === `file://${process.argv[1]}`) {
449
+ runTests();
450
+ }
451
+
452
+ export { runTests, TestRunner };