diff --git a/ccw/src/core/data-aggregator.ts b/ccw/src/core/data-aggregator.ts index b7a44dc7..961c2aa5 100644 --- a/ccw/src/core/data-aggregator.ts +++ b/ccw/src/core/data-aggregator.ts @@ -110,6 +110,34 @@ interface SessionReviewData { findings: Array; } +interface ProjectGuidelines { + conventions: { + coding_style: string[]; + naming_patterns: string[]; + file_structure: string[]; + documentation: string[]; + }; + constraints: { + architecture: string[]; + tech_stack: string[]; + performance: string[]; + security: string[]; + }; + quality_rules: Array<{ rule: string; scope: string; enforced_by?: string }>; + learnings: Array<{ + date: string; + session_id?: string; + insight: string; + context?: string; + category?: string; + }>; + _metadata?: { + created_at: string; + updated_at?: string; + version: string; + }; +} + interface ProjectOverview { projectName: string; description: string; @@ -144,6 +172,7 @@ interface ProjectOverview { analysis_timestamp: string | null; analysis_mode: string; }; + guidelines: ProjectGuidelines | null; } /** @@ -156,11 +185,13 @@ export async function aggregateData(sessions: ScanSessionsResult, workflowDir: s // Initialize cache manager const cache = createDashboardCache(workflowDir); - // Prepare paths to watch for changes + // Prepare paths to watch for changes (includes both new dual files and legacy) const watchPaths = [ join(workflowDir, 'active'), join(workflowDir, 'archives'), - join(workflowDir, 'project.json'), + join(workflowDir, 'project-tech.json'), + join(workflowDir, 'project-guidelines.json'), + join(workflowDir, 'project.json'), // Legacy support ...sessions.active.map(s => s.path), ...sessions.archived.map(s => s.path) ]; @@ -516,12 +547,19 @@ function sortTaskIds(a: string, b: string): number { } /** - * Load project overview from project.json + * Load project overview from project-tech.json and project-guidelines.json + * Supports dual file structure with backward compatibility for legacy project.json * @param workflowDir - Path to .workflow directory * @returns Project overview data or null if not found */ function loadProjectOverview(workflowDir: string): ProjectOverview | null { - const projectFile = join(workflowDir, 'project.json'); + const techFile = join(workflowDir, 'project-tech.json'); + const guidelinesFile = join(workflowDir, 'project-guidelines.json'); + const legacyFile = join(workflowDir, 'project.json'); + + // Check for new dual file structure first, fallback to legacy + const useLegacy = !existsSync(techFile) && existsSync(legacyFile); + const projectFile = useLegacy ? legacyFile : techFile; if (!existsSync(projectFile)) { console.log(`Project file not found at: ${projectFile}`); @@ -532,15 +570,59 @@ function loadProjectOverview(workflowDir: string): ProjectOverview | null { const fileContent = readFileSync(projectFile, 'utf8'); const projectData = JSON.parse(fileContent) as Record; - console.log(`Successfully loaded project overview: ${projectData.project_name || 'Unknown'}`); + console.log(`Successfully loaded project overview: ${projectData.project_name || 'Unknown'} (${useLegacy ? 'legacy' : 'tech'})`); + // Parse tech data (compatible with both legacy and new structure) const overview = projectData.overview as Record | undefined; - const technologyStack = overview?.technology_stack as Record | undefined; - const architecture = overview?.architecture as Record | undefined; - const developmentIndex = projectData.development_index as Record | undefined; - const statistics = projectData.statistics as Record | undefined; + const technologyAnalysis = projectData.technology_analysis as Record | undefined; + const developmentStatus = projectData.development_status as Record | undefined; + + // Support both old and new schema field names + const technologyStack = (overview?.technology_stack || technologyAnalysis?.technology_stack) as Record | undefined; + const architecture = (overview?.architecture || technologyAnalysis?.architecture) as Record | undefined; + const developmentIndex = (projectData.development_index || developmentStatus?.development_index) as Record | undefined; + const statistics = (projectData.statistics || developmentStatus?.statistics) as Record | undefined; const metadata = projectData._metadata as Record | undefined; + // Load guidelines from separate file if exists + let guidelines: ProjectGuidelines | null = null; + if (existsSync(guidelinesFile)) { + try { + const guidelinesContent = readFileSync(guidelinesFile, 'utf8'); + const guidelinesData = JSON.parse(guidelinesContent) as Record; + + const conventions = guidelinesData.conventions as Record | undefined; + const constraints = guidelinesData.constraints as Record | undefined; + + guidelines = { + conventions: { + coding_style: conventions?.coding_style || [], + naming_patterns: conventions?.naming_patterns || [], + file_structure: conventions?.file_structure || [], + documentation: conventions?.documentation || [] + }, + constraints: { + architecture: constraints?.architecture || [], + tech_stack: constraints?.tech_stack || [], + performance: constraints?.performance || [], + security: constraints?.security || [] + }, + quality_rules: (guidelinesData.quality_rules as Array<{ rule: string; scope: string; enforced_by?: string }>) || [], + learnings: (guidelinesData.learnings as Array<{ + date: string; + session_id?: string; + insight: string; + context?: string; + category?: string; + }>) || [], + _metadata: guidelinesData._metadata as ProjectGuidelines['_metadata'] | undefined + }; + console.log(`Successfully loaded project guidelines`); + } catch (guidelinesErr) { + console.error(`Failed to parse project-guidelines.json:`, (guidelinesErr as Error).message); + } + } + return { projectName: (projectData.project_name as string) || 'Unknown', description: (overview?.description as string) || '', @@ -574,10 +656,11 @@ function loadProjectOverview(workflowDir: string): ProjectOverview | null { initialized_by: (metadata?.initialized_by as string) || 'unknown', analysis_timestamp: (metadata?.analysis_timestamp as string) || null, analysis_mode: (metadata?.analysis_mode as string) || 'unknown' - } + }, + guidelines }; } catch (err) { - console.error(`Failed to parse project.json at ${projectFile}:`, (err as Error).message); + console.error(`Failed to parse project file at ${projectFile}:`, (err as Error).message); console.error('Error stack:', (err as Error).stack); return null; } diff --git a/ccw/src/templates/dashboard-js/views/project-overview.js b/ccw/src/templates/dashboard-js/views/project-overview.js index ebc3e42e..ec7057f9 100644 --- a/ccw/src/templates/dashboard-js/views/project-overview.js +++ b/ccw/src/templates/dashboard-js/views/project-overview.js @@ -169,6 +169,9 @@ function renderProjectOverview() { ${renderDevelopmentIndex(project.developmentIndex)} + + ${renderProjectGuidelines(project.guidelines)} +

@@ -248,3 +251,153 @@ function renderDevelopmentIndex(devIndex) {

`; } + +function renderProjectGuidelines(guidelines) { + if (!guidelines) { + return ` +
+

+ Project Guidelines +

+

+ No guidelines configured. Run /session:solidify to add project constraints and conventions. +

+
+ `; + } + + // Count total items + const conventionCount = Object.values(guidelines.conventions || {}).flat().length; + const constraintCount = Object.values(guidelines.constraints || {}).flat().length; + const rulesCount = (guidelines.quality_rules || []).length; + const learningsCount = (guidelines.learnings || []).length; + const totalCount = conventionCount + constraintCount + rulesCount + learningsCount; + + if (totalCount === 0) { + return ` +
+

+ Project Guidelines +

+

+ Guidelines file exists but is empty. Run /session:solidify to add project constraints and conventions. +

+
+ `; + } + + return ` +
+

+ Project Guidelines + ${totalCount} items +

+ +
+ + ${renderGuidelinesSection('Conventions', guidelines.conventions, 'book-marked', 'bg-success-light text-success', [ + { key: 'coding_style', label: 'Coding Style' }, + { key: 'naming_patterns', label: 'Naming Patterns' }, + { key: 'file_structure', label: 'File Structure' }, + { key: 'documentation', label: 'Documentation' } + ])} + + + ${renderGuidelinesSection('Constraints', guidelines.constraints, 'shield-alert', 'bg-destructive/10 text-destructive', [ + { key: 'architecture', label: 'Architecture' }, + { key: 'tech_stack', label: 'Tech Stack' }, + { key: 'performance', label: 'Performance' }, + { key: 'security', label: 'Security' } + ])} + + + ${renderQualityRules(guidelines.quality_rules)} + + + ${renderLearnings(guidelines.learnings)} +
+
+ `; +} + +function renderGuidelinesSection(title, data, icon, badgeClass, categories) { + if (!data) return ''; + + const items = categories.flatMap(cat => (data[cat.key] || []).map(item => ({ category: cat.label, value: item }))); + if (items.length === 0) return ''; + + return ` +
+

+ + ${title} + ${items.length} +

+
+ ${items.slice(0, 8).map(item => ` +
+ ${escapeHtml(item.category)} + ${escapeHtml(item.value)} +
+ `).join('')} + ${items.length > 8 ? `
... and ${items.length - 8} more
` : ''} +
+
+ `; +} + +function renderQualityRules(rules) { + if (!rules || rules.length === 0) return ''; + + return ` +
+

+ + Quality Rules + ${rules.length} +

+
+ ${rules.slice(0, 6).map(rule => ` +
+
+ ${escapeHtml(rule.rule)} + ${rule.enforced_by ? `${escapeHtml(rule.enforced_by)}` : ''} +
+ Scope: ${escapeHtml(rule.scope)} +
+ `).join('')} + ${rules.length > 6 ? `
... and ${rules.length - 6} more
` : ''} +
+
+ `; +} + +function renderLearnings(learnings) { + if (!learnings || learnings.length === 0) return ''; + + return ` +
+

+ + Session Learnings + ${learnings.length} +

+
+ ${learnings.slice(0, 5).map(learning => ` +
+
+ ${escapeHtml(learning.insight)} + ${formatDate(learning.date)} +
+
+ ${learning.category ? `${escapeHtml(learning.category)}` : ''} + ${learning.session_id ? `${escapeHtml(learning.session_id)}` : ''} +
+ ${learning.context ? `

${escapeHtml(learning.context)}

` : ''} +
+ `).join('')} + ${learnings.length > 5 ? `
... and ${learnings.length - 5} more
` : ''} +
+
+ `; +} diff --git a/codex-lens/src/codex_lens.egg-info/SOURCES.txt b/codex-lens/src/codex_lens.egg-info/SOURCES.txt index 9f475809..eef97b36 100644 --- a/codex-lens/src/codex_lens.egg-info/SOURCES.txt +++ b/codex-lens/src/codex_lens.egg-info/SOURCES.txt @@ -41,6 +41,7 @@ src/codexlens/semantic/vector_store.py src/codexlens/storage/__init__.py src/codexlens/storage/dir_index.py src/codexlens/storage/file_cache.py +src/codexlens/storage/global_index.py src/codexlens/storage/index_tree.py src/codexlens/storage/migration_manager.py src/codexlens/storage/path_mapper.py @@ -64,6 +65,8 @@ tests/test_enrichment.py tests/test_entities.py tests/test_errors.py tests/test_file_cache.py +tests/test_global_index.py +tests/test_global_symbol_index.py tests/test_hybrid_chunker.py tests/test_hybrid_search_e2e.py tests/test_incremental_indexing.py diff --git a/package.json b/package.json index 5df96973..882865c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-code-workflow", - "version": "6.3.9", + "version": "6.3.10", "description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution", "type": "module", "main": "ccw/src/index.js",