@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,175 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Configuration Validator Test Suite
5
+ * Tests the config validator with various scenarios
6
+ */
7
+
8
+ import {
9
+ validateRedisUrl,
10
+ validateGitHubToken,
11
+ validateOpenAiKey,
12
+ validateConfig,
13
+ printReport
14
+ } from './config.js';
15
+ import chalk from 'chalk';
16
+
17
+ console.log(chalk.bold.blue('\n🧪 Boss Claude Configuration Validator Tests\n'));
18
+
19
+ // Test 1: Valid Redis URLs
20
+ console.log(chalk.bold('Test 1: Redis URL Validation'));
21
+ console.log('─'.repeat(60));
22
+
23
+ const redisTests = [
24
+ {
25
+ name: 'Valid Redis URL (Railway)',
26
+ url: 'redis://user:password@test.example.com:6379',
27
+ shouldPass: true
28
+ },
29
+ {
30
+ name: 'Valid Redis URL with SSL',
31
+ url: 'rediss://default:password@redis.production.com:6379/0',
32
+ shouldPass: true
33
+ },
34
+ {
35
+ name: 'Valid Redis URL (no auth)',
36
+ url: 'redis://localhost:6379',
37
+ shouldPass: false // Should fail as placeholder
38
+ },
39
+ {
40
+ name: 'Invalid Redis URL (missing host)',
41
+ url: 'redis://:password@:6379',
42
+ shouldPass: false
43
+ },
44
+ {
45
+ name: 'Invalid Redis URL (wrong protocol)',
46
+ url: 'http://localhost:6379',
47
+ shouldPass: false
48
+ }
49
+ ];
50
+
51
+ redisTests.forEach(test => {
52
+ const result = validateRedisUrl(test.url);
53
+ const passed = result.valid === test.shouldPass;
54
+ const icon = passed ? chalk.green('✓') : chalk.red('✗');
55
+ console.log(`${icon} ${test.name}`);
56
+ if (!passed) {
57
+ console.log(` Expected: ${test.shouldPass}, Got: ${result.valid}`);
58
+ console.log(` Error: ${result.error}`);
59
+ }
60
+ });
61
+
62
+ console.log();
63
+
64
+ // Test 2: GitHub Token Validation
65
+ console.log(chalk.bold('Test 2: GitHub Token Validation'));
66
+ console.log('─'.repeat(60));
67
+
68
+ const githubTests = [
69
+ {
70
+ name: 'Valid classic token',
71
+ token: 'ghp_1234567890abcdefghijklmnopqrstuvwxyz12',
72
+ shouldPass: true
73
+ },
74
+ {
75
+ name: 'Valid fine-grained token',
76
+ token: 'github_pat_11ABCDEFG0123456789abcdefghijklmnopqrstuvwxyz1234567890',
77
+ shouldPass: true
78
+ },
79
+ {
80
+ name: 'Invalid - placeholder',
81
+ token: 'YOUR_TOKEN_HERE',
82
+ shouldPass: false
83
+ },
84
+ {
85
+ name: 'Invalid - too short',
86
+ token: 'ghp_short',
87
+ shouldPass: false
88
+ },
89
+ {
90
+ name: 'Invalid - wrong prefix',
91
+ token: 'abc_1234567890abcdefghijklmnopqrstuvwxyz12',
92
+ shouldPass: false
93
+ }
94
+ ];
95
+
96
+ githubTests.forEach(test => {
97
+ const result = validateGitHubToken(test.token);
98
+ const passed = result.valid === test.shouldPass;
99
+ const icon = passed ? chalk.green('✓') : chalk.red('✗');
100
+ console.log(`${icon} ${test.name}`);
101
+ if (!passed) {
102
+ console.log(` Expected: ${test.shouldPass}, Got: ${result.valid}`);
103
+ console.log(` Error: ${result.error}`);
104
+ }
105
+ });
106
+
107
+ console.log();
108
+
109
+ // Test 3: OpenAI API Key Validation
110
+ console.log(chalk.bold('Test 3: OpenAI API Key Validation'));
111
+ console.log('─'.repeat(60));
112
+
113
+ const openaiTests = [
114
+ {
115
+ name: 'Valid legacy key',
116
+ key: 'sk-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJ',
117
+ shouldPass: true
118
+ },
119
+ {
120
+ name: 'Valid project key',
121
+ key: 'sk-proj-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJ1234567890',
122
+ shouldPass: true
123
+ },
124
+ {
125
+ name: 'Invalid - placeholder',
126
+ key: 'YOUR_OPENAI_KEY',
127
+ shouldPass: false
128
+ },
129
+ {
130
+ name: 'Invalid - wrong prefix',
131
+ key: 'pk-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJ',
132
+ shouldPass: false
133
+ },
134
+ {
135
+ name: 'Invalid - too short',
136
+ key: 'sk-short',
137
+ shouldPass: false
138
+ }
139
+ ];
140
+
141
+ openaiTests.forEach(test => {
142
+ const result = validateOpenAiKey(test.key);
143
+ const passed = result.valid === test.shouldPass;
144
+ const icon = passed ? chalk.green('✓') : chalk.red('✗');
145
+ console.log(`${icon} ${test.name}`);
146
+ if (!passed) {
147
+ console.log(` Expected: ${test.shouldPass}, Got: ${result.valid}`);
148
+ console.log(` Error: ${result.error}`);
149
+ }
150
+ });
151
+
152
+ console.log();
153
+
154
+ // Test 4: Full Configuration Validation
155
+ console.log(chalk.bold('Test 4: Full Configuration Validation'));
156
+ console.log('─'.repeat(60));
157
+ console.log(chalk.dim('Testing actual ~/.boss-claude/.env file...\n'));
158
+
159
+ try {
160
+ const report = await validateConfig({ includeOptional: true });
161
+ printReport(report);
162
+
163
+ if (report.valid) {
164
+ console.log(chalk.green.bold('\n✅ All tests passed!'));
165
+ } else {
166
+ console.log(chalk.yellow.bold('\n⚠️ Some tests failed (see report above)'));
167
+ }
168
+ } catch (error) {
169
+ console.error(chalk.red('\n❌ Error running full validation:'), error.message);
170
+ process.exit(1);
171
+ }
172
+
173
+ console.log('\n' + '═'.repeat(60));
174
+ console.log(chalk.bold.blue('🎯 Test Suite Complete'));
175
+ console.log('═'.repeat(60) + '\n');
@@ -0,0 +1,310 @@
1
+ import { Octokit } from '@octokit/rest';
2
+
3
+ /**
4
+ * GitHub Token Validator
5
+ * Tests if a GitHub token has the required permissions for Boss Claude operations
6
+ *
7
+ * Required Permissions:
8
+ * - Issues: Read/Write (repo scope or issues scope)
9
+ * - Contents: Read/Write (repo scope or contents scope)
10
+ */
11
+
12
+ /**
13
+ * Validates GitHub token permissions
14
+ * @param {string} token - GitHub personal access token
15
+ * @param {Object} options - Validation options
16
+ * @param {boolean} options.strict - If true, requires exact scopes. If false, allows broader scopes like 'repo'
17
+ * @returns {Promise<Object>} Validation result
18
+ */
19
+ export async function validateGitHubToken(token, options = { strict: false }) {
20
+ if (!token) {
21
+ return {
22
+ valid: false,
23
+ error: 'No GitHub token provided',
24
+ missingPermissions: ['issues', 'contents'],
25
+ suggestion: 'Provide a GitHub personal access token'
26
+ };
27
+ }
28
+
29
+ const octokit = new Octokit({ auth: token });
30
+
31
+ try {
32
+ // Get token information
33
+ const { data: user, headers } = await octokit.rest.users.getAuthenticated();
34
+
35
+ // Parse X-OAuth-Scopes header to get token scopes
36
+ const scopeHeader = headers['x-oauth-scopes'];
37
+ const scopes = scopeHeader ? scopeHeader.split(',').map(s => s.trim()) : [];
38
+
39
+ // Check for required permissions
40
+ const requiredPermissions = {
41
+ issues: checkIssuesPermission(scopes, options.strict),
42
+ contents: checkContentsPermission(scopes, options.strict)
43
+ };
44
+
45
+ const hasAllPermissions = requiredPermissions.issues && requiredPermissions.contents;
46
+ const missingPermissions = [];
47
+
48
+ if (!requiredPermissions.issues) {
49
+ missingPermissions.push('issues');
50
+ }
51
+ if (!requiredPermissions.contents) {
52
+ missingPermissions.push('contents');
53
+ }
54
+
55
+ if (hasAllPermissions) {
56
+ return {
57
+ valid: true,
58
+ user: user.login,
59
+ scopes: scopes,
60
+ permissions: {
61
+ issues: 'read/write',
62
+ contents: 'read/write'
63
+ },
64
+ message: `✅ GitHub token valid for user: ${user.login}`
65
+ };
66
+ } else {
67
+ return {
68
+ valid: false,
69
+ user: user.login,
70
+ scopes: scopes,
71
+ error: 'Token is missing required permissions',
72
+ missingPermissions: missingPermissions,
73
+ currentPermissions: {
74
+ issues: requiredPermissions.issues ? 'read/write' : 'none or read-only',
75
+ contents: requiredPermissions.contents ? 'read/write' : 'none or read-only'
76
+ },
77
+ suggestion: generatePermissionSuggestion(missingPermissions, scopes)
78
+ };
79
+ }
80
+
81
+ } catch (error) {
82
+ // Handle specific error cases
83
+ if (error.status === 401) {
84
+ return {
85
+ valid: false,
86
+ error: 'Invalid or expired GitHub token',
87
+ details: error.message,
88
+ suggestion: 'Generate a new personal access token at https://github.com/settings/tokens'
89
+ };
90
+ }
91
+
92
+ if (error.status === 403) {
93
+ return {
94
+ valid: false,
95
+ error: 'GitHub API rate limit exceeded or token lacks basic access',
96
+ details: error.message,
97
+ suggestion: 'Wait for rate limit reset or check token permissions'
98
+ };
99
+ }
100
+
101
+ return {
102
+ valid: false,
103
+ error: 'Failed to validate GitHub token',
104
+ details: error.message,
105
+ suggestion: 'Check your network connection and token validity'
106
+ };
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Check if token has issues read/write permission
112
+ * @param {Array<string>} scopes - Token scopes
113
+ * @param {boolean} strict - Strict mode flag
114
+ * @returns {boolean} True if has permission
115
+ */
116
+ function checkIssuesPermission(scopes, strict) {
117
+ // 'repo' scope includes full access to repositories (issues, contents, etc.)
118
+ // 'public_repo' includes access to public repositories
119
+ // Fine-grained tokens might have specific 'issues' scope
120
+
121
+ if (strict) {
122
+ // In strict mode, require explicit issues scope or repo scope
123
+ return scopes.includes('repo') || scopes.includes('issues');
124
+ }
125
+
126
+ // In non-strict mode, allow broader scopes
127
+ return scopes.includes('repo') ||
128
+ scopes.includes('public_repo') ||
129
+ scopes.includes('issues');
130
+ }
131
+
132
+ /**
133
+ * Check if token has contents read/write permission
134
+ * @param {Array<string>} scopes - Token scopes
135
+ * @param {boolean} strict - Strict mode flag
136
+ * @returns {boolean} True if has permission
137
+ */
138
+ function checkContentsPermission(scopes, strict) {
139
+ // 'repo' scope includes full access to repositories
140
+ // 'public_repo' includes access to public repositories
141
+ // Fine-grained tokens might have specific 'contents' scope
142
+
143
+ if (strict) {
144
+ // In strict mode, require explicit contents scope or repo scope
145
+ return scopes.includes('repo') || scopes.includes('contents');
146
+ }
147
+
148
+ // In non-strict mode, allow broader scopes
149
+ return scopes.includes('repo') ||
150
+ scopes.includes('public_repo');
151
+ }
152
+
153
+ /**
154
+ * Generate helpful permission suggestion based on missing permissions
155
+ * @param {Array<string>} missing - Missing permission names
156
+ * @param {Array<string>} current - Current scopes
157
+ * @returns {string} Helpful suggestion
158
+ */
159
+ function generatePermissionSuggestion(missing, current) {
160
+ const suggestions = [];
161
+
162
+ if (missing.length === 0) {
163
+ return 'Token has all required permissions';
164
+ }
165
+
166
+ suggestions.push('To fix this, create a new GitHub token with the following scopes:');
167
+
168
+ if (current.includes('repo')) {
169
+ suggestions.push(' - Your token already has "repo" scope, but it may be read-only');
170
+ suggestions.push(' - Ensure the token has write access to repositories');
171
+ } else {
172
+ suggestions.push(' - Add "repo" scope for full repository access (recommended)');
173
+ suggestions.push(' OR');
174
+
175
+ if (missing.includes('issues')) {
176
+ suggestions.push(' - Add "issues" scope for issues read/write access');
177
+ }
178
+ if (missing.includes('contents')) {
179
+ suggestions.push(' - Add "contents" scope for repository contents read/write access');
180
+ }
181
+ }
182
+
183
+ suggestions.push('');
184
+ suggestions.push('Create a new token at: https://github.com/settings/tokens/new');
185
+ suggestions.push('Or use fine-grained tokens at: https://github.com/settings/personal-access-tokens/new');
186
+
187
+ return suggestions.join('\n');
188
+ }
189
+
190
+ /**
191
+ * Test token against a specific repository
192
+ * @param {string} token - GitHub token
193
+ * @param {string} owner - Repository owner
194
+ * @param {string} repo - Repository name
195
+ * @returns {Promise<Object>} Test result
196
+ */
197
+ export async function testTokenOnRepository(token, owner, repo) {
198
+ const octokit = new Octokit({ auth: token });
199
+
200
+ const tests = {
201
+ readIssues: false,
202
+ writeIssues: false,
203
+ readContents: false,
204
+ writeContents: false
205
+ };
206
+
207
+ const errors = [];
208
+
209
+ try {
210
+ // Test 1: Read issues
211
+ try {
212
+ await octokit.rest.issues.listForRepo({ owner, repo, per_page: 1 });
213
+ tests.readIssues = true;
214
+ } catch (error) {
215
+ errors.push(`Read Issues: ${error.message}`);
216
+ }
217
+
218
+ // Test 2: Create a test issue (we'll close it immediately)
219
+ try {
220
+ const { data: issue } = await octokit.rest.issues.create({
221
+ owner,
222
+ repo,
223
+ title: '[Boss Claude] Token validation test',
224
+ body: 'This is an automated test. Closing immediately.',
225
+ labels: ['bot', 'test']
226
+ });
227
+ tests.writeIssues = true;
228
+
229
+ // Close the test issue
230
+ await octokit.rest.issues.update({
231
+ owner,
232
+ repo,
233
+ issue_number: issue.number,
234
+ state: 'closed'
235
+ });
236
+ } catch (error) {
237
+ errors.push(`Write Issues: ${error.message}`);
238
+ }
239
+
240
+ // Test 3: Read contents
241
+ try {
242
+ await octokit.rest.repos.getContent({ owner, repo, path: 'README.md' });
243
+ tests.readContents = true;
244
+ } catch (error) {
245
+ errors.push(`Read Contents: ${error.message}`);
246
+ }
247
+
248
+ // Test 4: Write contents (get latest commit to test write access)
249
+ try {
250
+ await octokit.rest.repos.listCommits({ owner, repo, per_page: 1 });
251
+ tests.writeContents = true;
252
+ } catch (error) {
253
+ errors.push(`Write Contents: ${error.message}`);
254
+ }
255
+
256
+ const allTestsPassed = Object.values(tests).every(t => t === true);
257
+
258
+ return {
259
+ success: allTestsPassed,
260
+ repository: `${owner}/${repo}`,
261
+ tests,
262
+ errors: errors.length > 0 ? errors : null,
263
+ message: allTestsPassed
264
+ ? `✅ All permissions verified on ${owner}/${repo}`
265
+ : `❌ Some permissions are missing on ${owner}/${repo}`
266
+ };
267
+
268
+ } catch (error) {
269
+ return {
270
+ success: false,
271
+ repository: `${owner}/${repo}`,
272
+ error: `Failed to test repository: ${error.message}`,
273
+ suggestion: 'Ensure the repository exists and the token has access to it'
274
+ };
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Validate and display results in a user-friendly format
280
+ * @param {string} token - GitHub token
281
+ * @param {Object} options - Validation options
282
+ * @returns {Promise<boolean>} True if valid
283
+ */
284
+ export async function validateAndDisplay(token, options = {}) {
285
+ const result = await validateGitHubToken(token, options);
286
+
287
+ if (result.valid) {
288
+ console.log('\n✅ GitHub Token Validation: PASSED');
289
+ console.log(`User: ${result.user}`);
290
+ console.log(`Scopes: ${result.scopes.join(', ')}`);
291
+ console.log(`Permissions: Issues (R/W), Contents (R/W)`);
292
+ } else {
293
+ console.log('\n❌ GitHub Token Validation: FAILED');
294
+ console.log(`Error: ${result.error}`);
295
+ if (result.missingPermissions) {
296
+ console.log(`Missing: ${result.missingPermissions.join(', ')}`);
297
+ }
298
+ if (result.suggestion) {
299
+ console.log(`\n${result.suggestion}`);
300
+ }
301
+ }
302
+
303
+ return result.valid;
304
+ }
305
+
306
+ export default {
307
+ validateGitHubToken,
308
+ testTokenOnRepository,
309
+ validateAndDisplay
310
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * GitHub Token Validator Test
3
+ *
4
+ * Usage:
5
+ * node lib/validators/github.test.js <github-token>
6
+ * node lib/validators/github.test.js <github-token> <owner> <repo>
7
+ *
8
+ * Examples:
9
+ * node lib/validators/github.test.js ghp_abc123...
10
+ * node lib/validators/github.test.js ghp_abc123... cpretzinger boss-claude
11
+ */
12
+
13
+ import { validateGitHubToken, testTokenOnRepository, validateAndDisplay } from './github.js';
14
+
15
+ async function main() {
16
+ const args = process.argv.slice(2);
17
+
18
+ if (args.length === 0) {
19
+ console.log('Usage: node github.test.js <github-token> [owner] [repo]');
20
+ console.log('');
21
+ console.log('Examples:');
22
+ console.log(' node github.test.js ghp_abc123...');
23
+ console.log(' node github.test.js ghp_abc123... cpretzinger boss-claude');
24
+ process.exit(1);
25
+ }
26
+
27
+ const token = args[0];
28
+ const owner = args[1];
29
+ const repo = args[2];
30
+
31
+ console.log('🔍 Validating GitHub Token...\n');
32
+
33
+ // Basic validation
34
+ const isValid = await validateAndDisplay(token);
35
+
36
+ // If repository specified, run repository-specific tests
37
+ if (owner && repo && isValid) {
38
+ console.log('\n🔍 Testing Token on Repository...\n');
39
+ const repoTest = await testTokenOnRepository(token, owner, repo);
40
+
41
+ console.log(`Repository: ${repoTest.repository}`);
42
+ console.log(`Status: ${repoTest.success ? '✅ PASSED' : '❌ FAILED'}`);
43
+ console.log('\nPermission Tests:');
44
+ console.log(` Read Issues: ${repoTest.tests.readIssues ? '✅' : '❌'}`);
45
+ console.log(` Write Issues: ${repoTest.tests.writeIssues ? '✅' : '❌'}`);
46
+ console.log(` Read Contents: ${repoTest.tests.readContents ? '✅' : '❌'}`);
47
+ console.log(` Write Contents: ${repoTest.tests.writeContents ? '✅' : '❌'}`);
48
+
49
+ if (repoTest.errors) {
50
+ console.log('\nErrors:');
51
+ repoTest.errors.forEach(err => console.log(` - ${err}`));
52
+ }
53
+ }
54
+
55
+ process.exit(isValid ? 0 : 1);
56
+ }
57
+
58
+ main().catch(error => {
59
+ console.error('Error:', error.message);
60
+ process.exit(1);
61
+ });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Boss Claude Validators
3
+ *
4
+ * Validation utilities for external service credentials and permissions
5
+ */
6
+
7
+ export * from './github.js';
8
+ export * from './postgres.js';
9
+ export * from './config.js';
10
+
11
+ export default {
12
+ github: () => import('./github.js'),
13
+ postgres: () => import('./postgres.js'),
14
+ config: () => import('./config.js')
15
+ };