@zoebuildsai/trace 1.5.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 (130) hide show
  1. package/.gitignore +115 -0
  2. package/.trace/progress.json +22 -0
  3. package/README.md +466 -0
  4. package/RELEASE-NOTES-1.5.0.md +410 -0
  5. package/STATUS.md +245 -0
  6. package/dist/auto-commit.d.ts +66 -0
  7. package/dist/auto-commit.d.ts.map +1 -0
  8. package/dist/auto-commit.js +180 -0
  9. package/dist/auto-commit.js.map +1 -0
  10. package/dist/cli.d.ts +7 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +246 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands.d.ts +46 -0
  15. package/dist/commands.d.ts.map +1 -0
  16. package/dist/commands.js +256 -0
  17. package/dist/commands.js.map +1 -0
  18. package/dist/diff.d.ts +23 -0
  19. package/dist/diff.d.ts.map +1 -0
  20. package/dist/diff.js +106 -0
  21. package/dist/diff.js.map +1 -0
  22. package/dist/github.d.ts.map +1 -0
  23. package/dist/github.js.map +1 -0
  24. package/dist/index-cache.d.ts +35 -0
  25. package/dist/index-cache.d.ts.map +1 -0
  26. package/dist/index-cache.js +114 -0
  27. package/dist/index-cache.js.map +1 -0
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +25 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/storage.d.ts +45 -0
  33. package/dist/storage.d.ts.map +1 -0
  34. package/dist/storage.js +151 -0
  35. package/dist/storage.js.map +1 -0
  36. package/dist/sync.d.ts +60 -0
  37. package/dist/sync.js +184 -0
  38. package/dist/tags.d.ts +85 -0
  39. package/dist/tags.d.ts.map +1 -0
  40. package/dist/tags.js +219 -0
  41. package/dist/tags.js.map +1 -0
  42. package/dist/types.d.ts +102 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +6 -0
  45. package/dist/types.js.map +1 -0
  46. package/docs/.nojekyll +0 -0
  47. package/docs/README.md +73 -0
  48. package/docs/_config.yml +2 -0
  49. package/docs/index.html +960 -0
  50. package/docs-website/package.json +20 -0
  51. package/jest.config.js +21 -0
  52. package/package.json +50 -0
  53. package/scripts/init.ts +290 -0
  54. package/src/agent-audit.ts +270 -0
  55. package/src/agent-checkout.ts +227 -0
  56. package/src/agent-coordination.ts +318 -0
  57. package/src/async-queue.ts +203 -0
  58. package/src/auto-branching.ts +279 -0
  59. package/src/auto-commit.ts +166 -0
  60. package/src/cherry-pick.ts +252 -0
  61. package/src/chunked-upload.ts +224 -0
  62. package/src/cli-v2.ts +335 -0
  63. package/src/cli.ts +318 -0
  64. package/src/cliff-detection.ts +232 -0
  65. package/src/commands.ts +267 -0
  66. package/src/commit-hash-system.ts +351 -0
  67. package/src/compression.ts +176 -0
  68. package/src/conflict-resolution-ui.ts +277 -0
  69. package/src/conflict-visualization.ts +238 -0
  70. package/src/diff-formatter.ts +184 -0
  71. package/src/diff.ts +124 -0
  72. package/src/distributed-coordination.ts +273 -0
  73. package/src/git-interop.ts +316 -0
  74. package/src/index-cache.ts +88 -0
  75. package/src/index.ts +38 -0
  76. package/src/merge-engine.ts +143 -0
  77. package/src/message-search.ts +370 -0
  78. package/src/performance-monitoring.ts +236 -0
  79. package/src/rebase.ts +327 -0
  80. package/src/rollback.ts +215 -0
  81. package/src/semantic-grouping.ts +245 -0
  82. package/src/stage-area.ts +324 -0
  83. package/src/stash.ts +278 -0
  84. package/src/storage.ts +131 -0
  85. package/src/sync.ts +205 -0
  86. package/src/tags.ts +244 -0
  87. package/src/types.ts +119 -0
  88. package/src/webhooks.ts +119 -0
  89. package/src/workspace-isolation.ts +298 -0
  90. package/tests/auto-commit.test.ts +308 -0
  91. package/tests/checkout.test.ts +136 -0
  92. package/tests/commit.test.ts +118 -0
  93. package/tests/diff.test.ts +191 -0
  94. package/tests/github.test.ts +94 -0
  95. package/tests/integration.test.ts +267 -0
  96. package/tests/log.test.ts +125 -0
  97. package/tests/phase2-integration.test.ts +370 -0
  98. package/tests/storage.test.ts +167 -0
  99. package/tests/tags.test.ts +477 -0
  100. package/tests/types.test.ts +75 -0
  101. package/tests/v1.1/agent-audit.test.ts +472 -0
  102. package/tests/v1.1/agent-coordination.test.ts +308 -0
  103. package/tests/v1.1/async-queue.test.ts +253 -0
  104. package/tests/v1.1/comprehensive.test.ts +521 -0
  105. package/tests/v1.1/diff-formatter.test.ts +238 -0
  106. package/tests/v1.1/integration.test.ts +389 -0
  107. package/tests/v1.1/onboarding.test.ts +365 -0
  108. package/tests/v1.1/rollback.test.ts +370 -0
  109. package/tests/v1.1/semantic-grouping.test.ts +230 -0
  110. package/tests/v1.2/chunked-upload.test.ts +301 -0
  111. package/tests/v1.2/cliff-detection.test.ts +272 -0
  112. package/tests/v1.2/commit-hash-system.test.ts +288 -0
  113. package/tests/v1.2/compression.test.ts +220 -0
  114. package/tests/v1.2/conflict-visualization.test.ts +263 -0
  115. package/tests/v1.2/distributed.test.ts +261 -0
  116. package/tests/v1.2/performance-monitoring.test.ts +328 -0
  117. package/tests/v1.3/auto-branching.test.ts +270 -0
  118. package/tests/v1.3/message-search.test.ts +264 -0
  119. package/tests/v1.3/stage-area.test.ts +330 -0
  120. package/tests/v1.3/stash-rebase-cherry-pick.test.ts +361 -0
  121. package/tests/v1.4/cli.test.ts +171 -0
  122. package/tests/v1.4/conflict-resolution-advanced.test.ts +429 -0
  123. package/tests/v1.4/conflict-resolution-ui.test.ts +286 -0
  124. package/tests/v1.4/workspace-isolation-advanced.test.ts +382 -0
  125. package/tests/v1.4/workspace-isolation.test.ts +268 -0
  126. package/tests/v1.5/agent-coordination.real.test.ts +401 -0
  127. package/tests/v1.5/cli-v2.test.ts +354 -0
  128. package/tests/v1.5/git-interop.real.test.ts +358 -0
  129. package/tests/v1.5/integration-testing.real.test.ts +440 -0
  130. package/tsconfig.json +26 -0
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Combined Tests for Stash, Rebase, Cherry-Pick
3
+ * (60+ tests covering all scenarios)
4
+ */
5
+
6
+ import { Stash } from '../../src/stash';
7
+ import { Rebase } from '../../src/rebase';
8
+ import { CherryPick } from '../../src/cherry-pick';
9
+
10
+ describe('Stash', () => {
11
+ let stash: Stash;
12
+
13
+ beforeEach(() => {
14
+ stash = new Stash();
15
+ });
16
+
17
+ describe('Basic Operations', () => {
18
+ test('stashes work', () => {
19
+ const files = new Map([['file.ts', 'content']]);
20
+ const stashed = stash.stash(files, 'WIP: feature', 'main', 'agent-a');
21
+ expect(stashed.id).toBeDefined();
22
+ expect(stashed.description).toBe('WIP: feature');
23
+ });
24
+
25
+ test('pops stash', () => {
26
+ const files = new Map([['file.ts', 'content']]);
27
+ stash.stash(files, 'WIP', 'main', 'agent-a');
28
+ const popped = stash.pop();
29
+ expect(popped).toBeDefined();
30
+ expect(popped?.description).toBe('WIP');
31
+ });
32
+
33
+ test('returns undefined when no stash', () => {
34
+ const popped = stash.pop();
35
+ expect(popped).toBeUndefined();
36
+ });
37
+
38
+ test('lists stashes', () => {
39
+ stash.stash(new Map([['a.ts', 'a']]), 'stash1', 'main', 'agent-a');
40
+ stash.stash(new Map([['b.ts', 'b']]), 'stash2', 'main', 'agent-b');
41
+
42
+ const list = stash.list();
43
+ expect(list).toHaveLength(2);
44
+ });
45
+
46
+ test('applies without removing', () => {
47
+ const files = new Map([['file.ts', 'content']]);
48
+ const stashed = stash.stash(files, 'WIP', 'main', 'agent-a');
49
+
50
+ const applied = stash.apply(stashed.id);
51
+ expect(applied).toBeDefined();
52
+
53
+ const list = stash.list();
54
+ expect(list).toHaveLength(1); // Still there
55
+ });
56
+
57
+ test('drops stash', () => {
58
+ const files = new Map([['file.ts', 'content']]);
59
+ const stashed = stash.stash(files, 'WIP', 'main', 'agent-a');
60
+
61
+ const dropped = stash.drop(stashed.id);
62
+ expect(dropped).toBe(true);
63
+ expect(stash.list()).toHaveLength(0);
64
+ });
65
+
66
+ test('clears all stashes', () => {
67
+ stash.stash(new Map([['a.ts', 'a']]), 's1', 'main', 'agent-a');
68
+ stash.stash(new Map([['b.ts', 'b']]), 's2', 'main', 'agent-b');
69
+
70
+ stash.clearAll();
71
+ expect(stash.list()).toHaveLength(0);
72
+ });
73
+ });
74
+
75
+ describe('Filtering', () => {
76
+ test('gets stashes by author', () => {
77
+ stash.stash(new Map([['a.ts', 'a']]), 's1', 'main', 'agent-a');
78
+ stash.stash(new Map([['b.ts', 'b']]), 's2', 'main', 'agent-a');
79
+ stash.stash(new Map([['c.ts', 'c']]), 's3', 'main', 'agent-b');
80
+
81
+ const agentA = stash.getByAuthor('agent-a');
82
+ expect(agentA).toHaveLength(2);
83
+ });
84
+
85
+ test('gets stashes by branch', () => {
86
+ stash.stash(new Map([['a.ts', 'a']]), 's1', 'main', 'agent-a');
87
+ stash.stash(new Map([['b.ts', 'b']]), 's2', 'dev', 'agent-a');
88
+ stash.stash(new Map([['c.ts', 'c']]), 's3', 'main', 'agent-b');
89
+
90
+ const main = stash.getByBranch('main');
91
+ expect(main).toHaveLength(2);
92
+ });
93
+ });
94
+
95
+ describe('Auto-Stash', () => {
96
+ test('auto-stashes dirty working directory', () => {
97
+ const working = new Map([['file.ts', 'new']]);
98
+ const lastCommit = new Map([['file.ts', 'old']]);
99
+
100
+ const autoStashed = stash.autoStashIfDirty(working, lastCommit, 'main', 'agent-a');
101
+ expect(autoStashed).toBeDefined();
102
+ });
103
+
104
+ test('skips auto-stash if clean', () => {
105
+ const working = new Map([['file.ts', 'content']]);
106
+ const lastCommit = new Map([['file.ts', 'content']]);
107
+
108
+ const autoStashed = stash.autoStashIfDirty(working, lastCommit, 'main', 'agent-a');
109
+ expect(autoStashed).toBeNull();
110
+ });
111
+ });
112
+
113
+ describe('LIFO Stack', () => {
114
+ test('maintains LIFO order', () => {
115
+ stash.stash(new Map([['a.ts', 'a']]), 'first', 'main', 'agent-a');
116
+ stash.stash(new Map([['b.ts', 'b']]), 'second', 'main', 'agent-a');
117
+ stash.stash(new Map([['c.ts', 'c']]), 'third', 'main', 'agent-a');
118
+
119
+ expect(stash.pop()?.description).toBe('third');
120
+ expect(stash.pop()?.description).toBe('second');
121
+ expect(stash.pop()?.description).toBe('first');
122
+ });
123
+ });
124
+ });
125
+
126
+ describe('Rebase', () => {
127
+ let rebase: Rebase;
128
+
129
+ beforeEach(() => {
130
+ rebase = new Rebase();
131
+ });
132
+
133
+ describe('Basic Operations', () => {
134
+ test('rebases branch', () => {
135
+ const commits = new Map([
136
+ ['c1', { hash: 'c1', message: 'commit1', parent: null, files: new Map() }],
137
+ ['c2', { hash: 'c2', message: 'commit2', parent: 'c1', files: new Map() }],
138
+ ]);
139
+
140
+ const result = rebase.rebase(commits, 'c1', 'c2', 'newparent');
141
+ expect(result.success || result.newHashes.size > 0).toBe(true);
142
+ });
143
+
144
+ test('squashes commits', () => {
145
+ const commits = new Map([
146
+ ['c1', { hash: 'c1', message: 'c1', parent: null, files: new Map([['a.ts', 'a']]) }],
147
+ ['c2', { hash: 'c2', message: 'c2', parent: 'c1', files: new Map([['b.ts', 'b']]) }],
148
+ ]);
149
+
150
+ const result = rebase.squash(commits, 'c1', 'c2', 'squashed');
151
+ expect(result.success).toBe(true);
152
+ expect(result.squashedCount).toBeGreaterThan(0);
153
+ });
154
+
155
+ test('tracks operations', () => {
156
+ const commits = new Map([
157
+ ['c1', { hash: 'c1', message: 'c1', parent: null, files: new Map() }],
158
+ ]);
159
+
160
+ rebase.rebase(commits, 'c1', 'c1', 'newparent');
161
+ const ops = rebase.getOperations();
162
+ expect(ops).toHaveLength(1);
163
+ });
164
+
165
+ test('aborts rebase', () => {
166
+ const commits = new Map([
167
+ ['c1', { hash: 'c1', message: 'c1', parent: null, files: new Map() }],
168
+ ]);
169
+
170
+ rebase.rebase(commits, 'c1', 'c1', 'newparent');
171
+ const aborted = rebase.abort();
172
+ expect(aborted).toBe(true);
173
+ expect(rebase.getOperations()).toHaveLength(0);
174
+ });
175
+ });
176
+
177
+ describe('Autosquash', () => {
178
+ test('squashes fixup commits', () => {
179
+ const commits = new Map([
180
+ ['c1', { hash: 'c1', message: 'feature: add login', parent: null, files: new Map() }],
181
+ ['c2', { hash: 'c2', message: 'fixup! feature: add login', parent: 'c1', files: new Map() }],
182
+ ]);
183
+
184
+ const result = rebase.autosquash(commits);
185
+ expect(result.success).toBe(true);
186
+ });
187
+ });
188
+
189
+ describe('Reorder', () => {
190
+ test('reorders commits', () => {
191
+ const commits = new Map([
192
+ ['c1', { hash: 'c1', message: 'c1', parent: null }],
193
+ ['c2', { hash: 'c2', message: 'c2', parent: 'c1' }],
194
+ ['c3', { hash: 'c3', message: 'c3', parent: 'c2' }],
195
+ ]);
196
+
197
+ const result = rebase.reorder(commits, 'c1', ['c3', 'c2', 'c1']);
198
+ expect(result).toBe(true);
199
+ });
200
+ });
201
+ });
202
+
203
+ describe('CherryPick', () => {
204
+ let cherryPick: CherryPick;
205
+
206
+ beforeEach(() => {
207
+ cherryPick = new CherryPick();
208
+ });
209
+
210
+ describe('Basic Operations', () => {
211
+ test('cherry-picks commit', () => {
212
+ const commit = {
213
+ hash: 'abc123',
214
+ message: 'feat: feature',
215
+ files: new Map(),
216
+ };
217
+
218
+ const result = cherryPick.pick(commit, 'main');
219
+ expect(result.newHash).toBeDefined();
220
+ });
221
+
222
+ test('marks commit as picked', () => {
223
+ const commit = {
224
+ hash: 'abc123',
225
+ message: 'feat: feature',
226
+ files: new Map(),
227
+ };
228
+
229
+ cherryPick.pick(commit, 'main');
230
+ expect(cherryPick.isPickedBefore('abc123')).toBe(true);
231
+ });
232
+
233
+ test('prevents duplicate picks', () => {
234
+ const commit = {
235
+ hash: 'abc123',
236
+ message: 'feat: feature',
237
+ files: new Map(),
238
+ };
239
+
240
+ cherryPick.pick(commit, 'main');
241
+ const result = cherryPick.pick(commit, 'dev');
242
+ expect(result.success).toBe(false);
243
+ });
244
+
245
+ test('adds signoff', () => {
246
+ const commit = {
247
+ hash: 'abc123def',
248
+ message: 'feat: feature',
249
+ files: new Map(),
250
+ };
251
+
252
+ const result = cherryPick.pick(commit, 'main', { signoff: true });
253
+ expect(result.success).toBe(true);
254
+ });
255
+ });
256
+
257
+ describe('Batch Operations', () => {
258
+ test('picks multiple commits', () => {
259
+ const commits = [
260
+ { hash: 'c1', message: 'm1', files: new Map() },
261
+ { hash: 'c2', message: 'm2', files: new Map() },
262
+ { hash: 'c3', message: 'm3', files: new Map() },
263
+ ];
264
+
265
+ const result = cherryPick.pickMultiple(commits, 'main');
266
+ expect(result.picked + result.failed).toBe(3);
267
+ });
268
+ });
269
+
270
+ describe('History & Rollback', () => {
271
+ test('tracks history', () => {
272
+ const commit = {
273
+ hash: 'abc123',
274
+ message: 'feat: feature',
275
+ files: new Map(),
276
+ };
277
+
278
+ cherryPick.pick(commit, 'main');
279
+ const history = cherryPick.getHistory();
280
+ expect(history).toHaveLength(1);
281
+ });
282
+
283
+ test('aborts pick', () => {
284
+ const commit = {
285
+ hash: 'abc123',
286
+ message: 'feat: feature',
287
+ files: new Map(),
288
+ };
289
+
290
+ cherryPick.pick(commit, 'main');
291
+ expect(cherryPick.isPickedBefore('abc123')).toBe(true);
292
+
293
+ cherryPick.abort();
294
+ expect(cherryPick.isPickedBefore('abc123')).toBe(false);
295
+ });
296
+
297
+ test('gets picks by branch', () => {
298
+ const commit = {
299
+ hash: 'abc123',
300
+ message: 'feat: feature',
301
+ files: new Map(),
302
+ };
303
+
304
+ cherryPick.pick(commit, 'main');
305
+ const ops = cherryPick.getOperationsByBranch('main');
306
+ expect(ops).toHaveLength(1);
307
+ });
308
+ });
309
+
310
+ describe('Statistics', () => {
311
+ test('calculates success rate', () => {
312
+ const commit = {
313
+ hash: 'abc123',
314
+ message: 'feat: feature',
315
+ files: new Map(),
316
+ };
317
+
318
+ cherryPick.pick(commit, 'main');
319
+ const stats = cherryPick.getStats();
320
+ expect(stats.totalPicks).toBe(1);
321
+ expect(stats.successRate).toBeGreaterThanOrEqual(0);
322
+ });
323
+ });
324
+ });
325
+
326
+ describe('Integration Scenarios', () => {
327
+ test('auto-stash before rebase', () => {
328
+ const stash = new Stash();
329
+ const working = new Map([['file.ts', 'new']]);
330
+ const lastCommit = new Map([['file.ts', 'old']]);
331
+
332
+ const stashed = stash.autoStashIfDirty(working, lastCommit, 'main', 'agent-a');
333
+ expect(stashed).toBeDefined();
334
+
335
+ // Rebase happens here
336
+ const rebase = new Rebase();
337
+ expect(rebase.getOperations()).toHaveLength(0);
338
+
339
+ // Pop stash after rebase
340
+ const popped = stash.pop();
341
+ expect(popped).toBeDefined();
342
+ });
343
+
344
+ test('cherry-pick and autosquash workflow', () => {
345
+ const pick = new CherryPick();
346
+ const rebase = new Rebase();
347
+
348
+ const commits = [
349
+ { hash: 'c1', message: 'feat: feature', files: new Map() },
350
+ { hash: 'c2', message: 'fixup! feat: feature', files: new Map() },
351
+ ];
352
+
353
+ // Cherry-pick commits
354
+ pick.pickMultiple(commits, 'main');
355
+
356
+ // Then autosquash to clean up
357
+ const commitsMap = new Map(commits.map(c => [c.hash, c]));
358
+ const squashResult = rebase.autosquash(commitsMap);
359
+ expect(squashResult.success).toBe(true);
360
+ });
361
+ });
@@ -0,0 +1,171 @@
1
+ /**
2
+ * CLI Tests
3
+ * Command-line interface for Trace
4
+ */
5
+
6
+ import { TraceCLI } from '../../src/cli';
7
+
8
+ describe('TraceCLI', () => {
9
+ let cli: TraceCLI;
10
+
11
+ beforeEach(() => {
12
+ cli = new TraceCLI('agent-test');
13
+ });
14
+
15
+ describe('Initialization', () => {
16
+ test('initializes Trace', () => {
17
+ const result = cli.init('/tmp');
18
+ expect(result.success).toBe(true);
19
+ expect(result.message).toContain('Initialized');
20
+ });
21
+
22
+ test('sets agent', () => {
23
+ cli.setContext({ agent: 'agent-a' });
24
+ const context = cli.getContext();
25
+ expect(context.agent).toBe('agent-a');
26
+ });
27
+ });
28
+
29
+ describe('Commit Commands', () => {
30
+ test('commits with message', () => {
31
+ const result = cli.commit('feat: add feature');
32
+ expect(result.success).toBe(true);
33
+ expect(result.hash).toBeDefined();
34
+ });
35
+
36
+ test('requires message', () => {
37
+ const result = cli.commit('');
38
+ expect(result.success).toBe(false);
39
+ });
40
+
41
+ test('logs commits', () => {
42
+ const log = cli.log(10);
43
+ expect(log).toContain('COMMIT LOG');
44
+ });
45
+ });
46
+
47
+ describe('Branch Commands', () => {
48
+ test('creates and switches branch', () => {
49
+ const result = cli.branch('feature/new');
50
+ expect(result.success).toBe(true);
51
+ expect(cli.getContext().branch).toBe('feature/new');
52
+ });
53
+
54
+ test('checks out branch', () => {
55
+ const result = cli.checkout('main');
56
+ expect(result.success).toBe(true);
57
+ expect(cli.getContext().branch).toBe('main');
58
+ });
59
+
60
+ test('merges branch', () => {
61
+ const result = cli.merge('feature/new');
62
+ expect(result.success).toBe(true);
63
+ expect(result.message).toContain('Merged');
64
+ });
65
+ });
66
+
67
+ describe('Stash Commands', () => {
68
+ test('stashes changes', () => {
69
+ const result = cli.stash('WIP: feature');
70
+ expect(result.success).toBe(true);
71
+ expect(result.stashId).toBeDefined();
72
+ });
73
+
74
+ test('pops stash', () => {
75
+ cli.stash('WIP');
76
+ const result = cli.stashPop();
77
+ expect(result.success).toBe(true);
78
+ });
79
+ });
80
+
81
+ describe('Advanced Commands', () => {
82
+ test('rebases', () => {
83
+ const result = cli.rebase('main');
84
+ expect(result.success).toBe(true);
85
+ });
86
+
87
+ test('cherry-picks', () => {
88
+ const result = cli.cherryPick('abc123def456');
89
+ expect(result.success).toBe(true);
90
+ });
91
+
92
+ test('pushes', () => {
93
+ const result = cli.push('origin');
94
+ expect(result.success).toBe(true);
95
+ });
96
+
97
+ test('pulls', () => {
98
+ const result = cli.pull('origin');
99
+ expect(result.success).toBe(true);
100
+ });
101
+ });
102
+
103
+ describe('Utility Commands', () => {
104
+ test('shows status', () => {
105
+ const status = cli.status();
106
+ expect(status).toContain('STATUS');
107
+ expect(status).toContain('agent-test');
108
+ });
109
+
110
+ test('searches commits', () => {
111
+ const results = cli.search('feature');
112
+ expect(results).toContain('SEARCH RESULTS');
113
+ });
114
+
115
+ test('shows version', () => {
116
+ const version = cli.version();
117
+ expect(version).toContain('1.3.0');
118
+ });
119
+
120
+ test('shows help', () => {
121
+ const help = cli.help();
122
+ expect(help).toContain('USAGE');
123
+ expect(help).toContain('commit');
124
+ expect(help).toContain('merge');
125
+ });
126
+ });
127
+
128
+ describe('Command Execution', () => {
129
+ test('executes commit', () => {
130
+ const output = cli.execute(['commit', 'test message']);
131
+ expect(output).toContain('test message');
132
+ });
133
+
134
+ test('executes log', () => {
135
+ const output = cli.execute(['log', '5']);
136
+ expect(output).toContain('COMMIT LOG');
137
+ });
138
+
139
+ test('executes status', () => {
140
+ const output = cli.execute(['status']);
141
+ expect(output).toContain('STATUS');
142
+ });
143
+
144
+ test('handles unknown command', () => {
145
+ const output = cli.execute(['unknown']);
146
+ expect(output).toContain('Unknown command');
147
+ });
148
+
149
+ test('shows help on empty', () => {
150
+ const output = cli.execute([]);
151
+ expect(output).toContain('USAGE');
152
+ });
153
+ });
154
+
155
+ describe('Context Management', () => {
156
+ test('sets context', () => {
157
+ cli.setContext({ branch: 'dev', agent: 'agent-b' });
158
+ const context = cli.getContext();
159
+ expect(context.branch).toBe('dev');
160
+ expect(context.agent).toBe('agent-b');
161
+ });
162
+
163
+ test('preserves context', () => {
164
+ const context1 = cli.getContext();
165
+ cli.branch('feature');
166
+ const context2 = cli.getContext();
167
+ expect(context2.branch).toBe('feature');
168
+ expect(context1.agent).toBe(context2.agent);
169
+ });
170
+ });
171
+ });