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,600 @@
|
|
|
1
|
+
import { createOpenAI } from '@ai-sdk/openai'
|
|
2
|
+
import { generateText } from 'ai'
|
|
3
|
+
import { promises as fs } from 'fs'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
import { homedir } from 'os'
|
|
6
|
+
import { exec } from 'child_process'
|
|
7
|
+
import { promisify } from 'util'
|
|
8
|
+
|
|
9
|
+
const execAsync = promisify(exec)
|
|
10
|
+
const SETTINGS_PATH = join(homedir(), '.prjct-cli', 'settings.json')
|
|
11
|
+
const GLOBAL_STORAGE = join(homedir(), '.prjct-cli', 'projects')
|
|
12
|
+
const PRJCT_CLI_PATH = join(__dirname, '..', '..', '..', '..')
|
|
13
|
+
|
|
14
|
+
// Complete JSON Schema definitions for new architecture
|
|
15
|
+
// JSON is source of truth, MD is generated for Claude
|
|
16
|
+
// ENRICHED SCHEMAS - Extract all rich data from MD files
|
|
17
|
+
const SCHEMAS = {
|
|
18
|
+
state: `{
|
|
19
|
+
"currentTask": {
|
|
20
|
+
"id": "task_xxxxxxxx - unique ID",
|
|
21
|
+
"description": "string",
|
|
22
|
+
"startedAt": "ISO-8601 timestamp",
|
|
23
|
+
"sessionId": "sess_xxxxxxxx",
|
|
24
|
+
"featureId": "feat_xxxxxxxx (optional)"
|
|
25
|
+
} | null,
|
|
26
|
+
"previousTask": {
|
|
27
|
+
"id": "string",
|
|
28
|
+
"description": "string",
|
|
29
|
+
"status": "paused",
|
|
30
|
+
"startedAt": "ISO-8601",
|
|
31
|
+
"pausedAt": "ISO-8601"
|
|
32
|
+
} | null (optional - for paused tasks),
|
|
33
|
+
"lastUpdated": "ISO-8601 timestamp"
|
|
34
|
+
}`,
|
|
35
|
+
|
|
36
|
+
queue: `{
|
|
37
|
+
"tasks": [{
|
|
38
|
+
"id": "task_xxxxxxxx",
|
|
39
|
+
"description": "string",
|
|
40
|
+
"priority": "critical|high|medium|low",
|
|
41
|
+
"type": "feature|bug|improvement|chore (detect from emoji 🐛=bug)",
|
|
42
|
+
"featureId": "feat_xxx (optional)",
|
|
43
|
+
"originFeature": "string - from (from: Feature Name) pattern (optional)",
|
|
44
|
+
"completed": boolean,
|
|
45
|
+
"completedAt": "ISO-8601 (if completed)",
|
|
46
|
+
"createdAt": "ISO-8601",
|
|
47
|
+
"section": "active|backlog|previously_active (based on MD section)",
|
|
48
|
+
"agent": "fe|be|fe + be (extract from **Agent**: pattern in task group)",
|
|
49
|
+
"groupName": "string - group/section name like 'Sales Reports', 'Stock Audits'",
|
|
50
|
+
"groupId": "string - unique ID for the group (optional)"
|
|
51
|
+
}],
|
|
52
|
+
"lastUpdated": "ISO-8601"
|
|
53
|
+
}`,
|
|
54
|
+
|
|
55
|
+
ideas: `{
|
|
56
|
+
"ideas": [{
|
|
57
|
+
"id": "idea_xxxxxxxx",
|
|
58
|
+
"text": "title/summary",
|
|
59
|
+
"details": "expanded description (optional)",
|
|
60
|
+
"priority": "high|medium|low",
|
|
61
|
+
"status": "pending|converted|completed|archived",
|
|
62
|
+
"tags": ["array", "of", "tags"],
|
|
63
|
+
"addedAt": "ISO-8601",
|
|
64
|
+
"completedAt": "ISO-8601 (if status=completed, extract from 'COMPLETED YYYY-MM-DD')",
|
|
65
|
+
"convertedTo": "feat_xxx (if status=converted)",
|
|
66
|
+
"source": "docs/technical-spec-v1.md, docs/edr-v1.md (from **Source**: pattern)",
|
|
67
|
+
"sourceFiles": ["array of source file paths"],
|
|
68
|
+
"painPoints": ["array of pain points from ### Pain Points or ### Riesgos section"],
|
|
69
|
+
"solutions": ["array of solutions from ### Solutions section"],
|
|
70
|
+
"filesAffected": ["array of file paths from **Files:** section"],
|
|
71
|
+
"impactEffort": {"impact": "high|medium|low", "effort": "high|medium|low"} (optional),
|
|
72
|
+
"stack": {
|
|
73
|
+
"frontend": "Next.js 14, HeroUI",
|
|
74
|
+
"backend": "Supabase (Auth, DB, RLS, Realtime)",
|
|
75
|
+
"payments": "Stripe Billing",
|
|
76
|
+
"ai": "Vercel AI SDK",
|
|
77
|
+
"deploy": "Vercel"
|
|
78
|
+
} (extract from ### Stack section),
|
|
79
|
+
"modules": [{"name": "Multi-tenant", "description": "Empresas con RLS estricto"}] (from ### Módulos section),
|
|
80
|
+
"roles": [{"name": "SUPER_ADMIN", "description": "(global, impersonation)"}] (from ### Roles section),
|
|
81
|
+
"risks": ["array of risks from ### Riesgos Críticos section"],
|
|
82
|
+
"risksCount": number (from '33 pitfalls documented')
|
|
83
|
+
}],
|
|
84
|
+
"lastUpdated": "ISO-8601"
|
|
85
|
+
}`,
|
|
86
|
+
|
|
87
|
+
roadmap: `{
|
|
88
|
+
"strategy": {
|
|
89
|
+
"goal": "strategic goal string (optional)",
|
|
90
|
+
"phases": [{"id": "P0", "name": "string", "status": "completed|active|planned", "completedAt": "ISO"}],
|
|
91
|
+
"successMetrics": ["array of KPIs"]
|
|
92
|
+
} | null (optional),
|
|
93
|
+
"features": [{
|
|
94
|
+
"id": "feat_xxxxxxxx",
|
|
95
|
+
"name": "string",
|
|
96
|
+
"description": "string (optional)",
|
|
97
|
+
"date": "YYYY-MM-DD creation date",
|
|
98
|
+
"impact": "high|medium|low (from Impact: HIGH)",
|
|
99
|
+
"effort": "1-2 days or similar string",
|
|
100
|
+
"status": "planned|active|completed|shipped",
|
|
101
|
+
"progress": 0-100 (calculate from tasks completed/total),
|
|
102
|
+
"type": "feature|breaking_change|refactor|infrastructure (from Type: BREAKING CHANGE)",
|
|
103
|
+
"roi": 1-5 (count ⭐ stars, optional),
|
|
104
|
+
"why": ["array from ### Why This Feature? section"],
|
|
105
|
+
"technicalNotes": ["array from ### Technical Notes section"],
|
|
106
|
+
"compatibility": "string (optional)",
|
|
107
|
+
"phase": "P0|P1|P2|P3 (optional)",
|
|
108
|
+
"tasks": [{
|
|
109
|
+
"id": "task_xxxxxxxx",
|
|
110
|
+
"description": "string",
|
|
111
|
+
"completed": boolean ([ ]=false, [x]=true),
|
|
112
|
+
"completedAt": "ISO-8601 (if completed)"
|
|
113
|
+
}],
|
|
114
|
+
"createdAt": "ISO-8601",
|
|
115
|
+
"shippedAt": "ISO-8601 (if shipped)",
|
|
116
|
+
"version": "0.11.6 (extract from v0.11.6 pattern)",
|
|
117
|
+
"duration": {"hours": number, "minutes": number, "totalMinutes": number, "display": "~25m"} (parse from '~25m', '~1h'),
|
|
118
|
+
"taskCount": number (extract from '7 tasks' in header),
|
|
119
|
+
"agent": "fe+be|fe|be (from **Agent**: pattern)",
|
|
120
|
+
"sprintName": "Sprint 6 - Reports + Audits (full sprint name)",
|
|
121
|
+
"completedDate": "2025-12-09 (exact completion date from MD)"
|
|
122
|
+
}],
|
|
123
|
+
"backlog": ["string array"],
|
|
124
|
+
"lastUpdated": "ISO-8601"
|
|
125
|
+
}`,
|
|
126
|
+
|
|
127
|
+
shipped: `{
|
|
128
|
+
"items": [{
|
|
129
|
+
"id": "ship_xxxxxxxx",
|
|
130
|
+
"name": "string",
|
|
131
|
+
"version": "0.11.6 (extract from (v0.11.6) or v0.11.6 patterns, null if none)",
|
|
132
|
+
"type": "feature|fix|improvement|refactor",
|
|
133
|
+
"agent": "fe|be|fe+be|devops|ai (extract from **Agent**: pattern)",
|
|
134
|
+
"description": "string - full narrative description text (NOT bullet points)",
|
|
135
|
+
"changes": [{"description": "string", "type": "added|changed|fixed|removed"}] (from bullet points),
|
|
136
|
+
"codeSnippets": ["string array of code blocks if any"] (optional),
|
|
137
|
+
"commit": {
|
|
138
|
+
"hash": "0a7bbea (short hash from **Commit**: pattern)",
|
|
139
|
+
"message": "feat(security): Multi-tenant... (commit message)"
|
|
140
|
+
} (optional),
|
|
141
|
+
"codeMetrics": {
|
|
142
|
+
"filesChanged": number|null,
|
|
143
|
+
"linesAdded": number|null,
|
|
144
|
+
"linesRemoved": number|null,
|
|
145
|
+
"commits": number|null
|
|
146
|
+
} (extract from 'Files: 4 | +160/-31 | Commits: 0'),
|
|
147
|
+
"qualityMetrics": {
|
|
148
|
+
"lintStatus": "pass|warning|fail|skipped|null",
|
|
149
|
+
"lintDetails": "string (optional)",
|
|
150
|
+
"testStatus": "pass|warning|fail|skipped|null",
|
|
151
|
+
"testDetails": "string - e.g. (2 date-helper tests)"
|
|
152
|
+
} (extract from 'Lint: warnings | Tests: failed (details)'),
|
|
153
|
+
"quantitativeImpact": "81% (1,079 → 204 lines) (optional)",
|
|
154
|
+
"duration": {"hours": number, "minutes": number, "totalMinutes": number} (parse from '~45m', '~1h', '13h 38m'),
|
|
155
|
+
"tasksCompleted": number|null (extract from 'Tasks: 6' or '6 tasks'),
|
|
156
|
+
"shippedAt": "ISO-8601",
|
|
157
|
+
"featureId": "feat_xxx (optional)"
|
|
158
|
+
}],
|
|
159
|
+
"lastUpdated": "ISO-8601"
|
|
160
|
+
}`,
|
|
161
|
+
|
|
162
|
+
metrics: `{
|
|
163
|
+
"currentSprint": {
|
|
164
|
+
"tasksStarted": number,
|
|
165
|
+
"tasksCompleted": number,
|
|
166
|
+
"inProgress": number
|
|
167
|
+
},
|
|
168
|
+
"allTime": {
|
|
169
|
+
"featuresShipped": number,
|
|
170
|
+
"tasksCompleted": number,
|
|
171
|
+
"totalTimeTracked": {"hours": number, "minutes": number, "totalMinutes": number},
|
|
172
|
+
"daysActive": number
|
|
173
|
+
},
|
|
174
|
+
"velocity": {
|
|
175
|
+
"featuresPerWeek": number,
|
|
176
|
+
"tasksPerDay": number
|
|
177
|
+
},
|
|
178
|
+
"recentActivity": [{
|
|
179
|
+
"timestamp": "ISO-8601",
|
|
180
|
+
"action": "started|completed|shipped|paused",
|
|
181
|
+
"description": "string",
|
|
182
|
+
"duration": {"hours": number, "minutes": number} (optional, parse from '(1m)' or '(7h 39m)'),
|
|
183
|
+
"codeChanges": {"files": number, "added": number, "removed": number, "commits": number} (optional)
|
|
184
|
+
}] (parse from Last Activity section and activity lines),
|
|
185
|
+
"lastUpdated": "ISO-8601"
|
|
186
|
+
}`
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function getApiKey(): Promise<string | null> {
|
|
190
|
+
try {
|
|
191
|
+
const content = await fs.readFile(SETTINGS_PATH, 'utf-8')
|
|
192
|
+
const settings = JSON.parse(content)
|
|
193
|
+
return settings.openRouterApiKey || null
|
|
194
|
+
} catch {
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function readMdFile(projectId: string, relativePath: string): Promise<string | null> {
|
|
200
|
+
try {
|
|
201
|
+
const fullPath = join(GLOBAL_STORAGE, projectId, relativePath)
|
|
202
|
+
return await fs.readFile(fullPath, 'utf-8')
|
|
203
|
+
} catch {
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function writeJsonFile(projectId: string, relativePath: string, data: unknown): Promise<void> {
|
|
209
|
+
const fullPath = join(GLOBAL_STORAGE, projectId, relativePath)
|
|
210
|
+
const dir = fullPath.substring(0, fullPath.lastIndexOf('/'))
|
|
211
|
+
await fs.mkdir(dir, { recursive: true })
|
|
212
|
+
await fs.writeFile(fullPath, JSON.stringify(data, null, 2))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type MigrationFile = {
|
|
216
|
+
mdPaths: string[] // Multiple source files to read
|
|
217
|
+
jsonPath: string // Destination in data/ directory
|
|
218
|
+
schemaKey: keyof typeof SCHEMAS
|
|
219
|
+
name: string
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// New architecture: MD → data/*.json
|
|
223
|
+
// All JSON files go to data/ directory
|
|
224
|
+
const MIGRATION_FILES: MigrationFile[] = [
|
|
225
|
+
{
|
|
226
|
+
mdPaths: ['core/now.md', 'core/now.json', 'sessions/current.json'],
|
|
227
|
+
jsonPath: 'data/state.json',
|
|
228
|
+
schemaKey: 'state',
|
|
229
|
+
name: 'state'
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
mdPaths: ['core/next.md', 'core/next.json'],
|
|
233
|
+
jsonPath: 'data/queue.json',
|
|
234
|
+
schemaKey: 'queue',
|
|
235
|
+
name: 'queue'
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
mdPaths: ['planning/ideas.md', 'planning/ideas.json'],
|
|
239
|
+
jsonPath: 'data/ideas.json',
|
|
240
|
+
schemaKey: 'ideas',
|
|
241
|
+
name: 'ideas'
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
mdPaths: ['planning/roadmap.md', 'planning/roadmap.json'],
|
|
245
|
+
jsonPath: 'data/roadmap.json',
|
|
246
|
+
schemaKey: 'roadmap',
|
|
247
|
+
name: 'roadmap'
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
mdPaths: ['progress/shipped.md', 'progress/shipped.json'],
|
|
251
|
+
jsonPath: 'data/shipped.json',
|
|
252
|
+
schemaKey: 'shipped',
|
|
253
|
+
name: 'shipped'
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
mdPaths: ['progress/metrics.md'],
|
|
257
|
+
jsonPath: 'data/metrics.json',
|
|
258
|
+
schemaKey: 'metrics',
|
|
259
|
+
name: 'metrics'
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
// Legacy files to delete after successful migration
|
|
264
|
+
const LEGACY_FILES = [
|
|
265
|
+
'core/now.md',
|
|
266
|
+
'core/now.json',
|
|
267
|
+
'core/next.md',
|
|
268
|
+
'core/next.json',
|
|
269
|
+
'core/context.md',
|
|
270
|
+
'planning/ideas.md',
|
|
271
|
+
'planning/ideas.json',
|
|
272
|
+
'planning/roadmap.md',
|
|
273
|
+
'planning/roadmap.json',
|
|
274
|
+
'progress/shipped.md',
|
|
275
|
+
'progress/shipped.json',
|
|
276
|
+
'progress/metrics.md',
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
async function deleteLegacyFiles(projectId: string): Promise<string[]> {
|
|
280
|
+
const deleted: string[] = []
|
|
281
|
+
for (const file of LEGACY_FILES) {
|
|
282
|
+
try {
|
|
283
|
+
await fs.unlink(join(GLOBAL_STORAGE, projectId, file))
|
|
284
|
+
deleted.push(file)
|
|
285
|
+
} catch {
|
|
286
|
+
// File doesn't exist or can't be deleted
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Try to remove empty directories
|
|
291
|
+
const dirsToCheck = ['core', 'planning', 'progress']
|
|
292
|
+
for (const dir of dirsToCheck) {
|
|
293
|
+
try {
|
|
294
|
+
const dirPath = join(GLOBAL_STORAGE, projectId, dir)
|
|
295
|
+
const files = await fs.readdir(dirPath)
|
|
296
|
+
if (files.length === 0) {
|
|
297
|
+
await fs.rmdir(dirPath)
|
|
298
|
+
deleted.push(dir + '/')
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
// Directory doesn't exist or not empty
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return deleted
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export type MigrationResult = {
|
|
309
|
+
file: string
|
|
310
|
+
success: boolean
|
|
311
|
+
error?: string
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function migrateProject(projectId: string, deleteLegacy: boolean = true): Promise<{
|
|
315
|
+
success: boolean
|
|
316
|
+
results: MigrationResult[]
|
|
317
|
+
deletedFiles: string[]
|
|
318
|
+
viewsGenerated?: boolean
|
|
319
|
+
error?: string
|
|
320
|
+
}> {
|
|
321
|
+
const apiKey = await getApiKey()
|
|
322
|
+
if (!apiKey) {
|
|
323
|
+
return { success: false, results: [], deletedFiles: [], error: 'No OpenRouter API key configured' }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const openrouter = createOpenAI({
|
|
327
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
328
|
+
apiKey
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
const results: MigrationResult[] = []
|
|
332
|
+
|
|
333
|
+
for (const file of MIGRATION_FILES) {
|
|
334
|
+
// Read all source files for this migration
|
|
335
|
+
const sourceContents: string[] = []
|
|
336
|
+
for (const mdPath of file.mdPaths) {
|
|
337
|
+
const content = await readMdFile(projectId, mdPath)
|
|
338
|
+
if (content && content.trim()) {
|
|
339
|
+
sourceContents.push(`## Source: ${mdPath}\n${content}`)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (sourceContents.length === 0) {
|
|
344
|
+
results.push({ file: file.name, success: true, error: 'No source files found, skipped' })
|
|
345
|
+
continue
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const combinedContent = sourceContents.join('\n\n---\n\n')
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const schema = SCHEMAS[file.schemaKey]
|
|
352
|
+
const { text } = await generateText({
|
|
353
|
+
model: openrouter('anthropic/claude-3.5-haiku'),
|
|
354
|
+
prompt: `Parse these source files and extract ALL structured data. Return ONLY valid JSON matching this schema (no markdown, no explanation):
|
|
355
|
+
|
|
356
|
+
## Target Schema:
|
|
357
|
+
${schema}
|
|
358
|
+
|
|
359
|
+
## Source Files Content:
|
|
360
|
+
${combinedContent}
|
|
361
|
+
|
|
362
|
+
## CRITICAL Pattern Extraction Rules - ZERO DATA LOSS:
|
|
363
|
+
|
|
364
|
+
AGENT PATTERNS - Extract agent type:
|
|
365
|
+
- "**Agent**: be" → agent: "be"
|
|
366
|
+
- "**Agent**: fe + be" → agent: "fe+be"
|
|
367
|
+
- "**Agent**: fe" → agent: "fe"
|
|
368
|
+
|
|
369
|
+
COMMIT PATTERNS - Extract git commit info:
|
|
370
|
+
- "**Commit**: \`0a7bbea\` feat(security): Multi-tenant..." → commit: {hash: "0a7bbea", message: "feat(security): Multi-tenant..."}
|
|
371
|
+
- "**Commits**: \`feat(sprint-6): Sales Reports\`, \`feat(sprint-6): Stock audit\`" → extract multiple commit messages
|
|
372
|
+
|
|
373
|
+
DURATION PATTERNS - Parse to {hours, minutes, totalMinutes, display}:
|
|
374
|
+
- "~1m" or "(1m)" → {hours: 0, minutes: 1, totalMinutes: 1, display: "~1m"}
|
|
375
|
+
- "~45m" → {hours: 0, minutes: 45, totalMinutes: 45, display: "~45m"}
|
|
376
|
+
- "~1h" or "(1h)" → {hours: 1, minutes: 0, totalMinutes: 60, display: "~1h"}
|
|
377
|
+
- "(7h 39m)" → {hours: 7, minutes: 39, totalMinutes: 499, display: "7h 39m"}
|
|
378
|
+
- "**Time**: ~28h" → {hours: 28, minutes: 0, totalMinutes: 1680, display: "~28h"}
|
|
379
|
+
|
|
380
|
+
TASK COUNT PATTERNS:
|
|
381
|
+
- "**Tasks**: 7" → taskCount: 7
|
|
382
|
+
- "(7 tasks, ~25m)" → taskCount: 7, duration: {hours: 0, minutes: 25, totalMinutes: 25}
|
|
383
|
+
- "(8 tasks)" → taskCount: 8
|
|
384
|
+
|
|
385
|
+
CODE METRICS - Parse "Files: X | +Y/-Z | Commits: N":
|
|
386
|
+
- "Files: 4 | +160/-31 | Commits: 0" → {filesChanged: 4, linesAdded: 160, linesRemoved: 31, commits: 0}
|
|
387
|
+
- "**Files**: 59" → {filesChanged: 59, linesAdded: null, linesRemoved: null, commits: null}
|
|
388
|
+
|
|
389
|
+
QUALITY STATUS - Parse "Lint: X | Tests: Y":
|
|
390
|
+
- "Lint: warnings" → lintStatus: "warning"
|
|
391
|
+
- "Tests: failed (2 date-helper tests)" → testStatus: "fail", testDetails: "2 date-helper tests"
|
|
392
|
+
|
|
393
|
+
IMPACT/EFFORT - Parse "Impact: X | Effort: Y":
|
|
394
|
+
- "Impact: **HIGH** | Effort: 1-2 days" → impact: "high", effort: "1-2 days"
|
|
395
|
+
|
|
396
|
+
ROI - Count stars:
|
|
397
|
+
- "⭐⭐⭐⭐⭐" → roi: 5
|
|
398
|
+
|
|
399
|
+
TASK TYPE - Detect from emoji:
|
|
400
|
+
- "🐛 [HIGH]" → type: "bug", priority: "high"
|
|
401
|
+
- "[ ] Task" → completed: false
|
|
402
|
+
- "[x] Done" → completed: true
|
|
403
|
+
|
|
404
|
+
VERSION - Extract number:
|
|
405
|
+
- "(v0.11.6)" or "v0.11.6" → version: "0.11.6"
|
|
406
|
+
- "Security Hardening v0.2.0" → name: "Security Hardening", version: "0.2.0"
|
|
407
|
+
|
|
408
|
+
SECTIONS - Detect from headers:
|
|
409
|
+
- "## Active Tasks" → section: "active"
|
|
410
|
+
- "## Previously Active" → section: "previously_active"
|
|
411
|
+
- "### Pain Points:" or "### Riesgos Críticos" → painPoints[] or risks[]
|
|
412
|
+
- "### Why This Feature?" → why[]
|
|
413
|
+
- "### Technical Notes" → technicalNotes[]
|
|
414
|
+
- "### Stack Definido" → stack: {frontend, backend, payments, ai, deploy}
|
|
415
|
+
- "### Módulos V1" → modules: [{name, description}]
|
|
416
|
+
- "### Roles" → roles: [{name, description}]
|
|
417
|
+
|
|
418
|
+
SOURCE PATTERNS:
|
|
419
|
+
- "**Source**: docs/technical-spec-v1.md, docs/edr-v1.md" → source: "docs/...", sourceFiles: ["docs/technical-spec-v1.md", "docs/edr-v1.md"]
|
|
420
|
+
|
|
421
|
+
STATUS WITH DATE:
|
|
422
|
+
- "**Status**: COMPLETED 2025-11-29" → status: "completed", completedAt: "2025-11-29T00:00:00.000Z"
|
|
423
|
+
- "### Sprint 6 - Reports + Audits (7 tasks, ~25m) - 2025-12-09 ✅" → sprintName, taskCount, duration, completedDate, status: "completed"
|
|
424
|
+
|
|
425
|
+
DESCRIPTION EXTRACTION:
|
|
426
|
+
- Capture ALL narrative text between the title and bullet points as "description"
|
|
427
|
+
- Code blocks (triple backticks) → codeSnippets: ["code string"]
|
|
428
|
+
- "CRITICAL: Multi-tenant isolation..." → description: "CRITICAL: Multi-tenant isolation..."
|
|
429
|
+
|
|
430
|
+
## Instructions:
|
|
431
|
+
- Return ONLY the JSON object, nothing else
|
|
432
|
+
- Generate unique IDs: task_xxxxxxxx, feat_xxxxxxxx, idea_xxxxxxxx, ship_xxxxxxxx
|
|
433
|
+
- ALL dates must be ISO-8601: YYYY-MM-DDTHH:mm:ss.sssZ
|
|
434
|
+
- Set lastUpdated to: "${new Date().toISOString()}"
|
|
435
|
+
- Preserve ALL rich data from MD - don't lose any information
|
|
436
|
+
- If a field is optional and no data exists, omit it completely`
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
// Parse the JSON response
|
|
440
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/)
|
|
441
|
+
if (!jsonMatch) {
|
|
442
|
+
throw new Error('No valid JSON found in response')
|
|
443
|
+
}
|
|
444
|
+
const parsedData = JSON.parse(jsonMatch[0])
|
|
445
|
+
|
|
446
|
+
await writeJsonFile(projectId, file.jsonPath, parsedData)
|
|
447
|
+
results.push({ file: file.name, success: true })
|
|
448
|
+
} catch (error) {
|
|
449
|
+
results.push({
|
|
450
|
+
file: file.name,
|
|
451
|
+
success: false,
|
|
452
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Copy project.json to data/ if exists
|
|
458
|
+
try {
|
|
459
|
+
const projectJson = await readMdFile(projectId, 'project.json')
|
|
460
|
+
if (projectJson) {
|
|
461
|
+
await writeJsonFile(projectId, 'data/project.json', JSON.parse(projectJson))
|
|
462
|
+
results.push({ file: 'project', success: true })
|
|
463
|
+
}
|
|
464
|
+
} catch {
|
|
465
|
+
// project.json doesn't exist or invalid, skip
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const allSuccess = results.filter(r => !r.error?.includes('skipped')).every(r => r.success)
|
|
469
|
+
|
|
470
|
+
// Delete legacy files only if all migrations succeeded
|
|
471
|
+
let deletedFiles: string[] = []
|
|
472
|
+
if (allSuccess && deleteLegacy) {
|
|
473
|
+
deletedFiles = await deleteLegacyFiles(projectId)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Generate views from new JSON files
|
|
477
|
+
// Fire and forget - don't block request to prevent OOM from subprocess spawning
|
|
478
|
+
let viewsGenerated = false
|
|
479
|
+
if (allSuccess) {
|
|
480
|
+
try {
|
|
481
|
+
const child = exec(`bun ${join(PRJCT_CLI_PATH, 'bin', 'generate-views.js')} --project=${projectId}`)
|
|
482
|
+
child.on('error', (err) => console.error('[Views] Generation error:', err))
|
|
483
|
+
child.unref() // Allow parent to exit independently
|
|
484
|
+
viewsGenerated = true
|
|
485
|
+
results.push({ file: 'views', success: true })
|
|
486
|
+
} catch (viewError) {
|
|
487
|
+
// Views generation failed but migration still succeeded
|
|
488
|
+
results.push({
|
|
489
|
+
file: 'views',
|
|
490
|
+
success: false,
|
|
491
|
+
error: viewError instanceof Error ? viewError.message : 'Failed to generate views'
|
|
492
|
+
})
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return { success: allSuccess, results, deletedFiles, viewsGenerated }
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export type ProjectInfo = {
|
|
500
|
+
id: string
|
|
501
|
+
name: string
|
|
502
|
+
needsMigration: boolean
|
|
503
|
+
hasMdFiles: boolean
|
|
504
|
+
hasDataDir: boolean
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export async function getProjectsToMigrate(): Promise<ProjectInfo[]> {
|
|
508
|
+
try {
|
|
509
|
+
const dirs = await fs.readdir(GLOBAL_STORAGE)
|
|
510
|
+
const validProjects: ProjectInfo[] = []
|
|
511
|
+
|
|
512
|
+
for (const dir of dirs) {
|
|
513
|
+
// Skip hidden directories
|
|
514
|
+
if (dir.startsWith('.')) continue
|
|
515
|
+
|
|
516
|
+
const projectPath = join(GLOBAL_STORAGE, dir)
|
|
517
|
+
|
|
518
|
+
// Must have CLAUDE.md or project.json to be a valid project
|
|
519
|
+
let claudeMd: string | null = null
|
|
520
|
+
try {
|
|
521
|
+
claudeMd = await fs.readFile(join(projectPath, 'CLAUDE.md'), 'utf-8')
|
|
522
|
+
} catch {
|
|
523
|
+
// No CLAUDE.md
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Check for project.json in root or data/
|
|
527
|
+
let projectJson: Record<string, unknown> | null = null
|
|
528
|
+
try {
|
|
529
|
+
projectJson = JSON.parse(await fs.readFile(join(projectPath, 'project.json'), 'utf-8'))
|
|
530
|
+
} catch {
|
|
531
|
+
try {
|
|
532
|
+
projectJson = JSON.parse(await fs.readFile(join(projectPath, 'data', 'project.json'), 'utf-8'))
|
|
533
|
+
} catch {
|
|
534
|
+
// No project.json
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Must have at least one identifier
|
|
539
|
+
if (!claudeMd && !projectJson) continue
|
|
540
|
+
|
|
541
|
+
// Check if project has legacy MD files to migrate
|
|
542
|
+
let hasMdFiles = false
|
|
543
|
+
for (const file of MIGRATION_FILES) {
|
|
544
|
+
for (const mdPath of file.mdPaths) {
|
|
545
|
+
if (mdPath.endsWith('.md')) {
|
|
546
|
+
try {
|
|
547
|
+
const content = await fs.readFile(join(projectPath, mdPath), 'utf-8')
|
|
548
|
+
if (content && content.trim().length > 20) {
|
|
549
|
+
hasMdFiles = true
|
|
550
|
+
break
|
|
551
|
+
}
|
|
552
|
+
} catch {
|
|
553
|
+
// File doesn't exist
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (hasMdFiles) break
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Check if data/ directory exists with JSON files
|
|
561
|
+
let hasDataDir = false
|
|
562
|
+
try {
|
|
563
|
+
const dataFiles = await fs.readdir(join(projectPath, 'data'))
|
|
564
|
+
hasDataDir = dataFiles.some(f => f.endsWith('.json'))
|
|
565
|
+
} catch {
|
|
566
|
+
// data/ doesn't exist
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Project needs migration if it has MD files but no data/ or incomplete data/
|
|
570
|
+
const needsMigration = hasMdFiles
|
|
571
|
+
|
|
572
|
+
// Get project name
|
|
573
|
+
let name = dir
|
|
574
|
+
if (projectJson && typeof projectJson.name === 'string') {
|
|
575
|
+
name = projectJson.name
|
|
576
|
+
} else if (claudeMd) {
|
|
577
|
+
const match = claudeMd.match(/# (.+) - Project Context/)
|
|
578
|
+
if (match) name = match[1]
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
validProjects.push({
|
|
582
|
+
id: dir,
|
|
583
|
+
name,
|
|
584
|
+
needsMigration,
|
|
585
|
+
hasMdFiles,
|
|
586
|
+
hasDataDir
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Sort: projects needing migration first, then by name
|
|
591
|
+
return validProjects.sort((a, b) => {
|
|
592
|
+
if (a.needsMigration !== b.needsMigration) {
|
|
593
|
+
return a.needsMigration ? -1 : 1
|
|
594
|
+
}
|
|
595
|
+
return a.name.localeCompare(b.name)
|
|
596
|
+
})
|
|
597
|
+
} catch {
|
|
598
|
+
return []
|
|
599
|
+
}
|
|
600
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projects 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 { loadProject, type ProjectJson } from '@/lib/json-loader'
|
|
11
|
+
import { getProjects as getProjectsList, getProject as getProjectLegacy } from '@/lib/projects'
|
|
12
|
+
|
|
13
|
+
export type { ProjectJson }
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get single project by ID - cached per request
|
|
17
|
+
*/
|
|
18
|
+
export const getProject = cache(async (projectId: string): Promise<ProjectJson | null> => {
|
|
19
|
+
// Try JSON first
|
|
20
|
+
const jsonProject = await loadProject(projectId)
|
|
21
|
+
if (jsonProject) {
|
|
22
|
+
return jsonProject
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Fallback to legacy
|
|
26
|
+
try {
|
|
27
|
+
const legacyProject = await getProjectLegacy(projectId)
|
|
28
|
+
if (legacyProject) {
|
|
29
|
+
return {
|
|
30
|
+
projectId: legacyProject.id,
|
|
31
|
+
name: legacyProject.name,
|
|
32
|
+
repoPath: legacyProject.path,
|
|
33
|
+
techStack: legacyProject.techStack || [],
|
|
34
|
+
fileCount: legacyProject.filesCount ? parseInt(legacyProject.filesCount) : 0,
|
|
35
|
+
commitCount: legacyProject.commitsCount ? parseInt(legacyProject.commitsCount) : 0,
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
lastSync: new Date().toISOString()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// Ignore errors
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get all projects - cached per request
|
|
49
|
+
*/
|
|
50
|
+
export const getProjects = cache(async () => {
|
|
51
|
+
return getProjectsList()
|
|
52
|
+
})
|