sinapse-ai 1.20.0 → 1.20.1

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 (70) hide show
  1. package/.sinapse-ai/core/atlas/render-html.js +10 -8
  2. package/.sinapse-ai/core/registry/service-registry.json +2 -32
  3. package/.sinapse-ai/data/entity-registry.yaml +440 -195
  4. package/.sinapse-ai/development/agents/analyst.md +18 -34
  5. package/.sinapse-ai/development/agents/architect.md +18 -34
  6. package/.sinapse-ai/development/agents/data-engineer.md +15 -28
  7. package/.sinapse-ai/development/agents/developer.md +0 -6
  8. package/.sinapse-ai/development/agents/devops.md +15 -28
  9. package/.sinapse-ai/development/agents/product-lead.md +18 -34
  10. package/.sinapse-ai/development/agents/project-lead.md +18 -34
  11. package/.sinapse-ai/development/agents/quality-gate.md +18 -34
  12. package/.sinapse-ai/development/agents/squad-creator.md +15 -27
  13. package/.sinapse-ai/development/agents/ux-design-expert.md +15 -27
  14. package/.sinapse-ai/development/scripts/populate-entity-registry.js +3 -0
  15. package/.sinapse-ai/development/tasks/execute-epic-plan.md +0 -4
  16. package/.sinapse-ai/development/workflows/epic-orchestration.yaml +0 -1
  17. package/.sinapse-ai/git-hooks/lib/staged-secret-scan.js +3 -1
  18. package/.sinapse-ai/install-manifest.yaml +40 -72
  19. package/.sinapse-ai/product/templates/personalized-agent-template.md +3 -5
  20. package/.sinapse-ai/workflow-intelligence/index.js +3 -21
  21. package/AGENTS.md +2 -2
  22. package/CHANGELOG.md +38 -19
  23. package/bin/commands/install.js +5 -4
  24. package/bin/lib/command-generator.js +4 -4
  25. package/bin/lib/fs-utils.js +18 -0
  26. package/bin/lib/setup-statusline.js +3 -1
  27. package/bin/modules/chrome-brain-installer.js +5 -3
  28. package/bin/modules/mcp-installer.js +3 -6
  29. package/bin/postinstall.js +2 -1
  30. package/bin/sinapse-init.js +8 -9
  31. package/bin/sinapse.js +3 -1
  32. package/bin/utils/staged-secret-scan.js +3 -1
  33. package/docs/framework/atlas/OPERATING-ATLAS.md +1 -1
  34. package/docs/framework/atlas/atlas-data.json +22 -22
  35. package/docs/framework/atlas/atlas.html +12 -10
  36. package/docs/framework/source-tree.md +1 -2
  37. package/package.json +1 -1
  38. package/packages/installer/src/config/configure-environment.js +8 -6
  39. package/packages/installer/src/installer/brownfield-upgrader.js +5 -3
  40. package/packages/installer/src/installer/file-hasher.js +29 -21
  41. package/packages/installer/src/installer/manifest-signature.js +61 -49
  42. package/packages/installer/src/installer/post-install-validator.js +24 -17
  43. package/packages/installer/src/merger/parsers/markdown-section-parser.js +3 -2
  44. package/packages/installer/src/updater/index.js +7 -1
  45. package/packages/installer/src/utils/atomic-write.js +35 -0
  46. package/packages/installer/src/wizard/index.js +5 -3
  47. package/packages/sinapse-install/src/capabilities/chrome-brain.js +5 -3
  48. package/packages/sinapse-install/src/installer.js +3 -1
  49. package/packages/sinapse-install/src/utils/atomic-write.js +67 -0
  50. package/scripts/apply-persona-disclaimer.js +2 -1
  51. package/scripts/sinapse-patch.js +2 -1
  52. package/scripts/sync-squad-yaml-components.js +2 -1
  53. package/scripts/validate-agents-md.js +2 -1
  54. package/scripts/validate-article-xi.js +10 -8
  55. package/scripts/validate-no-external-refs.js +14 -8
  56. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +3 -8
  57. package/squads/claude-code-mastery/agents/config-engineer.md +5 -12
  58. package/squads/claude-code-mastery/agents/hooks-architect.md +5 -11
  59. package/squads/claude-code-mastery/agents/mcp-integrator.md +5 -12
  60. package/squads/claude-code-mastery/agents/project-integrator.md +5 -12
  61. package/squads/claude-code-mastery/agents/roadmap-sentinel.md +5 -12
  62. package/squads/claude-code-mastery/agents/skill-craftsman.md +5 -11
  63. package/squads/claude-code-mastery/agents/swarm-orqx.md +15 -28
  64. package/.sinapse-ai/core/execution/context-injector.js +0 -539
  65. package/.sinapse-ai/core/execution/parallel-executor.js +0 -301
  66. package/.sinapse-ai/core/execution/parallel-monitor.js +0 -432
  67. package/.sinapse-ai/core/execution/semantic-merge-engine.js +0 -1750
  68. package/.sinapse-ai/core/execution/wave-executor.js +0 -403
  69. package/.sinapse-ai/development/tasks/waves.md +0 -205
  70. package/.sinapse-ai/workflow-intelligence/engine/wave-analyzer.js +0 -692
@@ -1,539 +0,0 @@
1
- /**
2
- * Context Injector
3
- * Story 10.3 - Parallel Agent Execution
4
- *
5
- * @abandoned-path — ver docs/epics/epic-ultra-optimization/decisions/DEC-03 (2026-07-02)
6
- *
7
- * Injects rich context into subagent prompts including:
8
- * - Project context
9
- * - Relevant files
10
- * - Memory/patterns
11
- * - Gotchas
12
- * - Recent decisions
13
- */
14
-
15
- const fs = require('fs');
16
- const path = require('path');
17
-
18
- // Import dependencies with fallbacks
19
- let MemoryQuery, GotchasMemory, SessionMemory;
20
- try {
21
- MemoryQuery = require('../memory/memory-query');
22
- } catch {
23
- MemoryQuery = null;
24
- }
25
- try {
26
- GotchasMemory = require('../memory/gotchas-memory');
27
- } catch {
28
- GotchasMemory = null;
29
- }
30
- try {
31
- SessionMemory = require('../memory/session-memory');
32
- } catch {
33
- SessionMemory = null;
34
- }
35
-
36
- class ContextInjector {
37
- constructor(config = {}) {
38
- // Token budget for context (prevents overwhelming the LLM)
39
- this.tokenBudget = config.tokenBudget || 4000;
40
-
41
- // Approximate characters per token
42
- this.charsPerToken = config.charsPerToken || 4;
43
-
44
- // Cache TTL (5 minutes)
45
- this.cacheTTL = config.cacheTTL || 5 * 60 * 1000;
46
-
47
- // Context cache
48
- this.cache = new Map();
49
-
50
- // Dependencies
51
- this.memoryQuery = config.memoryQuery || (MemoryQuery ? new MemoryQuery() : null);
52
- this.gotchasMemory = config.gotchasMemory || (GotchasMemory ? new GotchasMemory() : null);
53
- this.sessionMemory = config.sessionMemory || (SessionMemory ? new SessionMemory() : null);
54
-
55
- // Root path
56
- this.rootPath = config.rootPath || process.cwd();
57
-
58
- // Metrics
59
- this.metrics = {
60
- injections: 0,
61
- cacheHits: 0,
62
- avgContextSize: 0,
63
- avgInjectionTime: 0,
64
- };
65
- }
66
-
67
- /**
68
- * Inject context for a task
69
- * @param {Object} task - Task to inject context for
70
- * @param {Object} baseContext - Base context
71
- * @returns {Promise<string>} - Formatted context string
72
- */
73
- async inject(task, _baseContext = {}) {
74
- const startTime = Date.now();
75
- const cacheKey = this.getCacheKey(task);
76
-
77
- // Build injection payload
78
- const injection = {
79
- // 1. Task description (required)
80
- task: {
81
- id: task.id,
82
- description: task.description,
83
- acceptanceCriteria: task.acceptanceCriteria || [],
84
- verification: task.verification,
85
- },
86
-
87
- // 2. Project context (from cache or fresh)
88
- project: await this.getProjectContext(cacheKey),
89
-
90
- // 3. Relevant files (task-specific)
91
- files: await this.getRelevantFiles(task),
92
-
93
- // 4. Memory context
94
- memory: await this.getRelevantMemory(task),
95
-
96
- // 5. Gotchas
97
- gotchas: await this.getRelevantGotchas(task),
98
-
99
- // 6. Recent decisions
100
- decisions: await this.getRecentDecisions(),
101
- };
102
-
103
- // Format for LLM consumption
104
- const formatted = this.formatForLLM(injection);
105
-
106
- // Ensure within token budget
107
- const trimmed = this.trimToTokenBudget(formatted, this.tokenBudget);
108
-
109
- // Update metrics
110
- const injectionTime = Date.now() - startTime;
111
- this.updateMetrics(trimmed, injectionTime);
112
-
113
- return trimmed;
114
- }
115
-
116
- /**
117
- * Get cache key for a task
118
- * @param {Object} task - Task
119
- * @returns {string} - Cache key
120
- */
121
- getCacheKey(task) {
122
- return `${task.type || 'default'}-${task.service || 'core'}`;
123
- }
124
-
125
- /**
126
- * Get cached value if valid
127
- * @param {string} key - Cache key
128
- * @returns {any|null} - Cached value or null
129
- */
130
- getCached(key) {
131
- const cached = this.cache.get(key);
132
- if (!cached) return null;
133
-
134
- if (Date.now() - cached.timestamp > this.cacheTTL) {
135
- this.cache.delete(key);
136
- return null;
137
- }
138
-
139
- this.metrics.cacheHits++;
140
- return cached.value;
141
- }
142
-
143
- /**
144
- * Set cache value
145
- * @param {string} key - Cache key
146
- * @param {any} value - Value to cache
147
- */
148
- setCache(key, value) {
149
- this.cache.set(key, {
150
- value,
151
- timestamp: Date.now(),
152
- });
153
- }
154
-
155
- /**
156
- * Get project context
157
- * @param {string} cacheKey - Cache key for type-specific context
158
- * @returns {Promise<Object>} - Project context
159
- */
160
- async getProjectContext(cacheKey) {
161
- // Check cache
162
- const cached = this.getCached(`project-${cacheKey}`);
163
- if (cached) return cached;
164
-
165
- const context = {
166
- name: 'unknown',
167
- framework: null,
168
- patterns: [],
169
- conventions: [],
170
- };
171
-
172
- try {
173
- // Try to read codebase map
174
- const codebaseMapPath = path.join(this.rootPath, '.sinapse', 'codebase-map.json');
175
- if (fs.existsSync(codebaseMapPath)) {
176
- const codebaseMap = JSON.parse(fs.readFileSync(codebaseMapPath, 'utf8'));
177
- context.name = codebaseMap.name || codebaseMap.projectName || 'project';
178
- context.framework = codebaseMap.framework || codebaseMap.mainFramework;
179
- context.patterns = codebaseMap.patterns || [];
180
- context.entryPoints = codebaseMap.entryPoints || [];
181
- }
182
-
183
- // Try to read package.json
184
- const packagePath = path.join(this.rootPath, 'package.json');
185
- if (fs.existsSync(packagePath)) {
186
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
187
- context.name = pkg.name || context.name;
188
- context.version = pkg.version;
189
- context.dependencies = Object.keys(pkg.dependencies || {}).slice(0, 10);
190
- }
191
-
192
- // Try to detect conventions
193
- context.conventions = await this.detectConventions();
194
- } catch (_error) {
195
- // Silently fail - context is optional enhancement
196
- }
197
-
198
- // Cache the result
199
- this.setCache(`project-${cacheKey}`, context);
200
-
201
- return context;
202
- }
203
-
204
- /**
205
- * Detect project conventions
206
- * @returns {Promise<Array>} - Detected conventions
207
- */
208
- async detectConventions() {
209
- const conventions = [];
210
-
211
- try {
212
- // Check for TypeScript
213
- if (fs.existsSync(path.join(this.rootPath, 'tsconfig.json'))) {
214
- conventions.push('TypeScript project');
215
- }
216
-
217
- // Check for ESLint
218
- const eslintFiles = ['.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml'];
219
- if (eslintFiles.some((f) => fs.existsSync(path.join(this.rootPath, f)))) {
220
- conventions.push('Uses ESLint');
221
- }
222
-
223
- // Check for Prettier
224
- if (fs.existsSync(path.join(this.rootPath, '.prettierrc'))) {
225
- conventions.push('Uses Prettier');
226
- }
227
-
228
- // Check for tests directory
229
- if (fs.existsSync(path.join(this.rootPath, 'tests'))) {
230
- conventions.push('Tests in /tests directory');
231
- } else if (fs.existsSync(path.join(this.rootPath, '__tests__'))) {
232
- conventions.push('Tests in /__tests__ directory');
233
- }
234
- } catch (_error) {
235
- // Ignore errors
236
- }
237
-
238
- return conventions;
239
- }
240
-
241
- /**
242
- * Get relevant files for a task
243
- * @param {Object} task - Task
244
- * @returns {Promise<Array>} - Relevant files
245
- */
246
- async getRelevantFiles(task) {
247
- const files = [];
248
-
249
- // Add explicitly specified files
250
- if (task.files) {
251
- for (const file of task.files) {
252
- files.push({
253
- path: file,
254
- purpose: 'Specified in task',
255
- exists: fs.existsSync(path.join(this.rootPath, file)),
256
- });
257
- }
258
- }
259
-
260
- // Infer files from task description
261
- const inferredFiles = this.inferFilesFromDescription(task.description);
262
- for (const file of inferredFiles) {
263
- if (!files.some((f) => f.path === file)) {
264
- files.push({
265
- path: file,
266
- purpose: 'Inferred from description',
267
- exists: fs.existsSync(path.join(this.rootPath, file)),
268
- });
269
- }
270
- }
271
-
272
- return files.slice(0, 10); // Limit to 10 files
273
- }
274
-
275
- /**
276
- * Infer files from task description
277
- * @param {string} description - Task description
278
- * @returns {Array<string>} - Inferred file paths
279
- */
280
- inferFilesFromDescription(description) {
281
- if (!description) return [];
282
-
283
- const files = [];
284
-
285
- // Match file paths in backticks
286
- const backtickMatches = description.match(/`([^`]+\.[a-zA-Z]+)`/g) || [];
287
- for (const match of backtickMatches) {
288
- files.push(match.replace(/`/g, ''));
289
- }
290
-
291
- // Match quoted paths
292
- const quotedMatches = description.match(/['"]([^'"]+\.[a-zA-Z]+)['"]/g) || [];
293
- for (const match of quotedMatches) {
294
- files.push(match.replace(/['"]/g, ''));
295
- }
296
-
297
- return [...new Set(files)];
298
- }
299
-
300
- /**
301
- * Get relevant memory for a task
302
- * @param {Object} task - Task
303
- * @returns {Promise<Array>} - Relevant memory entries
304
- */
305
- async getRelevantMemory(task) {
306
- if (!this.memoryQuery) return [];
307
-
308
- try {
309
- const query = task.description || task.id;
310
- const results = await this.memoryQuery.query(query, { limit: 5 });
311
- return results.map((r) => ({
312
- type: r.type,
313
- content: r.content || r.summary,
314
- relevance: r.score || r.relevance,
315
- }));
316
- } catch (_error) {
317
- return [];
318
- }
319
- }
320
-
321
- /**
322
- * Get relevant gotchas for a task
323
- * @param {Object} task - Task
324
- * @returns {Promise<Array>} - Relevant gotchas
325
- */
326
- async getRelevantGotchas(task) {
327
- if (!this.gotchasMemory) return [];
328
-
329
- try {
330
- return await this.gotchasMemory.getContextForTask(task.description || task.id);
331
- } catch (_error) {
332
- return [];
333
- }
334
- }
335
-
336
- /**
337
- * Get recent decisions from session memory
338
- * @returns {Promise<Array>} - Recent decisions
339
- */
340
- async getRecentDecisions() {
341
- if (!this.sessionMemory) return [];
342
-
343
- try {
344
- const decisions = await this.sessionMemory.getDecisions({ limit: 5 });
345
- return decisions.map((d) => ({
346
- decision: d.decision || d.content,
347
- reason: d.reason,
348
- timestamp: d.timestamp,
349
- }));
350
- } catch (_error) {
351
- return [];
352
- }
353
- }
354
-
355
- /**
356
- * Format injection payload for LLM consumption
357
- * @param {Object} injection - Injection payload
358
- * @returns {string} - Formatted markdown
359
- */
360
- formatForLLM(injection) {
361
- let output = '';
362
-
363
- // Task section (always included)
364
- output += '## Task Context\n\n';
365
- output += '### Current Task\n';
366
- output += `**ID:** ${injection.task.id}\n`;
367
- output += `**Description:** ${injection.task.description}\n\n`;
368
-
369
- // Acceptance Criteria
370
- if (injection.task.acceptanceCriteria && injection.task.acceptanceCriteria.length > 0) {
371
- output += '### Acceptance Criteria\n';
372
- const criteria = Array.isArray(injection.task.acceptanceCriteria)
373
- ? injection.task.acceptanceCriteria
374
- : [injection.task.acceptanceCriteria];
375
- criteria.forEach((ac, i) => {
376
- output += `${i + 1}. ${ac}\n`;
377
- });
378
- output += '\n';
379
- }
380
-
381
- // Relevant Files
382
- if (injection.files && injection.files.length > 0) {
383
- output += '### Relevant Files\n';
384
- injection.files.forEach((f) => {
385
- const status = f.exists ? '✓' : '?';
386
- output += `- [${status}] \`${f.path}\`: ${f.purpose}\n`;
387
- });
388
- output += '\n';
389
- }
390
-
391
- // Project Patterns
392
- if (injection.project.patterns && injection.project.patterns.length > 0) {
393
- output += '### Project Patterns\n';
394
- injection.project.patterns.slice(0, 5).forEach((p) => {
395
- const name = typeof p === 'string' ? p : p.name;
396
- output += `- ${name}\n`;
397
- });
398
- output += '\n';
399
- }
400
-
401
- // Conventions
402
- if (injection.project.conventions && injection.project.conventions.length > 0) {
403
- output += '### Conventions\n';
404
- injection.project.conventions.forEach((c) => {
405
- output += `- ${c}\n`;
406
- });
407
- output += '\n';
408
- }
409
-
410
- // Active Gotchas
411
- if (injection.gotchas && injection.gotchas.length > 0) {
412
- output += '### Active Gotchas (Avoid These)\n';
413
- injection.gotchas.slice(0, 5).forEach((g) => {
414
- const title = g.title || g.pattern || 'Warning';
415
- const workaround = g.workaround || g.description || 'Be careful';
416
- output += `⚠️ **${title}**: ${workaround}\n\n`;
417
- });
418
- }
419
-
420
- // Recent Decisions
421
- if (injection.decisions && injection.decisions.length > 0) {
422
- output += '### Recent Decisions\n';
423
- injection.decisions.slice(0, 3).forEach((d) => {
424
- output += `- ${d.decision}`;
425
- if (d.reason) output += ` (${d.reason})`;
426
- output += '\n';
427
- });
428
- output += '\n';
429
- }
430
-
431
- // Relevant Memory
432
- if (injection.memory && injection.memory.length > 0) {
433
- output += '### Relevant Context from Memory\n';
434
- injection.memory.slice(0, 3).forEach((m) => {
435
- output += `- [${m.type}] ${m.content}\n`;
436
- });
437
- output += '\n';
438
- }
439
-
440
- return output;
441
- }
442
-
443
- /**
444
- * Trim content to fit token budget
445
- * @param {string} content - Content to trim
446
- * @param {number} tokenBudget - Maximum tokens
447
- * @returns {string} - Trimmed content
448
- */
449
- trimToTokenBudget(content, tokenBudget) {
450
- const maxChars = tokenBudget * this.charsPerToken;
451
-
452
- if (content.length <= maxChars) {
453
- return content;
454
- }
455
-
456
- // Try to trim intelligently by sections
457
- const sections = content.split(/(?=###)/);
458
- let trimmed = '';
459
-
460
- // Always include the task section
461
- if (sections[0]) {
462
- trimmed = sections[0];
463
- }
464
-
465
- // Add other sections until budget is reached
466
- for (let i = 1; i < sections.length; i++) {
467
- if (trimmed.length + sections[i].length <= maxChars) {
468
- trimmed += sections[i];
469
- }
470
- }
471
-
472
- // If still too long, hard truncate
473
- if (trimmed.length > maxChars) {
474
- trimmed = trimmed.substring(0, maxChars - 50) + '\n\n[Context truncated for token budget]\n';
475
- }
476
-
477
- return trimmed;
478
- }
479
-
480
- /**
481
- * Update metrics
482
- * @param {string} context - Generated context
483
- * @param {number} time - Injection time
484
- */
485
- updateMetrics(context, time) {
486
- this.metrics.injections++;
487
- this.metrics.avgContextSize =
488
- (this.metrics.avgContextSize * (this.metrics.injections - 1) + context.length) /
489
- this.metrics.injections;
490
- this.metrics.avgInjectionTime =
491
- (this.metrics.avgInjectionTime * (this.metrics.injections - 1) + time) /
492
- this.metrics.injections;
493
- }
494
-
495
- /**
496
- * Get metrics
497
- * @returns {Object} - Current metrics
498
- */
499
- getMetrics() {
500
- return {
501
- ...this.metrics,
502
- avgContextSize: Math.round(this.metrics.avgContextSize),
503
- avgInjectionTime: Math.round(this.metrics.avgInjectionTime),
504
- cacheSize: this.cache.size,
505
- };
506
- }
507
-
508
- /**
509
- * Clear cache
510
- */
511
- clearCache() {
512
- this.cache.clear();
513
- }
514
-
515
- /**
516
- * Format status for CLI
517
- * @returns {string} - Formatted status
518
- */
519
- formatStatus() {
520
- const metrics = this.getMetrics();
521
-
522
- let output = '💉 Context Injector Status\n';
523
- output += '━'.repeat(40) + '\n\n';
524
-
525
- output += '**Metrics:**\n';
526
- output += ` Total Injections: ${metrics.injections}\n`;
527
- output += ` Cache Hits: ${metrics.cacheHits}\n`;
528
- output += ` Cache Size: ${metrics.cacheSize}\n`;
529
- output += ` Avg Context Size: ${metrics.avgContextSize} chars\n`;
530
- output += ` Avg Injection Time: ${metrics.avgInjectionTime}ms\n`;
531
- output += ` Token Budget: ${this.tokenBudget}\n`;
532
-
533
- return output;
534
- }
535
- }
536
-
537
- module.exports = ContextInjector;
538
- module.exports.ContextInjector = ContextInjector;
539
-