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,620 +0,0 @@
1
- /**
2
- * @jest-environment node
3
- */
4
-
5
- const fs = require('fs').promises;
6
- const path = require('path');
7
- const os = require('os');
8
- const WorktreeManager = require('../scripts/worktree-manager');
9
-
10
- describe('WorktreeManager', () => {
11
- let manager;
12
- let testRoot;
13
-
14
- beforeEach(async () => {
15
- // Use OS temp directory to ensure complete isolation from source tree
16
- testRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'sinapse-worktree-test-'));
17
-
18
- // Initialize git repo for tests
19
- try {
20
- const execa = require('execa');
21
- await execa('git', ['init'], { cwd: testRoot });
22
- await execa('git', ['config', 'user.email', 'test@test.com'], {
23
- cwd: testRoot,
24
- });
25
- await execa('git', ['config', 'user.name', 'Test User'], { cwd: testRoot });
26
-
27
- // Create initial commit (required for worktrees)
28
- const testFile = path.join(testRoot, 'README.md');
29
- await fs.writeFile(testFile, '# Test Repo');
30
- await execa('git', ['add', '.'], { cwd: testRoot });
31
- await execa('git', ['commit', '-m', 'Initial commit'], { cwd: testRoot });
32
- } catch (error) {
33
- console.warn('Git not available, some tests will be skipped:', error.message);
34
- }
35
-
36
- manager = new WorktreeManager(testRoot, {
37
- maxWorktrees: 3,
38
- staleDays: 30,
39
- });
40
- });
41
-
42
- afterEach(async () => {
43
- // Cleanup test directory
44
- try {
45
- await fs.rm(testRoot, { recursive: true, force: true });
46
- } catch (error) {
47
- // Ignore cleanup errors
48
- }
49
- });
50
-
51
- describe('constructor', () => {
52
- it('should use default options when none provided', () => {
53
- const defaultManager = new WorktreeManager('/tmp/test');
54
- expect(defaultManager.maxWorktrees).toBe(10);
55
- expect(defaultManager.worktreeDir).toBe('.sinapse/worktrees');
56
- expect(defaultManager.branchPrefix).toBe('auto-claude/');
57
- expect(defaultManager.staleDays).toBe(30);
58
- });
59
-
60
- it('should accept custom options', () => {
61
- const customManager = new WorktreeManager('/tmp/test', {
62
- maxWorktrees: 5,
63
- worktreeDir: '.custom/worktrees',
64
- branchPrefix: 'custom/',
65
- staleDays: 15,
66
- });
67
- expect(customManager.maxWorktrees).toBe(5);
68
- expect(customManager.worktreeDir).toBe('.custom/worktrees');
69
- expect(customManager.branchPrefix).toBe('custom/');
70
- expect(customManager.staleDays).toBe(15);
71
- });
72
- });
73
-
74
- describe('getWorktreePath', () => {
75
- it('should return correct path for story', () => {
76
- const wtPath = manager.getWorktreePath('STORY-42');
77
- expect(wtPath).toBe(path.join(testRoot, '.sinapse/worktrees', 'STORY-42'));
78
- });
79
- });
80
-
81
- describe('getBranchName', () => {
82
- it('should return correct branch name for story', () => {
83
- const branch = manager.getBranchName('STORY-42');
84
- expect(branch).toBe('auto-claude/STORY-42');
85
- });
86
- });
87
-
88
- describe('exists', () => {
89
- it('should return false for non-existent worktree', async () => {
90
- const exists = await manager.exists('NONEXISTENT');
91
- expect(exists).toBe(false);
92
- });
93
- });
94
-
95
- describe('create', () => {
96
- it('should create worktree with correct structure', async () => {
97
- try {
98
- const info = await manager.create('STORY-42');
99
-
100
- expect(info).not.toBeNull();
101
- expect(info.storyId).toBe('STORY-42');
102
- expect(info.branch).toBe('auto-claude/STORY-42');
103
- expect(info.status).toBe('active');
104
- expect(info.uncommittedChanges).toBe(0);
105
-
106
- // Verify directory was created
107
- const exists = await manager.exists('STORY-42');
108
- expect(exists).toBe(true);
109
- } catch (error) {
110
- // Git might not be available in CI
111
- if (error.message.includes('Git command failed')) {
112
- console.warn('Git not available, skipping test');
113
- expect(true).toBe(true);
114
- } else {
115
- throw error;
116
- }
117
- }
118
- });
119
-
120
- it('should throw error if worktree already exists', async () => {
121
- try {
122
- await manager.create('STORY-42');
123
- await expect(manager.create('STORY-42')).rejects.toThrow(
124
- "Worktree for story 'STORY-42' already exists",
125
- );
126
- } catch (error) {
127
- if (error.message.includes('Git command failed')) {
128
- console.warn('Git not available, skipping test');
129
- expect(true).toBe(true);
130
- } else if (!error.message.includes('already exists')) {
131
- throw error;
132
- }
133
- }
134
- });
135
-
136
- it('should throw error when max worktrees reached', async () => {
137
- try {
138
- // Create max worktrees (3 in test config)
139
- await manager.create('STORY-1');
140
- await manager.create('STORY-2');
141
- await manager.create('STORY-3');
142
-
143
- // Fourth should fail
144
- await expect(manager.create('STORY-4')).rejects.toThrow(
145
- 'Maximum worktrees limit (3) reached',
146
- );
147
- } catch (error) {
148
- if (error.message.includes('Git command failed')) {
149
- console.warn('Git not available, skipping test');
150
- expect(true).toBe(true);
151
- } else if (!error.message.includes('Maximum worktrees limit')) {
152
- throw error;
153
- }
154
- }
155
- });
156
- });
157
-
158
- describe('list', () => {
159
- it('should return empty array when no worktrees', async () => {
160
- try {
161
- const worktrees = await manager.list();
162
- expect(worktrees).toEqual([]);
163
- } catch (error) {
164
- if (error.message.includes('Git command failed')) {
165
- console.warn('Git not available, skipping test');
166
- expect(true).toBe(true);
167
- } else {
168
- throw error;
169
- }
170
- }
171
- });
172
-
173
- it('should return created worktrees', async () => {
174
- try {
175
- await manager.create('STORY-1');
176
- await manager.create('STORY-2');
177
-
178
- const worktrees = await manager.list();
179
- expect(worktrees.length).toBe(2);
180
- expect(worktrees.map((w) => w.storyId).sort()).toEqual(['STORY-1', 'STORY-2']);
181
- } catch (error) {
182
- if (error.message.includes('Git command failed')) {
183
- console.warn('Git not available, skipping test');
184
- expect(true).toBe(true);
185
- } else {
186
- throw error;
187
- }
188
- }
189
- });
190
- });
191
-
192
- describe('get', () => {
193
- it('should return null for non-existent worktree', async () => {
194
- const info = await manager.get('NONEXISTENT');
195
- expect(info).toBeNull();
196
- });
197
-
198
- it('should return worktree info for existing worktree', async () => {
199
- try {
200
- await manager.create('STORY-42');
201
- const info = await manager.get('STORY-42');
202
-
203
- expect(info).not.toBeNull();
204
- expect(info.storyId).toBe('STORY-42');
205
- expect(info.branch).toBe('auto-claude/STORY-42');
206
- } catch (error) {
207
- if (error.message.includes('Git command failed')) {
208
- console.warn('Git not available, skipping test');
209
- expect(true).toBe(true);
210
- } else {
211
- throw error;
212
- }
213
- }
214
- });
215
- });
216
-
217
- describe('remove', () => {
218
- it('should throw error for non-existent worktree', async () => {
219
- await expect(manager.remove('NONEXISTENT')).rejects.toThrow(
220
- "Worktree for story 'NONEXISTENT' does not exist",
221
- );
222
- });
223
-
224
- it('should remove existing worktree', async () => {
225
- try {
226
- await manager.create('STORY-42');
227
- expect(await manager.exists('STORY-42')).toBe(true);
228
-
229
- await manager.remove('STORY-42');
230
- expect(await manager.exists('STORY-42')).toBe(false);
231
- } catch (error) {
232
- if (error.message.includes('Git command failed')) {
233
- console.warn('Git not available, skipping test');
234
- expect(true).toBe(true);
235
- } else {
236
- throw error;
237
- }
238
- }
239
- });
240
- });
241
-
242
- describe('getCount', () => {
243
- it('should return correct counts', async () => {
244
- try {
245
- const count = await manager.getCount();
246
- expect(count.total).toBe(0);
247
- expect(count.active).toBe(0);
248
- expect(count.stale).toBe(0);
249
-
250
- await manager.create('STORY-1');
251
- await manager.create('STORY-2');
252
-
253
- const count2 = await manager.getCount();
254
- expect(count2.total).toBe(2);
255
- expect(count2.active).toBe(2);
256
- expect(count2.stale).toBe(0);
257
- } catch (error) {
258
- if (error.message.includes('Git command failed')) {
259
- console.warn('Git not available, skipping test');
260
- expect(true).toBe(true);
261
- } else {
262
- throw error;
263
- }
264
- }
265
- });
266
- });
267
-
268
- describe('formatList', () => {
269
- it('should return empty message for no worktrees', () => {
270
- const output = manager.formatList([]);
271
- expect(output).toContain('No active worktrees');
272
- });
273
-
274
- it('should format worktrees correctly', () => {
275
- const worktrees = [
276
- {
277
- storyId: 'STORY-42',
278
- path: '/test/path',
279
- branch: 'auto-claude/STORY-42',
280
- createdAt: new Date(),
281
- uncommittedChanges: 3,
282
- status: 'active',
283
- },
284
- ];
285
-
286
- const output = manager.formatList(worktrees);
287
- expect(output).toContain('STORY-42');
288
- expect(output).toContain('auto-claude/STORY-42');
289
- expect(output).toContain('3 uncommitted');
290
- });
291
- });
292
-
293
- describe('formatAge', () => {
294
- it('should format just now correctly', () => {
295
- const age = manager.formatAge(new Date());
296
- expect(age).toBe('just now');
297
- });
298
-
299
- it('should format hours correctly', () => {
300
- const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000);
301
- const age = manager.formatAge(twoHoursAgo);
302
- expect(age).toBe('2h ago');
303
- });
304
-
305
- it('should format days correctly', () => {
306
- const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
307
- const age = manager.formatAge(threeDaysAgo);
308
- expect(age).toBe('3d ago');
309
- });
310
- });
311
-
312
- describe('detectConflicts', () => {
313
- it('should throw error for non-existent worktree', async () => {
314
- await expect(manager.detectConflicts('NONEXISTENT')).rejects.toThrow(
315
- "Worktree for story 'NONEXISTENT' does not exist",
316
- );
317
- });
318
-
319
- it('should return empty array when no conflicts', async () => {
320
- try {
321
- await manager.create('STORY-42');
322
-
323
- // Make a change in worktree without conflicting
324
- const execa = require('execa');
325
- const worktreePath = manager.getWorktreePath('STORY-42');
326
- const newFile = path.join(worktreePath, 'new-file.txt');
327
- await fs.writeFile(newFile, 'New content');
328
- await execa('git', ['add', '.'], { cwd: worktreePath });
329
- await execa('git', ['commit', '-m', 'Add new file'], { cwd: worktreePath });
330
-
331
- const conflicts = await manager.detectConflicts('STORY-42');
332
- expect(conflicts).toEqual([]);
333
- } catch (error) {
334
- if (error.message.includes('Git command failed')) {
335
- console.warn('Git not available, skipping test');
336
- expect(true).toBe(true);
337
- } else {
338
- throw error;
339
- }
340
- }
341
- });
342
-
343
- it('should detect conflicting files', async () => {
344
- try {
345
- await manager.create('STORY-42');
346
- const execa = require('execa');
347
- const worktreePath = manager.getWorktreePath('STORY-42');
348
-
349
- // Modify README.md in worktree
350
- const wtReadme = path.join(worktreePath, 'README.md');
351
- await fs.writeFile(wtReadme, '# Modified in worktree');
352
- await execa('git', ['add', '.'], { cwd: worktreePath });
353
- await execa('git', ['commit', '-m', 'Modify README in worktree'], { cwd: worktreePath });
354
-
355
- // Modify README.md in main
356
- const mainReadme = path.join(testRoot, 'README.md');
357
- await fs.writeFile(mainReadme, '# Modified in main');
358
- await execa('git', ['add', '.'], { cwd: testRoot });
359
- await execa('git', ['commit', '-m', 'Modify README in main'], { cwd: testRoot });
360
-
361
- const conflicts = await manager.detectConflicts('STORY-42');
362
- expect(conflicts).toContain('README.md');
363
- } catch (error) {
364
- if (error.message.includes('Git command failed')) {
365
- console.warn('Git not available, skipping test');
366
- expect(true).toBe(true);
367
- } else {
368
- throw error;
369
- }
370
- }
371
- });
372
- });
373
-
374
- describe('mergeToBase', () => {
375
- it('should throw error for non-existent worktree', async () => {
376
- await expect(manager.mergeToBase('NONEXISTENT')).rejects.toThrow(
377
- "Worktree for story 'NONEXISTENT' does not exist",
378
- );
379
- });
380
-
381
- it('should merge worktree successfully', async () => {
382
- try {
383
- await manager.create('STORY-42');
384
- const execa = require('execa');
385
- const worktreePath = manager.getWorktreePath('STORY-42');
386
-
387
- // Make a non-conflicting change
388
- const newFile = path.join(worktreePath, 'feature.txt');
389
- await fs.writeFile(newFile, 'New feature content');
390
- await execa('git', ['add', '.'], { cwd: worktreePath });
391
- await execa('git', ['commit', '-m', 'Add feature'], { cwd: worktreePath });
392
-
393
- const result = await manager.mergeToBase('STORY-42');
394
-
395
- expect(result.success).toBe(true);
396
- expect(result.storyId).toBe('STORY-42');
397
- expect(result.sourceBranch).toBe('auto-claude/STORY-42');
398
- expect(result.conflicts).toEqual([]);
399
- expect(result.commitHash).toBeDefined();
400
- expect(result.logPath).toBeDefined();
401
-
402
- // Verify file exists in main after merge
403
- const mainFile = path.join(testRoot, 'feature.txt');
404
- const exists = await fs
405
- .access(mainFile)
406
- .then(() => true)
407
- .catch(() => false);
408
- expect(exists).toBe(true);
409
- } catch (error) {
410
- if (error.message.includes('Git command failed')) {
411
- console.warn('Git not available, skipping test');
412
- expect(true).toBe(true);
413
- } else {
414
- throw error;
415
- }
416
- }
417
- });
418
-
419
- it('should support staged option (--no-commit)', async () => {
420
- try {
421
- await manager.create('STORY-42');
422
- const execa = require('execa');
423
- const worktreePath = manager.getWorktreePath('STORY-42');
424
-
425
- const newFile = path.join(worktreePath, 'staged-feature.txt');
426
- await fs.writeFile(newFile, 'Staged feature');
427
- await execa('git', ['add', '.'], { cwd: worktreePath });
428
- await execa('git', ['commit', '-m', 'Add staged feature'], { cwd: worktreePath });
429
-
430
- // Get HEAD before merge
431
- const headBefore = await execa('git', ['rev-parse', 'HEAD'], { cwd: testRoot });
432
-
433
- const result = await manager.mergeToBase('STORY-42', { staged: true });
434
-
435
- expect(result.success).toBe(true);
436
- expect(result.commitHash).toBeUndefined(); // Not committed
437
-
438
- // Get HEAD after merge - should be same (no commit was made)
439
- const headAfter = await execa('git', ['rev-parse', 'HEAD'], { cwd: testRoot });
440
- expect(headAfter.stdout).toBe(headBefore.stdout);
441
-
442
- // Verify the file was merged to working tree
443
- const mainFile = path.join(testRoot, 'staged-feature.txt');
444
- const fileExists = await fs
445
- .access(mainFile)
446
- .then(() => true)
447
- .catch(() => false);
448
- expect(fileExists).toBe(true);
449
-
450
- // Clean up merge state for next tests
451
- await execa('git', ['merge', '--abort'], { cwd: testRoot }).catch(() => {
452
- return execa('git', ['reset', '--hard', 'HEAD'], { cwd: testRoot });
453
- });
454
- } catch (error) {
455
- if (error.message.includes('Git command failed')) {
456
- console.warn('Git not available, skipping test');
457
- expect(true).toBe(true);
458
- } else {
459
- throw error;
460
- }
461
- }
462
- });
463
-
464
- it('should support squash option', async () => {
465
- try {
466
- await manager.create('STORY-42');
467
- const execa = require('execa');
468
- const worktreePath = manager.getWorktreePath('STORY-42');
469
-
470
- // Make multiple commits
471
- for (let i = 1; i <= 3; i++) {
472
- const file = path.join(worktreePath, `file${i}.txt`);
473
- await fs.writeFile(file, `Content ${i}`);
474
- await execa('git', ['add', '.'], { cwd: worktreePath });
475
- await execa('git', ['commit', '-m', `Commit ${i}`], { cwd: worktreePath });
476
- }
477
-
478
- const result = await manager.mergeToBase('STORY-42', { squash: true });
479
-
480
- expect(result.success).toBe(true);
481
- expect(result.commitHash).toBeDefined();
482
- } catch (error) {
483
- if (error.message.includes('Git command failed')) {
484
- console.warn('Git not available, skipping test');
485
- expect(true).toBe(true);
486
- } else {
487
- throw error;
488
- }
489
- }
490
- });
491
-
492
- it('should fail gracefully on conflicts', async () => {
493
- try {
494
- await manager.create('STORY-42');
495
- const execa = require('execa');
496
- const worktreePath = manager.getWorktreePath('STORY-42');
497
-
498
- // Create conflict
499
- const wtReadme = path.join(worktreePath, 'README.md');
500
- await fs.writeFile(wtReadme, '# Worktree version');
501
- await execa('git', ['add', '.'], { cwd: worktreePath });
502
- await execa('git', ['commit', '-m', 'Modify in worktree'], { cwd: worktreePath });
503
-
504
- const mainReadme = path.join(testRoot, 'README.md');
505
- await fs.writeFile(mainReadme, '# Main version');
506
- await execa('git', ['add', '.'], { cwd: testRoot });
507
- await execa('git', ['commit', '-m', 'Modify in main'], { cwd: testRoot });
508
-
509
- const result = await manager.mergeToBase('STORY-42');
510
-
511
- expect(result.success).toBe(false);
512
- expect(result.conflicts.length).toBeGreaterThan(0);
513
- expect(result.error).toContain('conflict');
514
- } catch (error) {
515
- if (error.message.includes('Git command failed')) {
516
- console.warn('Git not available, skipping test');
517
- expect(true).toBe(true);
518
- } else {
519
- throw error;
520
- }
521
- }
522
- });
523
-
524
- it('should cleanup worktree after merge when cleanup option is true', async () => {
525
- try {
526
- await manager.create('STORY-42');
527
- const execa = require('execa');
528
- const worktreePath = manager.getWorktreePath('STORY-42');
529
-
530
- const newFile = path.join(worktreePath, 'cleanup-test.txt');
531
- await fs.writeFile(newFile, 'Cleanup test');
532
- await execa('git', ['add', '.'], { cwd: worktreePath });
533
- await execa('git', ['commit', '-m', 'Add cleanup test'], { cwd: worktreePath });
534
-
535
- const result = await manager.mergeToBase('STORY-42', { cleanup: true });
536
-
537
- expect(result.success).toBe(true);
538
- expect(await manager.exists('STORY-42')).toBe(false);
539
- } catch (error) {
540
- if (error.message.includes('Git command failed')) {
541
- console.warn('Git not available, skipping test');
542
- expect(true).toBe(true);
543
- } else {
544
- throw error;
545
- }
546
- }
547
- });
548
- });
549
-
550
- describe('getMergeHistory', () => {
551
- it('should return empty array when no merge history', async () => {
552
- const history = await manager.getMergeHistory();
553
- expect(history).toEqual([]);
554
- });
555
-
556
- it('should return merge history after merge', async () => {
557
- try {
558
- await manager.create('STORY-42');
559
- const execa = require('execa');
560
- const worktreePath = manager.getWorktreePath('STORY-42');
561
-
562
- const newFile = path.join(worktreePath, 'history-test.txt');
563
- await fs.writeFile(newFile, 'History test');
564
- await execa('git', ['add', '.'], { cwd: worktreePath });
565
- await execa('git', ['commit', '-m', 'Add history test'], { cwd: worktreePath });
566
-
567
- await manager.mergeToBase('STORY-42');
568
-
569
- const history = await manager.getMergeHistory();
570
- expect(history.length).toBe(1);
571
- expect(history[0].storyId).toBe('STORY-42');
572
- expect(history[0].success).toBe(true);
573
- } catch (error) {
574
- if (error.message.includes('Git command failed')) {
575
- console.warn('Git not available, skipping test');
576
- expect(true).toBe(true);
577
- } else {
578
- throw error;
579
- }
580
- }
581
- });
582
-
583
- it('should filter by storyId', async () => {
584
- try {
585
- await manager.create('STORY-42');
586
- await manager.create('STORY-43');
587
- const execa = require('execa');
588
-
589
- // Merge STORY-42
590
- const wt42 = manager.getWorktreePath('STORY-42');
591
- await fs.writeFile(path.join(wt42, 'file42.txt'), 'Content 42');
592
- await execa('git', ['add', '.'], { cwd: wt42 });
593
- await execa('git', ['commit', '-m', 'Add file 42'], { cwd: wt42 });
594
- await manager.mergeToBase('STORY-42');
595
-
596
- // Merge STORY-43
597
- const wt43 = manager.getWorktreePath('STORY-43');
598
- await fs.writeFile(path.join(wt43, 'file43.txt'), 'Content 43');
599
- await execa('git', ['add', '.'], { cwd: wt43 });
600
- await execa('git', ['commit', '-m', 'Add file 43'], { cwd: wt43 });
601
- await manager.mergeToBase('STORY-43');
602
-
603
- const allHistory = await manager.getMergeHistory();
604
- expect(allHistory.length).toBe(2);
605
-
606
- const story42History = await manager.getMergeHistory('STORY-42');
607
- expect(story42History.length).toBe(1);
608
- expect(story42History[0].storyId).toBe('STORY-42');
609
- } catch (error) {
610
- if (error.message.includes('Git command failed')) {
611
- console.warn('Git not available, skipping test');
612
- expect(true).toBe(true);
613
- } else {
614
- throw error;
615
- }
616
- }
617
- });
618
- });
619
- });
620
-