@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,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();
|