@fixy/core 0.0.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.
- package/dist/__tests__/diff-parser.test.d.ts +2 -0
- package/dist/__tests__/diff-parser.test.d.ts.map +1 -0
- package/dist/__tests__/diff-parser.test.js +89 -0
- package/dist/__tests__/diff-parser.test.js.map +1 -0
- package/dist/__tests__/fixy-commands.test.d.ts +2 -0
- package/dist/__tests__/fixy-commands.test.d.ts.map +1 -0
- package/dist/__tests__/fixy-commands.test.js +176 -0
- package/dist/__tests__/fixy-commands.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +66 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/router.test.d.ts +2 -0
- package/dist/__tests__/router.test.d.ts.map +1 -0
- package/dist/__tests__/router.test.js +77 -0
- package/dist/__tests__/router.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +2 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +7 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/store.test.d.ts +2 -0
- package/dist/__tests__/store.test.d.ts.map +1 -0
- package/dist/__tests__/store.test.js +121 -0
- package/dist/__tests__/store.test.js.map +1 -0
- package/dist/__tests__/turn.test.d.ts +2 -0
- package/dist/__tests__/turn.test.d.ts.map +1 -0
- package/dist/__tests__/turn.test.js +194 -0
- package/dist/__tests__/turn.test.js.map +1 -0
- package/dist/__tests__/worktree.test.d.ts +2 -0
- package/dist/__tests__/worktree.test.d.ts.map +1 -0
- package/dist/__tests__/worktree.test.js +119 -0
- package/dist/__tests__/worktree.test.js.map +1 -0
- package/dist/adapter.d.ts +75 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +3 -0
- package/dist/adapter.js.map +1 -0
- package/dist/diff-parser.d.ts +3 -0
- package/dist/diff-parser.d.ts.map +1 -0
- package/dist/diff-parser.js +38 -0
- package/dist/diff-parser.js.map +1 -0
- package/dist/fixy-commands.d.ts +25 -0
- package/dist/fixy-commands.d.ts.map +1 -0
- package/dist/fixy-commands.js +154 -0
- package/dist/fixy-commands.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/paths.d.ts +17 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +36 -0
- package/dist/paths.js.map +1 -0
- package/dist/registry.d.ts +12 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +32 -0
- package/dist/registry.js.map +1 -0
- package/dist/router.d.ts +21 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +36 -0
- package/dist/router.js.map +1 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +127 -0
- package/dist/store.js.map +1 -0
- package/dist/thread.d.ts +47 -0
- package/dist/thread.d.ts.map +1 -0
- package/dist/thread.js +3 -0
- package/dist/thread.js.map +1 -0
- package/dist/turn.d.ts +20 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +130 -0
- package/dist/turn.js.map +1 -0
- package/dist/worktree.d.ts +36 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +91 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +21 -0
- package/src/__tests__/diff-parser.test.ts +99 -0
- package/src/__tests__/fixy-commands.test.ts +231 -0
- package/src/__tests__/registry.test.ts +79 -0
- package/src/__tests__/router.test.ts +91 -0
- package/src/__tests__/smoke.test.ts +7 -0
- package/src/__tests__/store.test.ts +151 -0
- package/src/__tests__/turn.test.ts +266 -0
- package/src/__tests__/worktree.test.ts +155 -0
- package/src/adapter.ts +84 -0
- package/src/diff-parser.ts +46 -0
- package/src/fixy-commands.ts +201 -0
- package/src/index.ts +40 -0
- package/src/paths.ts +43 -0
- package/src/registry.ts +40 -0
- package/src/router.ts +49 -0
- package/src/store.ts +164 -0
- package/src/thread.ts +50 -0
- package/src/turn.ts +165 -0
- package/src/worktree.ts +119 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAE5C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,SAAS,WAAW,CAAC,OAAe,EAAE,OAAoC,MAAM;IAC9E,OAAO;QACL,EAAE,EAAE,UAAU,EAAE;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI;QACJ,OAAO,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;QAC3C,OAAO;QACP,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI;QAC7C,YAAY,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;QAC/C,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,IAAI,OAAe,CAAC;AACpB,IAAI,KAAuB,CAAC;AAC5B,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAEzC,UAAU,CAAC,KAAK,IAAI,EAAE;IACpB,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;IACnC,KAAK,GAAG,IAAI,gBAAgB,EAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;YACnF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAEtD,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;YAC/B,MAAM,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAErC,8CAA8C;YAC9C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;YAC5E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAEtD,MAAM,IAAI,GAAG,WAAW,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,WAAW,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,IAAI,GAAG,WAAW,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YAElD,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;YACzD,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;YACzD,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;YAEzD,0CAA0C;YAC1C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;YACtE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;YACvE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAEvD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YACtD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAEhC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAChE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAEpC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAChE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YACtD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAErC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YACpE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAEzC,0CAA0C;YAC1C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;YAC1E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YACtD,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YAEzE,wEAAwE;YACxE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YACtD,MAAM,UAAU,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;YAE/C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;QACpD,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;YAEvC,MAAM,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC1E,kBAAkB,CACnB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/turn.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// packages/core/src/__tests__/turn.test.ts
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { TurnController } from '../turn.js';
|
|
7
|
+
import { AdapterRegistry } from '../registry.js';
|
|
8
|
+
import { LocalThreadStore } from '../store.js';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Helpers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
function createStubAdapter(id, name, executeFn) {
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
name,
|
|
16
|
+
probe: async () => ({
|
|
17
|
+
available: true,
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
authStatus: 'ok',
|
|
20
|
+
detail: null,
|
|
21
|
+
}),
|
|
22
|
+
execute: executeFn ??
|
|
23
|
+
(async () => ({
|
|
24
|
+
exitCode: 0,
|
|
25
|
+
signal: null,
|
|
26
|
+
timedOut: false,
|
|
27
|
+
summary: `response from ${id}`,
|
|
28
|
+
session: null,
|
|
29
|
+
patches: [],
|
|
30
|
+
warnings: [],
|
|
31
|
+
errorMessage: null,
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Suite
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
describe('TurnController', () => {
|
|
39
|
+
let tmpDir;
|
|
40
|
+
let store;
|
|
41
|
+
let thread;
|
|
42
|
+
let registry;
|
|
43
|
+
let controller;
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'fixy-turn-test-'));
|
|
46
|
+
process.env['FIXY_HOME'] = tmpDir;
|
|
47
|
+
store = new LocalThreadStore();
|
|
48
|
+
await store.init();
|
|
49
|
+
thread = await store.createThread('/tmp/fake-project');
|
|
50
|
+
registry = new AdapterRegistry();
|
|
51
|
+
registry.register(createStubAdapter('claude', 'Claude'));
|
|
52
|
+
registry.register(createStubAdapter('codex', 'Codex'));
|
|
53
|
+
controller = new TurnController();
|
|
54
|
+
});
|
|
55
|
+
afterEach(async () => {
|
|
56
|
+
delete process.env['FIXY_HOME'];
|
|
57
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
58
|
+
});
|
|
59
|
+
function makeParams(overrides) {
|
|
60
|
+
return {
|
|
61
|
+
thread,
|
|
62
|
+
input: '',
|
|
63
|
+
registry,
|
|
64
|
+
store,
|
|
65
|
+
onLog: () => { },
|
|
66
|
+
signal: AbortSignal.timeout(5000),
|
|
67
|
+
...overrides,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
71
|
+
it('single mention dispatches to the mentioned adapter', async () => {
|
|
72
|
+
await controller.runTurn(makeParams({ input: '@claude hello' }));
|
|
73
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
74
|
+
expect(updated.messages).toHaveLength(2);
|
|
75
|
+
const [userMsg, agentMsg] = updated.messages;
|
|
76
|
+
expect(userMsg.role).toBe('user');
|
|
77
|
+
expect(userMsg.content).toBe('@claude hello');
|
|
78
|
+
expect(agentMsg.role).toBe('agent');
|
|
79
|
+
expect(agentMsg.agentId).toBe('claude');
|
|
80
|
+
expect(agentMsg.content).toBe('response from claude');
|
|
81
|
+
});
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
it('multi mention dispatches sequentially, second sees first response', async () => {
|
|
84
|
+
const capturedMessages = [];
|
|
85
|
+
registry.unregister('claude');
|
|
86
|
+
registry.unregister('codex');
|
|
87
|
+
registry.register(createStubAdapter('claude', 'Claude', async () => ({
|
|
88
|
+
exitCode: 0,
|
|
89
|
+
signal: null,
|
|
90
|
+
timedOut: false,
|
|
91
|
+
summary: 'claude says hi',
|
|
92
|
+
session: null,
|
|
93
|
+
patches: [],
|
|
94
|
+
warnings: [],
|
|
95
|
+
errorMessage: null,
|
|
96
|
+
})));
|
|
97
|
+
registry.register(createStubAdapter('codex', 'Codex', async (ctx) => {
|
|
98
|
+
capturedMessages.push(...ctx.messages);
|
|
99
|
+
return {
|
|
100
|
+
exitCode: 0,
|
|
101
|
+
signal: null,
|
|
102
|
+
timedOut: false,
|
|
103
|
+
summary: 'codex says bye',
|
|
104
|
+
session: null,
|
|
105
|
+
patches: [],
|
|
106
|
+
warnings: [],
|
|
107
|
+
errorMessage: null,
|
|
108
|
+
};
|
|
109
|
+
}));
|
|
110
|
+
await controller.runTurn(makeParams({ input: '@claude @codex brainstorm' }));
|
|
111
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
112
|
+
expect(updated.messages).toHaveLength(3);
|
|
113
|
+
expect(updated.messages[0].role).toBe('user');
|
|
114
|
+
expect(updated.messages[1].role).toBe('agent');
|
|
115
|
+
expect(updated.messages[1].agentId).toBe('claude');
|
|
116
|
+
expect(updated.messages[2].role).toBe('agent');
|
|
117
|
+
expect(updated.messages[2].agentId).toBe('codex');
|
|
118
|
+
// Codex's execute received messages that include claude's response.
|
|
119
|
+
const claudeResponseInCodexCtx = capturedMessages.find((m) => m.role === 'agent' && m.agentId === 'claude' && m.content === 'claude says hi');
|
|
120
|
+
expect(claudeResponseInCodexCtx).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
// -------------------------------------------------------------------------
|
|
123
|
+
it('bare message falls to last agent that spoke', async () => {
|
|
124
|
+
const { randomUUID } = await import('node:crypto');
|
|
125
|
+
// Manually append an agent message from codex so it is the last agent.
|
|
126
|
+
await store.appendMessage(thread.id, thread.projectRoot, {
|
|
127
|
+
id: randomUUID(),
|
|
128
|
+
createdAt: new Date().toISOString(),
|
|
129
|
+
role: 'agent',
|
|
130
|
+
agentId: 'codex',
|
|
131
|
+
content: 'previous codex response',
|
|
132
|
+
runId: randomUUID(),
|
|
133
|
+
dispatchedTo: [],
|
|
134
|
+
patches: [],
|
|
135
|
+
warnings: [],
|
|
136
|
+
});
|
|
137
|
+
// Re-read so the thread object held by TurnController has the new message.
|
|
138
|
+
thread = await store.getThread(thread.id, thread.projectRoot);
|
|
139
|
+
await controller.runTurn(makeParams({ input: 'continue', thread }));
|
|
140
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
141
|
+
const lastAgentMsg = [...updated.messages].reverse().find((m) => m.role === 'agent');
|
|
142
|
+
expect(lastAgentMsg?.agentId).toBe('codex');
|
|
143
|
+
});
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
it('bare message falls to workerModel when no prior agent', async () => {
|
|
146
|
+
// Thread has no messages yet; workerModel defaults to 'claude'.
|
|
147
|
+
expect(thread.messages).toHaveLength(0);
|
|
148
|
+
expect(thread.workerModel).toBe('claude');
|
|
149
|
+
await controller.runTurn(makeParams({ input: 'hello' }));
|
|
150
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
151
|
+
const agentMsg = updated.messages.find((m) => m.role === 'agent');
|
|
152
|
+
expect(agentMsg?.agentId).toBe('claude');
|
|
153
|
+
});
|
|
154
|
+
// -------------------------------------------------------------------------
|
|
155
|
+
it('@fixy /status returns adapter status', async () => {
|
|
156
|
+
await controller.runTurn(makeParams({ input: '@fixy /status' }));
|
|
157
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
158
|
+
const systemMsg = updated.messages.find((m) => m.role === 'system');
|
|
159
|
+
expect(systemMsg).toBeDefined();
|
|
160
|
+
expect(systemMsg?.content).toContain('claude');
|
|
161
|
+
expect(systemMsg?.content).toContain('codex');
|
|
162
|
+
expect(systemMsg?.content).toContain('yes');
|
|
163
|
+
});
|
|
164
|
+
// -------------------------------------------------------------------------
|
|
165
|
+
it('@fixy /worker changes workerModel', async () => {
|
|
166
|
+
expect(thread.workerModel).toBe('claude');
|
|
167
|
+
await controller.runTurn(makeParams({ input: '@fixy /worker codex' }));
|
|
168
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
169
|
+
expect(updated.workerModel).toBe('codex');
|
|
170
|
+
const systemMsg = updated.messages.find((m) => m.role === 'system');
|
|
171
|
+
expect(systemMsg?.content).toBe('worker set to codex');
|
|
172
|
+
});
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
it('unknown mention appends error system message', async () => {
|
|
175
|
+
await controller.runTurn(makeParams({ input: '@unknown do stuff' }));
|
|
176
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
177
|
+
const systemMsg = updated.messages.find((m) => m.role === 'system');
|
|
178
|
+
expect(systemMsg?.content).toBe('unknown agent: @unknown');
|
|
179
|
+
const agentMsgs = updated.messages.filter((m) => m.role === 'agent');
|
|
180
|
+
expect(agentMsgs).toHaveLength(0);
|
|
181
|
+
});
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
it('4 mentions rejects with max adapters message', async () => {
|
|
184
|
+
registry.register(createStubAdapter('aider', 'Aider'));
|
|
185
|
+
registry.register(createStubAdapter('gemini', 'Gemini'));
|
|
186
|
+
await controller.runTurn(makeParams({ input: '@claude @codex @aider @gemini do stuff' }));
|
|
187
|
+
const updated = await store.getThread(thread.id, thread.projectRoot);
|
|
188
|
+
const systemMsg = updated.messages.find((m) => m.role === 'system');
|
|
189
|
+
expect(systemMsg?.content).toContain('maximum 3 adapters per turn');
|
|
190
|
+
const agentMsgs = updated.messages.filter((m) => m.role === 'agent');
|
|
191
|
+
expect(agentMsgs).toHaveLength(0);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=turn.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn.test.js","sourceRoot":"","sources":["../../src/__tests__/turn.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/worktree.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// packages/core/src/__tests__/worktree.test.ts
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import { mkdtemp, rm, writeFile, access } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { WorktreeManager } from '../worktree.js';
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Setup / teardown
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
let tempDir;
|
|
14
|
+
let projectRoot;
|
|
15
|
+
let manager;
|
|
16
|
+
const THREAD_ID = 'test-thread-001';
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
tempDir = await mkdtemp(join(tmpdir(), 'fixy-wt-test-'));
|
|
19
|
+
process.env['FIXY_HOME'] = join(tempDir, 'fixy-home');
|
|
20
|
+
// Create a real git repo as fixture
|
|
21
|
+
projectRoot = join(tempDir, 'repo');
|
|
22
|
+
await execFileAsync('mkdir', ['-p', projectRoot]);
|
|
23
|
+
await execFileAsync('git', ['init'], { cwd: projectRoot });
|
|
24
|
+
await execFileAsync('git', ['config', 'user.email', 'test@test.com'], { cwd: projectRoot });
|
|
25
|
+
await execFileAsync('git', ['config', 'user.name', 'Test'], { cwd: projectRoot });
|
|
26
|
+
// Need at least one commit for worktrees
|
|
27
|
+
await writeFile(join(projectRoot, 'README.md'), '# Test\n');
|
|
28
|
+
await execFileAsync('git', ['add', '.'], { cwd: projectRoot });
|
|
29
|
+
await execFileAsync('git', ['commit', '-m', 'init'], { cwd: projectRoot });
|
|
30
|
+
manager = new WorktreeManager();
|
|
31
|
+
});
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
delete process.env['FIXY_HOME'];
|
|
34
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Tests
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
describe('WorktreeManager', { timeout: 15000 }, () => {
|
|
40
|
+
it('ensure() creates a worktree with correct handle fields', async () => {
|
|
41
|
+
const handle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
42
|
+
// Shape
|
|
43
|
+
expect(handle.agentId).toBe('claude');
|
|
44
|
+
expect(handle.threadId).toBe(THREAD_ID);
|
|
45
|
+
expect(handle.branch).toBe('fixy/test-thread-001-claude');
|
|
46
|
+
expect(handle.path).toContain('test-thread-001');
|
|
47
|
+
expect(handle.path).toContain('claude');
|
|
48
|
+
// Directory actually exists on disk
|
|
49
|
+
await expect(access(handle.path)).resolves.toBeUndefined();
|
|
50
|
+
// git worktree list shows the path
|
|
51
|
+
const { stdout } = await execFileAsync('git', ['worktree', 'list', '--porcelain'], {
|
|
52
|
+
cwd: projectRoot,
|
|
53
|
+
});
|
|
54
|
+
expect(stdout).toContain(handle.path);
|
|
55
|
+
});
|
|
56
|
+
it('ensure() is idempotent — second call returns same handle, no error', async () => {
|
|
57
|
+
const handle1 = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
58
|
+
const handle2 = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
59
|
+
expect(handle2.path).toBe(handle1.path);
|
|
60
|
+
expect(handle2.branch).toBe(handle1.branch);
|
|
61
|
+
expect(handle2.agentId).toBe(handle1.agentId);
|
|
62
|
+
expect(handle2.threadId).toBe(handle1.threadId);
|
|
63
|
+
});
|
|
64
|
+
it('collectPatches() returns patches for modified tracked files', async () => {
|
|
65
|
+
const handle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
66
|
+
// README.md was tracked from the init commit and is present in the worktree
|
|
67
|
+
await writeFile(join(handle.path, 'README.md'), '# Test\n\nAdded a line.\n');
|
|
68
|
+
const patches = await manager.collectPatches(handle);
|
|
69
|
+
expect(patches).toHaveLength(1);
|
|
70
|
+
expect(patches[0].relativePath).toBe('README.md');
|
|
71
|
+
// One line added
|
|
72
|
+
expect(patches[0].stats.additions).toBeGreaterThanOrEqual(1);
|
|
73
|
+
// Original line counted as deletion since it was replaced
|
|
74
|
+
expect(patches[0].stats.deletions).toBeGreaterThanOrEqual(0);
|
|
75
|
+
expect(patches[0].diff).toContain('diff --git');
|
|
76
|
+
});
|
|
77
|
+
it('collectPatches() returns empty array on clean worktree', async () => {
|
|
78
|
+
const handle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
79
|
+
const patches = await manager.collectPatches(handle);
|
|
80
|
+
expect(patches).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
it('reset() re-provisions a clean worktree', async () => {
|
|
83
|
+
const handle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
84
|
+
// Dirty the worktree
|
|
85
|
+
await writeFile(join(handle.path, 'README.md'), '# Dirty\n');
|
|
86
|
+
await manager.reset(handle, projectRoot);
|
|
87
|
+
// The same path should exist again (re-provisioned)
|
|
88
|
+
await expect(access(handle.path)).resolves.toBeUndefined();
|
|
89
|
+
// collectPatches on the fresh handle should be empty
|
|
90
|
+
const freshHandle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
91
|
+
const patches = await manager.collectPatches(freshHandle);
|
|
92
|
+
expect(patches).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
it('remove() permanently removes the worktree directory and its branch', async () => {
|
|
95
|
+
const handle = await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
96
|
+
await manager.remove(handle, projectRoot);
|
|
97
|
+
// Directory no longer exists
|
|
98
|
+
await expect(access(handle.path)).rejects.toThrow();
|
|
99
|
+
// Branch is deleted
|
|
100
|
+
const { stdout } = await execFileAsync('git', ['branch', '--list', handle.branch], {
|
|
101
|
+
cwd: projectRoot,
|
|
102
|
+
});
|
|
103
|
+
expect(stdout.trim()).toBe('');
|
|
104
|
+
});
|
|
105
|
+
it('list() returns all worktrees registered for a thread', async () => {
|
|
106
|
+
await manager.ensure(projectRoot, THREAD_ID, 'claude');
|
|
107
|
+
await manager.ensure(projectRoot, THREAD_ID, 'codex');
|
|
108
|
+
const handles = await manager.list(THREAD_ID);
|
|
109
|
+
expect(handles).toHaveLength(2);
|
|
110
|
+
const agentIds = handles.map((h) => h.agentId);
|
|
111
|
+
expect(agentIds).toContain('claude');
|
|
112
|
+
expect(agentIds).toContain('codex');
|
|
113
|
+
// All handles should report the correct threadId
|
|
114
|
+
for (const h of handles) {
|
|
115
|
+
expect(h.threadId).toBe(THREAD_ID);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=worktree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.test.js","sourceRoot":"","sources":["../../src/__tests__/worktree.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { FixyMessage, FixyPatch } from './thread.js';
|
|
2
|
+
export interface FixyAgent {
|
|
3
|
+
/** Stable handle without '@', e.g. "claude", "codex". */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Display name shown in the terminal. */
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FixyThreadContext {
|
|
9
|
+
threadId: string;
|
|
10
|
+
projectRoot: string;
|
|
11
|
+
worktreePath: string;
|
|
12
|
+
repoRef: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface FixyExecutionContext {
|
|
15
|
+
runId: string;
|
|
16
|
+
agent: FixyAgent;
|
|
17
|
+
threadContext: FixyThreadContext;
|
|
18
|
+
/** Full normalized message history Fixy decided to send this turn. */
|
|
19
|
+
messages: FixyMessage[];
|
|
20
|
+
/** Fresh user input for this turn, already stripped of the @mention prefix. */
|
|
21
|
+
prompt: string;
|
|
22
|
+
/** Opaque adapter-owned state from the previous turn in this thread. */
|
|
23
|
+
session: FixySession | null;
|
|
24
|
+
/** Streamed stdout/stderr chunks. Adapters MUST call this. */
|
|
25
|
+
onLog: (stream: 'stdout' | 'stderr', chunk: string) => void;
|
|
26
|
+
/** Called once with the resolved command + args + env for transcript/logging. */
|
|
27
|
+
onMeta: (meta: FixyInvocationMeta) => void;
|
|
28
|
+
/** Called with the child pid the moment the process spawns. */
|
|
29
|
+
onSpawn: (pid: number) => void;
|
|
30
|
+
/** Abort signal propagated from `/reset` and Ctrl-C. */
|
|
31
|
+
signal: AbortSignal;
|
|
32
|
+
}
|
|
33
|
+
export interface FixyExecutionResult {
|
|
34
|
+
exitCode: number | null;
|
|
35
|
+
signal: string | null;
|
|
36
|
+
timedOut: boolean;
|
|
37
|
+
/** Human-readable summary the router appends to the thread. */
|
|
38
|
+
summary: string;
|
|
39
|
+
/** Opaque per-adapter state to persist for the next turn (e.g. Claude session id). */
|
|
40
|
+
session: FixySession | null;
|
|
41
|
+
/** Any patches/diffs the adapter produced, keyed by file path. */
|
|
42
|
+
patches: FixyPatch[];
|
|
43
|
+
/** Non-fatal warnings shown to the user after the turn completes. */
|
|
44
|
+
warnings: string[];
|
|
45
|
+
errorMessage: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface FixySession {
|
|
48
|
+
/** Adapter-native session id, e.g. Claude `--resume` id or Codex thread id. */
|
|
49
|
+
sessionId: string;
|
|
50
|
+
/** Any adapter-specific params needed to resume. Must be JSON-serializable. */
|
|
51
|
+
params: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export interface FixyInvocationMeta {
|
|
54
|
+
resolvedCommand: string;
|
|
55
|
+
args: string[];
|
|
56
|
+
cwd: string;
|
|
57
|
+
env: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
export interface FixyAdapter {
|
|
60
|
+
/** Stable id, matches the mention handle without '@'. */
|
|
61
|
+
readonly id: string;
|
|
62
|
+
/** Human-readable name for `/status`. */
|
|
63
|
+
readonly name: string;
|
|
64
|
+
/** Verify the CLI is installed and the user's auth is valid. */
|
|
65
|
+
probe(): Promise<FixyProbeResult>;
|
|
66
|
+
/** Run one turn. Must honor `ctx.signal`. */
|
|
67
|
+
execute(ctx: FixyExecutionContext): Promise<FixyExecutionResult>;
|
|
68
|
+
}
|
|
69
|
+
export interface FixyProbeResult {
|
|
70
|
+
available: boolean;
|
|
71
|
+
version: string | null;
|
|
72
|
+
authStatus: 'ok' | 'needs_login' | 'unknown';
|
|
73
|
+
detail: string | null;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,iBAAiB,CAAC;IACjC,sEAAsE;IACtE,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,8DAA8D;IAC9D,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,iFAAiF;IACjF,MAAM,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC3C,+DAA+D;IAC/D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,wDAAwD;IACxD,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,kEAAkE;IAClE,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,KAAK,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC,6CAA6C;IAC7C,OAAO,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,IAAI,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,+BAA+B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-parser.d.ts","sourceRoot":"","sources":["../src/diff-parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,CAyCtF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// packages/core/src/diff-parser.ts
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export function parseUnifiedDiff(diffOutput, worktreePath) {
|
|
4
|
+
if (!diffOutput || !diffOutput.trim()) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
// Split on diff --git boundaries using lookahead to keep the delimiter with each segment
|
|
8
|
+
const segments = diffOutput.split(/(?=^diff --git )/m).filter((segment) => segment.trim());
|
|
9
|
+
const patches = [];
|
|
10
|
+
for (const segment of segments) {
|
|
11
|
+
// Extract file path from the diff --git header line (b/ side)
|
|
12
|
+
const headerMatch = /^diff --git a\/.+ b\/(.+)$/m.exec(segment);
|
|
13
|
+
if (!headerMatch) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const relativePath = headerMatch[1].trim();
|
|
17
|
+
const filePath = join(worktreePath, relativePath);
|
|
18
|
+
let additions = 0;
|
|
19
|
+
let deletions = 0;
|
|
20
|
+
const lines = segment.split('\n');
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
23
|
+
additions++;
|
|
24
|
+
}
|
|
25
|
+
else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
26
|
+
deletions++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
patches.push({
|
|
30
|
+
filePath,
|
|
31
|
+
relativePath,
|
|
32
|
+
diff: segment,
|
|
33
|
+
stats: { additions, deletions },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return patches;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=diff-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-parser.js","sourceRoot":"","sources":["../src/diff-parser.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,YAAoB;IACvE,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QACtC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,yFAAyF;IACzF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE3F,MAAM,OAAO,GAAgB,EAAE,CAAC;IAEhC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,8DAA8D;QAC9D,MAAM,WAAW,GAAG,6BAA6B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAElD,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpD,SAAS,EAAE,CAAC;YACd,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,SAAS,EAAE,CAAC;YACd,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ;YACR,YAAY;YACZ,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE;SAChC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AdapterRegistry } from './registry.js';
|
|
2
|
+
import type { LocalThreadStore } from './store.js';
|
|
3
|
+
import type { FixyThread } from './thread.js';
|
|
4
|
+
import type { WorktreeManager } from './worktree.js';
|
|
5
|
+
export interface FixyCommandContext {
|
|
6
|
+
thread: FixyThread;
|
|
7
|
+
rest: string;
|
|
8
|
+
store: LocalThreadStore;
|
|
9
|
+
registry: AdapterRegistry;
|
|
10
|
+
worktreeManager: WorktreeManager;
|
|
11
|
+
onLog: (stream: 'stdout' | 'stderr', chunk: string) => void;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
export declare class FixyCommandRunner {
|
|
15
|
+
run(ctx: FixyCommandContext): Promise<void>;
|
|
16
|
+
private _handleWorker;
|
|
17
|
+
private _handleAll;
|
|
18
|
+
private _handleSettings;
|
|
19
|
+
private _handleReset;
|
|
20
|
+
private _handleStatus;
|
|
21
|
+
private _handleBare;
|
|
22
|
+
private _persistThread;
|
|
23
|
+
private _appendSystemMessage;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=fixy-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixy-commands.d.ts","sourceRoot":"","sources":["../src/fixy-commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,eAAe,EAAE,eAAe,CAAC;IACjC,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,qBAAa,iBAAiB;IACtB,GAAG,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;YAiCnC,aAAa;YAwBb,UAAU;YAOV,eAAe;YAIf,YAAY;YAwBZ,aAAa;YAwBb,WAAW;YA0CX,cAAc;YAOd,oBAAoB;CAcnC"}
|