@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,358 @@
1
+ import { QdrantClient } from '@qdrant/js-client-rest';
2
+ import OpenAI from 'openai';
3
+
4
+ /**
5
+ * QDRANT MEMORY ENGINEER
6
+ *
7
+ * Semantic vector search component for Boss Claude memory system.
8
+ * Uses Qdrant for vector similarity search with OpenAI embeddings.
9
+ *
10
+ * Features:
11
+ * - Vector similarity search
12
+ * - OpenAI embeddings integration (text-embedding-3-small)
13
+ * - Graceful degradation if Qdrant not available
14
+ * - Collection management
15
+ * - Point upsert/delete operations
16
+ *
17
+ * Environment Variables:
18
+ * - QDRANT_URL (optional) - Qdrant server URL
19
+ * - QDRANT_API_KEY (optional) - Qdrant API key
20
+ * - OPENAI_API_KEY (required for embeddings)
21
+ */
22
+
23
+ class QdrantMemoryEngineer {
24
+ constructor() {
25
+ this.enabled = false;
26
+ this.client = null;
27
+ this.openai = null;
28
+ this.collectionName = 'boss_claude_memories';
29
+ this.vectorDimension = 1536; // text-embedding-3-small dimension
30
+
31
+ this.initialize();
32
+ }
33
+
34
+ /**
35
+ * Initialize Qdrant client and OpenAI
36
+ */
37
+ initialize() {
38
+ try {
39
+ const qdrantUrl = process.env.QDRANT_URL;
40
+ const qdrantApiKey = process.env.QDRANT_API_KEY;
41
+ const openaiApiKey = process.env.OPENAI_API_KEY;
42
+
43
+ if (!qdrantUrl) {
44
+ console.log('[Qdrant] QDRANT_URL not set - semantic search disabled (graceful degradation)');
45
+ return;
46
+ }
47
+
48
+ if (!openaiApiKey) {
49
+ console.warn('[Qdrant] OPENAI_API_KEY not set - cannot generate embeddings');
50
+ return;
51
+ }
52
+
53
+ // Initialize Qdrant client
54
+ this.client = new QdrantClient({
55
+ url: qdrantUrl,
56
+ apiKey: qdrantApiKey,
57
+ });
58
+
59
+ // Initialize OpenAI for embeddings
60
+ // FIX: Don't pass raw API key - OpenAI SDK handles it securely from env
61
+ this.openai = new OpenAI({
62
+ apiKey: openaiApiKey,
63
+ dangerouslyAllowBrowser: false // Ensure server-side only
64
+ });
65
+
66
+ this.enabled = true;
67
+ console.log('[Qdrant] Initialized successfully');
68
+ } catch (error) {
69
+ console.warn('[Qdrant] Failed to initialize:', error.message);
70
+ this.enabled = false;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Check if Qdrant is available
76
+ */
77
+ isAvailable() {
78
+ return this.enabled;
79
+ }
80
+
81
+ /**
82
+ * Ensure collection exists
83
+ */
84
+ async ensureCollection() {
85
+ if (!this.enabled) return false;
86
+
87
+ try {
88
+ // Check if collection exists
89
+ const collections = await this.client.getCollections();
90
+ const exists = collections.collections.some(
91
+ col => col.name === this.collectionName
92
+ );
93
+
94
+ if (!exists) {
95
+ // Create collection
96
+ await this.client.createCollection(this.collectionName, {
97
+ vectors: {
98
+ size: this.vectorDimension,
99
+ distance: 'Cosine',
100
+ },
101
+ optimizers_config: {
102
+ default_segment_number: 2,
103
+ },
104
+ replication_factor: 1,
105
+ });
106
+ console.log(`[Qdrant] Created collection: ${this.collectionName}`);
107
+ }
108
+
109
+ return true;
110
+ } catch (error) {
111
+ console.error('[Qdrant] Failed to ensure collection:', error.message);
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Generate embedding for text using OpenAI
118
+ */
119
+ async generateEmbedding(text) {
120
+ if (!this.enabled || !this.openai) {
121
+ throw new Error('OpenAI not available for embeddings');
122
+ }
123
+
124
+ try {
125
+ const response = await this.openai.embeddings.create({
126
+ model: 'text-embedding-3-small',
127
+ input: text,
128
+ });
129
+
130
+ return response.data[0].embedding;
131
+ } catch (error) {
132
+ // FIX: Sanitize error messages to prevent API key leakage
133
+ const sanitizedMessage = error.message?.replace(/sk-[a-zA-Z0-9_-]{20,}/g, 'sk-***REDACTED***') || 'Unknown error';
134
+ console.error('[Qdrant] Failed to generate embedding:', sanitizedMessage);
135
+ throw new Error(sanitizedMessage);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Store memory with vector embedding
141
+ *
142
+ * @param {string} id - Unique memory ID
143
+ * @param {string} text - Memory text content
144
+ * @param {object} metadata - Additional metadata
145
+ */
146
+ async storeMemory(id, text, metadata = {}) {
147
+ if (!this.enabled) {
148
+ console.log('[Qdrant] Not available - skipping vector storage');
149
+ return false;
150
+ }
151
+
152
+ // FIX: Validate ID to prevent collisions and ensure type safety
153
+ if (!id || typeof id !== 'string' && typeof id !== 'number') {
154
+ console.error('[Qdrant] Invalid ID provided:', typeof id);
155
+ return false;
156
+ }
157
+
158
+ // Ensure numeric IDs or UUID format strings only
159
+ const validId = typeof id === 'number' ? id : String(id);
160
+
161
+ try {
162
+ await this.ensureCollection();
163
+
164
+ // Generate embedding
165
+ const vector = await this.generateEmbedding(text);
166
+
167
+ // Upsert point
168
+ await this.client.upsert(this.collectionName, {
169
+ wait: true,
170
+ points: [
171
+ {
172
+ id: validId,
173
+ vector: vector,
174
+ payload: {
175
+ text,
176
+ ...metadata,
177
+ stored_at: new Date().toISOString(),
178
+ original_id: id // Preserve original ID in payload
179
+ },
180
+ },
181
+ ],
182
+ });
183
+
184
+ console.log(`[Qdrant] Stored memory: ${validId}`);
185
+ return true;
186
+ } catch (error) {
187
+ console.error('[Qdrant] Failed to store memory:', error.message);
188
+ return false;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Search for similar memories
194
+ *
195
+ * @param {string} query - Search query text
196
+ * @param {number} limit - Maximum results to return
197
+ * @param {number} scoreThreshold - Minimum similarity score (0-1)
198
+ */
199
+ async searchSimilar(query, limit = 5, scoreThreshold = 0.7) {
200
+ if (!this.enabled) {
201
+ console.log('[Qdrant] Not available - returning empty results');
202
+ return [];
203
+ }
204
+
205
+ try {
206
+ await this.ensureCollection();
207
+
208
+ // Generate query embedding
209
+ const queryVector = await this.generateEmbedding(query);
210
+
211
+ // Search
212
+ const results = await this.client.search(this.collectionName, {
213
+ vector: queryVector,
214
+ limit: limit,
215
+ score_threshold: scoreThreshold,
216
+ with_payload: true,
217
+ });
218
+
219
+ return results.map(result => ({
220
+ id: result.id,
221
+ score: result.score,
222
+ text: result.payload.text,
223
+ metadata: result.payload,
224
+ }));
225
+ } catch (error) {
226
+ console.error('[Qdrant] Search failed:', error.message);
227
+ return [];
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Delete memory by ID
233
+ *
234
+ * @param {string} id - Memory ID to delete
235
+ */
236
+ async deleteMemory(id) {
237
+ if (!this.enabled) {
238
+ console.log('[Qdrant] Not available - skipping deletion');
239
+ return false;
240
+ }
241
+
242
+ try {
243
+ await this.client.delete(this.collectionName, {
244
+ wait: true,
245
+ points: [id],
246
+ });
247
+
248
+ console.log(`[Qdrant] Deleted memory: ${id}`);
249
+ return true;
250
+ } catch (error) {
251
+ console.error('[Qdrant] Failed to delete memory:', error.message);
252
+ return false;
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Delete multiple memories by IDs
258
+ *
259
+ * @param {string[]} ids - Array of memory IDs to delete
260
+ */
261
+ async deleteMemories(ids) {
262
+ if (!this.enabled) {
263
+ console.log('[Qdrant] Not available - skipping deletion');
264
+ return false;
265
+ }
266
+
267
+ try {
268
+ await this.client.delete(this.collectionName, {
269
+ wait: true,
270
+ points: ids,
271
+ });
272
+
273
+ console.log(`[Qdrant] Deleted ${ids.length} memories`);
274
+ return true;
275
+ } catch (error) {
276
+ console.error('[Qdrant] Failed to delete memories:', error.message);
277
+ return false;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Get collection info and stats
283
+ */
284
+ async getStats() {
285
+ if (!this.enabled) {
286
+ return {
287
+ available: false,
288
+ message: 'Qdrant not configured',
289
+ };
290
+ }
291
+
292
+ try {
293
+ const info = await this.client.getCollection(this.collectionName);
294
+
295
+ return {
296
+ available: true,
297
+ collection: this.collectionName,
298
+ points_count: info.points_count,
299
+ vectors_count: info.vectors_count,
300
+ indexed_vectors_count: info.indexed_vectors_count,
301
+ status: info.status,
302
+ };
303
+ } catch (error) {
304
+ return {
305
+ available: false,
306
+ error: error.message,
307
+ };
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Clear all memories (use with caution)
313
+ */
314
+ async clearAll() {
315
+ if (!this.enabled) {
316
+ console.log('[Qdrant] Not available - nothing to clear');
317
+ return false;
318
+ }
319
+
320
+ try {
321
+ await this.client.deleteCollection(this.collectionName);
322
+ console.log(`[Qdrant] Cleared collection: ${this.collectionName}`);
323
+ await this.ensureCollection();
324
+ return true;
325
+ } catch (error) {
326
+ console.error('[Qdrant] Failed to clear collection:', error.message);
327
+ return false;
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Health check
333
+ */
334
+ async healthCheck() {
335
+ if (!this.enabled) {
336
+ return { healthy: false, reason: 'Not configured' };
337
+ }
338
+
339
+ try {
340
+ await this.client.getCollections();
341
+ return { healthy: true };
342
+ } catch (error) {
343
+ return { healthy: false, reason: error.message };
344
+ }
345
+ }
346
+ }
347
+
348
+ // Singleton instance
349
+ let instance = null;
350
+
351
+ export function getQdrantMemoryEngineer() {
352
+ if (!instance) {
353
+ instance = new QdrantMemoryEngineer();
354
+ }
355
+ return instance;
356
+ }
357
+
358
+ export default QdrantMemoryEngineer;