From 588aaff3729dea5e9be189d513f1a9caf26d9a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=BE=E5=B1=B1=E8=B2=B4=E8=87=B3?= Date: Sun, 31 May 2026 22:18:56 +0900 Subject: [PATCH] feat(import): label imported sessions with a human summary Imported Claude Code sessions were labelled `claude-code import `, which reads as an opaque id in `basou session list` and the handoff. Label them with the date and activity counts instead (`claude-code : commands, files`). The Claude session id is kept structurally in `source.external_id`, so traceability and idempotent re-import are unaffected. Paths are excluded from the label because it is not path-sanitized downstream. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 3 +++ .../adapters/claude-code/transcript-importer.test.ts | 6 +++++- .../src/adapters/claude-code/transcript-importer.ts | 11 +++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0940165..a06ca095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ All notable changes to **basou** are recorded here. The project follows action are skipped. Imports reuse the existing `session import` pipeline, so path sanitization and id minting are unchanged. Re-running an import is idempotent: a transcript already imported is skipped rather than duplicated. + Imported sessions are labelled with a human summary + (`claude-code : commands, files`) instead of the raw session + id, so they read as content in `basou session list` and the handoff. - `claude-code-import` session `source.kind` (additive enum value) to distinguish transcript-derived sessions from live `claude-code-adapter` runs and Basou-format `import`s. diff --git a/packages/core/src/adapters/claude-code/transcript-importer.test.ts b/packages/core/src/adapters/claude-code/transcript-importer.test.ts index 8efc136b..53464eda 100644 --- a/packages/core/src/adapters/claude-code/transcript-importer.test.ts +++ b/packages/core/src/adapters/claude-code/transcript-importer.test.ts @@ -71,6 +71,8 @@ describe("claudeTranscriptToImportPayload", () => { expect(payload.session.related_files).toEqual([`${CWD}/a.ts`, `${CWD}/b.ts`]); // The transcript's own sessionId becomes the source external_id (dedup key). expect(payload.session.source.external_id).toBe("abc-123"); + // The label is a human-readable summary (date + counts), not an opaque id. + expect(payload.session.label).toBe("claude-code 2026-05-10: 1 command, 2 files"); const types = payload.events.map((e) => e.type); expect(types).toEqual([ @@ -177,7 +179,9 @@ describe("claudeTranscriptToImportPayload", () => { expect(payload).not.toBeNull(); if (payload === null) return; expect(payload.session.source.external_id).toBe("from-option"); - expect(payload.session.label).toContain("from-option"); + // The id lives in source.external_id; the label is a content summary, not the id. + expect(payload.session.label).not.toContain("from-option"); + expect(payload.session.label).toMatch(/^claude-code \d{4}-\d{2}-\d{2}: \d+ command/); }); it("returns null when no observable command / file action exists", () => { diff --git a/packages/core/src/adapters/claude-code/transcript-importer.ts b/packages/core/src/adapters/claude-code/transcript-importer.ts index 80c51480..0ac519a6 100644 --- a/packages/core/src/adapters/claude-code/transcript-importer.ts +++ b/packages/core/src/adapters/claude-code/transcript-importer.ts @@ -124,8 +124,15 @@ export function claudeTranscriptToImportPayload( ]; const externalId = options.externalId ?? claudeSessionId; - const label = - externalId !== undefined ? `claude-code import ${externalId}` : "claude-code import"; + // Human-readable label: when + how much, so the session reads as content in + // `basou session list` / handoff rather than an opaque id. The source id is + // kept structurally in `source.external_id` (not the label), and paths are + // deliberately excluded — the label is NOT path-sanitized downstream, so a + // raw file path here would leak an operator-private prefix. + const commandCount = derived.reduce((n, e) => (e.type === "command_executed" ? n + 1 : n), 0); + const fileCount = relatedFiles.size; + const date = minTs.slice(0, 10); + const label = `claude-code ${date}: ${commandCount} ${commandCount === 1 ? "command" : "commands"}, ${fileCount} ${fileCount === 1 ? "file" : "files"}`; const payload: SessionImportPayload = { schema_version: "0.1.0",