mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
feat: 增强会话管理功能,添加会话状态跟踪和进程披露,优化钩子管理界面
This commit is contained in:
@@ -138,23 +138,14 @@ const HOOK_TEMPLATES = {
|
||||
category: 'memory',
|
||||
timeout: 5000
|
||||
},
|
||||
// Session Context - Progressive Disclosure (session start - recent sessions)
|
||||
// Session Context - Fires once per session at startup
|
||||
// Uses state file to detect first prompt, only fires once
|
||||
'session-context': {
|
||||
event: 'UserPromptSubmit',
|
||||
matcher: '',
|
||||
command: 'bash',
|
||||
args: ['-c', 'curl -s -X POST -H "Content-Type: application/json" -d "{\\"type\\":\\"session-start\\",\\"sessionId\\":\\"$CLAUDE_SESSION_ID\\"}" http://localhost:3456/api/hook 2>/dev/null | jq -r ".content // empty"'],
|
||||
description: 'Load recent sessions at session start (time-sorted)',
|
||||
category: 'context',
|
||||
timeout: 5000
|
||||
},
|
||||
// Session Context - Continuous Disclosure (intent matching on every prompt)
|
||||
'session-context-continuous': {
|
||||
event: 'UserPromptSubmit',
|
||||
matcher: '',
|
||||
command: 'bash',
|
||||
args: ['-c', 'PROMPT=$(cat | jq -r ".prompt // empty"); curl -s -X POST -H "Content-Type: application/json" -d "{\\"type\\":\\"context\\",\\"sessionId\\":\\"$CLAUDE_SESSION_ID\\",\\"prompt\\":\\"$PROMPT\\"}" http://localhost:3456/api/hook 2>/dev/null | jq -r ".content // empty"'],
|
||||
description: 'Load intent-matched sessions on every prompt (similarity-based)',
|
||||
args: ['-c', 'STATE_FILE="/tmp/.ccw-session-$CLAUDE_SESSION_ID"; [ -f "$STATE_FILE" ] && exit 0; touch "$STATE_FILE"; curl -s -X POST -H "Content-Type: application/json" -d "{\\"sessionId\\":\\"$CLAUDE_SESSION_ID\\"}" http://localhost:3456/api/hook/session-context 2>/dev/null | jq -r ".content // empty"'],
|
||||
description: 'Load session context once at startup (cluster overview)',
|
||||
category: 'context',
|
||||
timeout: 5000
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const i18n = {
|
||||
// Search
|
||||
'search.placeholder': 'Search...',
|
||||
|
||||
// Session cards
|
||||
// Session cards - 3 states: planning, active, completed (archived location)
|
||||
'session.status.active': 'ACTIVE',
|
||||
'session.status.archived': 'ARCHIVED',
|
||||
'session.status.planning': 'PLANNING',
|
||||
@@ -714,10 +714,8 @@ const i18n = {
|
||||
'hook.template.gitAddDesc': 'Auto stage written files',
|
||||
|
||||
// Hook Quick Install Templates
|
||||
'hook.tpl.sessionContext': 'Session Context (Start)',
|
||||
'hook.tpl.sessionContextDesc': 'Load recent sessions at session start (time-sorted)',
|
||||
'hook.tpl.sessionContextContinuous': 'Session Context (Continuous)',
|
||||
'hook.tpl.sessionContextContinuousDesc': 'Load intent-matched sessions on every prompt (similarity-based)',
|
||||
'hook.tpl.sessionContext': 'Session Context',
|
||||
'hook.tpl.sessionContextDesc': 'Load cluster overview once at session start',
|
||||
'hook.tpl.codexlensSync': 'CodexLens Auto-Sync',
|
||||
'hook.tpl.codexlensSyncDesc': 'Auto-update code index when files are written or edited',
|
||||
'hook.tpl.ccwDashboardNotify': 'CCW Dashboard Notify',
|
||||
@@ -2021,10 +2019,8 @@ const i18n = {
|
||||
'hook.template.gitAddDesc': '自动暂存写入的文件',
|
||||
|
||||
// Hook Quick Install Templates
|
||||
'hook.tpl.sessionContext': 'Session 上下文(启动)',
|
||||
'hook.tpl.sessionContextDesc': '会话启动时加载最近会话(按时间排序)',
|
||||
'hook.tpl.sessionContextContinuous': 'Session 上下文(持续)',
|
||||
'hook.tpl.sessionContextContinuousDesc': '每次提示词时加载意图匹配会话(相似度排序)',
|
||||
'hook.tpl.sessionContext': 'Session 上下文',
|
||||
'hook.tpl.sessionContextDesc': '会话启动时加载集群概览(仅触发一次)',
|
||||
'hook.tpl.codexlensSync': 'CodexLens 自动同步',
|
||||
'hook.tpl.codexlensSyncDesc': '文件写入或编辑时自动更新代码索引',
|
||||
'hook.tpl.ccwDashboardNotify': 'CCW 控制面板通知',
|
||||
|
||||
@@ -102,24 +102,28 @@ function renderSessionCard(session) {
|
||||
const isActive = session._isActive !== false;
|
||||
const date = session.created_at;
|
||||
|
||||
// Detect planning status from session.status field
|
||||
const isPlanning = session.status === 'planning';
|
||||
// Get session status from metadata (default to 'planning' for new sessions)
|
||||
// 3 states: planning → active → completed (archived)
|
||||
const sessionStatus = session.status || 'planning';
|
||||
const isPlanning = sessionStatus === 'planning';
|
||||
|
||||
// Get session type badge
|
||||
const sessionType = session.type || 'workflow';
|
||||
const typeBadge = sessionType !== 'workflow' ? `<span class="session-type-badge ${sessionType}">${sessionType}</span>` : '';
|
||||
|
||||
// Determine status badge class and text
|
||||
// Priority: archived > planning > active
|
||||
// Priority: archived location > planning status > active status
|
||||
let statusClass, statusText;
|
||||
if (!isActive) {
|
||||
// Archived sessions always show as ARCHIVED regardless of status field
|
||||
// Archived sessions (completed) always show as ARCHIVED
|
||||
statusClass = 'archived';
|
||||
statusText = t('session.status.archived');
|
||||
} else if (isPlanning) {
|
||||
// Planning state - session created but not yet executed
|
||||
statusClass = 'planning';
|
||||
statusText = t('session.status.planning');
|
||||
} else {
|
||||
// Active state - session is being executed
|
||||
statusClass = 'active';
|
||||
statusText = t('session.status.active');
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ async function renderHookManager() {
|
||||
|
||||
<div class="hook-templates-grid grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
${renderQuickInstallCard('session-context', t('hook.tpl.sessionContext'), t('hook.tpl.sessionContextDesc'), 'UserPromptSubmit', '')}
|
||||
${renderQuickInstallCard('session-context-continuous', t('hook.tpl.sessionContextContinuous'), t('hook.tpl.sessionContextContinuousDesc'), 'UserPromptSubmit', '')}
|
||||
${renderQuickInstallCard('codexlens-update', t('hook.tpl.codexlensSync'), t('hook.tpl.codexlensSyncDesc'), 'PostToolUse', 'Write|Edit')}
|
||||
${renderQuickInstallCard('ccw-notify', t('hook.tpl.ccwDashboardNotify'), t('hook.tpl.ccwDashboardNotifyDesc'), 'PostToolUse', 'Write')}
|
||||
${renderQuickInstallCard('log-tool', t('hook.tpl.toolLogger'), t('hook.tpl.toolLoggerDesc'), 'PostToolUse', 'All')}
|
||||
@@ -506,11 +505,37 @@ async function uninstallHookTemplate(templateId) {
|
||||
const template = HOOK_TEMPLATES[templateId];
|
||||
if (!template) return;
|
||||
|
||||
// Extract unique identifier from template args for matching
|
||||
// Template args format: ['-c', 'actual command...']
|
||||
const templateArgs = template.args || [];
|
||||
const templateFullCmd = templateArgs.length > 0 ? templateArgs.join(' ') : '';
|
||||
|
||||
// Define unique patterns for each template type
|
||||
const uniquePatterns = {
|
||||
'session-context': 'api/hook/session-context',
|
||||
'codexlens-update': 'codexlens update',
|
||||
'ccw-notify': 'api/hook',
|
||||
'log-tool': 'tool-usage.log',
|
||||
'lint-check': 'eslint',
|
||||
'git-add': 'git add',
|
||||
'memory-file-read': 'memory track',
|
||||
'memory-file-write': 'memory track',
|
||||
'memory-prompt-track': 'memory track'
|
||||
};
|
||||
|
||||
const uniquePattern = uniquePatterns[templateId] || template.command;
|
||||
|
||||
// Helper to check if a hook matches the template
|
||||
const matchesTemplate = (h) => {
|
||||
const hookCmd = h.hooks?.[0]?.command || h.command || '';
|
||||
return hookCmd.includes(uniquePattern);
|
||||
};
|
||||
|
||||
// Find and remove from project hooks
|
||||
const projectHooks = hookConfig.project?.hooks?.[template.event];
|
||||
if (projectHooks) {
|
||||
const hookList = Array.isArray(projectHooks) ? projectHooks : [projectHooks];
|
||||
const index = hookList.findIndex(h => h.command === template.command);
|
||||
const index = hookList.findIndex(matchesTemplate);
|
||||
if (index !== -1) {
|
||||
await removeHook('project', template.event, index);
|
||||
return;
|
||||
@@ -521,12 +546,14 @@ async function uninstallHookTemplate(templateId) {
|
||||
const globalHooks = hookConfig.global?.hooks?.[template.event];
|
||||
if (globalHooks) {
|
||||
const hookList = Array.isArray(globalHooks) ? globalHooks : [globalHooks];
|
||||
const index = hookList.findIndex(h => h.command === template.command);
|
||||
const index = hookList.findIndex(matchesTemplate);
|
||||
if (index !== -1) {
|
||||
await removeHook('global', template.event, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
showRefreshToast('Hook not found', 'error');
|
||||
}
|
||||
|
||||
function attachHookEventListeners() {
|
||||
|
||||
Reference in New Issue
Block a user