mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-27 09:13:07 +08:00
fix(clean): correct project-tech field references and add sync vs clean disambiguation
- 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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 && (
|
||||
<Badge variant="outline" className="gap-1 text-xs">
|
||||
<FileJson className="h-3 w-3" />
|
||||
native
|
||||
{formatMessage({ id: 'history.badge.native' })}
|
||||
</Badge>
|
||||
)}
|
||||
<Badge variant={status.variant} className="gap-1 text-xs ml-auto">
|
||||
|
||||
@@ -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 (
|
||||
<details className="group/tool border border-border/50 rounded-md overflow-hidden">
|
||||
<summary className="flex items-center gap-2 px-3 py-2 text-xs cursor-pointer hover:bg-muted/50 select-none">
|
||||
@@ -155,7 +156,7 @@ function ToolCallItem({ toolCall, index }: ToolCallItemProps) {
|
||||
<div className="border-t border-border/50 divide-y divide-border/50">
|
||||
{toolCall.arguments && (
|
||||
<div className="p-3">
|
||||
<p className="text-xs font-medium text-muted-foreground mb-1">Input</p>
|
||||
<p className="text-xs font-medium text-muted-foreground mb-1">{formatMessage({ id: 'nativeSession.toolCall.input' })}</p>
|
||||
<pre className="p-2 bg-muted/30 rounded text-xs whitespace-pre-wrap overflow-x-auto font-mono leading-relaxed max-h-60 overflow-y-auto">
|
||||
{toolCall.arguments}
|
||||
</pre>
|
||||
@@ -163,7 +164,7 @@ function ToolCallItem({ toolCall, index }: ToolCallItemProps) {
|
||||
)}
|
||||
{toolCall.output && (
|
||||
<div className="p-3">
|
||||
<p className="text-xs font-medium text-muted-foreground mb-1">Output</p>
|
||||
<p className="text-xs font-medium text-muted-foreground mb-1">{formatMessage({ id: 'nativeSession.toolCall.output' })}</p>
|
||||
<pre className="p-2 bg-muted/30 rounded text-xs whitespace-pre-wrap overflow-x-auto font-mono leading-relaxed max-h-60 overflow-y-auto">
|
||||
{toolCall.output}
|
||||
</pre>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<string, string>;
|
||||
|
||||
33
ccw/frontend/src/locales/en/native-session.json
Normal file
33
ccw/frontend/src/locales/en/native-session.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "删除全部历史",
|
||||
|
||||
@@ -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<string, string>;
|
||||
|
||||
33
ccw/frontend/src/locales/zh/native-session.json
Normal file
33
ccw/frontend/src/locales/zh/native-session.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user