feat: add templates for epics, product brief, and requirements PRD

- Created a new directory structure for epics and stories with templates for individual epics and an index file.
- Added a product brief template for generating product brief documents in Phase 2.
- Introduced a requirements PRD template for generating a Product Requirements Document as a directory of individual requirement files in Phase 3.

feat: implement V2PipelineTab component for Memory V2 management

- Developed the V2PipelineTab component to manage extraction and consolidation processes.
- Included ExtractionCard and ConsolidationCard components to handle respective functionalities.
- Added JobsList component to display job statuses and allow filtering by job kind.

feat: create hooks for Memory V2 pipeline

- Implemented custom hooks for managing extraction and consolidation statuses, as well as job listings.
- Added mutation hooks to trigger extraction and consolidation processes with automatic query invalidation on success.
This commit is contained in:
catlog22
2026-02-27 13:27:27 +08:00
parent 99a3561f71
commit dd72e95e4d
57 changed files with 11018 additions and 1915 deletions

View File

@@ -376,5 +376,5 @@ export function run(argv: string[]): void {
program.parse(argv);
}
// Invoke CLI when run directly
run(process.argv);
// Note: run() is called by bin/ccw.js entry point
// Do not call run() here to avoid duplicate execution

View File

@@ -224,6 +224,7 @@ export async function handleSpecRoutes(ctx: RouteContext): Promise<boolean> {
const resolvedPath = resolvePath(projectPath);
const mode = url.searchParams.get('mode') || 'required'; // required | all | keywords
const preview = url.searchParams.get('preview') === 'true';
const category = url.searchParams.get('category') || undefined; // optional category filter
try {
const { getDimensionIndex, SPEC_DIMENSIONS } = await import(
@@ -254,6 +255,11 @@ export async function handleSpecRoutes(ctx: RouteContext): Promise<boolean> {
continue;
}
// Filter by category if specified
if (category && (entry.category || 'general') !== category) {
continue;
}
const fileData: InjectionFile = {
file: entry.file,
title: entry.title,

View File

@@ -444,28 +444,28 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
// API: Get project-tech stats for development progress injection
if (pathname === '/api/project-tech/stats' && req.method === 'GET') {
const projectPath = url.searchParams.get('path') || initialPath;
const resolvedPath = resolvePath(projectPath);
const techPath = join(resolvedPath, '.workflow', 'project-tech.json');
if (!existsSync(techPath)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
total_features: 0,
total_sessions: 0,
last_updated: null,
categories: {
feature: 0,
enhancement: 0,
bugfix: 0,
refactor: 0,
docs: 0
}
}));
return true;
}
try {
const projectPath = url.searchParams.get('path') || initialPath;
const resolvedPath = resolvePath(projectPath);
const techPath = join(resolvedPath, '.workflow', 'project-tech.json');
if (!existsSync(techPath)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
total_features: 0,
total_sessions: 0,
last_updated: null,
categories: {
feature: 0,
enhancement: 0,
bugfix: 0,
refactor: 0,
docs: 0
}
}));
return true;
}
const rawContent = readFileSync(techPath, 'utf-8');
const tech = JSON.parse(rawContent) as {
development_index?: {

View File

@@ -626,13 +626,13 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
if (await handleFilesRoutes(routeContext)) return;
}
// System routes (data, health, version, paths, shutdown, notify, storage, dialog, a2ui answer broker, system settings)
// System routes (data, health, version, paths, shutdown, notify, storage, dialog, a2ui answer broker, system settings, project-tech)
if (pathname === '/api/data' || pathname === '/api/health' ||
pathname === '/api/version-check' || pathname === '/api/shutdown' ||
pathname === '/api/recent-paths' || pathname === '/api/switch-path' ||
pathname === '/api/remove-recent-path' || pathname === '/api/system/notify' ||
pathname === '/api/system/settings' || pathname === '/api/system/hooks/install-recommended' ||
pathname === '/api/a2ui/answer' ||
pathname === '/api/a2ui/answer' || pathname === '/api/project-tech/stats' ||
pathname.startsWith('/api/storage/') || pathname.startsWith('/api/dialog/')) {
if (await handleSystemRoutes(routeContext)) return;
}