kanna-code 0.24.0 → 0.25.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/README.md +6 -2
- package/dist/client/assets/index-Hy4LEj8j.css +32 -0
- package/dist/client/assets/index-Tg96DNSK.js +2554 -0
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/src/server/diff-store.test.ts +173 -31
- package/src/server/diff-store.ts +545 -82
- package/src/server/generate-commit-message.test.ts +0 -2
- package/src/server/generate-commit-message.ts +9 -4
- package/src/server/keybindings.test.ts +12 -0
- package/src/server/keybindings.ts +3 -0
- package/src/server/read-models.test.ts +1 -2
- package/src/server/read-models.ts +1 -4
- package/src/server/ws-router.test.ts +275 -8
- package/src/server/ws-router.ts +79 -86
- package/src/shared/protocol.ts +37 -1
- package/src/shared/types.ts +49 -45
- package/dist/client/assets/index-0UyEIBeu.js +0 -2532
- package/dist/client/assets/index-CYUEw41P.css +0 -32
|
@@ -11,7 +11,6 @@ describe("generateCommitMessageDetailed", () => {
|
|
|
11
11
|
files: [{
|
|
12
12
|
path: "app.ts",
|
|
13
13
|
changeType: "modified",
|
|
14
|
-
isUntracked: false,
|
|
15
14
|
patch: "diff --git a/app.ts b/app.ts\n--- a/app.ts\n+++ b/app.ts\n@@\n-old\n+new\n",
|
|
16
15
|
}],
|
|
17
16
|
},
|
|
@@ -39,7 +38,6 @@ describe("generateCommitMessageDetailed", () => {
|
|
|
39
38
|
files: [{
|
|
40
39
|
path: "src/feature.ts",
|
|
41
40
|
changeType: "modified",
|
|
42
|
-
isUntracked: false,
|
|
43
41
|
patch: "diff --git a/src/feature.ts b/src/feature.ts\n",
|
|
44
42
|
}],
|
|
45
43
|
},
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import path from "node:path"
|
|
2
|
-
import type { ChatDiffFile } from "../shared/types"
|
|
3
2
|
import { QuickResponseAdapter } from "./quick-response"
|
|
4
3
|
|
|
4
|
+
interface CommitMessageFile {
|
|
5
|
+
path: string
|
|
6
|
+
changeType: "added" | "deleted" | "modified" | "renamed"
|
|
7
|
+
patch: string
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
const COMMIT_MESSAGE_SCHEMA = {
|
|
6
11
|
type: "object",
|
|
7
12
|
properties: {
|
|
@@ -44,7 +49,7 @@ function sanitizeBody(value: unknown): string {
|
|
|
44
49
|
return value.trim()
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
function fallbackSubject(files:
|
|
52
|
+
function fallbackSubject(files: CommitMessageFile[]) {
|
|
48
53
|
if (files.length === 1) {
|
|
49
54
|
const fileName = path.posix.basename(files[0]?.path ?? "file")
|
|
50
55
|
const normalized = `Update ${fileName}`.replace(/\s+/g, " ").trim()
|
|
@@ -56,7 +61,7 @@ function fallbackSubject(files: ChatDiffFile[]) {
|
|
|
56
61
|
|
|
57
62
|
function buildCommitMessagePrompt(args: {
|
|
58
63
|
branchName?: string
|
|
59
|
-
files:
|
|
64
|
+
files: CommitMessageFile[]
|
|
60
65
|
}) {
|
|
61
66
|
const fileList = args.files.map((file) => `${file.changeType}: ${file.path}`).join("\n")
|
|
62
67
|
const combinedPatch = args.files.map((file) => file.patch).join("\n\n")
|
|
@@ -83,7 +88,7 @@ export async function generateCommitMessageDetailed(
|
|
|
83
88
|
args: {
|
|
84
89
|
cwd: string
|
|
85
90
|
branchName?: string
|
|
86
|
-
files:
|
|
91
|
+
files: CommitMessageFile[]
|
|
87
92
|
},
|
|
88
93
|
adapter = new QuickResponseAdapter()
|
|
89
94
|
): Promise<GenerateCommitMessageResult> {
|
|
@@ -38,6 +38,9 @@ describe("normalizeKeybindings", () => {
|
|
|
38
38
|
openInFinder: ["Cmd+Alt+F"],
|
|
39
39
|
openInEditor: ["Cmd+Shift+O"],
|
|
40
40
|
addSplitTerminal: ["Cmd+Shift+J"],
|
|
41
|
+
jumpToSidebarChat: ["Cmd+Alt"],
|
|
42
|
+
createChatInCurrentProject: ["Cmd+Alt+N"],
|
|
43
|
+
openAddProject: ["Cmd+Alt+O"],
|
|
41
44
|
}, TEST_FILE_PATH)
|
|
42
45
|
|
|
43
46
|
expect(snapshot).toEqual({
|
|
@@ -47,6 +50,9 @@ describe("normalizeKeybindings", () => {
|
|
|
47
50
|
openInFinder: ["cmd+alt+f"],
|
|
48
51
|
openInEditor: ["cmd+shift+o"],
|
|
49
52
|
addSplitTerminal: ["cmd+shift+j"],
|
|
53
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
54
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
55
|
+
openAddProject: ["cmd+alt+o"],
|
|
50
56
|
},
|
|
51
57
|
warning: null,
|
|
52
58
|
filePathDisplay: TEST_FILE_PATH,
|
|
@@ -97,6 +103,9 @@ describe("KeybindingsManager", () => {
|
|
|
97
103
|
openInFinder: ["Cmd+Alt+F"],
|
|
98
104
|
openInEditor: ["Cmd+Shift+O"],
|
|
99
105
|
addSplitTerminal: ["Cmd+Shift+J"],
|
|
106
|
+
jumpToSidebarChat: ["Cmd+Alt"],
|
|
107
|
+
createChatInCurrentProject: ["Cmd+Alt+N"],
|
|
108
|
+
openAddProject: ["Cmd+Alt+O"],
|
|
100
109
|
})
|
|
101
110
|
|
|
102
111
|
expect(snapshot).toEqual({
|
|
@@ -106,6 +115,9 @@ describe("KeybindingsManager", () => {
|
|
|
106
115
|
openInFinder: ["cmd+alt+f"],
|
|
107
116
|
openInEditor: ["cmd+shift+o"],
|
|
108
117
|
addSplitTerminal: ["cmd+shift+j"],
|
|
118
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
119
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
120
|
+
openAddProject: ["cmd+alt+o"],
|
|
109
121
|
},
|
|
110
122
|
warning: null,
|
|
111
123
|
filePathDisplay: filePath,
|
|
@@ -159,6 +159,9 @@ function createDefaultSnapshot(filePath: string, warning: string | null = null):
|
|
|
159
159
|
openInFinder: [...DEFAULT_KEYBINDINGS.openInFinder],
|
|
160
160
|
openInEditor: [...DEFAULT_KEYBINDINGS.openInEditor],
|
|
161
161
|
addSplitTerminal: [...DEFAULT_KEYBINDINGS.addSplitTerminal],
|
|
162
|
+
jumpToSidebarChat: [...DEFAULT_KEYBINDINGS.jumpToSidebarChat],
|
|
163
|
+
createChatInCurrentProject: [...DEFAULT_KEYBINDINGS.createChatInCurrentProject],
|
|
164
|
+
openAddProject: [...DEFAULT_KEYBINDINGS.openAddProject],
|
|
162
165
|
},
|
|
163
166
|
warning,
|
|
164
167
|
filePathDisplay: formatDisplayPath(filePath),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
ChatDiffSnapshot,
|
|
3
2
|
ChatRuntime,
|
|
4
3
|
ChatSnapshot,
|
|
5
4
|
KannaStatus,
|
|
@@ -105,8 +104,7 @@ export function deriveChatSnapshot(
|
|
|
105
104
|
activeStatuses: Map<string, KannaStatus>,
|
|
106
105
|
drainingChatIds: Set<string>,
|
|
107
106
|
chatId: string,
|
|
108
|
-
getMessages: (chatId: string) => Pick<ChatSnapshot, "messages" | "history"
|
|
109
|
-
getDiffs: (chatId: string) => ChatDiffSnapshot
|
|
107
|
+
getMessages: (chatId: string) => Pick<ChatSnapshot, "messages" | "history">
|
|
110
108
|
): ChatSnapshot | null {
|
|
111
109
|
const chat = state.chatsById.get(chatId)
|
|
112
110
|
if (!chat || chat.deletedAt) return null
|
|
@@ -131,7 +129,6 @@ export function deriveChatSnapshot(
|
|
|
131
129
|
runtime,
|
|
132
130
|
messages: transcript.messages,
|
|
133
131
|
history: transcript.history,
|
|
134
|
-
diffs: getDiffs(chat.id),
|
|
135
132
|
availableProviders: [...SERVER_PROVIDERS],
|
|
136
133
|
}
|
|
137
134
|
}
|
|
@@ -22,6 +22,9 @@ const DEFAULT_KEYBINDINGS_SNAPSHOT: KeybindingsSnapshot = {
|
|
|
22
22
|
openInFinder: ["cmd+alt+f"],
|
|
23
23
|
openInEditor: ["cmd+shift+o"],
|
|
24
24
|
addSplitTerminal: ["cmd+shift+j"],
|
|
25
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
26
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
27
|
+
openAddProject: ["cmd+alt+o"],
|
|
25
28
|
},
|
|
26
29
|
warning: null,
|
|
27
30
|
filePathDisplay: "~/.kanna/keybindings.json",
|
|
@@ -176,6 +179,264 @@ describe("ws-router", () => {
|
|
|
176
179
|
})
|
|
177
180
|
})
|
|
178
181
|
|
|
182
|
+
test("subscribes to project git snapshots independently from chat snapshots", async () => {
|
|
183
|
+
const state = createEmptyState()
|
|
184
|
+
state.projectsById.set("project-1", {
|
|
185
|
+
id: "project-1",
|
|
186
|
+
localPath: "/tmp/project",
|
|
187
|
+
title: "Project",
|
|
188
|
+
createdAt: 1,
|
|
189
|
+
updatedAt: 1,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const router = createWsRouter({
|
|
193
|
+
store: {
|
|
194
|
+
state,
|
|
195
|
+
getProject: () => state.projectsById.get("project-1") ?? null,
|
|
196
|
+
} as never,
|
|
197
|
+
diffStore: {
|
|
198
|
+
getProjectSnapshot: () => ({
|
|
199
|
+
status: "ready",
|
|
200
|
+
branchName: "main",
|
|
201
|
+
files: [],
|
|
202
|
+
branchHistory: { entries: [] },
|
|
203
|
+
}),
|
|
204
|
+
refreshSnapshot: async () => false,
|
|
205
|
+
listBranches: async () => ({ recent: [], local: [], remote: [], pullRequests: [], pullRequestsStatus: "unavailable" }),
|
|
206
|
+
previewMergeBranch: async () => ({ currentBranchName: "main", targetBranchName: "feature/test", targetDisplayName: "feature/test", status: "mergeable", commitCount: 1, hasConflicts: false, message: "ready" }),
|
|
207
|
+
mergeBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: false }),
|
|
208
|
+
syncBranch: async () => ({ ok: true, action: "fetch", snapshotChanged: false }),
|
|
209
|
+
checkoutBranch: async () => ({ ok: true, snapshotChanged: false }),
|
|
210
|
+
createBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: false }),
|
|
211
|
+
generateCommitMessage: async () => ({ subject: "", body: "", usedFallback: true, failureMessage: null }),
|
|
212
|
+
commitFiles: async () => ({ ok: true, mode: "commit_only", pushed: false, snapshotChanged: false }),
|
|
213
|
+
discardFile: async () => ({ snapshotChanged: false }),
|
|
214
|
+
ignoreFile: async () => ({ snapshotChanged: false }),
|
|
215
|
+
readPatch: async () => ({ patch: "" }),
|
|
216
|
+
} as never,
|
|
217
|
+
agent: { getActiveStatuses: () => new Map(), getDrainingChatIds: () => new Set() } as never,
|
|
218
|
+
terminals: {
|
|
219
|
+
getSnapshot: () => null,
|
|
220
|
+
onEvent: () => () => {},
|
|
221
|
+
} as never,
|
|
222
|
+
keybindings: {
|
|
223
|
+
getSnapshot: () => DEFAULT_KEYBINDINGS_SNAPSHOT,
|
|
224
|
+
onChange: () => () => {},
|
|
225
|
+
} as never,
|
|
226
|
+
refreshDiscovery: async () => [],
|
|
227
|
+
getDiscoveredProjects: () => [],
|
|
228
|
+
machineDisplayName: "Local Machine",
|
|
229
|
+
updateManager: null,
|
|
230
|
+
})
|
|
231
|
+
const ws = new FakeWebSocket()
|
|
232
|
+
|
|
233
|
+
await router.handleMessage(
|
|
234
|
+
ws as never,
|
|
235
|
+
JSON.stringify({
|
|
236
|
+
v: 1,
|
|
237
|
+
type: "subscribe",
|
|
238
|
+
id: "project-git-sub-1",
|
|
239
|
+
topic: { type: "project-git", projectId: "project-1" },
|
|
240
|
+
})
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
expect(ws.sent[0]).toEqual({
|
|
244
|
+
v: PROTOCOL_VERSION,
|
|
245
|
+
type: "snapshot",
|
|
246
|
+
id: "project-git-sub-1",
|
|
247
|
+
snapshot: {
|
|
248
|
+
type: "project-git",
|
|
249
|
+
data: {
|
|
250
|
+
status: "ready",
|
|
251
|
+
branchName: "main",
|
|
252
|
+
files: [],
|
|
253
|
+
branchHistory: { entries: [] },
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test("reads diff patches through the project-scoped command", async () => {
|
|
260
|
+
const state = createEmptyState()
|
|
261
|
+
state.projectsById.set("project-1", {
|
|
262
|
+
id: "project-1",
|
|
263
|
+
localPath: "/tmp/project",
|
|
264
|
+
title: "Project",
|
|
265
|
+
createdAt: 1,
|
|
266
|
+
updatedAt: 1,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
const router = createWsRouter({
|
|
270
|
+
store: {
|
|
271
|
+
state,
|
|
272
|
+
getProject: (projectId: string) => state.projectsById.get(projectId) ?? null,
|
|
273
|
+
} as never,
|
|
274
|
+
diffStore: {
|
|
275
|
+
getProjectSnapshot: () => null,
|
|
276
|
+
refreshSnapshot: async () => false,
|
|
277
|
+
listBranches: async () => ({ recent: [], local: [], remote: [], pullRequests: [], pullRequestsStatus: "unavailable" }),
|
|
278
|
+
previewMergeBranch: async () => ({ currentBranchName: "main", targetBranchName: "feature/test", targetDisplayName: "feature/test", status: "mergeable", commitCount: 1, hasConflicts: false, message: "ready" }),
|
|
279
|
+
mergeBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: false }),
|
|
280
|
+
syncBranch: async () => ({ ok: true, action: "fetch", snapshotChanged: false }),
|
|
281
|
+
checkoutBranch: async () => ({ ok: true, snapshotChanged: false }),
|
|
282
|
+
createBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: false }),
|
|
283
|
+
generateCommitMessage: async () => ({ subject: "", body: "", usedFallback: true, failureMessage: null }),
|
|
284
|
+
commitFiles: async () => ({ ok: true, mode: "commit_only", pushed: false, snapshotChanged: false }),
|
|
285
|
+
discardFile: async () => ({ snapshotChanged: false }),
|
|
286
|
+
ignoreFile: async () => ({ snapshotChanged: false }),
|
|
287
|
+
readPatch: async () => ({ patch: "diff --git a/app.txt b/app.txt" }),
|
|
288
|
+
} as never,
|
|
289
|
+
agent: { getActiveStatuses: () => new Map(), getDrainingChatIds: () => new Set() } as never,
|
|
290
|
+
terminals: {
|
|
291
|
+
getSnapshot: () => null,
|
|
292
|
+
onEvent: () => () => {},
|
|
293
|
+
} as never,
|
|
294
|
+
keybindings: {
|
|
295
|
+
getSnapshot: () => DEFAULT_KEYBINDINGS_SNAPSHOT,
|
|
296
|
+
onChange: () => () => {},
|
|
297
|
+
} as never,
|
|
298
|
+
refreshDiscovery: async () => [],
|
|
299
|
+
getDiscoveredProjects: () => [],
|
|
300
|
+
machineDisplayName: "Local Machine",
|
|
301
|
+
updateManager: null,
|
|
302
|
+
})
|
|
303
|
+
const ws = new FakeWebSocket()
|
|
304
|
+
|
|
305
|
+
await router.handleMessage(
|
|
306
|
+
ws as never,
|
|
307
|
+
JSON.stringify({
|
|
308
|
+
v: 1,
|
|
309
|
+
type: "command",
|
|
310
|
+
id: "read-patch-1",
|
|
311
|
+
command: {
|
|
312
|
+
type: "project.readDiffPatch",
|
|
313
|
+
projectId: "project-1",
|
|
314
|
+
path: "app.txt",
|
|
315
|
+
},
|
|
316
|
+
})
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
expect(ws.sent[0]).toEqual({
|
|
320
|
+
v: PROTOCOL_VERSION,
|
|
321
|
+
type: "ack",
|
|
322
|
+
id: "read-patch-1",
|
|
323
|
+
result: { patch: "diff --git a/app.txt b/app.txt" },
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
test("routes merge preview and merge commands through the diff store", async () => {
|
|
328
|
+
const state = createEmptyState()
|
|
329
|
+
state.projectsById.set("project-1", {
|
|
330
|
+
id: "project-1",
|
|
331
|
+
localPath: "/tmp/project",
|
|
332
|
+
title: "Project",
|
|
333
|
+
createdAt: 1,
|
|
334
|
+
updatedAt: 1,
|
|
335
|
+
})
|
|
336
|
+
state.chatsById.set("chat-1", {
|
|
337
|
+
id: "chat-1",
|
|
338
|
+
projectId: "project-1",
|
|
339
|
+
title: "Chat",
|
|
340
|
+
createdAt: 1,
|
|
341
|
+
updatedAt: 1,
|
|
342
|
+
unread: false,
|
|
343
|
+
provider: null,
|
|
344
|
+
planMode: false,
|
|
345
|
+
sessionToken: null,
|
|
346
|
+
lastTurnOutcome: null,
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
const router = createWsRouter({
|
|
350
|
+
store: {
|
|
351
|
+
state,
|
|
352
|
+
getProject: (projectId: string) => state.projectsById.get(projectId) ?? null,
|
|
353
|
+
getChat: (chatId: string) => state.chatsById.get(chatId) ?? null,
|
|
354
|
+
} as never,
|
|
355
|
+
diffStore: {
|
|
356
|
+
getProjectSnapshot: () => ({ status: "ready", branchName: "main", files: [], branchHistory: { entries: [] } }),
|
|
357
|
+
refreshSnapshot: async () => false,
|
|
358
|
+
listBranches: async () => ({ recent: [], local: [], remote: [], pullRequests: [], pullRequestsStatus: "unavailable" }),
|
|
359
|
+
previewMergeBranch: async () => ({ currentBranchName: "main", targetBranchName: "feature/test", targetDisplayName: "feature/test", status: "mergeable", commitCount: 2, hasConflicts: false, message: "2 commits from feature/test will merge into main." }),
|
|
360
|
+
mergeBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: true }),
|
|
361
|
+
syncBranch: async () => ({ ok: true, action: "fetch", snapshotChanged: false }),
|
|
362
|
+
checkoutBranch: async () => ({ ok: true, snapshotChanged: false }),
|
|
363
|
+
createBranch: async () => ({ ok: true, branchName: "main", snapshotChanged: false }),
|
|
364
|
+
generateCommitMessage: async () => ({ subject: "", body: "", usedFallback: true, failureMessage: null }),
|
|
365
|
+
commitFiles: async () => ({ ok: true, mode: "commit_only", pushed: false, snapshotChanged: false }),
|
|
366
|
+
discardFile: async () => ({ snapshotChanged: false }),
|
|
367
|
+
ignoreFile: async () => ({ snapshotChanged: false }),
|
|
368
|
+
readPatch: async () => ({ patch: "" }),
|
|
369
|
+
} as never,
|
|
370
|
+
agent: { getActiveStatuses: () => new Map(), getDrainingChatIds: () => new Set() } as never,
|
|
371
|
+
terminals: {
|
|
372
|
+
getSnapshot: () => null,
|
|
373
|
+
onEvent: () => () => {},
|
|
374
|
+
} as never,
|
|
375
|
+
keybindings: {
|
|
376
|
+
getSnapshot: () => DEFAULT_KEYBINDINGS_SNAPSHOT,
|
|
377
|
+
onChange: () => () => {},
|
|
378
|
+
} as never,
|
|
379
|
+
refreshDiscovery: async () => [],
|
|
380
|
+
getDiscoveredProjects: () => [],
|
|
381
|
+
machineDisplayName: "Local Machine",
|
|
382
|
+
updateManager: null,
|
|
383
|
+
})
|
|
384
|
+
const ws = new FakeWebSocket()
|
|
385
|
+
|
|
386
|
+
await router.handleMessage(
|
|
387
|
+
ws as never,
|
|
388
|
+
JSON.stringify({
|
|
389
|
+
v: 1,
|
|
390
|
+
type: "command",
|
|
391
|
+
id: "preview-merge-1",
|
|
392
|
+
command: {
|
|
393
|
+
type: "chat.previewMergeBranch",
|
|
394
|
+
chatId: "chat-1",
|
|
395
|
+
branch: { kind: "local", name: "feature/test" },
|
|
396
|
+
},
|
|
397
|
+
})
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
await router.handleMessage(
|
|
401
|
+
ws as never,
|
|
402
|
+
JSON.stringify({
|
|
403
|
+
v: 1,
|
|
404
|
+
type: "command",
|
|
405
|
+
id: "merge-1",
|
|
406
|
+
command: {
|
|
407
|
+
type: "chat.mergeBranch",
|
|
408
|
+
chatId: "chat-1",
|
|
409
|
+
branch: { kind: "local", name: "feature/test" },
|
|
410
|
+
},
|
|
411
|
+
})
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
expect(ws.sent[0]).toEqual({
|
|
415
|
+
v: PROTOCOL_VERSION,
|
|
416
|
+
type: "ack",
|
|
417
|
+
id: "preview-merge-1",
|
|
418
|
+
result: {
|
|
419
|
+
currentBranchName: "main",
|
|
420
|
+
targetBranchName: "feature/test",
|
|
421
|
+
targetDisplayName: "feature/test",
|
|
422
|
+
status: "mergeable",
|
|
423
|
+
commitCount: 2,
|
|
424
|
+
hasConflicts: false,
|
|
425
|
+
message: "2 commits from feature/test will merge into main.",
|
|
426
|
+
},
|
|
427
|
+
})
|
|
428
|
+
expect(ws.sent[1]).toEqual({
|
|
429
|
+
v: PROTOCOL_VERSION,
|
|
430
|
+
type: "ack",
|
|
431
|
+
id: "merge-1",
|
|
432
|
+
result: {
|
|
433
|
+
ok: true,
|
|
434
|
+
branchName: "main",
|
|
435
|
+
snapshotChanged: true,
|
|
436
|
+
},
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
|
|
179
440
|
test("loads older chat history pages", async () => {
|
|
180
441
|
const state = createEmptyState()
|
|
181
442
|
state.projectsById.set("project-1", {
|
|
@@ -506,6 +767,9 @@ describe("ws-router", () => {
|
|
|
506
767
|
openInFinder: ["cmd+shift+g"],
|
|
507
768
|
openInEditor: ["cmd+shift+p"],
|
|
508
769
|
addSplitTerminal: ["cmd+alt+j"],
|
|
770
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
771
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
772
|
+
openAddProject: ["cmd+alt+o"],
|
|
509
773
|
},
|
|
510
774
|
},
|
|
511
775
|
})
|
|
@@ -523,6 +787,9 @@ describe("ws-router", () => {
|
|
|
523
787
|
openInFinder: ["cmd+shift+g"],
|
|
524
788
|
openInEditor: ["cmd+shift+p"],
|
|
525
789
|
addSplitTerminal: ["cmd+alt+j"],
|
|
790
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
791
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
792
|
+
openAddProject: ["cmd+alt+o"],
|
|
526
793
|
},
|
|
527
794
|
warning: null,
|
|
528
795
|
filePathDisplay: "~/.kanna/keybindings.json",
|
|
@@ -675,14 +942,14 @@ describe("ws-router", () => {
|
|
|
675
942
|
lastTurnOutcome: null,
|
|
676
943
|
})
|
|
677
944
|
|
|
678
|
-
const discardCalls: Array<{
|
|
945
|
+
const discardCalls: Array<{ projectId: string; projectPath: string; path: string }> = []
|
|
679
946
|
const diffStore = {
|
|
680
|
-
|
|
947
|
+
getProjectSnapshot: () => ({ status: "ready" as const, files: [], defaultBranchName: "main", originRepoSlug: "acme/repo", aheadCount: 0, behindCount: 0, lastFetchedAt: undefined }),
|
|
681
948
|
refreshSnapshot: async () => false,
|
|
682
949
|
syncBranch: async () => ({ ok: true as const, action: "fetch" as const, snapshotChanged: false }),
|
|
683
950
|
generateCommitMessage: async () => ({ subject: "", body: "" }),
|
|
684
951
|
commitFiles: async () => ({ ok: true as const, mode: "commit_only" as const, pushed: false, snapshotChanged: false }),
|
|
685
|
-
discardFile: async (args: {
|
|
952
|
+
discardFile: async (args: { projectId: string; projectPath: string; path: string }) => {
|
|
686
953
|
discardCalls.push(args)
|
|
687
954
|
return { snapshotChanged: true }
|
|
688
955
|
},
|
|
@@ -739,7 +1006,7 @@ describe("ws-router", () => {
|
|
|
739
1006
|
)
|
|
740
1007
|
|
|
741
1008
|
expect(discardCalls).toEqual([{
|
|
742
|
-
|
|
1009
|
+
projectId: "project-1",
|
|
743
1010
|
projectPath: "/tmp/project",
|
|
744
1011
|
path: "app.txt",
|
|
745
1012
|
}])
|
|
@@ -774,7 +1041,7 @@ describe("ws-router", () => {
|
|
|
774
1041
|
lastTurnOutcome: null,
|
|
775
1042
|
})
|
|
776
1043
|
|
|
777
|
-
const ignoreCalls: Array<{
|
|
1044
|
+
const ignoreCalls: Array<{ projectId: string; projectPath: string; path: string }> = []
|
|
778
1045
|
const router = createWsRouter({
|
|
779
1046
|
store: {
|
|
780
1047
|
state,
|
|
@@ -782,13 +1049,13 @@ describe("ws-router", () => {
|
|
|
782
1049
|
getProject: (projectId: string) => state.projectsById.get(projectId) ?? null,
|
|
783
1050
|
} as never,
|
|
784
1051
|
diffStore: {
|
|
785
|
-
|
|
1052
|
+
getProjectSnapshot: () => ({ status: "ready" as const, files: [], defaultBranchName: "main", originRepoSlug: "acme/repo", aheadCount: 0, behindCount: 0, lastFetchedAt: undefined }),
|
|
786
1053
|
refreshSnapshot: async () => false,
|
|
787
1054
|
syncBranch: async () => ({ ok: true as const, action: "fetch" as const, snapshotChanged: false }),
|
|
788
1055
|
generateCommitMessage: async () => ({ subject: "", body: "" }),
|
|
789
1056
|
commitFiles: async () => ({ ok: true as const, mode: "commit_only" as const, pushed: false, snapshotChanged: false }),
|
|
790
1057
|
discardFile: async () => ({ snapshotChanged: false }),
|
|
791
|
-
ignoreFile: async (args: {
|
|
1058
|
+
ignoreFile: async (args: { projectId: string; projectPath: string; path: string }) => {
|
|
792
1059
|
ignoreCalls.push(args)
|
|
793
1060
|
return { snapshotChanged: false }
|
|
794
1061
|
},
|
|
@@ -824,7 +1091,7 @@ describe("ws-router", () => {
|
|
|
824
1091
|
)
|
|
825
1092
|
|
|
826
1093
|
expect(ignoreCalls).toEqual([{
|
|
827
|
-
|
|
1094
|
+
projectId: "project-1",
|
|
828
1095
|
projectPath: "/tmp/project",
|
|
829
1096
|
path: "scratch.log",
|
|
830
1097
|
}])
|