Skip to content

feat(agent): replay imported Claude Code CLI transcripts#2876

Draft
adboio wants to merge 1 commit into
mainfrom
posthog-code/import-cc-1-agent-replay
Draft

feat(agent): replay imported Claude Code CLI transcripts#2876
adboio wants to merge 1 commit into
mainfrom
posthog-code/import-cc-1-agent-replay

Conversation

@adboio

@adboio adboio commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Problem

Changes

How did you test this?

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Teach the Claude adapter to replay an imported CLI transcript into a
fresh session: emit user prompts and top-level assistant text/thinking
(no client history to dedupe against), recover typed slash-command
invocations, and mark replayed user chunks so the load path can promote
them into user bubbles. Also extracts encodeCwdToProjectKey from the
JSONL path helper and adds the shared IMPORTED_USER_PROMPT_META_KEY
marker plus the importedClaudeSession task-creation field.

Part 1/3 of splitting #2873 (import Claude Code sessions).

Generated-By: PostHog Code
Task-Id: 6c93b6e8-27b6-45c8-8135-73a09076ea93

adboio commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

@github-actions

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 442269e.

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(agent): replay imported Claude Code..." | Re-trigger Greptile

Comment on lines +321 to +341
it("surfaces a typed slash command, not its raw markers", async () => {
const { context, updates } = createImportReplayContext();
await handleUserAssistantMessage(
userMessage(
"<command-message>review</command-message>\n<command-name>/review</command-name>\n<command-args>#2198 - findings first</command-args>",
),
context,
);
expect(userChunkTexts(updates)).toEqual(["/review #2198 - findings first"]);
});

it("surfaces a no-arg slash command as just the command name", async () => {
const { context, updates } = createImportReplayContext();
await handleUserAssistantMessage(
userMessage(
"<command-message>compact</command-message>\n<command-name>/compact</command-name>\n<command-args></command-args>",
),
context,
);
expect(userChunkTexts(updates)).toEqual(["/compact"]);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Slash-command tests could be parameterised

The two tests below exercise the same code path (extractSlashCommandInvocation) with different inputs and expected outputs, which is exactly the pattern the repo's simplicity rules ask to consolidate with it.each. Calling createImportReplayContext() and handleUserAssistantMessage identically in both bodies is duplication that a parameterised table would remove — e.g. it.each([[rawInput, expectedOutput], ...])("renders slash command %s as %s", ...). The same applies to "emits plain-text user prompts" and "marks imported user prompts", which both call handleUserAssistantMessage(userMessage("my earlier prompt"), context) and could be a single test asserting both the emitted text and the _meta flag.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +1257 to +1258
contentToProcess =
extractSlashCommandInvocation(content) ?? stripMarkerTags(content);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 stripMarkerTags can return an empty string, producing a hollow user_message_chunk

If a user message consists entirely of marker tags (e.g. a bare <command-args>...</command-args> with no surrounding prose), extractSlashCommandInvocation returns null and stripMarkerTags returns "". That empty string is then forwarded to toAcpNotifications, which may emit a user_message_chunk notification with empty content; the notification is then stamped with IMPORTED_USER_PROMPT_META_KEY and recorded in notificationHistory. The analogous path in shouldSkipUserAssistantMessage already guards against this via stripLocalCommandMetadata, which returns null when nothing renderable remains. Applying the same null guard here (?? null with an early return) would keep the two paths consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant