spiracha 2.0.0 → 2.1.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 -1
- package/apps/ui/README.md +20 -2
- package/apps/ui/package.json +2 -2
- package/apps/ui/src/components/antigravity-conversations-table.tsx +50 -11
- package/apps/ui/src/components/antigravity-workspaces-table.tsx +1 -1
- package/apps/ui/src/components/app-shell.tsx +1 -0
- package/apps/ui/src/components/breadcrumbs.tsx +21 -3
- package/apps/ui/src/components/claude-code-sessions-table.tsx +42 -6
- package/apps/ui/src/components/claude-code-workspaces-table.tsx +3 -1
- package/apps/ui/src/components/cursor-threads-table.tsx +10 -47
- package/apps/ui/src/components/data-table.tsx +7 -5
- package/apps/ui/src/components/export-dialog.tsx +2 -12
- package/apps/ui/src/components/grok-sessions-table.tsx +146 -0
- package/apps/ui/src/components/grok-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/kiro-sessions-table.tsx +40 -4
- package/apps/ui/src/components/opencode-sessions-table.tsx +40 -4
- package/apps/ui/src/components/page-header.tsx +15 -9
- package/apps/ui/src/components/qoder-sessions-table.tsx +23 -2
- package/apps/ui/src/components/qoder-workspaces-table.tsx +5 -1
- package/apps/ui/src/components/selection-actions-toolbar.tsx +80 -0
- package/apps/ui/src/components/threads-table.tsx +9 -46
- package/apps/ui/src/components/transcript-view.tsx +148 -52
- package/apps/ui/src/components/ui/select.tsx +1 -1
- package/apps/ui/src/lib/antigravity-conversation-state.ts +17 -4
- package/apps/ui/src/lib/antigravity-server.ts +152 -7
- package/apps/ui/src/lib/antigravity-transcript-events.ts +11 -3
- package/apps/ui/src/lib/claude-code-queries.ts +8 -0
- package/apps/ui/src/lib/claude-code-server.ts +175 -7
- package/apps/ui/src/lib/claude-code-transcript-events.ts +8 -1
- package/apps/ui/src/lib/codex-queries.ts +19 -3
- package/apps/ui/src/lib/codex-server.ts +135 -53
- package/apps/ui/src/lib/cursor-server.ts +48 -5
- package/apps/ui/src/lib/formatters.ts +3 -5
- package/apps/ui/src/lib/grok-queries.ts +22 -0
- package/apps/ui/src/lib/grok-server.ts +169 -0
- package/apps/ui/src/lib/grok-transcript-events.ts +149 -0
- package/apps/ui/src/lib/kiro-server.ts +85 -7
- package/apps/ui/src/lib/opencode-server.ts +85 -7
- package/apps/ui/src/lib/qoder-server.ts +67 -11
- package/apps/ui/src/lib/route-search.ts +114 -0
- package/apps/ui/src/lib/source-session-export-server.ts +86 -3
- package/apps/ui/src/lib/thread-transcript-load.ts +15 -0
- package/apps/ui/src/lib/workspace-delete-navigation.ts +12 -0
- package/apps/ui/src/routeTree.gen.ts +107 -0
- package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +76 -10
- package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +269 -31
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +4 -0
- package/apps/ui/src/routes/api.v1.conversations.delete.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.export.ts +12 -0
- package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +143 -17
- package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +165 -26
- package/apps/ui/src/routes/cursor-threads.$composerId.tsx +2 -2
- package/apps/ui/src/routes/cursor.$workspaceKey.tsx +11 -1
- package/apps/ui/src/routes/grok-sessions.$sessionId.tsx +388 -0
- package/apps/ui/src/routes/grok.$workspaceKey.tsx +301 -0
- package/apps/ui/src/routes/grok.index.tsx +48 -0
- package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +66 -15
- package/apps/ui/src/routes/kiro.$workspaceKey.tsx +199 -32
- package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +105 -19
- package/apps/ui/src/routes/opencode.$workspaceKey.tsx +236 -44
- package/apps/ui/src/routes/opencode.index.tsx +16 -3
- package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +7 -4
- package/apps/ui/src/routes/qoder.$workspaceKey.tsx +75 -28
- package/apps/ui/src/routes/threads.$threadId.tsx +548 -64
- package/package.json +19 -18
- package/src/client.ts +134 -1
- package/src/lib/antigravity-db.ts +208 -32
- package/src/lib/antigravity-exporter-types.ts +3 -0
- package/src/lib/antigravity-projects.ts +201 -0
- package/src/lib/claude-code-db.ts +498 -32
- package/src/lib/claude-code-exporter-types.ts +5 -0
- package/src/lib/claude-code-transcript-phase.ts +60 -1
- package/src/lib/claude-code-transcript.ts +8 -5
- package/src/lib/codex-browser-export.ts +23 -27
- package/src/lib/codex-thread-cache.ts +41 -13
- package/src/lib/codex-thread-parser.ts +86 -35
- package/src/lib/codex-transcript-filter.ts +31 -0
- package/src/lib/codex-transcript-renderer.ts +39 -19
- package/src/lib/concurrency.ts +41 -0
- package/src/lib/conversation-api.ts +399 -19
- package/src/lib/conversation-data/antigravity-adapter.ts +21 -2
- package/src/lib/conversation-data/claude-code-adapter.ts +37 -6
- package/src/lib/conversation-data/codex-adapter.ts +28 -4
- package/src/lib/conversation-data/cursor-adapter.ts +35 -1
- package/src/lib/conversation-data/grok-adapter.ts +210 -0
- package/src/lib/conversation-data/index.ts +76 -1
- package/src/lib/conversation-data/kiro-adapter.ts +41 -7
- package/src/lib/conversation-data/opencode-adapter.ts +24 -2
- package/src/lib/conversation-data/qoder-adapter.ts +44 -19
- package/src/lib/conversation-data/types.ts +43 -0
- package/src/lib/conversation-zip-export.ts +57 -0
- package/src/lib/cursor-db.ts +34 -5
- package/src/lib/grok-db.ts +1026 -0
- package/src/lib/grok-exporter-types.ts +110 -0
- package/src/lib/grok-transcript-phase.ts +52 -0
- package/src/lib/grok-transcript.ts +154 -0
- package/src/lib/kiro-db.ts +52 -1
- package/src/lib/model-label.ts +11 -3
- package/src/lib/opencode-db.ts +598 -55
- package/src/lib/opencode-transcript.ts +8 -5
- package/src/lib/shared.ts +12 -0
- package/src/lib/transcript-load-limiter.ts +82 -0
- package/src/lib/ui-cache.ts +43 -17
- package/src/lib/ui-export-archive.ts +60 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import type { ExportFormat, JsonValue } from './shared';
|
|
4
|
+
|
|
5
|
+
export const getDefaultGrokHome = (_env: NodeJS.ProcessEnv = process.env, homeDir = os.homedir()): string => {
|
|
6
|
+
return path.join(homeDir, '.grok');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_GROK_HOME = getDefaultGrokHome();
|
|
10
|
+
|
|
11
|
+
export const resolveGrokHome = (): string => {
|
|
12
|
+
const configured = process.env.SPIRACHA_GROK_HOME?.trim() || process.env.SPIRACHA_GROK_DIR?.trim();
|
|
13
|
+
return configured ? configured : DEFAULT_GROK_HOME;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const resolveGrokSessionsDir = (): string => {
|
|
17
|
+
const configured = process.env.SPIRACHA_GROK_SESSIONS_DIR?.trim();
|
|
18
|
+
return configured ? configured : path.join(resolveGrokHome(), 'sessions');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type GrokWorkspaceGroup = {
|
|
22
|
+
assistantMessageCount: number;
|
|
23
|
+
chatMessageCount: number;
|
|
24
|
+
directoryName: string;
|
|
25
|
+
key: string;
|
|
26
|
+
label: string;
|
|
27
|
+
lastActiveAtIso: string | null;
|
|
28
|
+
lastActiveAtMs: number | null;
|
|
29
|
+
messageCount: number;
|
|
30
|
+
reasoningCount: number;
|
|
31
|
+
sessionCount: number;
|
|
32
|
+
toolCallCount: number;
|
|
33
|
+
toolResultCount: number;
|
|
34
|
+
uri: string;
|
|
35
|
+
userMessageCount: number;
|
|
36
|
+
worktree: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type GrokSessionSummary = {
|
|
40
|
+
agentName: string | null;
|
|
41
|
+
assistantMessageCount: number;
|
|
42
|
+
chatHistoryPath: string;
|
|
43
|
+
chatMessageCount: number;
|
|
44
|
+
createdAtIso: string | null;
|
|
45
|
+
createdAtMs: number | null;
|
|
46
|
+
currentModelId: string | null;
|
|
47
|
+
cwd: string | null;
|
|
48
|
+
gitBranch: string | null;
|
|
49
|
+
gitRemotes: string[];
|
|
50
|
+
gitRootDir: string | null;
|
|
51
|
+
headCommit: string | null;
|
|
52
|
+
lastActiveAtIso: string | null;
|
|
53
|
+
lastActiveAtMs: number | null;
|
|
54
|
+
messageCount: number;
|
|
55
|
+
modelLabel: string | null;
|
|
56
|
+
reasoningCount: number;
|
|
57
|
+
renderablePartCount: number;
|
|
58
|
+
sandboxProfile: string | null;
|
|
59
|
+
sessionDir: string;
|
|
60
|
+
sessionId: string;
|
|
61
|
+
summaryPath: string;
|
|
62
|
+
title: string;
|
|
63
|
+
toolCallCount: number;
|
|
64
|
+
toolResultCount: number;
|
|
65
|
+
updatesPath: string | null;
|
|
66
|
+
userMessageCount: number;
|
|
67
|
+
workspaceKey: string;
|
|
68
|
+
workspaceLabel: string;
|
|
69
|
+
worktree: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type GrokPartType = 'reasoning' | 'text' | 'tool_call' | 'tool_result' | 'unknown';
|
|
73
|
+
|
|
74
|
+
export type GrokTranscriptPart = {
|
|
75
|
+
argumentsText?: string | null;
|
|
76
|
+
outputText?: string | null;
|
|
77
|
+
partId: string;
|
|
78
|
+
raw: Record<string, JsonValue>;
|
|
79
|
+
text?: string;
|
|
80
|
+
toolCallId?: string | null;
|
|
81
|
+
toolName?: string | null;
|
|
82
|
+
type: GrokPartType;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type GrokTranscriptEntry = {
|
|
86
|
+
createdAtMs: number | null;
|
|
87
|
+
entryId: string;
|
|
88
|
+
modelFingerprint?: string | null;
|
|
89
|
+
modelId?: string | null;
|
|
90
|
+
parts: GrokTranscriptPart[];
|
|
91
|
+
raw: Record<string, JsonValue>;
|
|
92
|
+
role: string;
|
|
93
|
+
timestamp: string | null;
|
|
94
|
+
type: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type GrokSessionTranscript = {
|
|
98
|
+
entries: GrokTranscriptEntry[];
|
|
99
|
+
rawEvents: Record<string, JsonValue>[];
|
|
100
|
+
rawPayloadsOmitted?: boolean;
|
|
101
|
+
renderablePartCount: number;
|
|
102
|
+
session: GrokSessionSummary;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type GrokExportOptions = {
|
|
106
|
+
includeCommentary: boolean;
|
|
107
|
+
includeMetadata: boolean;
|
|
108
|
+
includeTools: boolean;
|
|
109
|
+
outputFormat: ExportFormat;
|
|
110
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { GrokTranscriptEntry, GrokTranscriptPart } from './grok-exporter-types';
|
|
2
|
+
|
|
3
|
+
export type GrokAssistantMessagePhase = 'commentary' | 'final_answer' | 'unknown';
|
|
4
|
+
|
|
5
|
+
export const getFinalGrokAssistantTextPartIds = (entries: GrokTranscriptEntry[]): Set<string> => {
|
|
6
|
+
const finalPartIds = new Set<string>();
|
|
7
|
+
let latestAssistantTextPartId: string | null = null;
|
|
8
|
+
|
|
9
|
+
const flushAssistantRun = () => {
|
|
10
|
+
if (latestAssistantTextPartId) {
|
|
11
|
+
finalPartIds.add(latestAssistantTextPartId);
|
|
12
|
+
latestAssistantTextPartId = null;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
if (entry.role === 'user') {
|
|
18
|
+
flushAssistantRun();
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (entry.role !== 'assistant') {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const part of entry.parts) {
|
|
27
|
+
if (part.type === 'tool_call') {
|
|
28
|
+
latestAssistantTextPartId = null;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (part.type === 'text' && part.text?.trim()) {
|
|
33
|
+
latestAssistantTextPartId = part.partId;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
flushAssistantRun();
|
|
39
|
+
return finalPartIds;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const getGrokTextPartPhase = (
|
|
43
|
+
entry: GrokTranscriptEntry,
|
|
44
|
+
part: GrokTranscriptPart,
|
|
45
|
+
finalTextPartIds: Set<string>,
|
|
46
|
+
): GrokAssistantMessagePhase => {
|
|
47
|
+
if (entry.role !== 'assistant') {
|
|
48
|
+
return 'unknown';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return finalTextPartIds.has(part.partId) ? 'final_answer' : 'commentary';
|
|
52
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GrokExportOptions,
|
|
3
|
+
GrokSessionSummary,
|
|
4
|
+
GrokSessionTranscript,
|
|
5
|
+
GrokTranscriptEntry,
|
|
6
|
+
GrokTranscriptPart,
|
|
7
|
+
} from './grok-exporter-types';
|
|
8
|
+
import {
|
|
9
|
+
cleanExtractedText,
|
|
10
|
+
cleanInlineTitle,
|
|
11
|
+
formatInlineLiteral,
|
|
12
|
+
type MetadataEntry,
|
|
13
|
+
renderCodeBlock,
|
|
14
|
+
renderDocumentTitle,
|
|
15
|
+
renderMetadataBlock,
|
|
16
|
+
renderSection,
|
|
17
|
+
} from './shared';
|
|
18
|
+
|
|
19
|
+
const TOOL_OUTPUT_PREVIEW_LIMIT = 4000;
|
|
20
|
+
|
|
21
|
+
const getSessionTitle = (session: GrokSessionSummary): string => {
|
|
22
|
+
return cleanInlineTitle(session.title || session.sessionId);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const buildMetadataEntries = (session: GrokSessionSummary): MetadataEntry[] => [
|
|
26
|
+
{ key: 'exported_from', value: 'grok_local_session' },
|
|
27
|
+
{ key: 'session_id', value: session.sessionId },
|
|
28
|
+
{ key: 'title', value: session.title },
|
|
29
|
+
{ key: 'source_transcript_path', value: session.chatHistoryPath },
|
|
30
|
+
{ key: 'workspace_key', value: session.workspaceKey },
|
|
31
|
+
{ key: 'worktree', value: session.worktree },
|
|
32
|
+
{ key: 'cwd', value: session.cwd },
|
|
33
|
+
{ key: 'model', value: session.currentModelId },
|
|
34
|
+
{ key: 'model_label', value: session.modelLabel },
|
|
35
|
+
{ key: 'agent_name', value: session.agentName },
|
|
36
|
+
{ key: 'git_branch', value: session.gitBranch },
|
|
37
|
+
{ key: 'created_at_iso', value: session.createdAtIso },
|
|
38
|
+
{ key: 'last_active_at_iso', value: session.lastActiveAtIso },
|
|
39
|
+
{ key: 'message_count', value: session.messageCount },
|
|
40
|
+
{ key: 'tool_call_count', value: session.toolCallCount },
|
|
41
|
+
{ key: 'tool_result_count', value: session.toolResultCount },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const roleTitle = (role: string): string => {
|
|
45
|
+
if (role === 'assistant') {
|
|
46
|
+
return 'Assistant';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (role === 'user') {
|
|
50
|
+
return 'User';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (role === 'system') {
|
|
54
|
+
return 'System';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (role === 'tool') {
|
|
58
|
+
return 'Tool';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return role ? cleanInlineTitle(role) : 'Message';
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const truncateOutput = (text: string): string => {
|
|
65
|
+
if (text.length <= TOOL_OUTPUT_PREVIEW_LIMIT) {
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return `${text.slice(0, TOOL_OUTPUT_PREVIEW_LIMIT)}\n... (truncated)`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const renderTextPart = (entry: GrokTranscriptEntry, part: GrokTranscriptPart, options: GrokExportOptions): string => {
|
|
73
|
+
const text = cleanExtractedText(part.text ?? '').trim();
|
|
74
|
+
return text ? renderSection(roleTitle(entry.role), text, options.outputFormat) : '';
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const renderReasoningPart = (part: GrokTranscriptPart, options: GrokExportOptions): string => {
|
|
78
|
+
if (!options.includeCommentary) {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const text = cleanExtractedText(part.text ?? '').trim();
|
|
83
|
+
return text ? renderSection('Reasoning', text, options.outputFormat) : '';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const renderToolCallPart = (part: GrokTranscriptPart, options: GrokExportOptions): string => {
|
|
87
|
+
if (!options.includeTools) {
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const toolName = part.toolName ?? 'unknown';
|
|
92
|
+
const lines = [`Tool: ${formatInlineLiteral(toolName, options.outputFormat)}`];
|
|
93
|
+
if (part.toolCallId) {
|
|
94
|
+
lines.push(`Call ID: ${part.toolCallId}`);
|
|
95
|
+
}
|
|
96
|
+
if (part.argumentsText?.trim()) {
|
|
97
|
+
lines.push('', 'Input:', '', renderCodeBlock(part.argumentsText.trim(), options.outputFormat));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return renderSection('Tool Call', lines.join('\n'), options.outputFormat);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const renderToolResultPart = (part: GrokTranscriptPart, options: GrokExportOptions): string => {
|
|
104
|
+
if (!options.includeTools) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const outputText = part.outputText?.trim();
|
|
109
|
+
if (!outputText) {
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lines: string[] = [];
|
|
114
|
+
if (part.toolCallId) {
|
|
115
|
+
lines.push(`Call ID: ${part.toolCallId}`, '');
|
|
116
|
+
}
|
|
117
|
+
lines.push(renderCodeBlock(truncateOutput(outputText), options.outputFormat));
|
|
118
|
+
return renderSection('Tool Output', lines.join('\n'), options.outputFormat);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const renderPart = (entry: GrokTranscriptEntry, part: GrokTranscriptPart, options: GrokExportOptions): string => {
|
|
122
|
+
switch (part.type) {
|
|
123
|
+
case 'text':
|
|
124
|
+
return renderTextPart(entry, part, options);
|
|
125
|
+
case 'reasoning':
|
|
126
|
+
return renderReasoningPart(part, options);
|
|
127
|
+
case 'tool_call':
|
|
128
|
+
return renderToolCallPart(part, options);
|
|
129
|
+
case 'tool_result':
|
|
130
|
+
return renderToolResultPart(part, options);
|
|
131
|
+
case 'unknown':
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const renderGrokTranscript = (transcript: GrokSessionTranscript, options: GrokExportOptions): string | null => {
|
|
137
|
+
const sections = transcript.entries.flatMap((entry) =>
|
|
138
|
+
entry.parts.map((part) => renderPart(entry, part, options)).filter(Boolean),
|
|
139
|
+
);
|
|
140
|
+
if (sections.length === 0) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const parts = [
|
|
145
|
+
renderDocumentTitle(getSessionTitle(transcript.session), options.outputFormat),
|
|
146
|
+
'',
|
|
147
|
+
options.includeMetadata
|
|
148
|
+
? renderMetadataBlock(buildMetadataEntries(transcript.session), options.outputFormat)
|
|
149
|
+
: '',
|
|
150
|
+
...sections,
|
|
151
|
+
].filter(Boolean);
|
|
152
|
+
|
|
153
|
+
return `${parts.join('\n').trimEnd()}\n`;
|
|
154
|
+
};
|
package/src/lib/kiro-db.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { readdir, rm, stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { mapWithConcurrency } from './concurrency';
|
|
5
5
|
import {
|
|
@@ -45,6 +45,11 @@ type KiroExecutionFile = {
|
|
|
45
45
|
raw: Record<string, JsonValue>;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
export type DeleteKiroSessionResult = {
|
|
49
|
+
deletedFiles: string[];
|
|
50
|
+
deletedSessionIds: string[];
|
|
51
|
+
};
|
|
52
|
+
|
|
48
53
|
type ReadSessionFileOptions = {
|
|
49
54
|
includeExecutions?: boolean;
|
|
50
55
|
sessionsDir: string;
|
|
@@ -932,6 +937,23 @@ const locateSessionFile = async (sessionsDir: string, sessionId: string): Promis
|
|
|
932
937
|
return files.find((file) => path.basename(file.filePath, '.json') === sessionId) ?? null;
|
|
933
938
|
};
|
|
934
939
|
|
|
940
|
+
const removeKiroSessionIndexEntry = async (workspaceDir: string, sessionId: string): Promise<void> => {
|
|
941
|
+
const indexPath = path.join(workspaceDir, 'sessions.json');
|
|
942
|
+
const value = (await Bun.file(indexPath)
|
|
943
|
+
.json()
|
|
944
|
+
.catch(() => null)) as JsonValue | null;
|
|
945
|
+
if (!Array.isArray(value)) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const next = value.filter((item) => asString(asObject(item)?.sessionId ?? null) !== sessionId);
|
|
950
|
+
if (next.length === value.length) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
await Bun.write(indexPath, JSON.stringify(next, null, 2));
|
|
955
|
+
};
|
|
956
|
+
|
|
935
957
|
export const readKiroSessionTranscript = async (
|
|
936
958
|
sessionsDir: string,
|
|
937
959
|
sessionId: string,
|
|
@@ -947,3 +969,32 @@ export const readKiroSessionTranscript = async (
|
|
|
947
969
|
|
|
948
970
|
return readSessionFile(file, { includeExecutions: true, sessionsDir });
|
|
949
971
|
};
|
|
972
|
+
|
|
973
|
+
export const deleteKiroSession = async (sessionsDir: string, sessionId: string): Promise<DeleteKiroSessionResult> => {
|
|
974
|
+
if (!(await pathExists(sessionsDir))) {
|
|
975
|
+
return { deletedFiles: [], deletedSessionIds: [] };
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const file = await locateSessionFile(sessionsDir, sessionId);
|
|
979
|
+
if (!file) {
|
|
980
|
+
return { deletedFiles: [], deletedSessionIds: [] };
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
const transcript = await readSessionFile(file, { includeExecutions: false, sessionsDir });
|
|
984
|
+
const executionFiles = transcript
|
|
985
|
+
? await listExecutionFilesForSession(
|
|
986
|
+
getKiroDataDirFromSessionsDir(sessionsDir),
|
|
987
|
+
sessionId,
|
|
988
|
+
transcript.session.worktree,
|
|
989
|
+
)
|
|
990
|
+
: [];
|
|
991
|
+
const deletedFiles = [file.filePath, ...executionFiles.map((execution) => execution.filePath)];
|
|
992
|
+
|
|
993
|
+
await Promise.all(deletedFiles.map((filePath) => rm(filePath, { force: true })));
|
|
994
|
+
await removeKiroSessionIndexEntry(path.dirname(file.filePath), sessionId);
|
|
995
|
+
|
|
996
|
+
return {
|
|
997
|
+
deletedFiles,
|
|
998
|
+
deletedSessionIds: [sessionId],
|
|
999
|
+
};
|
|
1000
|
+
};
|
package/src/lib/model-label.ts
CHANGED
|
@@ -3,9 +3,17 @@ export const formatModelLabel = (value: string | null | undefined): string => {
|
|
|
3
3
|
return 'Assistant';
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
6
|
+
const parts = value.split(/[-_\s]+/u).filter(Boolean);
|
|
7
|
+
if (parts[0]?.toLowerCase() === 'claude') {
|
|
8
|
+
const majorIndex = parts.findIndex(
|
|
9
|
+
(part, index) => /^\d{1,2}$/u.test(part) && /^\d{1,2}$/u.test(parts[index + 1] ?? ''),
|
|
10
|
+
);
|
|
11
|
+
if (majorIndex >= 0) {
|
|
12
|
+
parts.splice(majorIndex, 2, `${parts[majorIndex]}.${parts[majorIndex + 1]}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return parts
|
|
9
17
|
.map((part) => {
|
|
10
18
|
const lower = part.toLowerCase();
|
|
11
19
|
if (lower === 'gpt') {
|