feat: Add CodexLens Manager Page with tabbed interface for managing CodexLens features

feat: Implement ConflictTab component to display conflict resolution decisions in session detail

feat: Create ImplPlanTab component to show implementation plan with modal viewer in session detail

feat: Develop ReviewTab component to display review findings by dimension in session detail

test: Add end-to-end tests for CodexLens Manager functionality including navigation, tab switching, and settings validation
This commit is contained in:
catlog22
2026-02-01 17:45:38 +08:00
parent 8dc115a894
commit d46406df4a
79 changed files with 11819 additions and 2455 deletions

View File

@@ -13,6 +13,7 @@ interface HookOptions {
sessionId?: string;
prompt?: string;
type?: 'session-start' | 'context';
path?: string;
}
interface HookData {
@@ -209,6 +210,59 @@ async function sessionContextAction(options: HookOptions): Promise<void> {
}
}
/**
* Parse CCW status.json and output formatted status
*/
async function parseStatusAction(options: HookOptions): Promise<void> {
const { path: filePath } = options;
if (!filePath) {
console.error(chalk.red('Error: --path is required'));
process.exit(1);
}
try {
// Check if this is a CCW status.json file
if (!filePath.includes('status.json') ||
!filePath.match(/\.(ccw|ccw-coordinator|ccw-debug)[/\\]/)) {
console.log(chalk.gray('(Not a CCW status file)'));
process.exit(0);
}
// Read and parse status.json
if (!existsSync(filePath)) {
console.log(chalk.gray('(Status file not found)'));
process.exit(0);
}
const statusContent = readFileSync(filePath, 'utf8');
const status = JSON.parse(statusContent);
// Extract key information
const sessionId = status.session_id || 'unknown';
const workflow = status.workflow || status.mode || 'unknown';
// Find current command (running or last completed)
let currentCommand = status.command_chain?.find((cmd: { status: string }) => cmd.status === 'running')?.command;
if (!currentCommand) {
const completed = status.command_chain?.filter((cmd: { status: string }) => cmd.status === 'completed');
currentCommand = completed?.[completed.length - 1]?.command || 'unknown';
}
// Find next command (first pending)
const nextCommand = status.command_chain?.find((cmd: { status: string }) => cmd.status === 'pending')?.command || '无';
// Format status message
const message = `📋 CCW Status [${sessionId}] (${workflow}): 当前处于 ${currentCommand},下一个命令 ${nextCommand}`;
console.log(message);
process.exit(0);
} catch (error) {
console.error(chalk.red(`Error: ${(error as Error).message}`));
process.exit(1);
}
}
/**
* Notify dashboard action - send notification to running ccw view server
*/
@@ -255,15 +309,20 @@ ${chalk.bold('USAGE')}
ccw hook <subcommand> [options]
${chalk.bold('SUBCOMMANDS')}
parse-status Parse CCW status.json and display current/next command
session-context Progressive session context loading (replaces curl/bash hook)
notify Send notification to ccw view dashboard
${chalk.bold('OPTIONS')}
--stdin Read input from stdin (for Claude Code hooks)
--path Path to status.json file (for parse-status)
--session-id Session ID (alternative to stdin)
--prompt Current prompt text (alternative to stdin)
${chalk.bold('EXAMPLES')}
${chalk.gray('# Parse CCW status file:')}
ccw hook parse-status --path .workflow/.ccw/ccw-123/status.json
${chalk.gray('# Use in Claude Code hook (settings.json):')}
ccw hook session-context --stdin
@@ -274,14 +333,14 @@ ${chalk.bold('EXAMPLES')}
ccw hook notify --stdin
${chalk.bold('HOOK CONFIGURATION')}
${chalk.gray('Add to .claude/settings.json:')}
${chalk.gray('Add to .claude/settings.json for status tracking:')}
{
"hooks": {
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "ccw hook session-context --stdin"
}]
"PostToolUse": [{
"trigger": "PostToolUse",
"matcher": "Write",
"command": "bash",
"args": ["-c", "INPUT=$(cat); FILE_PATH=$(echo \\"$INPUT\\" | jq -r \\".tool_input.file_path // empty\\"); [ -n \\"$FILE_PATH\\" ] && ccw hook parse-status --path \\"$FILE_PATH\\""]
}]
}
}
@@ -297,6 +356,9 @@ export async function hookCommand(
options: HookOptions
): Promise<void> {
switch (subcommand) {
case 'parse-status':
await parseStatusAction(options);
break;
case 'session-context':
case 'context':
await sessionContextAction(options);