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,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
-