feat: add issue discovery by prompt command with Gemini planning

- Introduced `/issue:discover-by-prompt` command for user-driven issue discovery.
- Implemented multi-agent exploration with iterative feedback loops.
- Added ACE semantic search for context gathering and cross-module comparison capabilities.
- Enhanced user experience with natural language input and adaptive exploration strategies.

feat: implement memory update queue tool for batching updates

- Created `memory-update-queue.js` for managing CLAUDE.md updates.
- Added functionality for queuing paths, deduplication, and auto-flushing based on thresholds and timeouts.
- Implemented methods for queue status retrieval, flushing, and timeout checks.
- Configured to store queue data persistently in `~/.claude/.memory-queue.json`.
This commit is contained in:
catlog22
2026-01-13 21:04:45 +08:00
parent 7d8b13f34f
commit d5f57d29ed
10 changed files with 1483 additions and 38 deletions

View File

@@ -1256,5 +1256,89 @@ RULES: Be concise. Focus on practical understanding. Include function signatures
return true;
}
// API: Memory Queue - Add path to queue
if (pathname === '/api/memory/queue/add' && req.method === 'POST') {
handlePostRequest(req, res, async (body) => {
const { path: modulePath, tool = 'gemini', strategy = 'single-layer' } = body;
if (!modulePath) {
return { error: 'path is required', status: 400 };
}
try {
const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
const result = await memoryQueueTool.execute({
action: 'add',
path: modulePath,
tool,
strategy
}) as { queueSize?: number; willFlush?: boolean; flushed?: boolean };
// Broadcast queue update event
broadcastToClients({
type: 'MEMORY_QUEUE_UPDATED',
payload: {
action: 'add',
path: modulePath,
queueSize: result.queueSize || 0,
willFlush: result.willFlush || false,
flushed: result.flushed || false,
timestamp: new Date().toISOString()
}
});
return { success: true, ...result };
} catch (error: unknown) {
return { error: (error as Error).message, status: 500 };
}
});
return true;
}
// API: Memory Queue - Get queue status
if (pathname === '/api/memory/queue/status' && req.method === 'GET') {
try {
const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
const result = await memoryQueueTool.execute({ action: 'status' }) as Record<string, unknown>;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, ...result }));
} catch (error: unknown) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));
}
return true;
}
// API: Memory Queue - Flush queue immediately
if (pathname === '/api/memory/queue/flush' && req.method === 'POST') {
handlePostRequest(req, res, async () => {
try {
const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
const result = await memoryQueueTool.execute({ action: 'flush' }) as {
processed?: number;
success?: boolean;
errors?: unknown[];
};
// Broadcast queue flushed event
broadcastToClients({
type: 'MEMORY_QUEUE_FLUSHED',
payload: {
processed: result.processed || 0,
success: result.success || false,
errors: result.errors?.length || 0,
timestamp: new Date().toISOString()
}
});
return { success: true, ...result };
} catch (error: unknown) {
return { error: (error as Error).message, status: 500 };
}
});
return true;
}
return false;
}

View File

@@ -9,6 +9,15 @@ import { getCliToolsStatus } from '../../tools/cli-executor.js';
import { checkVenvStatus, checkSemanticStatus } from '../../tools/codex-lens.js';
import type { RouteContext } from './types.js';
// Performance logging helper
const PERF_LOG_ENABLED = process.env.CCW_PERF_LOG === '1' || true; // Enable by default for debugging
function perfLog(label: string, startTime: number, extra?: Record<string, unknown>): void {
if (!PERF_LOG_ENABLED) return;
const duration = Date.now() - startTime;
const extraStr = extra ? ` | ${JSON.stringify(extra)}` : '';
console.log(`[PERF][Status] ${label}: ${duration}ms${extraStr}`);
}
/**
* Check CCW installation status
* Verifies that required workflow files are installed in user's home directory
@@ -62,16 +71,39 @@ export async function handleStatusRoutes(ctx: RouteContext): Promise<boolean> {
// API: Aggregated Status (all statuses in one call)
if (pathname === '/api/status/all') {
const totalStart = Date.now();
console.log('[PERF][Status] === /api/status/all START ===');
try {
// Check CCW installation status (sync, fast)
const ccwStart = Date.now();
const ccwInstallStatus = checkCcwInstallStatus();
perfLog('checkCcwInstallStatus', ccwStart);
// Execute all status checks in parallel with individual timing
const cliStart = Date.now();
const codexStart = Date.now();
const semanticStart = Date.now();
// Execute all status checks in parallel
const [cliStatus, codexLensStatus, semanticStatus] = await Promise.all([
getCliToolsStatus(),
checkVenvStatus(),
getCliToolsStatus().then(result => {
perfLog('getCliToolsStatus', cliStart, { toolCount: Object.keys(result).length });
return result;
}),
checkVenvStatus().then(result => {
perfLog('checkVenvStatus', codexStart, { ready: result.ready });
return result;
}),
// Always check semantic status (will return available: false if CodexLens not ready)
checkSemanticStatus().catch(() => ({ available: false, backend: null }))
checkSemanticStatus()
.then(result => {
perfLog('checkSemanticStatus', semanticStart, { available: result.available });
return result;
})
.catch(() => {
perfLog('checkSemanticStatus (error)', semanticStart);
return { available: false, backend: null };
})
]);
const response = {
@@ -82,10 +114,13 @@ export async function handleStatusRoutes(ctx: RouteContext): Promise<boolean> {
timestamp: new Date().toISOString()
};
perfLog('=== /api/status/all TOTAL ===', totalStart);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
return true;
} catch (error) {
perfLog('=== /api/status/all ERROR ===', totalStart);
console.error('[Status Routes] Error fetching aggregated status:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));

View File

@@ -42,6 +42,10 @@ import { randomBytes } from 'crypto';
// Import health check service
import { getHealthCheckService } from './services/health-check-service.js';
// Import status check functions for warmup
import { checkSemanticStatus, checkVenvStatus } from '../tools/codex-lens.js';
import { getCliToolsStatus } from '../tools/cli-executor.js';
import type { ServerConfig } from '../types/config.js';
import type { PostRequestHandler } from './routes/types.js';
@@ -290,6 +294,56 @@ function setCsrfCookie(res: http.ServerResponse, token: string, maxAgeSeconds: n
appendSetCookie(res, attributes.join('; '));
}
/**
* Warmup function to pre-populate caches on server startup
* This runs asynchronously and non-blocking after the server starts
*/
async function warmupCaches(initialPath: string): Promise<void> {
console.log('[WARMUP] Starting cache warmup...');
const startTime = Date.now();
// Run all warmup tasks in parallel for faster startup
const warmupTasks = [
// Warmup semantic status cache (Python process startup - can be slow first time)
(async () => {
const taskStart = Date.now();
try {
const semanticStatus = await checkSemanticStatus();
console.log(`[WARMUP] Semantic status: ${semanticStatus.available ? 'available' : 'not available'} (${Date.now() - taskStart}ms)`);
} catch (err) {
console.warn(`[WARMUP] Semantic status check failed: ${(err as Error).message}`);
}
})(),
// Warmup venv status cache
(async () => {
const taskStart = Date.now();
try {
const venvStatus = await checkVenvStatus();
console.log(`[WARMUP] Venv status: ${venvStatus.ready ? 'ready' : 'not ready'} (${Date.now() - taskStart}ms)`);
} catch (err) {
console.warn(`[WARMUP] Venv status check failed: ${(err as Error).message}`);
}
})(),
// Warmup CLI tools status cache
(async () => {
const taskStart = Date.now();
try {
const cliStatus = await getCliToolsStatus();
const availableCount = Object.values(cliStatus).filter(s => s.available).length;
const totalCount = Object.keys(cliStatus).length;
console.log(`[WARMUP] CLI tools status: ${availableCount}/${totalCount} available (${Date.now() - taskStart}ms)`);
} catch (err) {
console.warn(`[WARMUP] CLI tools status check failed: ${(err as Error).message}`);
}
})()
];
await Promise.allSettled(warmupTasks);
console.log(`[WARMUP] Cache warmup complete (${Date.now() - startTime}ms total)`);
}
/**
* Generate dashboard HTML with embedded CSS and JS
*/
@@ -650,6 +704,14 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
console.warn('[Server] Failed to start health check service:', err);
}
// Start cache warmup asynchronously (non-blocking)
// Uses setImmediate to not delay server startup response
setImmediate(() => {
warmupCaches(initialPath).catch((err) => {
console.warn('[WARMUP] Cache warmup failed:', err);
});
});
resolve(server);
});
server.on('error', reject);