Skip to content

Commit 7aff6ed

Browse files
committed
Display Claude session rules with human-readable names
- If a rule is of type claude session, fetch session details to present a more readable label than a raw UUID. - Added sessionDetails endpoint to claude codegen and a helper for extracting human readable messages. - Updated ReduxTag for session details caching.
1 parent d872efa commit 7aff6ed

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

apps/desktop/src/components/Rule.svelte

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<script lang="ts">
22
import ReduxResult from '$components/ReduxResult.svelte';
3+
import { CLAUDE_CODE_SERVICE } from '$lib/codegen/claude';
4+
import { sessionMessage } from '$lib/codegen/types';
35
import {
46
semanticTypeToString,
57
treeStatusToShortString,
@@ -23,6 +25,7 @@
2325
2426
const stackService = inject(STACK_SERVICE);
2527
const rulesService = inject(RULES_SERVICE);
28+
const claudeCodeService = inject(CLAUDE_CODE_SERVICE);
2629
2730
const [deleteRule, deletingRule] = rulesService.deleteWorkspaceRule;
2831
@@ -86,9 +89,22 @@
8689
<Icon name="tag" opacity={0.6} />
8790
<span class="text-12 truncate">{semanticTypeToString(filter.subject.type)}</span>
8891
{:else if filter.type === 'claudeCodeSessionId'}
89-
<Icon name="tag" opacity={0.6} />
90-
<!-- TODO: Make an API call to get a rich name for the session, instead of rendering a UUID -->
91-
<span class="text-12 truncate">{filter.subject}</span>
92+
{@const sessionDetails = claudeCodeService.sessionDetails(projectId, filter.subject)}
93+
<ReduxResult {projectId} result={sessionDetails.current}>
94+
{#snippet loading()}
95+
<Icon name="tag" opacity={0.6} />
96+
<span class="text-12 truncate">{filter.subject}</span>
97+
{/snippet}
98+
{#snippet error()}
99+
<Icon name="tag" opacity={0.6} />
100+
<span class="text-12 truncate">{filter.subject}</span>
101+
{/snippet}
102+
{#snippet children(sessionDetails)}
103+
{@const title = sessionMessage(sessionDetails) ?? filter.subject}
104+
<Icon name="tag" opacity={0.6} />
105+
<span class="text-12 truncate" {title}>{title}</span>
106+
{/snippet}
107+
</ReduxResult>
92108
{/if}
93109
</div>
94110
{/snippet}

apps/desktop/src/lib/codegen/claude.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ClaudeMessage } from '$lib/codegen/types';
1+
import { type ClaudeMessage, type ClaudeSessionDetails } from '$lib/codegen/types';
22
import { hasTauriExtra } from '$lib/state/backendQuery';
33
import { invalidatesItem, providesItem, ReduxTag } from '$lib/state/tags';
44
import { InjectionToken } from '@gitbutler/shared/context';
@@ -20,6 +20,13 @@ export class ClaudeCodeService {
2020
get messages() {
2121
return this.api.endpoints.getMessages.useQuery;
2222
}
23+
24+
sessionDetails(projectId: string, sessionId: string) {
25+
return this.api.endpoints.getSessionDetails.useQuery({
26+
projectId,
27+
sessionId
28+
});
29+
}
2330
}
2431

2532
function injectEndpoints(api: ClientState['backendApi']) {
@@ -42,6 +49,16 @@ function injectEndpoints(api: ClientState['backendApi']) {
4249
invalidatesItem(ReduxTag.EditChangesSinceInitial, args.projectId + args.stackId)
4350
]
4451
}),
52+
getSessionDetails: build.query<
53+
ClaudeSessionDetails,
54+
{ projectId: string; sessionId: string }
55+
>({
56+
extraOptions: { command: 'claude_get_session_details' },
57+
query: (args) => args,
58+
providesTags: (_result, _error, args) => [
59+
...providesItem(ReduxTag.ClaudeSessionDetails, args.projectId + args.sessionId)
60+
]
61+
}),
4562
getMessages: build.query<ClaudeMessage[], { projectId: string; stackId: string }>({
4663
extraOptions: { command: 'claude_get_messages' },
4764
query: (args) => args,

apps/desktop/src/lib/codegen/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,16 @@ export type ClaudeMessageContent =
103103
type: 'userInput';
104104
subject: { message: string };
105105
};
106+
107+
/**
108+
* Details about a Claude session, extracted from the Claude transcript.
109+
* This data is derived just in time, i.e. not persisted by GitButler.
110+
*/
111+
export type ClaudeSessionDetails = {
112+
summary: string | null;
113+
lastPrompt: string | null;
114+
};
115+
116+
export function sessionMessage(sessionDetails: ClaudeSessionDetails): string | undefined {
117+
return sessionDetails.summary ?? sessionDetails.lastPrompt ?? undefined;
118+
}

apps/desktop/src/lib/state/tags.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// TODO: Refactor this enum into an object conataining invalidation rules.
22
export enum ReduxTag {
3-
InitalEditListing = 'InitialEditListing',
4-
EditChangesSinceInitial = 'EditChangesSinceInitial',
53
HeadMetadata = 'HeadMetadata',
64
Diff = 'Diff',
75
Stacks = 'Stacks',
@@ -25,7 +23,10 @@ export enum ReduxTag {
2523
SnapshotDiff = 'SnapshotDiff',
2624
WorkspaceRules = 'WorkspaceRules',
2725
Project = 'Project',
28-
ClaudeCodeTranscript = 'ClaudeCodeTranscript'
26+
ClaudeCodeTranscript = 'ClaudeCodeTranscript',
27+
ClaudeSessionDetails = 'ClaudeSessionDetails',
28+
InitalEditListing = 'InitialEditListing',
29+
EditChangesSinceInitial = 'EditChangesSinceInitial'
2930
}
3031

3132
type Tag<T extends string | number> = {

0 commit comments

Comments
 (0)