@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,201 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Memory Retrieval Integration Test
4
+ *
5
+ * Tests the Memory Supervisor agent with all 4 engineers:
6
+ * - postgres-n8n-specialist
7
+ * - redis-architect
8
+ * - n8n-workflow-architect
9
+ * - github-expert
10
+ *
11
+ * Requirements:
12
+ * - Query: "authentication work"
13
+ * - Show results from all 4 engineers
14
+ * - Display timing (must be <5s)
15
+ * - Show aggregation stats
16
+ *
17
+ * Usage: node scripts/test-memory-retrieval.js
18
+ */
19
+
20
+ import { queryMemorySupervisor, getMemoryCacheStats } from '../lib/agents/memory-supervisor.js';
21
+ import { formatResults, getAggregationStats } from '../lib/memory-result-aggregator.js';
22
+
23
+ const TEST_QUERY = 'authentication work';
24
+ const MAX_RESULTS = 10;
25
+ const TIMEOUT_MS = 5000;
26
+
27
+ /**
28
+ * Format duration in human-readable format
29
+ */
30
+ function formatDuration(ms) {
31
+ if (ms < 1000) return `${ms}ms`;
32
+ return `${(ms / 1000).toFixed(2)}s`;
33
+ }
34
+
35
+ /**
36
+ * Display results in readable format
37
+ */
38
+ function displayResults(response) {
39
+ console.log('\n' + '='.repeat(80));
40
+ console.log('MEMORY RETRIEVAL TEST RESULTS');
41
+ console.log('='.repeat(80));
42
+
43
+ console.log(`\nQuery: "${response.query}"`);
44
+ console.log(`Cache Hit: ${response.cache_hit ? 'YES' : 'NO'}`);
45
+ console.log(`Query Time: ${formatDuration(response.query_time_ms)} ${response.query_time_ms < TIMEOUT_MS ? '✅' : '❌ TIMEOUT'}`);
46
+ console.log(`Timestamp: ${response.timestamp}`);
47
+
48
+ console.log('\n' + '-'.repeat(80));
49
+ console.log('ENGINEER STATUS');
50
+ console.log('-'.repeat(80));
51
+
52
+ response.engineers_queried.forEach(eng => {
53
+ const status = eng.error ? `❌ ${eng.error}` : `✅ ${eng.result_count} results`;
54
+ console.log(` ${eng.name.padEnd(30)} [${eng.domain.padEnd(10)}] ${status}`);
55
+ });
56
+
57
+ console.log('\n' + '-'.repeat(80));
58
+ console.log(`TOP ${response.results.length} AGGREGATED RESULTS (of ${response.total_results} total)`);
59
+ console.log('-'.repeat(80));
60
+
61
+ if (response.results.length === 0) {
62
+ console.log('\n No results found.');
63
+ } else {
64
+ response.results.forEach((result, idx) => {
65
+ console.log(`\n [${idx + 1}] Score: ${result.relevance?.toFixed(3) || 'N/A'} | Source: ${result.source}`);
66
+ console.log(` Engineer: ${result.engineer}`);
67
+ console.log(` Title: ${result.title}`);
68
+ console.log(` URL: ${result.url}`);
69
+ if (result.labels && result.labels.length > 0) {
70
+ console.log(` Labels: ${result.labels.join(', ')}`);
71
+ }
72
+ if (result.summary) {
73
+ const summaryPreview = result.summary.substring(0, 100);
74
+ console.log(` Summary: ${summaryPreview}${result.summary.length > 100 ? '...' : ''}`);
75
+ }
76
+ });
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Display aggregation statistics
82
+ */
83
+ function displayAggregationStats(response) {
84
+ console.log('\n' + '-'.repeat(80));
85
+ console.log('AGGREGATION STATISTICS');
86
+ console.log('-'.repeat(80));
87
+
88
+ // Count results by source
89
+ const bySource = response.results.reduce((acc, r) => {
90
+ acc[r.source] = (acc[r.source] || 0) + 1;
91
+ return acc;
92
+ }, {});
93
+
94
+ console.log('\nResults by Source:');
95
+ Object.entries(bySource).forEach(([source, count]) => {
96
+ console.log(` ${source}: ${count}`);
97
+ });
98
+
99
+ // Calculate average relevance
100
+ const scores = response.results.map(r => r.relevance || 0);
101
+ const avgScore = scores.length > 0
102
+ ? scores.reduce((a, b) => a + b, 0) / scores.length
103
+ : 0;
104
+
105
+ console.log('\nRelevance Scores:');
106
+ console.log(` Average: ${avgScore.toFixed(3)}`);
107
+ console.log(` Highest: ${scores.length > 0 ? Math.max(...scores).toFixed(3) : 'N/A'}`);
108
+ console.log(` Lowest: ${scores.length > 0 ? Math.min(...scores).toFixed(3) : 'N/A'}`);
109
+ }
110
+
111
+ /**
112
+ * Display cache statistics
113
+ */
114
+ async function displayCacheStats() {
115
+ try {
116
+ const stats = await getMemoryCacheStats();
117
+
118
+ console.log('\n' + '-'.repeat(80));
119
+ console.log('REDIS CACHE STATISTICS');
120
+ console.log('-'.repeat(80));
121
+
122
+ console.log(`\nTotal Cached Queries: ${stats.total_cached_queries}`);
123
+
124
+ if (stats.cache_keys.length > 0) {
125
+ console.log('\nCached Queries:');
126
+ stats.cache_keys.forEach(cache => {
127
+ console.log(`\n Query: "${cache.query}"`);
128
+ console.log(` Results: ${cache.result_count}`);
129
+ console.log(` TTL: ${formatDuration(cache.ttl_remaining * 1000)}`);
130
+ console.log(` Cached At: ${cache.cached_at}`);
131
+ });
132
+ }
133
+ } catch (err) {
134
+ console.log('\n⚠️ Could not retrieve cache stats:', err.message);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Main test function
140
+ */
141
+ async function runTest() {
142
+ console.log('\n🧪 Starting Memory Retrieval Integration Test...\n');
143
+ console.log(`Testing query: "${TEST_QUERY}"`);
144
+ console.log(`Timeout threshold: ${formatDuration(TIMEOUT_MS)}`);
145
+ console.log(`Max results: ${MAX_RESULTS}`);
146
+
147
+ try {
148
+ // First run: Fresh query (cache miss expected)
149
+ console.log('\n📊 Test Run 1: Fresh Query (Cache Miss Expected)\n');
150
+ const response1 = await queryMemorySupervisor(TEST_QUERY, {
151
+ timeout: TIMEOUT_MS,
152
+ limit: MAX_RESULTS,
153
+ useCache: true
154
+ });
155
+
156
+ displayResults(response1);
157
+ displayAggregationStats(response1);
158
+
159
+ // Second run: Should hit cache
160
+ console.log('\n\n📊 Test Run 2: Repeat Query (Cache Hit Expected)\n');
161
+ const response2 = await queryMemorySupervisor(TEST_QUERY, {
162
+ timeout: TIMEOUT_MS,
163
+ limit: MAX_RESULTS,
164
+ useCache: true
165
+ });
166
+
167
+ displayResults(response2);
168
+
169
+ // Display cache stats
170
+ await displayCacheStats();
171
+
172
+ // Final summary
173
+ console.log('\n' + '='.repeat(80));
174
+ console.log('TEST SUMMARY');
175
+ console.log('='.repeat(80));
176
+
177
+ const test1Pass = response1.query_time_ms < TIMEOUT_MS;
178
+ const test2Pass = response2.query_time_ms < TIMEOUT_MS && response2.cache_hit;
179
+
180
+ console.log(`\n Run 1 (Fresh Query):`);
181
+ console.log(` Timing: ${formatDuration(response1.query_time_ms)} ${test1Pass ? '✅ PASS' : '❌ FAIL'}`);
182
+ console.log(` Results: ${response1.results.length} / ${response1.total_results}`);
183
+ console.log(` Engineers: ${response1.engineers_queried.filter(e => !e.error).length} / ${response1.engineers_queried.length} succeeded`);
184
+
185
+ console.log(`\n Run 2 (Cached Query):`);
186
+ console.log(` Timing: ${formatDuration(response2.query_time_ms)} ${test2Pass ? '✅ PASS' : '❌ FAIL'}`);
187
+ console.log(` Cache Hit: ${response2.cache_hit ? '✅ YES' : '❌ NO'}`);
188
+
189
+ console.log(`\n Overall: ${test1Pass && test2Pass ? '✅ ALL TESTS PASSED' : '❌ SOME TESTS FAILED'}\n`);
190
+
191
+ process.exit(test1Pass && test2Pass ? 0 : 1);
192
+
193
+ } catch (err) {
194
+ console.error('\n❌ Test failed with error:', err.message);
195
+ console.error(err.stack);
196
+ process.exit(1);
197
+ }
198
+ }
199
+
200
+ // Run the test
201
+ runTest();
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validate that all exports are properly importable
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const packagePath = path.join(__dirname, '../package.json');
13
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
14
+
15
+ console.log('\n=== Validating Exports ===\n');
16
+
17
+ if (!pkg.exports) {
18
+ console.log('⚠ No exports field found in package.json');
19
+ process.exit(0);
20
+ }
21
+
22
+ const errors = [];
23
+ const warnings = [];
24
+
25
+ for (const [exportKey, exportPath] of Object.entries(pkg.exports)) {
26
+ if (typeof exportPath === 'string') {
27
+ const fullPath = path.join(__dirname, '..', exportPath);
28
+ if (!fs.existsSync(fullPath)) {
29
+ errors.push(`Export "${exportKey}" -> "${exportPath}" does not exist`);
30
+ } else {
31
+ // Try to validate it's valid JavaScript/JSON
32
+ try {
33
+ const content = fs.readFileSync(fullPath, 'utf8');
34
+ if (content.length === 0) {
35
+ warnings.push(`Export "${exportKey}" is empty`);
36
+ } else {
37
+ console.log(`✓ ${exportKey} -> ${exportPath}`);
38
+ }
39
+ } catch (e) {
40
+ errors.push(`Export "${exportKey}": failed to read - ${e.message}`);
41
+ }
42
+ }
43
+ } else if (typeof exportPath === 'object') {
44
+ // Handle conditional exports
45
+ for (const [condition, condPath] of Object.entries(exportPath)) {
46
+ const fullPath = path.join(__dirname, '..', condPath);
47
+ if (!fs.existsSync(fullPath)) {
48
+ errors.push(`Export "${exportKey}[${condition}]" -> "${condPath}" does not exist`);
49
+ } else {
50
+ console.log(`✓ ${exportKey}[${condition}] -> ${condPath}`);
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ if (errors.length > 0) {
57
+ console.log('\nERRORS:\n');
58
+ errors.forEach(e => console.log(` ✗ ${e}`));
59
+ process.exit(1);
60
+ }
61
+
62
+ if (warnings.length > 0) {
63
+ console.log('\nWARNINGS:\n');
64
+ warnings.forEach(w => console.log(` ⚠ ${w}`));
65
+ }
66
+
67
+ console.log('\n✓ All exports are valid!\n');
68
+ process.exit(0);
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validate package.json is ready for npm publishing
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const packagePath = path.join(__dirname, '../package.json');
13
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
14
+
15
+ const errors = [];
16
+ const warnings = [];
17
+
18
+ // Required fields for scoped packages
19
+ const required = ['name', 'version', 'description', 'main', 'license', 'repository'];
20
+ for (const field of required) {
21
+ if (!pkg[field]) {
22
+ errors.push(`Missing required field: ${field}`);
23
+ }
24
+ }
25
+
26
+ // Check name format
27
+ if (pkg.name && !pkg.name.startsWith('@cpretzinger/')) {
28
+ errors.push(`Invalid scoped package name. Expected format: @cpretzinger/<name>, got: ${pkg.name}`);
29
+ }
30
+
31
+ // Check version format (semver)
32
+ if (pkg.version && !/^\d+\.\d+\.\d+/.test(pkg.version)) {
33
+ errors.push(`Invalid version format. Expected semver, got: ${pkg.version}`);
34
+ }
35
+
36
+ // Check files field
37
+ if (!pkg.files || pkg.files.length === 0) {
38
+ errors.push('Missing or empty "files" field - this limits what gets published');
39
+ }
40
+
41
+ // Check bin configuration
42
+ if (!pkg.bin || Object.keys(pkg.bin).length === 0) {
43
+ warnings.push('No bin entries found - package cannot be used as CLI tool');
44
+ }
45
+
46
+ // Check main entry point exists
47
+ if (pkg.main) {
48
+ const mainPath = path.join(__dirname, '..', pkg.main);
49
+ if (!fs.existsSync(mainPath)) {
50
+ errors.push(`Main entry point not found: ${pkg.main}`);
51
+ }
52
+ }
53
+
54
+ // Check all bin entries exist
55
+ if (pkg.bin) {
56
+ for (const [name, location] of Object.entries(pkg.bin)) {
57
+ const binPath = path.join(__dirname, '..', location);
58
+ if (!fs.existsSync(binPath)) {
59
+ errors.push(`Bin file not found: ${location}`);
60
+ }
61
+ }
62
+ }
63
+
64
+ // Check exports field
65
+ if (pkg.exports) {
66
+ for (const [key, location] of Object.entries(pkg.exports)) {
67
+ const exportPath = path.join(__dirname, '..', location);
68
+ if (!fs.existsSync(exportPath)) {
69
+ errors.push(`Export path not found: ${location} (${key})`);
70
+ }
71
+ }
72
+ }
73
+
74
+ // Check dependencies
75
+ if (!pkg.dependencies || Object.keys(pkg.dependencies).length === 0) {
76
+ warnings.push('No dependencies specified');
77
+ }
78
+
79
+ // Check engines
80
+ if (!pkg.engines || !pkg.engines.node) {
81
+ warnings.push('No Node.js version requirement specified in engines');
82
+ }
83
+
84
+ // Print results
85
+ console.log('\n=== NPM Package Validation ===\n');
86
+
87
+ if (errors.length === 0) {
88
+ console.log('✓ All required fields present');
89
+ console.log('✓ Package name format valid');
90
+ console.log('✓ Version format valid (semver)');
91
+ console.log('✓ Main entry point accessible');
92
+ console.log('✓ All bin files accessible');
93
+ console.log('✓ All export paths accessible');
94
+ console.log('✓ Files field properly configured');
95
+ } else {
96
+ console.log('ERRORS FOUND:\n');
97
+ errors.forEach(e => console.log(` ✗ ${e}`));
98
+ }
99
+
100
+ if (warnings.length > 0) {
101
+ console.log('\nWARNINGS:\n');
102
+ warnings.forEach(w => console.log(` ⚠ ${w}`));
103
+ }
104
+
105
+ console.log('\n=== Package Summary ===\n');
106
+ console.log(`Name: ${pkg.name}`);
107
+ console.log(`Version: ${pkg.version}`);
108
+ console.log(`Main: ${pkg.main}`);
109
+ console.log(`Type: ${pkg.type || 'commonjs'}`);
110
+ console.log(`License: ${pkg.license}`);
111
+ console.log(`Files: ${pkg.files.length} entries`);
112
+ console.log(`Bin: ${Object.keys(pkg.bin || {}).length} entries`);
113
+ console.log(`Exports: ${Object.keys(pkg.exports || {}).length} entries`);
114
+
115
+ if (errors.length > 0) {
116
+ process.exit(1);
117
+ }
118
+
119
+ console.log('\n✓ Package is ready for publishing!\n');
120
+ process.exit(0);
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ONYX HOOKS DEPLOYMENT VERIFICATION
4
+ *
5
+ * Quick verification script to ensure all components are ready
6
+ */
7
+
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import chalk from 'chalk';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const projectRoot = path.dirname(__dirname);
16
+
17
+ console.log(chalk.blue('\n🔍 ONYX HOOKS DEPLOYMENT VERIFICATION\n'));
18
+ console.log(chalk.dim('═'.repeat(80)));
19
+
20
+ const checks = [];
21
+
22
+ /**
23
+ * Verification checks
24
+ */
25
+
26
+ // Check 1: Core module files exist
27
+ async function checkCoreFiles() {
28
+ const files = [
29
+ 'lib/onyx-tool-interceptor.js',
30
+ 'lib/onyx-wrapper.js',
31
+ 'bin/onyx-guard.js',
32
+ '.claude-hooks/onyx-pre-hook.json',
33
+ 'scripts/install-onyx-hooks.js',
34
+ 'examples/onyx-hooks-demo.js',
35
+ 'test/onyx-hooks.test.js',
36
+ 'docs/ONYX-HOOKS-SYSTEM.md'
37
+ ];
38
+
39
+ const results = [];
40
+
41
+ for (const file of files) {
42
+ const filePath = path.join(projectRoot, file);
43
+ try {
44
+ await fs.access(filePath);
45
+ results.push({ file, exists: true });
46
+ } catch (error) {
47
+ results.push({ file, exists: false });
48
+ }
49
+ }
50
+
51
+ const allExist = results.every(r => r.exists);
52
+
53
+ checks.push({
54
+ name: 'Core module files',
55
+ passed: allExist,
56
+ details: results
57
+ });
58
+
59
+ return allExist;
60
+ }
61
+
62
+ // Check 2: Configuration is valid
63
+ async function checkConfiguration() {
64
+ const configPath = path.join(projectRoot, '.claude-hooks', 'onyx-pre-hook.json');
65
+
66
+ try {
67
+ const content = await fs.readFile(configPath, 'utf-8');
68
+ const config = JSON.parse(content);
69
+
70
+ const hasRequired =
71
+ config.name &&
72
+ config.version &&
73
+ config.agent === 'ONYX' &&
74
+ config.interceptor &&
75
+ config.rules &&
76
+ config.rules.forbiddenTools &&
77
+ config.rules.allowedTools &&
78
+ config.logging;
79
+
80
+ checks.push({
81
+ name: 'Configuration structure',
82
+ passed: hasRequired,
83
+ details: {
84
+ enabled: config.enabled,
85
+ agent: config.agent,
86
+ forbiddenTools: config.rules.forbiddenTools.length,
87
+ allowedTools: config.rules.allowedTools.length
88
+ }
89
+ });
90
+
91
+ return hasRequired;
92
+ } catch (error) {
93
+ checks.push({
94
+ name: 'Configuration structure',
95
+ passed: false,
96
+ error: error.message
97
+ });
98
+ return false;
99
+ }
100
+ }
101
+
102
+ // Check 3: Package exports are configured
103
+ async function checkPackageExports() {
104
+ const packagePath = path.join(projectRoot, 'package.json');
105
+
106
+ try {
107
+ const content = await fs.readFile(packagePath, 'utf-8');
108
+ const pkg = JSON.parse(content);
109
+
110
+ const hasExports =
111
+ pkg.exports['./onyx-tool-interceptor'] &&
112
+ pkg.exports['./onyx-wrapper'] &&
113
+ pkg.bin['onyx-guard'];
114
+
115
+ checks.push({
116
+ name: 'Package exports configured',
117
+ passed: hasExports,
118
+ details: {
119
+ interceptor: !!pkg.exports['./onyx-tool-interceptor'],
120
+ wrapper: !!pkg.exports['./onyx-wrapper'],
121
+ cli: !!pkg.bin['onyx-guard']
122
+ }
123
+ });
124
+
125
+ return hasExports;
126
+ } catch (error) {
127
+ checks.push({
128
+ name: 'Package exports configured',
129
+ passed: false,
130
+ error: error.message
131
+ });
132
+ return false;
133
+ }
134
+ }
135
+
136
+ // Check 4: NPM scripts are available
137
+ async function checkNpmScripts() {
138
+ const packagePath = path.join(projectRoot, 'package.json');
139
+
140
+ try {
141
+ const content = await fs.readFile(packagePath, 'utf-8');
142
+ const pkg = JSON.parse(content);
143
+
144
+ const requiredScripts = [
145
+ 'install:onyx-hooks',
146
+ 'uninstall:onyx-hooks',
147
+ 'verify:onyx-hooks',
148
+ 'test:onyx-hooks',
149
+ 'test:onyx-guard',
150
+ 'demo:onyx-hooks'
151
+ ];
152
+
153
+ const scriptResults = requiredScripts.map(script => ({
154
+ script,
155
+ exists: !!pkg.scripts[script]
156
+ }));
157
+
158
+ const allExist = scriptResults.every(r => r.exists);
159
+
160
+ checks.push({
161
+ name: 'NPM scripts configured',
162
+ passed: allExist,
163
+ details: scriptResults
164
+ });
165
+
166
+ return allExist;
167
+ } catch (error) {
168
+ checks.push({
169
+ name: 'NPM scripts configured',
170
+ passed: false,
171
+ error: error.message
172
+ });
173
+ return false;
174
+ }
175
+ }
176
+
177
+ // Check 5: Modules can be imported
178
+ async function checkModuleImports() {
179
+ try {
180
+ const { OnyxToolInterceptor } = await import('../lib/onyx-tool-interceptor.js');
181
+ const { OnyxWrapper } = await import('../lib/onyx-wrapper.js');
182
+
183
+ const interceptorValid = typeof OnyxToolInterceptor === 'function';
184
+ const wrapperValid = typeof OnyxWrapper === 'function';
185
+
186
+ checks.push({
187
+ name: 'Module imports work',
188
+ passed: interceptorValid && wrapperValid,
189
+ details: {
190
+ interceptor: interceptorValid,
191
+ wrapper: wrapperValid
192
+ }
193
+ });
194
+
195
+ return interceptorValid && wrapperValid;
196
+ } catch (error) {
197
+ checks.push({
198
+ name: 'Module imports work',
199
+ passed: false,
200
+ error: error.message
201
+ });
202
+ return false;
203
+ }
204
+ }
205
+
206
+ // Check 6: File permissions (executables)
207
+ async function checkFilePermissions() {
208
+ const executables = [
209
+ 'bin/onyx-guard.js',
210
+ 'lib/onyx-tool-interceptor.js',
211
+ 'lib/onyx-wrapper.js'
212
+ ];
213
+
214
+ const results = [];
215
+
216
+ for (const file of executables) {
217
+ const filePath = path.join(projectRoot, file);
218
+ try {
219
+ const stats = await fs.stat(filePath);
220
+ const isExecutable = (stats.mode & 0o111) !== 0;
221
+ results.push({ file, executable: isExecutable });
222
+ } catch (error) {
223
+ results.push({ file, executable: false, error: error.message });
224
+ }
225
+ }
226
+
227
+ const allExecutable = results.every(r => r.executable);
228
+
229
+ checks.push({
230
+ name: 'File permissions (executable)',
231
+ passed: allExecutable,
232
+ details: results
233
+ });
234
+
235
+ return allExecutable;
236
+ }
237
+
238
+ /**
239
+ * Run all checks
240
+ */
241
+ async function runVerification() {
242
+ const startTime = Date.now();
243
+
244
+ try {
245
+ await checkCoreFiles();
246
+ await checkConfiguration();
247
+ await checkPackageExports();
248
+ await checkNpmScripts();
249
+ await checkModuleImports();
250
+ await checkFilePermissions();
251
+
252
+ const duration = Date.now() - startTime;
253
+ const totalChecks = checks.length;
254
+ const passedChecks = checks.filter(c => c.passed).length;
255
+ const failedChecks = totalChecks - passedChecks;
256
+
257
+ // Display results
258
+ console.log('\n');
259
+ checks.forEach((check, index) => {
260
+ const icon = check.passed ? chalk.green('✓') : chalk.red('✗');
261
+ console.log(`${icon} ${check.name}`);
262
+
263
+ if (!check.passed && check.error) {
264
+ console.log(chalk.dim(` Error: ${check.error}`));
265
+ }
266
+
267
+ if (check.details && !check.passed) {
268
+ console.log(chalk.dim(` Details: ${JSON.stringify(check.details, null, 2)}`));
269
+ }
270
+ });
271
+
272
+ console.log(chalk.dim('\n' + '═'.repeat(80)));
273
+ console.log(chalk.white(`\nVerification Summary:`));
274
+ console.log(chalk.green(` Passed: ${passedChecks}/${totalChecks}`));
275
+
276
+ if (failedChecks > 0) {
277
+ console.log(chalk.red(` Failed: ${failedChecks}/${totalChecks}`));
278
+ }
279
+
280
+ console.log(chalk.dim(` Duration: ${duration}ms\n`));
281
+
282
+ if (passedChecks === totalChecks) {
283
+ console.log(chalk.green('✅ ONYX Hooks System: READY\n'));
284
+ console.log(chalk.white('Next steps:'));
285
+ console.log(chalk.dim(' 1. Run: npm run test:onyx-hooks'));
286
+ console.log(chalk.dim(' 2. Run: npm run demo:onyx-hooks'));
287
+ console.log(chalk.dim(' 3. Test CLI: npx onyx-guard check Read'));
288
+ console.log(chalk.dim(' 4. Integrate into ONYX workflow\n'));
289
+ process.exit(0);
290
+ } else {
291
+ console.log(chalk.red('❌ ONYX Hooks System: INCOMPLETE\n'));
292
+ console.log(chalk.white('Action required:'));
293
+ console.log(chalk.dim(' 1. Run: npm run install:onyx-hooks'));
294
+ console.log(chalk.dim(' 2. Fix failed checks above'));
295
+ console.log(chalk.dim(' 3. Re-run: node scripts/verify-onyx-deployment.js\n'));
296
+ process.exit(1);
297
+ }
298
+
299
+ } catch (error) {
300
+ console.error(chalk.red('\n❌ Verification failed:'), error.message);
301
+ if (error.stack) {
302
+ console.error(chalk.dim(error.stack));
303
+ }
304
+ process.exit(1);
305
+ }
306
+ }
307
+
308
+ // Run verification
309
+ runVerification();