prjct-cli 0.11.5 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +81 -25
- package/bin/dev.js +1 -1
- package/bin/generate-views.js +209 -0
- package/bin/migrate-to-json.js +742 -0
- package/bin/prjct +5 -5
- package/bin/serve.js +226 -50
- package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
- package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
- package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
- package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
- package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
- package/core/agentic/agent-router.ts +137 -0
- package/core/agentic/chain-of-thought.ts +228 -0
- package/core/agentic/command-executor/command-executor.ts +384 -0
- package/core/agentic/command-executor/index.ts +16 -0
- package/core/agentic/command-executor/status-signal.ts +38 -0
- package/core/agentic/command-executor/types.ts +79 -0
- package/core/agentic/command-executor.ts +8 -0
- package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
- package/core/agentic/context-filter.ts +365 -0
- package/core/agentic/ground-truth/index.ts +76 -0
- package/core/agentic/ground-truth/types.ts +33 -0
- package/core/agentic/ground-truth/utils.ts +48 -0
- package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
- package/core/agentic/ground-truth/verifiers/done.ts +75 -0
- package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
- package/core/agentic/ground-truth/verifiers/index.ts +37 -0
- package/core/agentic/ground-truth/verifiers/init.ts +52 -0
- package/core/agentic/ground-truth/verifiers/now.ts +57 -0
- package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
- package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
- package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
- package/core/agentic/ground-truth/verifiers.ts +6 -0
- package/core/agentic/ground-truth.ts +8 -0
- package/core/agentic/loop-detector/error-analysis.ts +97 -0
- package/core/agentic/loop-detector/hallucination.ts +71 -0
- package/core/agentic/loop-detector/index.ts +41 -0
- package/core/agentic/loop-detector/loop-detector.ts +222 -0
- package/core/agentic/loop-detector/types.ts +66 -0
- package/core/agentic/loop-detector.ts +8 -0
- package/core/agentic/memory-system/history.ts +53 -0
- package/core/agentic/memory-system/index.ts +192 -0
- package/core/agentic/memory-system/patterns.ts +156 -0
- package/core/agentic/memory-system/semantic-memories.ts +277 -0
- package/core/agentic/memory-system/session.ts +21 -0
- package/core/agentic/memory-system/types.ts +159 -0
- package/core/agentic/memory-system.ts +8 -0
- package/core/agentic/parallel-tools.ts +165 -0
- package/core/agentic/plan-mode/approval.ts +57 -0
- package/core/agentic/plan-mode/constants.ts +44 -0
- package/core/agentic/plan-mode/index.ts +28 -0
- package/core/agentic/plan-mode/plan-mode.ts +406 -0
- package/core/agentic/plan-mode/types.ts +193 -0
- package/core/agentic/plan-mode.ts +8 -0
- package/core/agentic/prompt-builder.ts +566 -0
- package/core/agentic/response-templates.ts +164 -0
- package/core/agentic/semantic-compression.ts +273 -0
- package/core/agentic/services.ts +206 -0
- package/core/agentic/smart-context.ts +476 -0
- package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
- package/core/agentic/think-blocks.ts +202 -0
- package/core/agentic/tool-registry.ts +119 -0
- package/core/agentic/validation-rules.ts +313 -0
- package/core/agents/index.ts +28 -0
- package/core/agents/performance.ts +444 -0
- package/core/agents/types.ts +126 -0
- package/core/bus/{index.js → index.ts} +57 -61
- package/core/command-registry/categories.ts +23 -0
- package/core/command-registry/commands.ts +15 -0
- package/core/command-registry/core-commands.ts +319 -0
- package/core/command-registry/index.ts +158 -0
- package/core/command-registry/optional-commands.ts +119 -0
- package/core/command-registry/setup-commands.ts +53 -0
- package/core/command-registry/types.ts +59 -0
- package/core/command-registry.ts +9 -0
- package/core/commands/analysis.ts +298 -0
- package/core/commands/analytics.ts +288 -0
- package/core/commands/base.ts +273 -0
- package/core/commands/index.ts +211 -0
- package/core/commands/maintenance.ts +226 -0
- package/core/commands/planning.ts +311 -0
- package/core/commands/setup.ts +309 -0
- package/core/commands/shipping.ts +188 -0
- package/core/commands/types.ts +183 -0
- package/core/commands/workflow.ts +226 -0
- package/core/commands.ts +11 -0
- package/core/constants/formats.ts +187 -0
- package/core/constants/index.ts +7 -0
- package/core/{context-sync.js → context-sync.ts} +59 -26
- package/core/data/agents-manager.ts +76 -0
- package/core/data/analysis-manager.ts +83 -0
- package/core/data/base-manager.ts +156 -0
- package/core/data/ideas-manager.ts +81 -0
- package/core/data/index.ts +32 -0
- package/core/data/outcomes-manager.ts +96 -0
- package/core/data/project-manager.ts +75 -0
- package/core/data/roadmap-manager.ts +118 -0
- package/core/data/shipped-manager.ts +65 -0
- package/core/data/state-manager.ts +214 -0
- package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
- package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
- package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
- package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
- package/core/domain/{analyzer.js → analyzer.ts} +91 -85
- package/core/domain/{architect-session.js → architect-session.ts} +49 -34
- package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
- package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
- package/core/domain/{product-standards.js → product-standards.ts} +40 -26
- package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
- package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
- package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
- package/core/domain/task-stack/index.ts +19 -0
- package/core/domain/task-stack/parser.ts +86 -0
- package/core/domain/task-stack/storage.ts +123 -0
- package/core/domain/task-stack/task-stack.ts +340 -0
- package/core/domain/task-stack/types.ts +51 -0
- package/core/domain/task-stack.ts +8 -0
- package/core/{index.js → index.ts} +61 -18
- package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
- package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
- package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
- package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
- package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
- package/core/infrastructure/command-installer/global-config.ts +106 -0
- package/core/infrastructure/command-installer/index.ts +25 -0
- package/core/infrastructure/command-installer/types.ts +41 -0
- package/core/infrastructure/command-installer.ts +8 -0
- package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
- package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
- package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
- package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
- package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
- package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
- package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
- package/core/infrastructure/legacy-installer-detector.ts +7 -0
- package/core/infrastructure/migrator/file-operations.ts +125 -0
- package/core/infrastructure/migrator/index.ts +288 -0
- package/core/infrastructure/migrator/project-scanner.ts +89 -0
- package/core/infrastructure/migrator/reports.ts +117 -0
- package/core/infrastructure/migrator/types.ts +124 -0
- package/core/infrastructure/migrator/validation.ts +94 -0
- package/core/infrastructure/migrator/version-migration.ts +117 -0
- package/core/infrastructure/migrator.ts +10 -0
- package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
- package/core/infrastructure/session-manager/index.ts +23 -0
- package/core/infrastructure/session-manager/migration.ts +88 -0
- package/core/infrastructure/session-manager/session-manager.ts +307 -0
- package/core/infrastructure/session-manager/types.ts +45 -0
- package/core/infrastructure/session-manager.ts +8 -0
- package/core/infrastructure/{setup.js → setup.ts} +29 -21
- package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
- package/core/outcomes/analyzer.ts +333 -0
- package/core/outcomes/index.ts +34 -0
- package/core/outcomes/recorder.ts +194 -0
- package/core/outcomes/types.ts +145 -0
- package/core/plugin/{hooks.js → hooks.ts} +56 -58
- package/core/plugin/{index.js → index.ts} +19 -8
- package/core/plugin/{loader.js → loader.ts} +87 -69
- package/core/plugin/{registry.js → registry.ts} +49 -45
- package/core/plugins/{webhook.js → webhook.ts} +43 -27
- package/core/schemas/agents.ts +27 -0
- package/core/schemas/analysis.ts +41 -0
- package/core/schemas/ideas.ts +83 -0
- package/core/schemas/index.ts +73 -0
- package/core/schemas/outcomes.ts +22 -0
- package/core/schemas/project.ts +26 -0
- package/core/schemas/roadmap.ts +90 -0
- package/core/schemas/shipped.ts +82 -0
- package/core/schemas/state.ts +107 -0
- package/core/session/index.ts +17 -0
- package/core/session/{metrics.js → metrics.ts} +64 -46
- package/core/session/{index.js → session-manager.ts} +51 -117
- package/core/session/types.ts +29 -0
- package/core/session/utils.ts +57 -0
- package/core/state/index.ts +25 -0
- package/core/state/manager.ts +376 -0
- package/core/state/types.ts +185 -0
- package/core/tsconfig.json +22 -0
- package/core/types/index.ts +506 -0
- package/core/utils/{animations.js → animations.ts} +74 -28
- package/core/utils/{branding.js → branding.ts} +29 -4
- package/core/utils/{date-helper.js → date-helper.ts} +31 -74
- package/core/utils/file-helper.ts +262 -0
- package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
- package/core/utils/{logger.js → logger.ts} +24 -12
- package/core/utils/{output.js → output.ts} +25 -13
- package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
- package/core/utils/{session-helper.js → session-helper.ts} +79 -66
- package/core/utils/{version.js → version.ts} +23 -31
- package/core/view-generator.ts +536 -0
- package/package.json +23 -17
- package/packages/shared/.turbo/turbo-build.log +14 -0
- package/packages/shared/dist/index.d.ts +8 -613
- package/packages/shared/dist/index.d.ts.map +1 -0
- package/packages/shared/dist/index.js +4110 -118
- package/packages/shared/dist/schemas.d.ts +408 -0
- package/packages/shared/dist/schemas.d.ts.map +1 -0
- package/packages/shared/dist/types.d.ts +144 -0
- package/packages/shared/dist/types.d.ts.map +1 -0
- package/packages/shared/dist/unified.d.ts +139 -0
- package/packages/shared/dist/unified.d.ts.map +1 -0
- package/packages/shared/dist/utils.d.ts +60 -0
- package/packages/shared/dist/utils.d.ts.map +1 -0
- package/packages/shared/package.json +4 -4
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/unified.ts +174 -0
- package/packages/web/app/api/claude/sessions/route.ts +1 -1
- package/packages/web/app/api/claude/status/route.ts +1 -1
- package/packages/web/app/api/migrate/route.ts +46 -0
- package/packages/web/app/api/projects/[id]/route.ts +1 -1
- package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
- package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
- package/packages/web/app/api/projects/route.ts +1 -1
- package/packages/web/app/api/settings/route.ts +97 -0
- package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
- package/packages/web/app/globals.css +38 -0
- package/packages/web/app/layout.tsx +10 -2
- package/packages/web/app/page.tsx +9 -224
- package/packages/web/app/project/[id]/page.tsx +191 -63
- package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
- package/packages/web/app/project/[id]/stats/page.tsx +204 -163
- package/packages/web/app/settings/page.tsx +222 -2
- package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
- package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
- package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
- package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
- package/packages/web/components/ActivityTimeline/index.ts +2 -0
- package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
- package/packages/web/components/AgentsCard/index.ts +2 -0
- package/packages/web/components/AppSidebar/AppSidebar.tsx +190 -0
- package/packages/web/components/AppSidebar/index.ts +1 -0
- package/packages/web/components/BackLink/BackLink.tsx +18 -0
- package/packages/web/components/BackLink/BackLink.types.ts +5 -0
- package/packages/web/components/BackLink/index.ts +2 -0
- package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
- package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
- package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
- package/packages/web/components/BentoCard/index.ts +2 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
- package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
- package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
- package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
- package/packages/web/components/BentoGrid/index.ts +2 -0
- package/packages/web/components/CommandButton/index.ts +1 -0
- package/packages/web/components/ConnectionStatus/index.ts +1 -0
- package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
- package/packages/web/components/DashboardContent/index.ts +1 -0
- package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
- package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
- package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
- package/packages/web/components/DateGroup/index.ts +2 -0
- package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
- package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
- package/packages/web/components/EmptyState/index.ts +2 -0
- package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
- package/packages/web/components/EventRow/EventRow.tsx +49 -0
- package/packages/web/components/EventRow/EventRow.types.ts +7 -0
- package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
- package/packages/web/components/EventRow/index.ts +2 -0
- package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
- package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
- package/packages/web/components/ExpandButton/index.ts +2 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
- package/packages/web/components/HealthGradientBackground/index.ts +2 -0
- package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
- package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
- package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
- package/packages/web/components/HeroSection/hooks/index.ts +2 -0
- package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
- package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
- package/packages/web/components/HeroSection/index.ts +2 -0
- package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
- package/packages/web/components/IdeasCard/index.ts +2 -0
- package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
- package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
- package/packages/web/components/InsightMessage/index.ts +2 -0
- package/packages/web/components/Logo/index.ts +1 -0
- package/packages/web/components/MarkdownContent/index.ts +1 -0
- package/packages/web/components/NowCard/NowCard.tsx +93 -0
- package/packages/web/components/NowCard/NowCard.types.ts +15 -0
- package/packages/web/components/NowCard/index.ts +2 -0
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
- package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
- package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
- package/packages/web/components/ProgressRing/index.ts +2 -0
- package/packages/web/components/ProjectAvatar/index.ts +1 -0
- package/packages/web/components/Providers/index.ts +1 -0
- package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
- package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
- package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
- package/packages/web/components/QueueCard/index.ts +2 -0
- package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
- package/packages/web/components/RoadmapCard/index.ts +2 -0
- package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
- package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
- package/packages/web/components/ShipsCard/index.ts +2 -0
- package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
- package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
- package/packages/web/components/SparklineChart/index.ts +2 -0
- package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
- package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
- package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
- package/packages/web/components/StreakCard/index.ts +2 -0
- package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
- package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
- package/packages/web/components/TasksCounter/index.ts +2 -0
- package/packages/web/components/TechStackBadges/index.ts +1 -0
- package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
- package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
- package/packages/web/components/TerminalTabs/index.ts +1 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
- package/packages/web/components/VelocityBadge/index.ts +2 -0
- package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
- package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
- package/packages/web/components/VelocityCard/index.ts +2 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
- package/packages/web/components/WeeklySparkline/index.ts +2 -0
- package/packages/web/components/ui/input.tsx +21 -0
- package/packages/web/context/TerminalTabsContext.tsx +46 -1
- package/packages/web/hooks/useClaudeTerminal.ts +71 -21
- package/packages/web/hooks/useProjectStats.ts +55 -0
- package/packages/web/hooks/useProjects.ts +6 -6
- package/packages/web/lib/actions/projects.ts +15 -0
- package/packages/web/lib/json-loader.ts +630 -0
- package/packages/web/lib/services/index.ts +9 -0
- package/packages/web/lib/services/migration.server.ts +600 -0
- package/packages/web/lib/services/projects.server.ts +52 -0
- package/packages/web/lib/services/stats.server.ts +264 -0
- package/packages/web/lib/unified-loader.ts +396 -0
- package/packages/web/package.json +10 -7
- package/packages/web/server.ts +58 -8
- package/templates/commands/done.md +76 -32
- package/templates/commands/feature.md +121 -47
- package/templates/commands/idea.md +81 -8
- package/templates/commands/now.md +41 -17
- package/templates/commands/ship.md +64 -25
- package/templates/commands/sync.md +28 -3
- package/core/agentic/agent-router.js +0 -140
- package/core/agentic/chain-of-thought.js +0 -578
- package/core/agentic/command-executor.js +0 -417
- package/core/agentic/context-filter.js +0 -354
- package/core/agentic/ground-truth.js +0 -591
- package/core/agentic/loop-detector.js +0 -406
- package/core/agentic/memory-system.js +0 -845
- package/core/agentic/parallel-tools.js +0 -366
- package/core/agentic/plan-mode.js +0 -572
- package/core/agentic/prompt-builder.js +0 -352
- package/core/agentic/response-templates.js +0 -290
- package/core/agentic/semantic-compression.js +0 -517
- package/core/agentic/think-blocks.js +0 -657
- package/core/agentic/tool-registry.js +0 -184
- package/core/agentic/validation-rules.js +0 -380
- package/core/command-registry.js +0 -698
- package/core/commands.js +0 -2237
- package/core/domain/task-stack.js +0 -497
- package/core/infrastructure/legacy-installer-detector.js +0 -546
- package/core/infrastructure/migrator.js +0 -796
- package/core/infrastructure/session-manager.js +0 -390
- package/core/utils/file-helper.js +0 -329
- package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
- package/packages/web/app/api/stats/route.ts +0 -38
- package/packages/web/components/AppSidebar.tsx +0 -113
- package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
- package/packages/web/components/stats/AgentsCard.tsx +0 -56
- package/packages/web/components/stats/BentoCard.tsx +0 -88
- package/packages/web/components/stats/HeroSection.tsx +0 -172
- package/packages/web/components/stats/NowCard.tsx +0 -71
- package/packages/web/components/stats/QueueCard.tsx +0 -58
- package/packages/web/components/stats/VelocityCard.tsx +0 -60
- package/packages/web/components/stats/index.ts +0 -17
- package/packages/web/hooks/useStats.ts +0 -28
- /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
- /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
- /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
- /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
- /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
- /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
- /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stats Service (Server-only)
|
|
3
|
+
*
|
|
4
|
+
* Direct data access for Server Components.
|
|
5
|
+
* No API calls needed - reads directly from filesystem.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import 'server-only'
|
|
9
|
+
import { cache } from 'react'
|
|
10
|
+
import { exec } from 'child_process'
|
|
11
|
+
import { promisify } from 'util'
|
|
12
|
+
import {
|
|
13
|
+
loadUnifiedJsonData,
|
|
14
|
+
hasJsonState,
|
|
15
|
+
type UnifiedJsonData,
|
|
16
|
+
type StateJson,
|
|
17
|
+
type QueueJson,
|
|
18
|
+
type MetricsJson,
|
|
19
|
+
type ProjectInsights,
|
|
20
|
+
} from '@/lib/json-loader'
|
|
21
|
+
import { getProjectStats as getLegacyStats, type ProjectStats } from '@/lib/parse-prjct-files'
|
|
22
|
+
import { getProjects } from './projects.server'
|
|
23
|
+
|
|
24
|
+
export type { UnifiedJsonData, StateJson, QueueJson, MetricsJson, ProjectInsights }
|
|
25
|
+
|
|
26
|
+
// Activity type for recent activity tracking
|
|
27
|
+
export interface RecentActivity {
|
|
28
|
+
timestamp: string
|
|
29
|
+
type: string
|
|
30
|
+
description?: string
|
|
31
|
+
duration?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const execAsync = promisify(exec)
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Global stats for dashboard (userName, totalProjects)
|
|
38
|
+
*/
|
|
39
|
+
export interface GlobalStats {
|
|
40
|
+
userName: string
|
|
41
|
+
totalProjects: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function getGitUserName(): Promise<string> {
|
|
45
|
+
try {
|
|
46
|
+
const { stdout } = await execAsync('git config user.name')
|
|
47
|
+
return stdout.trim() || 'Developer'
|
|
48
|
+
} catch {
|
|
49
|
+
return 'Developer'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get global stats for dashboard - cached per request
|
|
55
|
+
*/
|
|
56
|
+
export const getGlobalStats = cache(async (): Promise<GlobalStats> => {
|
|
57
|
+
const [projects, userName] = await Promise.all([
|
|
58
|
+
getProjects(),
|
|
59
|
+
getGitUserName()
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
userName,
|
|
64
|
+
totalProjects: projects.length
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Unified stats result that works with both JSON and legacy formats
|
|
70
|
+
*/
|
|
71
|
+
export interface StatsResult {
|
|
72
|
+
state: StateJson | null
|
|
73
|
+
queue: QueueJson | null
|
|
74
|
+
metrics: MetricsJson | null
|
|
75
|
+
insights: ProjectInsights
|
|
76
|
+
agents: UnifiedJsonData['agents']
|
|
77
|
+
ideas: UnifiedJsonData['ideas']
|
|
78
|
+
roadmap: UnifiedJsonData['roadmap']
|
|
79
|
+
shipped: UnifiedJsonData['shipped']
|
|
80
|
+
outcomes: UnifiedJsonData['outcomes']
|
|
81
|
+
hasData: boolean
|
|
82
|
+
isLegacy: boolean
|
|
83
|
+
legacyStats?: ProjectStats
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const DEFAULT_INSIGHTS: ProjectInsights = {
|
|
87
|
+
healthScore: 0,
|
|
88
|
+
estimateAccuracy: 0,
|
|
89
|
+
topBlockers: [],
|
|
90
|
+
patternsDetected: [],
|
|
91
|
+
recommendations: ['Run /p:sync to initialize project']
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const EMPTY_STATS_RESULT: StatsResult = {
|
|
95
|
+
state: null,
|
|
96
|
+
queue: null,
|
|
97
|
+
metrics: null,
|
|
98
|
+
insights: DEFAULT_INSIGHTS,
|
|
99
|
+
agents: [],
|
|
100
|
+
ideas: null,
|
|
101
|
+
roadmap: null,
|
|
102
|
+
shipped: null,
|
|
103
|
+
outcomes: [],
|
|
104
|
+
hasData: false,
|
|
105
|
+
isLegacy: false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get project stats - cached per request
|
|
110
|
+
*/
|
|
111
|
+
export const getStats = cache(async (projectId: string): Promise<StatsResult> => {
|
|
112
|
+
const hasJson = await hasJsonState(projectId)
|
|
113
|
+
|
|
114
|
+
if (hasJson) {
|
|
115
|
+
const jsonData = await loadUnifiedJsonData(projectId)
|
|
116
|
+
return {
|
|
117
|
+
state: jsonData.state,
|
|
118
|
+
queue: jsonData.queue,
|
|
119
|
+
metrics: jsonData.metrics,
|
|
120
|
+
insights: jsonData.insights,
|
|
121
|
+
agents: jsonData.agents,
|
|
122
|
+
ideas: jsonData.ideas,
|
|
123
|
+
roadmap: jsonData.roadmap,
|
|
124
|
+
shipped: jsonData.shipped,
|
|
125
|
+
outcomes: jsonData.outcomes,
|
|
126
|
+
hasData: jsonData.hasJsonData,
|
|
127
|
+
isLegacy: false
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Fallback to legacy markdown parsing
|
|
132
|
+
try {
|
|
133
|
+
const legacyStats = await getLegacyStats(projectId)
|
|
134
|
+
return {
|
|
135
|
+
...EMPTY_STATS_RESULT,
|
|
136
|
+
insights: {
|
|
137
|
+
...DEFAULT_INSIGHTS,
|
|
138
|
+
healthScore: 50,
|
|
139
|
+
recommendations: ['Run /p:sync to enable JSON format']
|
|
140
|
+
},
|
|
141
|
+
hasData: true,
|
|
142
|
+
isLegacy: true,
|
|
143
|
+
legacyStats
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
return EMPTY_STATS_RESULT
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Calculate streak from metrics (pure function, no mutation)
|
|
152
|
+
*/
|
|
153
|
+
export function calculateStreak(metrics: MetricsJson | null): number {
|
|
154
|
+
if (!metrics?.recentActivity?.length) return 0
|
|
155
|
+
|
|
156
|
+
const activityDates = new Set(
|
|
157
|
+
metrics.recentActivity.map((a: { timestamp: string }) => new Date(a.timestamp).toISOString().split('T')[0])
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const today = new Date()
|
|
161
|
+
today.setHours(0, 0, 0, 0)
|
|
162
|
+
|
|
163
|
+
// Generate last 30 days as array
|
|
164
|
+
const days = Array.from({ length: 30 }, (_, i) => {
|
|
165
|
+
const date = new Date(today)
|
|
166
|
+
date.setDate(date.getDate() - i)
|
|
167
|
+
return date.toISOString().split('T')[0]
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Find first gap (day without activity)
|
|
171
|
+
const firstGapIndex = days.findIndex(date => !activityDates.has(date))
|
|
172
|
+
|
|
173
|
+
// If today has no activity, check if yesterday does
|
|
174
|
+
if (firstGapIndex === 0) {
|
|
175
|
+
const yesterdayHasActivity = activityDates.has(days[1])
|
|
176
|
+
if (!yesterdayHasActivity) return 0
|
|
177
|
+
// Start counting from yesterday
|
|
178
|
+
const remainingDays = days.slice(1)
|
|
179
|
+
const gapFromYesterday = remainingDays.findIndex(date => !activityDates.has(date))
|
|
180
|
+
return gapFromYesterday === -1 ? remainingDays.length : gapFromYesterday
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return firstGapIndex === -1 ? days.length : firstGapIndex
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get health emoji based on score
|
|
188
|
+
*/
|
|
189
|
+
export function getHealthEmoji(score: number): string {
|
|
190
|
+
if (score >= 80) return '🔥'
|
|
191
|
+
if (score >= 60) return '💪'
|
|
192
|
+
if (score >= 40) return '👍'
|
|
193
|
+
if (score >= 20) return '🌱'
|
|
194
|
+
return '💤'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get insight message based on stats
|
|
199
|
+
*/
|
|
200
|
+
export function getInsightMessage(stats: StatsResult, streak: number): string {
|
|
201
|
+
if (!stats.hasData) return 'Run /p:sync to get started'
|
|
202
|
+
if (stats.state?.currentTask) return `Working on: ${stats.state.currentTask.description}`
|
|
203
|
+
if (streak >= 7) return `${streak} day streak! You're on fire! 🔥`
|
|
204
|
+
if (streak >= 3) return `${streak} day streak - keep it going!`
|
|
205
|
+
|
|
206
|
+
const queueLength = stats.queue?.tasks?.filter(t => !t.completed).length ?? 0
|
|
207
|
+
if (queueLength > 0) return `${queueLength} tasks in queue`
|
|
208
|
+
return 'Ready to start working'
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Calculate velocity change percentage
|
|
213
|
+
*/
|
|
214
|
+
export function getVelocityChange(velocity: number): number {
|
|
215
|
+
if (velocity > 2) return 15
|
|
216
|
+
if (velocity > 1) return 5
|
|
217
|
+
if (velocity > 0) return 0
|
|
218
|
+
return -10
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get weekly velocity data from metrics (last 7 days)
|
|
223
|
+
*/
|
|
224
|
+
export function getWeeklyVelocityData(metrics: MetricsJson | null): number[] {
|
|
225
|
+
if (!metrics?.recentActivity?.length) return []
|
|
226
|
+
|
|
227
|
+
const today = new Date()
|
|
228
|
+
|
|
229
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
230
|
+
const date = new Date(today)
|
|
231
|
+
date.setDate(date.getDate() - (6 - i))
|
|
232
|
+
const dateStr = date.toISOString().split('T')[0]
|
|
233
|
+
|
|
234
|
+
return metrics.recentActivity.filter((e: { timestamp: string }) =>
|
|
235
|
+
e.timestamp?.startsWith(dateStr)
|
|
236
|
+
).length
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Calculate health score from stats
|
|
242
|
+
*/
|
|
243
|
+
export function calculateHealthScore(stats: StatsResult): number {
|
|
244
|
+
if (!stats.hasData) return 0
|
|
245
|
+
if (stats.insights.healthScore > 0) return stats.insights.healthScore
|
|
246
|
+
|
|
247
|
+
// Fallback for legacy
|
|
248
|
+
if (stats.isLegacy && stats.legacyStats) {
|
|
249
|
+
const { metrics, currentTask, queue, timeline } = stats.legacyStats
|
|
250
|
+
const velocity = metrics?.velocity?.tasksPerDay ?? 0
|
|
251
|
+
const hasCurrentTask = Boolean(currentTask)
|
|
252
|
+
const queueSize = queue?.length ?? 0
|
|
253
|
+
const recentActivity = timeline?.slice(0, 7).length ?? 0
|
|
254
|
+
|
|
255
|
+
const velocityScore = Math.min(30, velocity * 15)
|
|
256
|
+
const taskScore = hasCurrentTask ? 20 : 0
|
|
257
|
+
const queueScore = queueSize > 0 && queueSize < 15 ? 20 : queueSize === 0 ? 5 : 10
|
|
258
|
+
const activityScore = Math.min(30, recentActivity * 5)
|
|
259
|
+
|
|
260
|
+
return Math.min(100, Math.round(velocityScore + taskScore + queueScore + activityScore))
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return 50
|
|
264
|
+
}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads project data from the new unified state modules.
|
|
5
|
+
* Provides faster access than parsing markdown files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fs } from 'fs'
|
|
9
|
+
import { join } from 'path'
|
|
10
|
+
import { homedir } from 'os'
|
|
11
|
+
|
|
12
|
+
const GLOBAL_STORAGE = join(homedir(), '.prjct-cli', 'projects')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Project state from state.json
|
|
16
|
+
*/
|
|
17
|
+
export interface ProjectState {
|
|
18
|
+
projectId: string
|
|
19
|
+
currentTask: CurrentTask | null
|
|
20
|
+
queue: QueuedTask[]
|
|
21
|
+
activeFeature: ActiveFeature | null
|
|
22
|
+
stats: PerformanceStats
|
|
23
|
+
recentActivity: RecentActivity[]
|
|
24
|
+
lastSync: string
|
|
25
|
+
version: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CurrentTask {
|
|
29
|
+
id: string
|
|
30
|
+
description: string
|
|
31
|
+
startedAt: string
|
|
32
|
+
agent?: string
|
|
33
|
+
agentConfidence?: number
|
|
34
|
+
estimatedDuration?: string
|
|
35
|
+
featureId?: string
|
|
36
|
+
pausedAt?: string
|
|
37
|
+
pauseReason?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface QueuedTask {
|
|
41
|
+
id: string
|
|
42
|
+
description: string
|
|
43
|
+
priority: 'low' | 'medium' | 'high' | 'critical'
|
|
44
|
+
featureId?: string
|
|
45
|
+
estimatedDuration?: string
|
|
46
|
+
tags?: string[]
|
|
47
|
+
createdAt: string
|
|
48
|
+
blockedReason?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ActiveFeature {
|
|
52
|
+
id: string
|
|
53
|
+
name: string
|
|
54
|
+
status: 'planned' | 'in_progress' | 'completed' | 'shipped'
|
|
55
|
+
tasksCompleted: number
|
|
56
|
+
tasksRemaining: number
|
|
57
|
+
estimatedEffort?: string
|
|
58
|
+
actualEffort?: string
|
|
59
|
+
startedAt: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PerformanceStats {
|
|
63
|
+
tasksToday: number
|
|
64
|
+
tasksThisWeek: number
|
|
65
|
+
avgDuration: string
|
|
66
|
+
velocity: string
|
|
67
|
+
estimateAccuracy: number
|
|
68
|
+
streak: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface RecentActivity {
|
|
72
|
+
type: 'task_completed' | 'feature_shipped' | 'idea_captured' | 'session_started'
|
|
73
|
+
description: string
|
|
74
|
+
timestamp: string
|
|
75
|
+
duration?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Outcome summary from analyzer
|
|
80
|
+
*/
|
|
81
|
+
export interface OutcomeSummary {
|
|
82
|
+
totalOutcomes: number
|
|
83
|
+
avgQualityScore: number
|
|
84
|
+
estimateAccuracy: number
|
|
85
|
+
topBlockers: string[]
|
|
86
|
+
topAgents: string[]
|
|
87
|
+
patternsDetected: string[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Agent performance data
|
|
92
|
+
*/
|
|
93
|
+
export interface AgentPerformance {
|
|
94
|
+
agentName: string
|
|
95
|
+
taskType: string
|
|
96
|
+
tasksCompleted: number
|
|
97
|
+
successRate: number
|
|
98
|
+
avgDuration: string
|
|
99
|
+
estimateAccuracy: number
|
|
100
|
+
improving: boolean
|
|
101
|
+
lastUpdated: string
|
|
102
|
+
bestFor: string[]
|
|
103
|
+
avoidFor: string[]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Insights computed from all data
|
|
108
|
+
*/
|
|
109
|
+
export interface ProjectInsights {
|
|
110
|
+
healthScore: number
|
|
111
|
+
estimateAccuracy: number
|
|
112
|
+
topBlockers: string[]
|
|
113
|
+
patternsDetected: string[]
|
|
114
|
+
recommendations: string[]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Unified response combining all data
|
|
119
|
+
*/
|
|
120
|
+
export interface UnifiedProjectData {
|
|
121
|
+
state: ProjectState | null
|
|
122
|
+
outcomes: OutcomeSummary | null
|
|
123
|
+
agentPerformance: AgentPerformance[]
|
|
124
|
+
insights: ProjectInsights
|
|
125
|
+
// Legacy fallback data
|
|
126
|
+
legacyFallback: boolean
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const DEFAULT_STATE: ProjectState = {
|
|
130
|
+
projectId: '',
|
|
131
|
+
currentTask: null,
|
|
132
|
+
queue: [],
|
|
133
|
+
activeFeature: null,
|
|
134
|
+
stats: {
|
|
135
|
+
tasksToday: 0,
|
|
136
|
+
tasksThisWeek: 0,
|
|
137
|
+
avgDuration: '0h',
|
|
138
|
+
velocity: '0',
|
|
139
|
+
estimateAccuracy: 0,
|
|
140
|
+
streak: 0,
|
|
141
|
+
},
|
|
142
|
+
recentActivity: [],
|
|
143
|
+
lastSync: new Date().toISOString(),
|
|
144
|
+
version: 1,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Read state.json for a project
|
|
149
|
+
*/
|
|
150
|
+
async function readState(projectId: string): Promise<ProjectState | null> {
|
|
151
|
+
const statePath = join(GLOBAL_STORAGE, projectId, 'core', 'state.json')
|
|
152
|
+
try {
|
|
153
|
+
const content = await fs.readFile(statePath, 'utf-8')
|
|
154
|
+
return JSON.parse(content) as ProjectState
|
|
155
|
+
} catch {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Read outcomes summary
|
|
162
|
+
*/
|
|
163
|
+
async function readOutcomes(projectId: string): Promise<OutcomeSummary | null> {
|
|
164
|
+
const outcomesPath = join(GLOBAL_STORAGE, projectId, 'outcomes', 'outcomes.jsonl')
|
|
165
|
+
try {
|
|
166
|
+
const content = await fs.readFile(outcomesPath, 'utf-8')
|
|
167
|
+
const outcomes = content
|
|
168
|
+
.trim()
|
|
169
|
+
.split('\n')
|
|
170
|
+
.filter(line => line.trim())
|
|
171
|
+
.map(line => JSON.parse(line))
|
|
172
|
+
|
|
173
|
+
if (outcomes.length === 0) return null
|
|
174
|
+
|
|
175
|
+
// Calculate summary
|
|
176
|
+
const avgQuality = outcomes.reduce((sum: number, o: { qualityScore: number }) =>
|
|
177
|
+
sum + o.qualityScore, 0) / outcomes.length
|
|
178
|
+
|
|
179
|
+
// Count accurate estimates (within 20%)
|
|
180
|
+
const accurateCount = outcomes.filter((o: { variance: string; estimatedDuration: string }) => {
|
|
181
|
+
if (!o.variance) return false
|
|
182
|
+
const variance = parseVariance(o.variance)
|
|
183
|
+
const estimated = parseDuration(o.estimatedDuration)
|
|
184
|
+
if (estimated === 0) return false
|
|
185
|
+
return Math.abs(variance) / estimated <= 0.2
|
|
186
|
+
}).length
|
|
187
|
+
|
|
188
|
+
// Count blockers
|
|
189
|
+
const blockerCounts = new Map<string, number>()
|
|
190
|
+
for (const outcome of outcomes) {
|
|
191
|
+
for (const blocker of outcome.blockers || []) {
|
|
192
|
+
blockerCounts.set(blocker, (blockerCounts.get(blocker) || 0) + 1)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const topBlockers = [...blockerCounts.entries()]
|
|
196
|
+
.sort((a, b) => b[1] - a[1])
|
|
197
|
+
.slice(0, 5)
|
|
198
|
+
.map(([blocker]) => blocker)
|
|
199
|
+
|
|
200
|
+
// Count agents
|
|
201
|
+
const agentCounts = new Map<string, number>()
|
|
202
|
+
for (const outcome of outcomes) {
|
|
203
|
+
if (outcome.agentUsed) {
|
|
204
|
+
agentCounts.set(outcome.agentUsed, (agentCounts.get(outcome.agentUsed) || 0) + 1)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const topAgents = [...agentCounts.entries()]
|
|
208
|
+
.sort((a, b) => b[1] - a[1])
|
|
209
|
+
.slice(0, 3)
|
|
210
|
+
.map(([agent]) => agent)
|
|
211
|
+
|
|
212
|
+
// Detect patterns
|
|
213
|
+
const patterns: string[] = []
|
|
214
|
+
const underestimated = outcomes.filter((o: { variance: string }) => {
|
|
215
|
+
const variance = parseVariance(o.variance)
|
|
216
|
+
return variance > 0
|
|
217
|
+
})
|
|
218
|
+
if (underestimated.length / outcomes.length > 0.6) {
|
|
219
|
+
patterns.push('Tasks consistently take longer than estimated')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
totalOutcomes: outcomes.length,
|
|
224
|
+
avgQualityScore: Math.round(avgQuality * 10) / 10,
|
|
225
|
+
estimateAccuracy: Math.round((accurateCount / outcomes.length) * 100),
|
|
226
|
+
topBlockers,
|
|
227
|
+
topAgents,
|
|
228
|
+
patternsDetected: patterns,
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Read agent performance data
|
|
237
|
+
*/
|
|
238
|
+
async function readAgentPerformance(projectId: string): Promise<AgentPerformance[]> {
|
|
239
|
+
const perfPath = join(GLOBAL_STORAGE, projectId, 'analysis', 'agent-performance.json')
|
|
240
|
+
try {
|
|
241
|
+
const content = await fs.readFile(perfPath, 'utf-8')
|
|
242
|
+
const data = JSON.parse(content)
|
|
243
|
+
return data.agents || []
|
|
244
|
+
} catch {
|
|
245
|
+
return []
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Compute insights from all data
|
|
251
|
+
*/
|
|
252
|
+
function computeInsights(
|
|
253
|
+
state: ProjectState | null,
|
|
254
|
+
outcomes: OutcomeSummary | null,
|
|
255
|
+
agentPerformance: AgentPerformance[]
|
|
256
|
+
): ProjectInsights {
|
|
257
|
+
let healthScore = 50 // Base score
|
|
258
|
+
|
|
259
|
+
// Adjust for state
|
|
260
|
+
if (state) {
|
|
261
|
+
// Has current task = +10
|
|
262
|
+
if (state.currentTask) healthScore += 10
|
|
263
|
+
|
|
264
|
+
// Velocity bonus (max +20)
|
|
265
|
+
const velocity = parseFloat(state.stats.velocity) || 0
|
|
266
|
+
healthScore += Math.min(20, velocity * 5)
|
|
267
|
+
|
|
268
|
+
// Streak bonus (max +15)
|
|
269
|
+
healthScore += Math.min(15, state.stats.streak * 3)
|
|
270
|
+
|
|
271
|
+
// Queue size penalty (too many = -5)
|
|
272
|
+
if (state.queue.length > 15) healthScore -= 5
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Adjust for outcomes
|
|
276
|
+
if (outcomes) {
|
|
277
|
+
// Estimate accuracy bonus (max +15)
|
|
278
|
+
healthScore += Math.round(outcomes.estimateAccuracy * 0.15)
|
|
279
|
+
|
|
280
|
+
// Quality score bonus (max +10)
|
|
281
|
+
healthScore += Math.round(outcomes.avgQualityScore * 2)
|
|
282
|
+
|
|
283
|
+
// Blockers penalty
|
|
284
|
+
healthScore -= outcomes.topBlockers.length * 2
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Adjust for agent performance
|
|
288
|
+
if (agentPerformance.length > 0) {
|
|
289
|
+
const avgSuccess = agentPerformance.reduce((sum, a) => sum + a.successRate, 0) / agentPerformance.length
|
|
290
|
+
healthScore += Math.round(avgSuccess * 0.1)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Clamp to 0-100
|
|
294
|
+
healthScore = Math.max(0, Math.min(100, healthScore))
|
|
295
|
+
|
|
296
|
+
// Build recommendations
|
|
297
|
+
const recommendations: string[] = []
|
|
298
|
+
|
|
299
|
+
if (!state?.currentTask) {
|
|
300
|
+
recommendations.push('Start a task with /p:now to maintain momentum')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (state && state.queue.length > 10) {
|
|
304
|
+
recommendations.push('Queue is large - consider prioritizing or archiving tasks')
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (outcomes && outcomes.estimateAccuracy < 50) {
|
|
308
|
+
recommendations.push('Estimate accuracy is low - try adding 30% buffer to estimates')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (agentPerformance.length === 0) {
|
|
312
|
+
recommendations.push('No agent performance data yet - track outcomes with /p:done')
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const improving = agentPerformance.filter(a => a.improving)
|
|
316
|
+
if (improving.length > 0) {
|
|
317
|
+
recommendations.push(`${improving.map(a => a.agentName).join(', ')} improving - great progress!`)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
healthScore,
|
|
322
|
+
estimateAccuracy: outcomes?.estimateAccuracy || 0,
|
|
323
|
+
topBlockers: outcomes?.topBlockers || [],
|
|
324
|
+
patternsDetected: outcomes?.patternsDetected || [],
|
|
325
|
+
recommendations: recommendations.slice(0, 4),
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Parse variance string to minutes
|
|
331
|
+
*/
|
|
332
|
+
function parseVariance(variance: string): number {
|
|
333
|
+
const match = variance.match(/^([+-])(\d+)([mh])$/)
|
|
334
|
+
if (!match) return 0
|
|
335
|
+
|
|
336
|
+
const sign = match[1] === '-' ? -1 : 1
|
|
337
|
+
const value = parseInt(match[2], 10)
|
|
338
|
+
const unit = match[3]
|
|
339
|
+
|
|
340
|
+
return sign * (unit === 'h' ? value * 60 : value)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Parse duration string to minutes
|
|
345
|
+
*/
|
|
346
|
+
function parseDuration(duration: string): number {
|
|
347
|
+
let minutes = 0
|
|
348
|
+
|
|
349
|
+
const hourMatch = duration.match(/(\d+)h/)
|
|
350
|
+
if (hourMatch) {
|
|
351
|
+
minutes += parseInt(hourMatch[1], 10) * 60
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const minMatch = duration.match(/(\d+)m/)
|
|
355
|
+
if (minMatch) {
|
|
356
|
+
minutes += parseInt(minMatch[1], 10)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return minutes
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Load unified project data
|
|
364
|
+
*/
|
|
365
|
+
export async function loadUnifiedProjectData(projectId: string): Promise<UnifiedProjectData> {
|
|
366
|
+
// Try to read from new unified state
|
|
367
|
+
const [state, outcomes, agentPerformance] = await Promise.all([
|
|
368
|
+
readState(projectId),
|
|
369
|
+
readOutcomes(projectId),
|
|
370
|
+
readAgentPerformance(projectId),
|
|
371
|
+
])
|
|
372
|
+
|
|
373
|
+
// Compute insights
|
|
374
|
+
const insights = computeInsights(state, outcomes, agentPerformance)
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
state,
|
|
378
|
+
outcomes,
|
|
379
|
+
agentPerformance,
|
|
380
|
+
insights,
|
|
381
|
+
legacyFallback: state === null,
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Check if unified state exists for a project
|
|
387
|
+
*/
|
|
388
|
+
export async function hasUnifiedState(projectId: string): Promise<boolean> {
|
|
389
|
+
const statePath = join(GLOBAL_STORAGE, projectId, 'core', 'state.json')
|
|
390
|
+
try {
|
|
391
|
+
await fs.access(statePath)
|
|
392
|
+
return true
|
|
393
|
+
} catch {
|
|
394
|
+
return false
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "
|
|
7
|
-
"dev:next": "next dev",
|
|
8
|
-
"build": "next build",
|
|
9
|
-
"start": "NODE_ENV=production
|
|
10
|
-
"start:prod": "NODE_ENV=production
|
|
6
|
+
"dev": "NODE_ENV=development PORT=9471 bun server.ts",
|
|
7
|
+
"dev:next": "bun next dev",
|
|
8
|
+
"build": "bun next build",
|
|
9
|
+
"start": "NODE_ENV=production PORT=9472 bun server.ts",
|
|
10
|
+
"start:prod": "NODE_ENV=production PORT=9472 bun server.ts",
|
|
11
11
|
"lint": "eslint"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@ai-sdk/openai": "^2.0.80",
|
|
14
15
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
15
16
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
16
17
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
"@xterm/addon-fit": "^0.10.0",
|
|
24
25
|
"@xterm/addon-web-links": "^0.11.0",
|
|
25
26
|
"@xterm/xterm": "^5.5.0",
|
|
27
|
+
"ai": "^5.0.108",
|
|
26
28
|
"class-variance-authority": "^0.7.1",
|
|
27
29
|
"clsx": "^2.1.1",
|
|
28
30
|
"lucide-react": "^0.556.0",
|
|
@@ -36,10 +38,12 @@
|
|
|
36
38
|
"recharts": "^3.5.1",
|
|
37
39
|
"remark-gfm": "^4.0.1",
|
|
38
40
|
"tailwind-merge": "^3.4.0",
|
|
39
|
-
"ws": "^8.18.3"
|
|
41
|
+
"ws": "^8.18.3",
|
|
42
|
+
"zod": "^4.1.13"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
45
|
"@tailwindcss/postcss": "^4",
|
|
46
|
+
"@types/bun": "latest",
|
|
43
47
|
"@types/node": "^20",
|
|
44
48
|
"@types/react": "^19",
|
|
45
49
|
"@types/react-dom": "^19",
|
|
@@ -47,7 +51,6 @@
|
|
|
47
51
|
"eslint": "^9",
|
|
48
52
|
"eslint-config-next": "16.0.7",
|
|
49
53
|
"tailwindcss": "^4",
|
|
50
|
-
"tsx": "^4.19.2",
|
|
51
54
|
"tw-animate-css": "^1.4.0",
|
|
52
55
|
"typescript": "^5"
|
|
53
56
|
}
|