From c92754505ac0227bd974cda3d242cbb53e963a3e Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 25 Feb 2026 22:34:15 +0800 Subject: [PATCH] fix(clean): correct project-tech field references and add sync vs clean disambiguation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Step 4.3: project.features → project.development_index, project.statistics → project._metadata - Add project-guidelines.json learnings cleanup on session deletion - Add sync vs clean disambiguation table explaining write vs reclaim --- .claude/commands/workflow/clean.md | 40 +++++++++++++++---- .../components/shared/ConversationCard.tsx | 31 ++++++++------ .../components/shared/NativeSessionPanel.tsx | 5 ++- ccw/frontend/src/locales/en/history.json | 8 ++++ ccw/frontend/src/locales/en/index.ts | 2 + .../src/locales/en/native-session.json | 33 +++++++++++++++ ccw/frontend/src/locales/zh/history.json | 8 ++++ ccw/frontend/src/locales/zh/index.ts | 2 + .../src/locales/zh/native-session.json | 33 +++++++++++++++ 9 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 ccw/frontend/src/locales/en/native-session.json create mode 100644 ccw/frontend/src/locales/zh/native-session.json diff --git a/.claude/commands/workflow/clean.md b/.claude/commands/workflow/clean.md index 04118d22..9ad3f25c 100644 --- a/.claude/commands/workflow/clean.md +++ b/.claude/commands/workflow/clean.md @@ -475,21 +475,43 @@ if (selectedCategories.includes('Sessions')) { } } -// Update project-tech.json if features referenced deleted sessions +// Update project-tech.json: remove development_index entries referencing deleted sessions const projectPath = '.workflow/project-tech.json' if (fileExists(projectPath)) { const project = JSON.parse(Read(projectPath)) - const deletedPaths = new Set(results.deleted) + const deletedSessionIds = results.deleted + .filter(p => p.match(/WFS-|lite-plan/)) + .map(p => p.split('/').pop()) - project.features = project.features.filter(f => - !deletedPaths.has(f.traceability?.archive_path) - ) - - project.statistics.total_features = project.features.length - project.statistics.last_updated = getUtc8ISOString() + if (project.development_index) { + for (const category of Object.keys(project.development_index)) { + project.development_index[category] = project.development_index[category].filter(entry => + !deletedSessionIds.includes(entry.session_id) + ) + } + } + project._metadata.last_updated = getUtc8ISOString() Write(projectPath, JSON.stringify(project, null, 2)) } + +// Update project-guidelines.json: remove learnings referencing deleted sessions +const guidelinesPath = '.workflow/project-guidelines.json' +if (fileExists(guidelinesPath)) { + const guidelines = JSON.parse(Read(guidelinesPath)) + const deletedSessionIds = results.deleted + .filter(p => p.match(/WFS-|lite-plan/)) + .map(p => p.split('/').pop()) + + if (guidelines.learnings) { + guidelines.learnings = guidelines.learnings.filter(l => + !deletedSessionIds.includes(l.session_id) + ) + } + + guidelines._metadata.updated_at = getUtc8ISOString() + Write(guidelinesPath, JSON.stringify(guidelines, null, 2)) +} ``` **Step 4.4: Report Results** @@ -541,8 +563,10 @@ Cleanup manifest archived to: ${sessionFolder}/cleanup-manifest.json | Manifest parse error | Regenerate from filesystem scan | | Empty discovery | Report "codebase is clean" | + ## Related Commands +- `/workflow:session:sync` - Sync session work to project-guidelines + project-tech (正向写入) - `/workflow:session:complete` - Properly archive active sessions - `memory-capture` skill - Save session memory before cleanup - `workflow-execute` skill - View current workflow state diff --git a/ccw/frontend/src/components/shared/ConversationCard.tsx b/ccw/frontend/src/components/shared/ConversationCard.tsx index 21e1d6e3..1b0413bd 100644 --- a/ccw/frontend/src/components/shared/ConversationCard.tsx +++ b/ccw/frontend/src/components/shared/ConversationCard.tsx @@ -75,20 +75,24 @@ function formatDuration(ms: number): string { } /** - * Get time ago string + * Hook to get localized time ago string */ -function getTimeAgo(dateString: string): string { - const date = new Date(dateString); - const now = new Date(); - const seconds = Math.floor((now.getTime() - date.getTime()) / 1000); +function useTimeAgo() { + const { formatMessage } = useIntl(); - if (seconds < 60) return 'just now'; - const minutes = Math.floor(seconds / 60); - if (minutes < 60) return `${minutes}m ago`; - const hours = Math.floor(minutes / 60); - if (hours < 24) return `${hours}h ago`; - const days = Math.floor(hours / 24); - return `${days}d ago`; + return React.useCallback((dateString: string): string => { + const date = new Date(dateString); + const now = new Date(); + const seconds = Math.floor((now.getTime() - date.getTime()) / 1000); + + if (seconds < 60) return formatMessage({ id: 'common.time.justNow' }); + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return formatMessage({ id: 'common.time.minutesAgo' }, { count: minutes }); + const hours = Math.floor(minutes / 60); + if (hours < 24) return formatMessage({ id: 'common.time.hoursAgo' }, { count: hours }); + const days = Math.floor(hours / 24); + return formatMessage({ id: 'common.time.daysAgo' }, { count: days }); + }, [formatMessage]); } /** @@ -104,6 +108,7 @@ export function ConversationCard({ actionsDisabled = false, }: ConversationCardProps) { const { formatMessage } = useIntl(); + const getTimeAgo = useTimeAgo(); const [copied, setCopied] = React.useState(false); const status = statusConfig[execution.status] || statusConfig.error; @@ -180,7 +185,7 @@ export function ConversationCard({ {execution.hasNativeSession && ( - native + {formatMessage({ id: 'history.badge.native' })} )} diff --git a/ccw/frontend/src/components/shared/NativeSessionPanel.tsx b/ccw/frontend/src/components/shared/NativeSessionPanel.tsx index fc0cef17..26856917 100644 --- a/ccw/frontend/src/components/shared/NativeSessionPanel.tsx +++ b/ccw/frontend/src/components/shared/NativeSessionPanel.tsx @@ -145,6 +145,7 @@ function TokenDisplay({ tokens, className }: TokenDisplayProps) { * ToolCallItem - Single tool call display with collapsible details */ function ToolCallItem({ toolCall, index }: ToolCallItemProps) { + const { formatMessage } = useIntl(); return (
@@ -155,7 +156,7 @@ function ToolCallItem({ toolCall, index }: ToolCallItemProps) {
{toolCall.arguments && (
-

Input

+

{formatMessage({ id: 'nativeSession.toolCall.input' })}

               {toolCall.arguments}
             
@@ -163,7 +164,7 @@ function ToolCallItem({ toolCall, index }: ToolCallItemProps) { )} {toolCall.output && (
-

Output

+

{formatMessage({ id: 'nativeSession.toolCall.output' })}

               {toolCall.output}
             
diff --git a/ccw/frontend/src/locales/en/history.json b/ccw/frontend/src/locales/en/history.json index fb39cdd4..0747d350 100644 --- a/ccw/frontend/src/locales/en/history.json +++ b/ccw/frontend/src/locales/en/history.json @@ -1,6 +1,10 @@ { "title": "CLI Execution History", "description": "View and manage your CLI execution history", + "tabs": { + "executions": "Executions", + "observability": "Session Audit" + }, "searchPlaceholder": "Search executions...", "filterAllTools": "All Tools", "deleteOptions": "Delete Options", @@ -9,10 +13,14 @@ "deleteAll": "Delete All History", "actions": { "view": "View Details", + "viewNative": "View Native Session", "delete": "Delete", "copyId": "Copy ID", "copied": "Copied!" }, + "badge": { + "native": "native" + }, "dialog": { "deleteTitle": "Confirm Delete", "deleteAllTitle": "Delete All History", diff --git a/ccw/frontend/src/locales/en/index.ts b/ccw/frontend/src/locales/en/index.ts index 8ba4313d..332beeb6 100644 --- a/ccw/frontend/src/locales/en/index.ts +++ b/ccw/frontend/src/locales/en/index.ts @@ -41,6 +41,7 @@ import cliViewer from './cli-viewer.json'; import team from './team.json'; import terminalDashboard from './terminal-dashboard.json'; import skillHub from './skill-hub.json'; +import nativeSession from './native-session.json'; /** * Flattens nested JSON object to dot-separated keys @@ -105,4 +106,5 @@ export default { ...flattenMessages(team, 'team'), ...flattenMessages(terminalDashboard, 'terminalDashboard'), ...flattenMessages(skillHub, 'skillHub'), + ...flattenMessages(nativeSession, 'nativeSession'), } as Record; diff --git a/ccw/frontend/src/locales/en/native-session.json b/ccw/frontend/src/locales/en/native-session.json new file mode 100644 index 00000000..4530fbba --- /dev/null +++ b/ccw/frontend/src/locales/en/native-session.json @@ -0,0 +1,33 @@ +{ + "title": "Native Session", + "tokens": { + "total": "Total tokens", + "input": "Input tokens", + "output": "Output tokens", + "cached": "Cached tokens" + }, + "turn": { + "latest": "Latest", + "thoughts": "Thoughts", + "toolCalls": "Tool Calls" + }, + "toolCall": { + "input": "Input", + "output": "Output" + }, + "meta": { + "startTime": "Start time", + "workingDir": "Working directory", + "projectHash": "Project hash", + "turns": "turns" + }, + "tokenSummary": "Total Tokens", + "loading": "Loading session...", + "error": "Failed to load session", + "empty": "No session data available", + "footer": { + "copied": "Copied!", + "copySessionId": "Copy Session ID", + "exportJson": "Export JSON" + } +} diff --git a/ccw/frontend/src/locales/zh/history.json b/ccw/frontend/src/locales/zh/history.json index 243d3290..e17d7a29 100644 --- a/ccw/frontend/src/locales/zh/history.json +++ b/ccw/frontend/src/locales/zh/history.json @@ -1,6 +1,10 @@ { "title": "CLI 执行历史", "description": "查看和管理 CLI 执行历史", + "tabs": { + "executions": "执行历史", + "observability": "会话审计" + }, "searchPlaceholder": "搜索执行记录...", "filterAllTools": "全部工具", "deleteOptions": "删除选项", @@ -9,10 +13,14 @@ "deleteAll": "删除全部历史", "actions": { "view": "查看详情", + "viewNative": "查看原生会话", "delete": "删除", "copyId": "复制 ID", "copied": "已复制!" }, + "badge": { + "native": "原生" + }, "dialog": { "deleteTitle": "确认删除", "deleteAllTitle": "删除全部历史", diff --git a/ccw/frontend/src/locales/zh/index.ts b/ccw/frontend/src/locales/zh/index.ts index 78fa23f5..14594fed 100644 --- a/ccw/frontend/src/locales/zh/index.ts +++ b/ccw/frontend/src/locales/zh/index.ts @@ -41,6 +41,7 @@ import cliViewer from './cli-viewer.json'; import team from './team.json'; import terminalDashboard from './terminal-dashboard.json'; import skillHub from './skill-hub.json'; +import nativeSession from './native-session.json'; /** * Flattens nested JSON object to dot-separated keys @@ -105,4 +106,5 @@ export default { ...flattenMessages(team, 'team'), ...flattenMessages(terminalDashboard, 'terminalDashboard'), ...flattenMessages(skillHub, 'skillHub'), + ...flattenMessages(nativeSession, 'nativeSession'), } as Record; diff --git a/ccw/frontend/src/locales/zh/native-session.json b/ccw/frontend/src/locales/zh/native-session.json new file mode 100644 index 00000000..a4e2af85 --- /dev/null +++ b/ccw/frontend/src/locales/zh/native-session.json @@ -0,0 +1,33 @@ +{ + "title": "原生会话", + "tokens": { + "total": "总 Token 数", + "input": "输入 Token", + "output": "输出 Token", + "cached": "缓存 Token" + }, + "turn": { + "latest": "最新", + "thoughts": "思考过程", + "toolCalls": "工具调用" + }, + "toolCall": { + "input": "输入", + "output": "输出" + }, + "meta": { + "startTime": "开始时间", + "workingDir": "工作目录", + "projectHash": "项目哈希", + "turns": "轮次" + }, + "tokenSummary": "总 Token", + "loading": "加载会话中...", + "error": "加载会话失败", + "empty": "无会话数据", + "footer": { + "copied": "已复制!", + "copySessionId": "复制会话 ID", + "exportJson": "导出 JSON" + } +}