@sumicom/quicksave 0.6.0 → 0.6.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 (71) hide show
  1. package/dist/ai/asyncQueue.d.ts +7 -6
  2. package/dist/ai/asyncQueue.d.ts.map +1 -1
  3. package/dist/ai/asyncQueue.js +31 -21
  4. package/dist/ai/asyncQueue.js.map +1 -1
  5. package/dist/ai/asyncQueue.test.js +18 -18
  6. package/dist/ai/asyncQueue.test.js.map +1 -1
  7. package/dist/ai/cardBuilder.d.ts +11 -2
  8. package/dist/ai/cardBuilder.d.ts.map +1 -1
  9. package/dist/ai/cardBuilder.js +113 -21
  10. package/dist/ai/cardBuilder.js.map +1 -1
  11. package/dist/ai/claudeCliProvider.d.ts +19 -0
  12. package/dist/ai/claudeCliProvider.d.ts.map +1 -0
  13. package/dist/ai/claudeCliProvider.js +466 -0
  14. package/dist/ai/claudeCliProvider.js.map +1 -0
  15. package/dist/ai/claudeSdkProvider.d.ts +18 -0
  16. package/dist/ai/claudeSdkProvider.d.ts.map +1 -0
  17. package/dist/ai/claudeSdkProvider.js +418 -0
  18. package/dist/ai/claudeSdkProvider.js.map +1 -0
  19. package/dist/ai/commitSummary.d.ts +4 -0
  20. package/dist/ai/commitSummary.d.ts.map +1 -1
  21. package/dist/ai/commitSummary.js +80 -40
  22. package/dist/ai/commitSummary.js.map +1 -1
  23. package/dist/ai/provider.d.ts +54 -0
  24. package/dist/ai/provider.d.ts.map +1 -0
  25. package/dist/ai/provider.js +2 -0
  26. package/dist/ai/provider.js.map +1 -0
  27. package/dist/ai/sandboxMcp.d.ts +12 -0
  28. package/dist/ai/sandboxMcp.d.ts.map +1 -0
  29. package/dist/ai/sandboxMcp.js +12 -0
  30. package/dist/ai/sandboxMcp.js.map +1 -0
  31. package/dist/ai/sandboxMcpStdio.d.ts +16 -0
  32. package/dist/ai/sandboxMcpStdio.d.ts.map +1 -0
  33. package/dist/ai/sandboxMcpStdio.js +127 -0
  34. package/dist/ai/sandboxMcpStdio.js.map +1 -0
  35. package/dist/ai/sessionManager.d.ts +100 -0
  36. package/dist/ai/sessionManager.d.ts.map +1 -0
  37. package/dist/ai/sessionManager.js +595 -0
  38. package/dist/ai/sessionManager.js.map +1 -0
  39. package/dist/ai/sessionRegistry.d.ts +28 -0
  40. package/dist/ai/sessionRegistry.d.ts.map +1 -0
  41. package/dist/ai/sessionRegistry.js +148 -0
  42. package/dist/ai/sessionRegistry.js.map +1 -0
  43. package/dist/git/operations.d.ts +6 -1
  44. package/dist/git/operations.d.ts.map +1 -1
  45. package/dist/git/operations.js +31 -2
  46. package/dist/git/operations.js.map +1 -1
  47. package/dist/handlers/messageHandler.d.ts +8 -3
  48. package/dist/handlers/messageHandler.d.ts.map +1 -1
  49. package/dist/handlers/messageHandler.js +96 -12
  50. package/dist/handlers/messageHandler.js.map +1 -1
  51. package/dist/handlers/messageHandler.test.js +1 -1
  52. package/dist/index.js +3 -40
  53. package/dist/index.js.map +1 -1
  54. package/dist/service/ensureDaemon.d.ts.map +1 -1
  55. package/dist/service/ensureDaemon.js +15 -2
  56. package/dist/service/ensureDaemon.js.map +1 -1
  57. package/dist/service/ipc.test.js +6 -6
  58. package/dist/service/ipcClient.js +1 -1
  59. package/dist/service/run.d.ts.map +1 -1
  60. package/dist/service/run.js +22 -15
  61. package/dist/service/run.js.map +1 -1
  62. package/dist/service/singleton.d.ts +4 -0
  63. package/dist/service/singleton.d.ts.map +1 -1
  64. package/dist/service/singleton.js +28 -2
  65. package/dist/service/singleton.js.map +1 -1
  66. package/dist/service/singleton.test.js +1 -1
  67. package/package.json +5 -3
  68. package/dist/ai/claudeCodeService.d.ts +0 -122
  69. package/dist/ai/claudeCodeService.d.ts.map +0 -1
  70. package/dist/ai/claudeCodeService.js +0 -869
  71. package/dist/ai/claudeCodeService.js.map +0 -1
@@ -0,0 +1,418 @@
1
+ import { join, dirname } from 'path';
2
+ import { existsSync } from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+ import { query as sdkQuery } from '@anthropic-ai/claude-agent-sdk';
5
+ import { SANDBOX_MCP_NAME } from './sandboxMcp.js';
6
+ import { AsyncQueue } from './asyncQueue.js';
7
+ const __ownDir = dirname(fileURLToPath(import.meta.url));
8
+ /** Extract readable text from tool_result content (string or array of blocks). */
9
+ function extractToolResultText(content) {
10
+ if (typeof content === 'string')
11
+ return content;
12
+ if (Array.isArray(content)) {
13
+ return content
14
+ .filter((b) => b.type === 'text')
15
+ .map((b) => b.text || '')
16
+ .join('\n');
17
+ }
18
+ return JSON.stringify(content);
19
+ }
20
+ // ============================================================================
21
+ // SdkProviderSession — wraps SDK Query for ProviderSession interface
22
+ // ============================================================================
23
+ class SdkProviderSession {
24
+ queryHandle;
25
+ inputQueue;
26
+ /** StreamIds queued by hot resume — consumeStream pops after each result. */
27
+ pendingStreamIds = [];
28
+ constructor(queryHandle, inputQueue) {
29
+ this.queryHandle = queryHandle;
30
+ this.inputQueue = inputQueue;
31
+ }
32
+ sendUserMessage(prompt) {
33
+ if (!this.queryHandle)
34
+ return;
35
+ const userMsg = {
36
+ type: 'user',
37
+ message: { role: 'user', content: prompt },
38
+ parent_tool_use_id: null,
39
+ };
40
+ this.inputQueue.push(userMsg);
41
+ }
42
+ interrupt() {
43
+ if (!this.queryHandle)
44
+ return;
45
+ this.queryHandle.interrupt().catch((err) => {
46
+ console.error('[sdk] interrupt failed:', err);
47
+ });
48
+ }
49
+ kill() {
50
+ if (this.queryHandle) {
51
+ this.queryHandle.close();
52
+ this.queryHandle = null;
53
+ }
54
+ this.inputQueue.end();
55
+ }
56
+ get alive() {
57
+ return this.queryHandle !== null;
58
+ }
59
+ /** Called when the stream ends naturally (query generator completes). */
60
+ markClosed() {
61
+ this.queryHandle = null;
62
+ }
63
+ }
64
+ // ============================================================================
65
+ // ClaudeSdkProvider — implements CodingAgentProvider via SDK query() API
66
+ // ============================================================================
67
+ export class ClaudeSdkProvider {
68
+ async startSession(opts, cardBuilder, callbacks) {
69
+ const inputQueue = new AsyncQueue();
70
+ // Push first user message
71
+ const firstMsg = {
72
+ type: 'user',
73
+ message: { role: 'user', content: opts.prompt },
74
+ parent_tool_use_id: null,
75
+ };
76
+ inputQueue.push(firstMsg);
77
+ const sessionIdRef = { current: '' };
78
+ const options = this.buildOptions(opts, callbacks, sessionIdRef);
79
+ const queryHandle = sdkQuery({ prompt: inputQueue, options });
80
+ // Wait for init message to get sessionId
81
+ const sessionId = await this.waitForInit(queryHandle, callbacks);
82
+ sessionIdRef.current = sessionId;
83
+ const sdkSession = new SdkProviderSession(queryHandle, inputQueue);
84
+ // Update cardBuilder with real sessionId
85
+ cardBuilder.updateSessionId(sessionId);
86
+ // Fire and forget the stream consumer
87
+ this.consumeStream(sessionId, opts.streamId, queryHandle, sdkSession, cardBuilder, callbacks, opts.prompt);
88
+ return { sessionId, session: sdkSession };
89
+ }
90
+ async resumeSession(opts, cardBuilder, callbacks) {
91
+ const inputQueue = new AsyncQueue();
92
+ // Push resume prompt
93
+ const resumeMsg = {
94
+ type: 'user',
95
+ message: { role: 'user', content: opts.prompt },
96
+ parent_tool_use_id: null,
97
+ };
98
+ inputQueue.push(resumeMsg);
99
+ const sessionIdRef = { current: opts.sessionId };
100
+ const options = this.buildOptions(opts, callbacks, sessionIdRef, opts.sessionId);
101
+ const queryHandle = sdkQuery({ prompt: inputQueue, options });
102
+ // Wait for init message
103
+ const sessionId = await this.waitForInit(queryHandle, callbacks);
104
+ sessionIdRef.current = sessionId;
105
+ const sdkSession = new SdkProviderSession(queryHandle, inputQueue);
106
+ // Update cardBuilder with confirmed sessionId
107
+ cardBuilder.updateSessionId(sessionId);
108
+ // Fire and forget
109
+ this.consumeStream(sessionId, opts.streamId, queryHandle, sdkSession, cardBuilder, callbacks, opts.prompt);
110
+ return { sessionId, session: sdkSession };
111
+ }
112
+ // ── Private: Build SDK Options ──
113
+ buildOptions(opts, callbacks, sessionIdRef, resumeSessionId) {
114
+ const permissionMode = this.mapPermissionMode(opts.permissionLevel);
115
+ const canUseTool = async (toolName, toolInput, permOpts) => {
116
+ const decision = await callbacks.handlePermissionRequest(sessionIdRef.current, {
117
+ toolName,
118
+ toolInput,
119
+ toolUseId: permOpts.toolUseID,
120
+ });
121
+ if (decision.action === 'deny') {
122
+ return { behavior: 'deny', message: decision.response || 'User denied permission' };
123
+ }
124
+ return {
125
+ behavior: 'allow',
126
+ updatedInput: decision.updatedInput,
127
+ };
128
+ };
129
+ // Build MCP server config for sandbox — same as CLI provider
130
+ const tsPath = join(__ownDir, 'sandboxMcpStdio.ts');
131
+ const jsPath = join(__ownDir, 'sandboxMcpStdio.js');
132
+ const hasTsPath = existsSync(tsPath);
133
+ const options = {
134
+ cwd: opts.cwd,
135
+ permissionMode,
136
+ canUseTool,
137
+ includePartialMessages: true,
138
+ settingSources: ['project'],
139
+ mcpServers: {
140
+ [SANDBOX_MCP_NAME]: {
141
+ type: 'stdio',
142
+ command: hasTsPath ? 'npx' : 'node',
143
+ args: hasTsPath
144
+ ? ['tsx', tsPath, '--cwd', opts.cwd]
145
+ : [jsPath, '--cwd', opts.cwd],
146
+ },
147
+ },
148
+ systemPrompt: opts.systemPrompt
149
+ ? { type: 'preset', preset: 'claude_code', append: opts.systemPrompt }
150
+ : { type: 'preset', preset: 'claude_code' },
151
+ };
152
+ if ('model' in opts && opts.model) {
153
+ options.model = opts.model;
154
+ }
155
+ if (permissionMode === 'bypassPermissions') {
156
+ options.allowDangerouslySkipPermissions = true;
157
+ }
158
+ if (resumeSessionId) {
159
+ options.resume = resumeSessionId;
160
+ }
161
+ return options;
162
+ }
163
+ mapPermissionMode(level) {
164
+ switch (level) {
165
+ case 'bypassPermissions': return 'bypassPermissions';
166
+ case 'acceptEdits': return 'acceptEdits';
167
+ case 'plan': return 'plan';
168
+ default: return 'default';
169
+ }
170
+ }
171
+ // ── Private: Wait for Init ──
172
+ async waitForInit(queryHandle, callbacks) {
173
+ const timeout = setTimeout(() => {
174
+ throw new Error('Timeout waiting for SDK init');
175
+ }, 30_000);
176
+ try {
177
+ for await (const msg of queryHandle) {
178
+ if (msg.type === 'system' && msg.subtype === 'init' && msg.session_id) {
179
+ const initMsg = msg;
180
+ if (initMsg.model) {
181
+ callbacks.onModelDetected(initMsg.model);
182
+ }
183
+ clearTimeout(timeout);
184
+ return initMsg.session_id;
185
+ }
186
+ // Buffer non-init messages — they will be consumed in consumeStream
187
+ // Since we can't "unread" from the async generator, we break after init
188
+ // and let consumeStream handle remaining messages.
189
+ // Actually, for a generator we can't push back. We need to handle
190
+ // the first non-init messages here. But the SDK should emit init first.
191
+ // If we get a non-init message, skip it (unlikely before init).
192
+ }
193
+ }
194
+ catch (err) {
195
+ clearTimeout(timeout);
196
+ throw err;
197
+ }
198
+ clearTimeout(timeout);
199
+ throw new Error('SDK query ended without init message');
200
+ }
201
+ // ── Private: Stream Consumer ──
202
+ async consumeStream(sessionId, initialStreamId, queryHandle, sdkSession, cb, callbacks, prompt) {
203
+ let streamId = initialStreamId;
204
+ let textBuffer = '';
205
+ let bufferTimer = null;
206
+ let resultEmitted = false;
207
+ const emitCard = (event) => { callbacks.emitCardEvent(event); };
208
+ cb.startNewTurn(streamId);
209
+ // Add user prompt to cardBuilder (for getCards on reconnect) but don't emit
210
+ if (prompt) {
211
+ cb.userMessage(prompt);
212
+ }
213
+ const flushText = () => {
214
+ if (textBuffer) {
215
+ emitCard(cb.assistantText(textBuffer));
216
+ textBuffer = '';
217
+ }
218
+ if (bufferTimer) {
219
+ clearTimeout(bufferTimer);
220
+ bufferTimer = null;
221
+ }
222
+ };
223
+ const bufferText = (text) => {
224
+ textBuffer += text;
225
+ if (!bufferTimer) {
226
+ bufferTimer = setTimeout(flushText, 150);
227
+ }
228
+ if (textBuffer.length > 2048) {
229
+ flushText();
230
+ }
231
+ };
232
+ try {
233
+ for await (const msg of queryHandle) {
234
+ const emittedResult = await this.routeMessage(sessionId, streamId, msg, sdkSession, cb, callbacks, emitCard, flushText, bufferText);
235
+ if (emittedResult) {
236
+ resultEmitted = true;
237
+ // Check for hot resume — start new turn with next pending streamId
238
+ const nextStreamId = sdkSession.pendingStreamIds.shift();
239
+ if (nextStreamId) {
240
+ streamId = nextStreamId;
241
+ cb.startNewTurn(streamId);
242
+ resultEmitted = false; // Reset for next turn
243
+ }
244
+ }
245
+ }
246
+ }
247
+ catch (error) {
248
+ flushText();
249
+ console.error(`[sdk] stream error session=${sessionId.slice(0, 8)}:`, error);
250
+ const msg = error instanceof Error ? error.message : 'Unknown error';
251
+ callbacks.emitStreamEnd({ streamId, sessionId, success: false, error: msg });
252
+ resultEmitted = true;
253
+ }
254
+ finally {
255
+ if (bufferTimer)
256
+ clearTimeout(bufferTimer);
257
+ sdkSession.markClosed();
258
+ if (!resultEmitted) {
259
+ callbacks.emitStreamEnd({ streamId, sessionId, success: false, error: 'SDK query ended unexpectedly' });
260
+ }
261
+ }
262
+ }
263
+ // ── Private: Route a single SDK message ──
264
+ // Returns true if a result/stream-end was emitted.
265
+ async routeMessage(sessionId, streamId, msg, _sdkSession, cb, callbacks, emitCard, flushText, bufferText) {
266
+ // ── System events ──
267
+ if (msg.type === 'system') {
268
+ const subtype = msg.subtype;
269
+ if (subtype === 'init') {
270
+ // Already handled in waitForInit — skip
271
+ return false;
272
+ }
273
+ if (subtype === 'task_started') {
274
+ flushText();
275
+ const m = msg;
276
+ emitCard(cb.subagentStart(m.description ?? '', m.task_id, m.tool_use_id));
277
+ return false;
278
+ }
279
+ if (subtype === 'task_progress') {
280
+ const m = msg;
281
+ const cardEvt = cb.subagentProgress(m.task_id, m.tool_use_id, m.usage?.tool_uses, m.last_tool_name);
282
+ if (cardEvt)
283
+ emitCard(cardEvt);
284
+ return false;
285
+ }
286
+ if (subtype === 'task_notification') {
287
+ const m = msg;
288
+ const cardEvt = cb.subagentEnd(m.task_id, m.tool_use_id, m.status, m.summary);
289
+ if (cardEvt)
290
+ emitCard(cardEvt);
291
+ return false;
292
+ }
293
+ // Other system subtypes (status, session_state_changed, compact_boundary, etc.) — skip
294
+ return false;
295
+ }
296
+ // ── Streaming partial events ──
297
+ if (msg.type === 'stream_event') {
298
+ // Filter subagent stream events by parent_tool_use_id
299
+ if (msg.parent_tool_use_id)
300
+ return false;
301
+ const event = msg.event;
302
+ if (event?.type === 'content_block_delta') {
303
+ const delta = event.delta;
304
+ if (delta?.type === 'text_delta' && delta.text) {
305
+ bufferText(delta.text);
306
+ }
307
+ }
308
+ return false;
309
+ }
310
+ // ── Rate limit events ──
311
+ if (msg.type === 'rate_limit_event')
312
+ return false;
313
+ // ── Complete assistant messages ──
314
+ if (msg.type === 'assistant') {
315
+ // Filter subagent messages by parent_tool_use_id
316
+ if (msg.parent_tool_use_id)
317
+ return false;
318
+ flushText();
319
+ const blocks = msg.message?.content ?? [];
320
+ for (const block of blocks) {
321
+ if (block.type === 'thinking' && block.thinking) {
322
+ emitCard(cb.thinkingBlock(block.thinking));
323
+ }
324
+ else if (block.type === 'redacted_thinking') {
325
+ emitCard(cb.thinkingBlock('[Redacted thinking]'));
326
+ }
327
+ else if (block.type === 'text' && block.text) {
328
+ const finalizeEvt = cb.finalizeAssistantText();
329
+ if (finalizeEvt) {
330
+ emitCard(finalizeEvt);
331
+ }
332
+ else {
333
+ emitCard(cb.assistantText(block.text));
334
+ }
335
+ }
336
+ else if (block.type === 'tool_use') {
337
+ if (block.name !== 'Agent') {
338
+ emitCard(cb.toolUse(block.name, block.input ?? {}, block.id));
339
+ }
340
+ }
341
+ else if (block.type === 'server_tool_use' || block.type === 'mcp_tool_use') {
342
+ emitCard(cb.toolUse(block.name ?? block.type, block.input ?? {}, block.id ?? ''));
343
+ }
344
+ }
345
+ return false;
346
+ }
347
+ // ── User messages (tool results) ──
348
+ if (msg.type === 'user') {
349
+ // Filter subagent messages by parent_tool_use_id
350
+ if (msg.parent_tool_use_id)
351
+ return false;
352
+ const content = msg.message?.content;
353
+ // Skip user text echoes — we already have them from the inputQueue
354
+ if (typeof content === 'string')
355
+ return false;
356
+ if (Array.isArray(content)) {
357
+ for (const block of content) {
358
+ if (block.type === 'tool_result') {
359
+ const resultContent = extractToolResultText(block.content);
360
+ const cardEvt = cb.toolResult(block.tool_use_id, resultContent, !!block.is_error);
361
+ if (cardEvt)
362
+ emitCard(cardEvt);
363
+ }
364
+ if (block.type === 'web_search_tool_result' || block.type === 'web_fetch_tool_result' ||
365
+ block.type === 'mcp_tool_result' || block.type === 'code_execution_tool_result' ||
366
+ block.type === 'tool_search_tool_result') {
367
+ const resultContent = extractToolResultText(block.content ?? block.text ?? '');
368
+ const parentId = block.tool_use_id;
369
+ if (parentId) {
370
+ const cardEvt = cb.toolResult(parentId, resultContent, !!block.is_error);
371
+ if (cardEvt)
372
+ emitCard(cardEvt);
373
+ }
374
+ }
375
+ }
376
+ }
377
+ return false;
378
+ }
379
+ // ── Result ──
380
+ if (msg.type === 'result') {
381
+ if (msg.session_id !== sessionId)
382
+ return false; // subagent result
383
+ flushText();
384
+ const terminalReason = msg.terminal_reason;
385
+ const interrupted = terminalReason === 'aborted_tools' || terminalReason === 'aborted_streaming';
386
+ const totalCost = msg.total_cost_usd;
387
+ console.log(`[sdk] result session=${sessionId.slice(0, 8)} subtype=${msg.subtype} cost=$${totalCost?.toFixed(4) ?? '?'}`);
388
+ if (interrupted) {
389
+ emitCard(cb.systemMessage('User interrupted'));
390
+ }
391
+ const finalizeEvent = cb.finalizeAssistantText();
392
+ if (finalizeEvent)
393
+ emitCard(finalizeEvent);
394
+ const usage = msg.usage;
395
+ const errors = msg.errors;
396
+ const streamEnd = {
397
+ streamId,
398
+ sessionId,
399
+ success: msg.subtype === 'success' && !interrupted,
400
+ error: (msg.subtype !== 'success' && !interrupted)
401
+ ? (errors?.join('; ') || `Session ended: ${msg.subtype}`)
402
+ : undefined,
403
+ interrupted,
404
+ totalCostUsd: totalCost,
405
+ tokenUsage: usage
406
+ ? { input: usage.input_tokens, output: usage.output_tokens }
407
+ : undefined,
408
+ };
409
+ callbacks.emitStreamEnd(streamEnd);
410
+ // Clear accumulated cards — JSONL now has the full history for this turn
411
+ cb.clearCards();
412
+ await cb.snapshotCutoff();
413
+ return true;
414
+ }
415
+ return false;
416
+ }
417
+ }
418
+ //# sourceMappingURL=claudeSdkProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeSdkProvider.js","sourceRoot":"","sources":["../../src/ai/claudeSdkProvider.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,10 @@ export interface GenerateSummaryOptions {
3
3
  diffs: FileDiff[];
4
4
  context?: string;
5
5
  model?: ClaudeModel;
6
+ recentCommits?: string[];
7
+ branchName?: string;
8
+ conventions?: string;
9
+ attribution?: boolean;
6
10
  }
7
11
  export interface GenerateSummaryResult {
8
12
  summary: string;
@@ -1 +1 @@
1
- {"version":3,"file":"commitSummary.d.ts","sourceRoot":"","sources":["../../src/ai/commitSummary.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEnF,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAeD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,eAAe,CAAqD;gBAEhE,MAAM,EAAE,MAAM;IAIpB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAoCxE,iBAAiB;IAyB/B,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,aAAa;CAyBtB"}
1
+ {"version":3,"file":"commitSummary.d.ts","sourceRoot":"","sources":["../../src/ai/commitSummary.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEnF,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAkCD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,eAAe,CAAqD;gBAEhE,MAAM,EAAE,MAAM;IAIpB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAyCxE,iBAAiB;IAmC/B,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,WAAW;IAwCnB,OAAO,CAAC,aAAa;CAmCtB"}
@@ -1,6 +1,24 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
2
  import { createHash } from 'crypto';
3
3
  const DEFAULT_MODEL = 'claude-haiku-4-5';
4
+ const COMMIT_MESSAGE_TOOL = {
5
+ name: 'commit_message',
6
+ description: 'Generate a structured git commit message',
7
+ input_schema: {
8
+ type: 'object',
9
+ properties: {
10
+ summary: {
11
+ type: 'string',
12
+ description: 'The commit summary line (under 72 characters, conventional commit format)',
13
+ },
14
+ description: {
15
+ type: 'string',
16
+ description: 'Optional extended description for complex changes',
17
+ },
18
+ },
19
+ required: ['summary'],
20
+ },
21
+ };
4
22
  // Max characters per file diff for AI generation (roughly 1KB)
5
23
  const MAX_DIFF_CHARS_PER_FILE = 1000;
6
24
  // Max total characters for all diffs combined
@@ -15,12 +33,12 @@ export class CommitSummaryService {
15
33
  this.client = new Anthropic({ apiKey });
16
34
  }
17
35
  async generateSummary(options) {
18
- const { diffs, context, model = DEFAULT_MODEL } = options;
36
+ const { diffs, context, model = DEFAULT_MODEL, recentCommits, branchName, conventions, attribution = true } = options;
19
37
  const diffText = this.formatDiffsForPrompt(diffs);
20
38
  if (!diffText.trim()) {
21
39
  return { summary: 'Update files' };
22
40
  }
23
- // Generate cache key from diff content and model
41
+ // Generate cache key from diff content, model, and context
24
42
  const cacheKey = this.getCacheKey(diffText, context, model);
25
43
  // Check cache first
26
44
  const cached = this.getFromCache(cacheKey);
@@ -34,7 +52,12 @@ export class CommitSummaryService {
34
52
  return { ...result, cached: true };
35
53
  }
36
54
  // Create the request promise and store it
37
- const requestPromise = this.executeGeneration(diffText, context, model, cacheKey);
55
+ const requestPromise = this.executeGeneration(diffText, context, model, cacheKey, {
56
+ recentCommits,
57
+ branchName,
58
+ conventions,
59
+ attribution,
60
+ });
38
61
  this.pendingRequests.set(cacheKey, requestPromise);
39
62
  try {
40
63
  return await requestPromise;
@@ -43,14 +66,22 @@ export class CommitSummaryService {
43
66
  this.pendingRequests.delete(cacheKey);
44
67
  }
45
68
  }
46
- async executeGeneration(diffText, context, model, cacheKey) {
47
- const prompt = this.buildPrompt(diffText, context);
69
+ async executeGeneration(diffText, context, model, cacheKey, extra) {
70
+ const prompt = this.buildPrompt(diffText, context, extra);
48
71
  const response = await this.client.messages.create({
49
72
  model,
50
73
  max_tokens: 500,
51
74
  messages: [{ role: 'user', content: prompt }],
75
+ tools: [COMMIT_MESSAGE_TOOL],
76
+ tool_choice: { type: 'tool', name: 'commit_message' },
52
77
  });
53
78
  const result = this.parseResponse(response);
79
+ if (extra.attribution !== false) {
80
+ const aiTrailer = 'Commit-message-by: Quicksave AI <save@quicksave.dev>';
81
+ result.description = result.description
82
+ ? `${result.description}\n\n${aiTrailer}`
83
+ : aiTrailer;
84
+ }
54
85
  // Store in cache
55
86
  this.cache.set(cacheKey, {
56
87
  result,
@@ -98,48 +129,57 @@ export class CommitSummaryService {
98
129
  }
99
130
  return formattedDiffs.join('\n\n---\n\n');
100
131
  }
101
- buildPrompt(diffText, context) {
102
- return `You are a helpful assistant that generates concise, descriptive git commit messages.
103
-
104
- Analyze the following git diff and generate a commit message following these guidelines:
105
- - Use conventional commit format when appropriate (feat:, fix:, docs:, refactor:, etc.)
106
- - Keep the summary line under 72 characters
107
- - Focus on WHAT changed and WHY, not HOW
108
- - Be specific but concise
109
-
110
- ${context ? `Additional context from the user: ${context}\n\n` : ''}
111
- Git diff:
112
- \`\`\`
113
- ${diffText}
114
- \`\`\`
115
-
116
- Respond in this exact JSON format:
117
- {
118
- "summary": "the commit summary line",
119
- "description": "optional extended description if the changes are complex"
120
- }`;
132
+ buildPrompt(diffText, context, extra) {
133
+ const sections = [
134
+ 'Generate a concise, descriptive git commit message for the following changes.',
135
+ '',
136
+ 'Guidelines:',
137
+ '- Use conventional commit format (feat:, fix:, docs:, refactor:, chore:, test:, style:, perf:, ci:, build:)',
138
+ '- Keep the summary line under 72 characters',
139
+ '- Focus on WHAT changed and WHY, not HOW',
140
+ '- Be specific but concise',
141
+ ];
142
+ if (extra?.conventions) {
143
+ sections.push('', 'Project commit conventions:', extra.conventions);
144
+ }
145
+ if (extra?.recentCommits?.length) {
146
+ sections.push('', 'Recent commits (match this style):', ...extra.recentCommits.map((msg) => `- ${msg}`));
147
+ }
148
+ if (extra?.branchName) {
149
+ sections.push('', `Branch: ${extra.branchName}`);
150
+ }
151
+ if (context) {
152
+ sections.push('', `User context: ${context}`);
153
+ }
154
+ sections.push('', 'Git diff:', '```', diffText, '```');
155
+ return sections.join('\n');
121
156
  }
122
157
  parseResponse(response) {
123
- const content = response.content[0];
124
- if (content.type !== 'text') {
125
- throw new Error('Unexpected response format');
126
- }
127
158
  const tokenUsage = {
128
159
  inputTokens: response.usage.input_tokens,
129
160
  outputTokens: response.usage.output_tokens,
130
161
  };
131
- // Parse JSON from response
132
- const jsonMatch = content.text.match(/\{[\s\S]*\}/);
133
- if (!jsonMatch) {
134
- // Fallback: use the raw text as summary
135
- return { summary: content.text.trim().slice(0, 72), tokenUsage };
162
+ // Extract from tool_use block (structured output)
163
+ const toolUse = response.content.find((block) => block.type === 'tool_use');
164
+ if (toolUse) {
165
+ const input = toolUse.input;
166
+ return {
167
+ summary: input.summary || 'Update code',
168
+ description: input.description,
169
+ tokenUsage,
170
+ };
136
171
  }
137
- const parsed = JSON.parse(jsonMatch[0]);
138
- return {
139
- summary: parsed.summary || 'Update code',
140
- description: parsed.description,
141
- tokenUsage,
142
- };
172
+ // Fallback: parse from text if tool_use not present
173
+ const textBlock = response.content.find((block) => block.type === 'text');
174
+ if (textBlock) {
175
+ const jsonMatch = textBlock.text.match(/\{[\s\S]*\}/);
176
+ if (jsonMatch) {
177
+ const parsed = JSON.parse(jsonMatch[0]);
178
+ return { summary: parsed.summary || 'Update code', description: parsed.description, tokenUsage };
179
+ }
180
+ return { summary: textBlock.text.trim().slice(0, 72), tokenUsage };
181
+ }
182
+ return { summary: 'Update code', tokenUsage };
143
183
  }
144
184
  }
145
185
  //# sourceMappingURL=commitSummary.js.map