sinapse-ai 1.9.0 → 1.9.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 (88) hide show
  1. package/.claude/rules/mandatory-delegation.md +1 -1
  2. package/.codex/delegation-matrix.json +4 -3
  3. package/.codex/delegation-parity.json +4 -3
  4. package/.codex/instructions.md +2 -2
  5. package/.sinapse-ai/constitution.md +2 -2
  6. package/.sinapse-ai/core/doctor/checks/git-hooks.js +76 -10
  7. package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
  8. package/.sinapse-ai/core/synapse/engine.js +15 -0
  9. package/.sinapse-ai/data/entity-registry.yaml +13 -13
  10. package/.sinapse-ai/development/agents/snps-orqx.md +4 -4
  11. package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +76 -4
  12. package/.sinapse-ai/git-hooks/pre-push +7 -1
  13. package/.sinapse-ai/install-manifest.yaml +9 -9
  14. package/AGENTS.md +2 -2
  15. package/CHANGELOG.md +1247 -0
  16. package/bin/commands/uninstall.js +2 -2
  17. package/bin/utils/secret-scanner-core.js +76 -4
  18. package/docs/agent-reference-guide.md +1 -1
  19. package/docs/framework/architecture-overview.md +4 -4
  20. package/docs/framework/guiding-principles.md +9 -9
  21. package/docs/getting-started.md +1 -1
  22. package/docs/guides/agent-reference.md +1 -1
  23. package/docs/guides/codex-config.md +4 -5
  24. package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
  25. package/package.json +8 -2
  26. package/packages/installer/src/installer/git-hooks-installer.js +3 -1
  27. package/packages/installer/src/wizard/ide-config-generator.js +9 -1
  28. package/packages/installer/src/wizard/index.js +3 -4
  29. package/scripts/regenerate-orqx-stubs.ps1 +0 -1
  30. package/scripts/sync-counts.js +10 -2
  31. package/scripts/sync-squad-yaml-components.js +108 -6
  32. package/scripts/validate-squad-orqx.js +19 -9
  33. package/sinapse/agents/sinapse-orqx.md +4 -4
  34. package/sinapse/agents/snps-orqx.md +4 -4
  35. package/sinapse/knowledge-base/routing-catalog.md +1 -1
  36. package/sinapse/tasks/diagnose-and-route.md +1 -1
  37. package/sinapse/tasks/squad-status-report.md +1 -1
  38. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
  39. package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
  40. package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
  41. package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
  42. package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
  43. package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
  44. package/squads/squad-animations/README.md +1 -1
  45. package/squads/squad-cloning/README.md +1 -1
  46. package/squads/squad-commercial/README.md +1 -1
  47. package/squads/squad-content/README.md +1 -1
  48. package/squads/squad-copy/README.md +1 -1
  49. package/squads/squad-council/README.md +1 -1
  50. package/squads/squad-courses/README.md +1 -1
  51. package/squads/squad-cybersecurity/README.md +1 -1
  52. package/squads/squad-design/README.md +1 -1
  53. package/squads/squad-finance/README.md +1 -1
  54. package/squads/squad-growth/README.md +1 -1
  55. package/squads/squad-paidmedia/README.md +1 -1
  56. package/squads/squad-product/README.md +1 -1
  57. package/squads/squad-research/README.md +1 -1
  58. package/squads/squad-storytelling/README.md +1 -1
  59. package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
  60. package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
  61. package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
  62. package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
  63. package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
  64. package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
  65. package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
  66. package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
  67. package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
  68. package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
  69. package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
  70. package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
  71. package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
  72. package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
  73. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -402
  74. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
  75. package/packages/installer/tests/unit/config-validator.test.js +0 -315
  76. package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
  77. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -675
  78. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
  79. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -192
  80. package/packages/installer/tests/unit/env-template.test.js +0 -187
  81. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
  82. package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
  83. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
  84. package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
  85. package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
  86. package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
  87. package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
  88. package/packages/sinapse-install/tests/unit/chrome-brain.smoke.test.js +0 -66
@@ -1,303 +0,0 @@
1
- /**
2
- * @fileoverview Unit tests for WorkflowRegistry
3
- * @story WIS-2 - Workflow Registry Enhancement
4
- */
5
-
6
- 'use strict';
7
-
8
- const path = require('path');
9
- const {
10
- WorkflowRegistry,
11
- createWorkflowRegistry,
12
- DEFAULT_CACHE_TTL,
13
- } = require('../registry/workflow-registry');
14
-
15
- describe('WorkflowRegistry', () => {
16
- let registry;
17
-
18
- beforeEach(() => {
19
- registry = createWorkflowRegistry();
20
- });
21
-
22
- afterEach(() => {
23
- registry.invalidateCache();
24
- });
25
-
26
- describe('constructor', () => {
27
- it('should create registry with default options', () => {
28
- expect(registry.cacheTTL).toBe(DEFAULT_CACHE_TTL);
29
- expect(registry.cache).toBeNull();
30
- });
31
-
32
- it('should accept custom cache TTL', () => {
33
- const customRegistry = createWorkflowRegistry({ cacheTTL: 1000 });
34
- expect(customRegistry.cacheTTL).toBe(1000);
35
- });
36
-
37
- it('should accept custom patterns path', () => {
38
- const customPath = '/custom/path.yaml';
39
- const customRegistry = createWorkflowRegistry({ patternsPath: customPath });
40
- expect(customRegistry.patternsPath).toBe(customPath);
41
- });
42
- });
43
-
44
- describe('loadWorkflows()', () => {
45
- it('should load workflow patterns from file', () => {
46
- const workflows = registry.loadWorkflows();
47
- expect(workflows).toBeDefined();
48
- expect(typeof workflows).toBe('object');
49
- });
50
-
51
- it('should return 10 workflows', () => {
52
- const workflows = registry.loadWorkflows();
53
- const names = Object.keys(workflows);
54
- expect(names.length).toBe(10);
55
- });
56
-
57
- it('should include story_development workflow', () => {
58
- const workflows = registry.loadWorkflows();
59
- expect(workflows.story_development).toBeDefined();
60
- });
61
-
62
- it('should include epic_creation workflow', () => {
63
- const workflows = registry.loadWorkflows();
64
- expect(workflows.epic_creation).toBeDefined();
65
- });
66
-
67
- it('should cache loaded workflows', () => {
68
- const workflows1 = registry.loadWorkflows();
69
- const workflows2 = registry.loadWorkflows();
70
- expect(workflows1).toBe(workflows2);
71
- });
72
-
73
- it('should throw error for invalid file path', () => {
74
- const badRegistry = createWorkflowRegistry({
75
- patternsPath: '/nonexistent/path.yaml',
76
- });
77
- expect(() => badRegistry.loadWorkflows()).toThrow('Workflow patterns file not found');
78
- });
79
- });
80
-
81
- describe('isCacheValid()', () => {
82
- it('should return false when cache is null', () => {
83
- expect(registry.isCacheValid()).toBe(false);
84
- });
85
-
86
- it('should return true after loading workflows', () => {
87
- registry.loadWorkflows();
88
- expect(registry.isCacheValid()).toBe(true);
89
- });
90
-
91
- it('should return false after invalidation', () => {
92
- registry.loadWorkflows();
93
- registry.invalidateCache();
94
- expect(registry.isCacheValid()).toBe(false);
95
- });
96
- });
97
-
98
- describe('getWorkflowNames()', () => {
99
- it('should return array of workflow names', () => {
100
- const names = registry.getWorkflowNames();
101
- expect(Array.isArray(names)).toBe(true);
102
- expect(names.length).toBe(10);
103
- });
104
-
105
- it('should include expected workflows', () => {
106
- const names = registry.getWorkflowNames();
107
- expect(names).toContain('story_development');
108
- expect(names).toContain('epic_creation');
109
- expect(names).toContain('backlog_management');
110
- expect(names).toContain('architecture_review');
111
- expect(names).toContain('git_workflow');
112
- expect(names).toContain('database_workflow');
113
- expect(names).toContain('code_quality_workflow');
114
- expect(names).toContain('documentation_workflow');
115
- expect(names).toContain('ux_workflow');
116
- expect(names).toContain('research_workflow');
117
- });
118
- });
119
-
120
- describe('getWorkflow()', () => {
121
- it('should return workflow by name', () => {
122
- const workflow = registry.getWorkflow('epic_creation');
123
- expect(workflow).toBeDefined();
124
- expect(workflow.description).toBeDefined();
125
- });
126
-
127
- it('should return null for unknown workflow', () => {
128
- const workflow = registry.getWorkflow('nonexistent');
129
- expect(workflow).toBeNull();
130
- });
131
-
132
- it('should include transitions in workflow', () => {
133
- const workflow = registry.getWorkflow('epic_creation');
134
- expect(workflow.transitions).toBeDefined();
135
- expect(typeof workflow.transitions).toBe('object');
136
- });
137
- });
138
-
139
- describe('matchWorkflow()', () => {
140
- it('should match workflow from command history', () => {
141
- const commands = ['create-epic', 'create-story'];
142
- const match = registry.matchWorkflow(commands);
143
-
144
- expect(match).not.toBeNull();
145
- expect(match.name).toBe('epic_creation');
146
- });
147
-
148
- it('should return best matching workflow', () => {
149
- const commands = ['validate-story-draft', 'develop', 'review-qa'];
150
- const match = registry.matchWorkflow(commands);
151
-
152
- expect(match).not.toBeNull();
153
- expect(match.name).toBe('story_development');
154
- });
155
-
156
- it('should return null for empty commands', () => {
157
- const match = registry.matchWorkflow([]);
158
- expect(match).toBeNull();
159
- });
160
-
161
- it('should return null for unmatched commands', () => {
162
- const match = registry.matchWorkflow(['random-cmd', 'another-random']);
163
- expect(match).toBeNull();
164
- });
165
-
166
- it('should include score in match result', () => {
167
- const match = registry.matchWorkflow(['create-epic', 'create-story']);
168
- expect(match.score).toBeGreaterThanOrEqual(2);
169
- });
170
-
171
- it('should include matched commands', () => {
172
- const match = registry.matchWorkflow(['create-epic', 'create-story']);
173
- expect(match.matchedCommands).toBeDefined();
174
- expect(Array.isArray(match.matchedCommands)).toBe(true);
175
- });
176
- });
177
-
178
- describe('getTransitions()', () => {
179
- it('should return transitions for valid state', () => {
180
- const transition = registry.getTransitions('epic_creation', 'epic_drafted');
181
-
182
- expect(transition).not.toBeNull();
183
- expect(transition.trigger).toBeDefined();
184
- expect(transition.confidence).toBeDefined();
185
- expect(transition.next_steps).toBeDefined();
186
- });
187
-
188
- it('should return null for unknown state', () => {
189
- const transition = registry.getTransitions('epic_creation', 'unknown_state');
190
- expect(transition).toBeNull();
191
- });
192
-
193
- it('should return null for unknown workflow', () => {
194
- const transition = registry.getTransitions('unknown_workflow', 'any_state');
195
- expect(transition).toBeNull();
196
- });
197
-
198
- it('should return empty array for workflow without transitions', () => {
199
- const result = registry.getAllTransitions('nonexistent');
200
- expect(result).toEqual({});
201
- });
202
- });
203
-
204
- describe('getNextSteps()', () => {
205
- it('should return sorted next steps', () => {
206
- const steps = registry.getNextSteps('epic_creation', 'epic_drafted');
207
-
208
- expect(Array.isArray(steps)).toBe(true);
209
- expect(steps.length).toBeGreaterThan(0);
210
- expect(steps[0].priority).toBeLessThanOrEqual(steps[1]?.priority || 99);
211
- });
212
-
213
- it('should include command and description', () => {
214
- const steps = registry.getNextSteps('epic_creation', 'epic_drafted');
215
-
216
- expect(steps[0].command).toBeDefined();
217
- expect(steps[0].description).toBeDefined();
218
- });
219
-
220
- it('should return empty array for unknown state', () => {
221
- const steps = registry.getNextSteps('epic_creation', 'unknown_state');
222
- expect(steps).toEqual([]);
223
- });
224
- });
225
-
226
- describe('findCurrentState()', () => {
227
- it('should find state from command', () => {
228
- const state = registry.findCurrentState('epic_creation', 'create-epic completed');
229
- expect(state).toBe('epic_drafted');
230
- });
231
-
232
- it('should find state without "completed" suffix', () => {
233
- const state = registry.findCurrentState('epic_creation', 'create-epic');
234
- expect(state).toBe('epic_drafted');
235
- });
236
-
237
- it('should return null for unknown command', () => {
238
- const state = registry.findCurrentState('epic_creation', 'random-command');
239
- expect(state).toBeNull();
240
- });
241
-
242
- it('should return null for unknown workflow', () => {
243
- const state = registry.findCurrentState('unknown', 'create-epic');
244
- expect(state).toBeNull();
245
- });
246
- });
247
-
248
- describe('getWorkflowsByAgent()', () => {
249
- it('should return workflows for agent', () => {
250
- const workflows = registry.getWorkflowsByAgent('@po');
251
-
252
- expect(Array.isArray(workflows)).toBe(true);
253
- expect(workflows.length).toBeGreaterThan(0);
254
- });
255
-
256
- it('should handle agent without @ prefix', () => {
257
- const workflows = registry.getWorkflowsByAgent('po');
258
- expect(workflows.length).toBeGreaterThan(0);
259
- });
260
-
261
- it('should return empty array for unknown agent', () => {
262
- const workflows = registry.getWorkflowsByAgent('@unknown');
263
- expect(workflows).toEqual([]);
264
- });
265
- });
266
-
267
- describe('getStats()', () => {
268
- it('should return registry statistics', () => {
269
- const stats = registry.getStats();
270
-
271
- expect(stats.totalWorkflows).toBe(10);
272
- expect(stats.workflowsWithTransitions).toBe(10);
273
- expect(stats.totalTransitions).toBeGreaterThan(0);
274
- });
275
-
276
- it('should report cache status', () => {
277
- registry.loadWorkflows();
278
- const stats = registry.getStats();
279
-
280
- expect(stats.cacheValid).toBe(true);
281
- expect(stats.cacheAge).toBeGreaterThanOrEqual(0);
282
- });
283
- });
284
-
285
- describe('normalizeCommand()', () => {
286
- it('should normalize command strings', () => {
287
- expect(registry.normalizeCommand('CREATE-EPIC')).toBe('create-epic');
288
- expect(registry.normalizeCommand('*create-epic')).toBe('create-epic');
289
- expect(registry.normalizeCommand('create-epic completed')).toBe('create-epic');
290
- });
291
-
292
- it('should handle null input', () => {
293
- expect(registry.normalizeCommand(null)).toBe('');
294
- });
295
- });
296
-
297
- describe('DEFAULT_CACHE_TTL', () => {
298
- it('should be 5 minutes', () => {
299
- expect(DEFAULT_CACHE_TTL).toBe(5 * 60 * 1000);
300
- });
301
- });
302
- });
303
-
@@ -1,383 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * SINAPSE Installer Performance Benchmark
5
- * Story INS-2: Installer Performance Optimization
6
- *
7
- * Measures baseline performance metrics for the installer to track optimization progress.
8
- *
9
- * Usage:
10
- * node performance-benchmark.js [--output <file>] [--runs <n>]
11
- *
12
- * Output:
13
- * JSON report with phase timings and statistics
14
- */
15
-
16
- const fs = require('fs');
17
- const fse = require('fs-extra');
18
- const path = require('path');
19
- const crypto = require('crypto');
20
- const { performance } = require('perf_hooks');
21
-
22
- // Configuration
23
- const CONFIG = {
24
- runs: 3, // Number of runs for averaging
25
- outputFile: null, // Output file path (null = stdout)
26
- testProjectSize: 1000, // Number of files for test project
27
- verbose: false,
28
- };
29
-
30
- // Parse CLI arguments
31
- process.argv.slice(2).forEach((arg, i, arr) => {
32
- if (arg === '--output' && arr[i + 1]) CONFIG.outputFile = arr[i + 1];
33
- if (arg === '--runs' && arr[i + 1]) CONFIG.runs = parseInt(arr[i + 1], 10);
34
- if (arg === '--verbose' || arg === '-v') CONFIG.verbose = true;
35
- if (arg === '--help' || arg === '-h') {
36
- console.log(`
37
- SINAPSE Installer Performance Benchmark
38
-
39
- Usage: node performance-benchmark.js [options]
40
-
41
- Options:
42
- --output <file> Save JSON report to file (default: stdout)
43
- --runs <n> Number of benchmark runs (default: 3)
44
- --verbose, -v Show detailed progress
45
- --help, -h Show this help
46
-
47
- Example:
48
- node performance-benchmark.js --output baseline.json --runs 5
49
- `);
50
- process.exit(0);
51
- }
52
- });
53
-
54
- // Benchmark results structure
55
- const results = {
56
- timestamp: new Date().toISOString(),
57
- system: {
58
- platform: process.platform,
59
- arch: process.arch,
60
- nodeVersion: process.version,
61
- cpus: require('os').cpus().length,
62
- totalMemory: Math.round(require('os').totalmem() / 1024 / 1024) + ' MB',
63
- },
64
- config: { ...CONFIG },
65
- phases: {},
66
- summary: {},
67
- };
68
-
69
- /**
70
- * Timer utility for measuring phase durations
71
- */
72
- class Timer {
73
- constructor(name) {
74
- this.name = name;
75
- this.start = null;
76
- this.end = null;
77
- this.runs = [];
78
- }
79
-
80
- begin() {
81
- this.start = performance.now();
82
- }
83
-
84
- stop() {
85
- this.end = performance.now();
86
- const duration = this.end - this.start;
87
- this.runs.push(duration);
88
- return duration;
89
- }
90
-
91
- getStats() {
92
- if (this.runs.length === 0) return null;
93
- const sorted = [...this.runs].sort((a, b) => a - b);
94
- return {
95
- min: Math.round(sorted[0]),
96
- max: Math.round(sorted[sorted.length - 1]),
97
- avg: Math.round(this.runs.reduce((a, b) => a + b, 0) / this.runs.length),
98
- median: Math.round(sorted[Math.floor(sorted.length / 2)]),
99
- runs: this.runs.map((r) => Math.round(r)),
100
- unit: 'ms',
101
- };
102
- }
103
- }
104
-
105
- // Phase timers
106
- const timers = {
107
- directoryRead: new Timer('Directory Read (readdirSync)'),
108
- directoryReadWithTypes: new Timer('Directory Read (withFileTypes)'),
109
- statLoop: new Timer('Stat Loop (statSync per file)'),
110
- realpathSingle: new Timer('Realpath (single call)'),
111
- realpathDouble: new Timer('Realpath (double call - current)'),
112
- hashSequential: new Timer('Hash Files (sequential)'),
113
- hashParallelBatch: new Timer('Hash Files (parallel batch)'),
114
- fileCopySequential: new Timer('File Copy (sequential)'),
115
- fileCopyParallel: new Timer('File Copy (parallel)'),
116
- totalInstallSimulation: new Timer('Total Install Simulation'),
117
- };
118
-
119
- /**
120
- * Log if verbose mode is enabled
121
- */
122
- function log(msg) {
123
- if (CONFIG.verbose) console.log(`[benchmark] ${msg}`);
124
- }
125
-
126
- /**
127
- * Get the .sinapse-ai directory for benchmarking
128
- */
129
- function getSinapseCoreDir() {
130
- const projectRoot = path.resolve(__dirname, '../../../../');
131
- return path.join(projectRoot, '.sinapse-ai');
132
- }
133
-
134
- /**
135
- * Benchmark: Directory read comparison
136
- */
137
- async function benchmarkDirectoryRead(dir) {
138
- const files = fs.readdirSync(dir);
139
-
140
- // Method 1: readdirSync + statSync for each
141
- timers.statLoop.begin();
142
- for (const file of files) {
143
- const fullPath = path.join(dir, file);
144
- fs.statSync(fullPath).isDirectory();
145
- }
146
- timers.statLoop.stop();
147
-
148
- // Method 2: readdirSync with withFileTypes
149
- timers.directoryReadWithTypes.begin();
150
- const entries = fs.readdirSync(dir, { withFileTypes: true });
151
- for (const entry of entries) {
152
- entry.isDirectory();
153
- }
154
- timers.directoryReadWithTypes.stop();
155
-
156
- return files.length;
157
- }
158
-
159
- /**
160
- * Benchmark: Realpath comparison
161
- */
162
- async function benchmarkRealpath(files) {
163
- const sampleFiles = files.slice(0, 100); // Sample 100 files
164
-
165
- // Method 1: Single realpath call
166
- timers.realpathSingle.begin();
167
- for (const file of sampleFiles) {
168
- fs.realpathSync(file);
169
- }
170
- timers.realpathSingle.stop();
171
-
172
- // Method 2: Double realpath call (current behavior)
173
- timers.realpathDouble.begin();
174
- for (const file of sampleFiles) {
175
- fs.realpathSync(file);
176
- fs.realpathSync(path.dirname(file)); // Simulates the duplicate call
177
- }
178
- timers.realpathDouble.stop();
179
- }
180
-
181
- /**
182
- * Benchmark: File hashing comparison
183
- */
184
- async function benchmarkHashing(files) {
185
- const sampleFiles = files.slice(0, 200); // Sample 200 files for hashing
186
-
187
- // Method 1: Sequential hashing
188
- timers.hashSequential.begin();
189
- for (const file of sampleFiles) {
190
- try {
191
- const content = fs.readFileSync(file);
192
- crypto.createHash('sha256').update(content).digest('hex');
193
- } catch {
194
- // Skip files that can't be read
195
- }
196
- }
197
- timers.hashSequential.stop();
198
-
199
- // Method 2: Parallel batch hashing
200
- timers.hashParallelBatch.begin();
201
- const batchSize = 50;
202
- for (let i = 0; i < sampleFiles.length; i += batchSize) {
203
- const batch = sampleFiles.slice(i, i + batchSize);
204
- await Promise.all(
205
- batch.map(async (file) => {
206
- try {
207
- const content = await fse.readFile(file);
208
- crypto.createHash('sha256').update(content).digest('hex');
209
- } catch {
210
- // Skip files that can't be read
211
- }
212
- }),
213
- );
214
- }
215
- timers.hashParallelBatch.stop();
216
- }
217
-
218
- /**
219
- * Collect all files recursively
220
- */
221
- function collectFiles(dir, maxFiles = 1000) {
222
- const files = [];
223
-
224
- function walk(currentDir) {
225
- if (files.length >= maxFiles) return;
226
-
227
- try {
228
- const entries = fs.readdirSync(currentDir, { withFileTypes: true });
229
- for (const entry of entries) {
230
- if (files.length >= maxFiles) break;
231
-
232
- const fullPath = path.join(currentDir, entry.name);
233
- if (entry.isDirectory()) {
234
- walk(fullPath);
235
- } else if (entry.isFile()) {
236
- files.push(fullPath);
237
- }
238
- }
239
- } catch {
240
- // Skip directories we can't read
241
- }
242
- }
243
-
244
- walk(dir);
245
- return files;
246
- }
247
-
248
- /**
249
- * Run all benchmarks
250
- */
251
- async function runBenchmarks() {
252
- const sinapseCoreDir = getSinapseCoreDir();
253
-
254
- if (!fs.existsSync(sinapseCoreDir)) {
255
- console.error(`Error: .sinapse-ai directory not found at ${sinapseCoreDir}`);
256
- process.exit(1);
257
- }
258
-
259
- log(`Starting benchmark with ${CONFIG.runs} runs`);
260
- log(`Using .sinapse-ai at: ${sinapseCoreDir}`);
261
-
262
- // Collect files for benchmarking
263
- log('Collecting files...');
264
- const allFiles = collectFiles(sinapseCoreDir, CONFIG.testProjectSize);
265
- log(`Collected ${allFiles.length} files`);
266
-
267
- results.fileCount = allFiles.length;
268
-
269
- // Run benchmarks multiple times
270
- for (let run = 1; run <= CONFIG.runs; run++) {
271
- log(`\n--- Run ${run}/${CONFIG.runs} ---`);
272
-
273
- // Directory read benchmarks
274
- const agentsDir = path.join(sinapseCoreDir, 'development', 'agents');
275
- if (fs.existsSync(agentsDir)) {
276
- log('Benchmarking directory read...');
277
- await benchmarkDirectoryRead(agentsDir);
278
- }
279
-
280
- // Realpath benchmarks
281
- log('Benchmarking realpath...');
282
- await benchmarkRealpath(allFiles);
283
-
284
- // Hashing benchmarks
285
- log('Benchmarking file hashing...');
286
- await benchmarkHashing(allFiles);
287
-
288
- // Total simulation
289
- log('Running total install simulation...');
290
- timers.totalInstallSimulation.begin();
291
-
292
- // Simulate full install: read dirs + hash files
293
- const devDir = path.join(sinapseCoreDir, 'development');
294
- if (fs.existsSync(devDir)) {
295
- const subdirs = fs.readdirSync(devDir, { withFileTypes: true });
296
- for (const subdir of subdirs) {
297
- if (subdir.isDirectory()) {
298
- const fullSubdir = path.join(devDir, subdir.name);
299
- fs.readdirSync(fullSubdir);
300
- }
301
- }
302
- }
303
-
304
- // Simulate sequential file processing
305
- for (const file of allFiles.slice(0, 500)) {
306
- try {
307
- fs.statSync(file);
308
- fs.readFileSync(file);
309
- } catch {
310
- // Skip
311
- }
312
- }
313
-
314
- timers.totalInstallSimulation.stop();
315
- }
316
-
317
- // Compile results
318
- log('\nCompiling results...');
319
-
320
- for (const [name, timer] of Object.entries(timers)) {
321
- const stats = timer.getStats();
322
- if (stats) {
323
- results.phases[name] = {
324
- description: timer.name,
325
- ...stats,
326
- };
327
- }
328
- }
329
-
330
- // Calculate summary
331
- const hashSeq = results.phases.hashSequential?.avg || 0;
332
- const hashPar = results.phases.hashParallelBatch?.avg || 0;
333
- const realpathSingle = results.phases.realpathSingle?.avg || 0;
334
- const realpathDouble = results.phases.realpathDouble?.avg || 0;
335
- const statLoop = results.phases.statLoop?.avg || 0;
336
- const withTypes = results.phases.directoryReadWithTypes?.avg || 0;
337
-
338
- results.summary = {
339
- totalFiles: results.fileCount,
340
- hashingSpeedup: hashSeq > 0 ? `${(hashSeq / hashPar).toFixed(2)}x` : 'N/A',
341
- realpathSavings: realpathDouble > 0 ? `${Math.round(((realpathDouble - realpathSingle) / realpathDouble) * 100)}%` : 'N/A',
342
- statLoopSavings: statLoop > 0 ? `${Math.round(((statLoop - withTypes) / statLoop) * 100)}%` : 'N/A',
343
- estimatedTotalTime: results.phases.totalInstallSimulation?.avg || 0,
344
- target: '<30000ms for 1000 files',
345
- baseline: `${results.phases.totalInstallSimulation?.avg || 'TBD'}ms`,
346
- };
347
-
348
- // Output results
349
- const output = JSON.stringify(results, null, 2);
350
-
351
- if (CONFIG.outputFile) {
352
- fs.writeFileSync(CONFIG.outputFile, output);
353
- console.log(`Benchmark results saved to: ${CONFIG.outputFile}`);
354
- } else {
355
- console.log(output);
356
- }
357
-
358
- // Print summary to stderr for visibility
359
- console.error('\n' + '='.repeat(60));
360
- console.error('SINAPSE Installer Performance Baseline');
361
- console.error('='.repeat(60));
362
- console.error(`Files analyzed: ${results.fileCount}`);
363
- console.error(`Runs: ${CONFIG.runs}`);
364
- console.error('');
365
- console.error('Phase Results (avg ms):');
366
- console.error(` Directory stat loop: ${statLoop}ms`);
367
- console.error(` Directory withFileTypes: ${withTypes}ms (${results.summary.statLoopSavings} faster)`);
368
- console.error(` Realpath single: ${realpathSingle}ms`);
369
- console.error(` Realpath double: ${realpathDouble}ms (${results.summary.realpathSavings} overhead)`);
370
- console.error(` Hash sequential: ${hashSeq}ms`);
371
- console.error(` Hash parallel: ${hashPar}ms (${results.summary.hashingSpeedup} faster)`);
372
- console.error('');
373
- console.error(`Total Install Simulation: ${results.summary.baseline}`);
374
- console.error(`Target: ${results.summary.target}`);
375
- console.error('='.repeat(60));
376
- }
377
-
378
- // Run benchmarks
379
- runBenchmarks().catch((err) => {
380
- console.error('Benchmark failed:', err);
381
- process.exit(1);
382
- });
383
-