sinapse-ai 1.9.0 → 1.10.0

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 (94) 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 +4 -4
  15. package/CHANGELOG.md +1257 -0
  16. package/README.en.md +4 -3
  17. package/README.md +5 -6
  18. package/bin/commands/install.js +6 -3
  19. package/bin/commands/uninstall.js +2 -2
  20. package/bin/sinapse.js +20 -0
  21. package/bin/utils/secret-scanner-core.js +76 -4
  22. package/docs/agent-reference-guide.md +1 -1
  23. package/docs/framework/architecture-overview.md +4 -4
  24. package/docs/framework/guiding-principles.md +9 -9
  25. package/docs/getting-started.md +1 -1
  26. package/docs/guides/agent-reference.md +1 -1
  27. package/docs/guides/codex-config.md +4 -5
  28. package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
  29. package/package.json +8 -2
  30. package/packages/installer/src/installer/git-hooks-installer.js +3 -1
  31. package/packages/installer/src/installer/sinapse-ai-installer.js +32 -7
  32. package/packages/installer/src/wizard/ide-config-generator.js +9 -1
  33. package/packages/installer/src/wizard/index.js +3 -4
  34. package/scripts/regenerate-orqx-stubs.ps1 +0 -1
  35. package/scripts/sync-counts.js +10 -2
  36. package/scripts/sync-squad-yaml-components.js +108 -6
  37. package/scripts/validate-article-vii.js +68 -7
  38. package/scripts/validate-squad-orqx.js +19 -9
  39. package/sinapse/agents/sinapse-orqx.md +4 -4
  40. package/sinapse/agents/snps-orqx.md +4 -4
  41. package/sinapse/knowledge-base/routing-catalog.md +1 -1
  42. package/sinapse/tasks/diagnose-and-route.md +1 -1
  43. package/sinapse/tasks/squad-status-report.md +1 -1
  44. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
  45. package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
  46. package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
  47. package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
  48. package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
  49. package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
  50. package/squads/squad-animations/README.md +1 -1
  51. package/squads/squad-cloning/README.md +1 -1
  52. package/squads/squad-commercial/README.md +1 -1
  53. package/squads/squad-content/README.md +1 -1
  54. package/squads/squad-copy/README.md +1 -1
  55. package/squads/squad-council/README.md +1 -1
  56. package/squads/squad-courses/README.md +1 -1
  57. package/squads/squad-cybersecurity/README.md +1 -1
  58. package/squads/squad-design/README.md +1 -1
  59. package/squads/squad-finance/README.md +1 -1
  60. package/squads/squad-growth/README.md +1 -1
  61. package/squads/squad-paidmedia/README.md +1 -1
  62. package/squads/squad-product/README.md +1 -1
  63. package/squads/squad-research/README.md +1 -1
  64. package/squads/squad-storytelling/README.md +1 -1
  65. package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
  66. package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
  67. package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
  68. package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
  69. package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
  70. package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
  71. package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
  72. package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
  73. package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
  74. package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
  75. package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
  76. package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
  77. package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
  78. package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
  79. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -402
  80. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
  81. package/packages/installer/tests/unit/config-validator.test.js +0 -315
  82. package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
  83. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -675
  84. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
  85. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -192
  86. package/packages/installer/tests/unit/env-template.test.js +0 -187
  87. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
  88. package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
  89. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
  90. package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
  91. package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
  92. package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
  93. package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
  94. 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
-