prjct-cli 0.11.5 → 0.12.0
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 +134 -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 +598 -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 +36 -6
- 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
|
@@ -1,206 +1,247 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { notFound } from 'next/navigation'
|
|
2
|
+
import {
|
|
3
|
+
getStats,
|
|
4
|
+
getInsightMessage,
|
|
5
|
+
calculateStreak,
|
|
6
|
+
calculateHealthScore,
|
|
7
|
+
getVelocityChange,
|
|
8
|
+
getWeeklyVelocityData,
|
|
9
|
+
type StatsResult
|
|
10
|
+
} from '@/lib/services/stats.server'
|
|
11
|
+
import { getProject } from '@/lib/services/projects.server'
|
|
9
12
|
import type { TimelineEvent } from '@/lib/parse-prjct-files'
|
|
10
13
|
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const today = new Date()
|
|
32
|
-
for (let i = 0; i < 30; i++) {
|
|
33
|
-
const date = new Date(today)
|
|
34
|
-
date.setDate(date.getDate() - i)
|
|
35
|
-
const dateStr = date.toISOString().split('T')[0]
|
|
36
|
-
if (dates.has(dateStr)) streak++
|
|
37
|
-
else if (i > 0) break
|
|
38
|
-
}
|
|
39
|
-
return streak
|
|
14
|
+
import { BentoGrid } from '@/components/BentoGrid'
|
|
15
|
+
import { HeroSection } from '@/components/HeroSection'
|
|
16
|
+
import { NowCard } from '@/components/NowCard'
|
|
17
|
+
import { VelocityCard } from '@/components/VelocityCard'
|
|
18
|
+
import { StreakCard } from '@/components/StreakCard'
|
|
19
|
+
import { QueueCard } from '@/components/QueueCard'
|
|
20
|
+
import { ShipsCard } from '@/components/ShipsCard'
|
|
21
|
+
import { IdeasCard } from '@/components/IdeasCard'
|
|
22
|
+
import { AgentsCard } from '@/components/AgentsCard'
|
|
23
|
+
import { RoadmapCard } from '@/components/RoadmapCard'
|
|
24
|
+
import { ActivityTimeline } from '@/components/ActivityTimeline'
|
|
25
|
+
|
|
26
|
+
// Types for normalized component data
|
|
27
|
+
interface NormalizedCurrentTask {
|
|
28
|
+
task: string
|
|
29
|
+
startedAt?: string
|
|
30
|
+
agent?: string
|
|
31
|
+
estimatedDuration?: string
|
|
32
|
+
pausedAt?: string
|
|
33
|
+
pauseReason?: string
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const queueSize = stats?.queue?.length || 0
|
|
48
|
-
const recentActivity = stats?.timeline?.slice(0, 7).length || 0
|
|
36
|
+
interface NormalizedQueueItem {
|
|
37
|
+
task: string
|
|
38
|
+
priority?: 'low' | 'medium' | 'high' | 'critical' | number
|
|
39
|
+
estimatedDuration?: string
|
|
40
|
+
}
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
interface NormalizedShip {
|
|
43
|
+
name: string
|
|
44
|
+
date: string
|
|
45
|
+
duration?: string
|
|
46
|
+
}
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
interface NormalizedIdea {
|
|
49
|
+
title: string
|
|
50
|
+
impact?: string
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
interface NormalizedAgent {
|
|
54
|
+
name: string
|
|
55
|
+
description?: string
|
|
56
|
+
successRate?: number
|
|
57
|
+
tasksCompleted?: number
|
|
58
|
+
bestFor?: string[]
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
interface NormalizedRoadmap {
|
|
62
|
+
phases: Array<{
|
|
63
|
+
name: string
|
|
64
|
+
progress: number
|
|
65
|
+
features?: Array<{ name: string; status: string }>
|
|
66
|
+
}>
|
|
67
|
+
progress: number
|
|
68
|
+
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
// Data normalization functions
|
|
71
|
+
function normalizeCurrentTask(stats: StatsResult): NormalizedCurrentTask | null {
|
|
72
|
+
if (stats.state?.currentTask) {
|
|
73
|
+
return {
|
|
74
|
+
task: stats.state.currentTask.description,
|
|
75
|
+
startedAt: stats.state.currentTask.startedAt,
|
|
76
|
+
// Simplified - removed legacy fields not in new schema
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return stats.legacyStats?.currentTask ?? null
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
function normalizeQueue(stats: StatsResult): NormalizedQueueItem[] {
|
|
83
|
+
if (stats.queue?.tasks) {
|
|
84
|
+
return stats.queue.tasks
|
|
85
|
+
.filter(t => !t.completed)
|
|
86
|
+
.map(q => ({
|
|
87
|
+
task: q.description,
|
|
88
|
+
priority: q.priority,
|
|
89
|
+
}))
|
|
90
|
+
}
|
|
91
|
+
return stats.legacyStats?.queue ?? []
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
function normalizeRoadmap(stats: StatsResult): NormalizedRoadmap | null {
|
|
95
|
+
const features = stats.roadmap?.features ?? []
|
|
96
|
+
if (features.length > 0) {
|
|
97
|
+
const completed = features.filter(f =>
|
|
98
|
+
f.status === 'shipped' || f.status === 'completed'
|
|
99
|
+
).length
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
phases: features.map(f => ({
|
|
103
|
+
name: f.name,
|
|
104
|
+
progress: f.status === 'shipped' || f.status === 'completed' ? 100 :
|
|
105
|
+
f.status === 'active' ? 50 : 0,
|
|
106
|
+
features: f.tasks.map(t => ({
|
|
107
|
+
name: t.description,
|
|
108
|
+
status: t.completed ? 'completed' : 'pending'
|
|
109
|
+
}))
|
|
110
|
+
})),
|
|
111
|
+
progress: Math.round((completed / features.length) * 100)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return stats.legacyStats?.roadmap ?? null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizeShipped(stats: StatsResult): NormalizedShip[] {
|
|
118
|
+
const items = stats.shipped?.items ?? []
|
|
119
|
+
return items.map(s => ({
|
|
120
|
+
name: s.name,
|
|
121
|
+
date: s.shippedAt,
|
|
122
|
+
}))
|
|
123
|
+
}
|
|
86
124
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
function normalizeIdeas(stats: StatsResult): NormalizedIdea[] {
|
|
126
|
+
const ideas = stats.ideas?.ideas ?? []
|
|
127
|
+
return ideas
|
|
128
|
+
.filter(i => i.status === 'pending')
|
|
129
|
+
.map(i => ({
|
|
130
|
+
title: i.text,
|
|
131
|
+
impact: i.priority.toUpperCase()
|
|
132
|
+
}))
|
|
133
|
+
}
|
|
91
134
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
135
|
+
function normalizeAgents(stats: StatsResult): NormalizedAgent[] {
|
|
136
|
+
return stats.agents.map(a => ({
|
|
137
|
+
name: a.name,
|
|
138
|
+
description: a.description,
|
|
139
|
+
successRate: a.successRate,
|
|
140
|
+
tasksCompleted: a.tasksCompleted,
|
|
141
|
+
bestFor: a.bestFor,
|
|
142
|
+
}))
|
|
143
|
+
}
|
|
96
144
|
|
|
97
|
-
|
|
145
|
+
function normalizeTimeline(stats: StatsResult): TimelineEvent[] {
|
|
146
|
+
if (stats.metrics?.recentActivity?.length) {
|
|
147
|
+
return stats.metrics.recentActivity.map((a: { timestamp: string; description: string; action?: string }) => ({
|
|
148
|
+
ts: a.timestamp,
|
|
149
|
+
type: a.action || 'task_completed',
|
|
150
|
+
task: a.description,
|
|
151
|
+
}))
|
|
98
152
|
}
|
|
153
|
+
return stats.legacyStats?.timeline ?? []
|
|
154
|
+
}
|
|
99
155
|
|
|
100
|
-
|
|
156
|
+
function getVelocity(stats: StatsResult): number {
|
|
157
|
+
if (stats.metrics?.velocity?.tasksPerDay) {
|
|
158
|
+
return stats.metrics.velocity.tasksPerDay
|
|
159
|
+
}
|
|
160
|
+
return stats.legacyStats?.metrics?.velocity?.tasksPerDay ?? 0
|
|
101
161
|
}
|
|
102
162
|
|
|
103
|
-
function
|
|
104
|
-
return
|
|
105
|
-
<div className="p-8 space-y-8">
|
|
106
|
-
<div className="flex items-start gap-6">
|
|
107
|
-
<div className="h-20 w-20 rounded-full bg-muted animate-pulse" />
|
|
108
|
-
<div className="space-y-3">
|
|
109
|
-
<div className="h-16 w-32 bg-muted rounded animate-pulse" />
|
|
110
|
-
<div className="h-4 w-48 bg-muted rounded animate-pulse" />
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
<BentoGrid>
|
|
114
|
-
<BentoCardSkeleton size="2x2" />
|
|
115
|
-
<BentoCardSkeleton size="1x1" />
|
|
116
|
-
<BentoCardSkeleton size="2x2" />
|
|
117
|
-
<BentoCardSkeleton size="1x1" />
|
|
118
|
-
<BentoCardSkeleton size="1x2" />
|
|
119
|
-
<BentoCardSkeleton size="1x2" />
|
|
120
|
-
<BentoCardSkeleton size="1x1" />
|
|
121
|
-
<BentoCardSkeleton size="1x1" />
|
|
122
|
-
</BentoGrid>
|
|
123
|
-
</div>
|
|
124
|
-
)
|
|
163
|
+
function getTotalShips(stats: StatsResult): number {
|
|
164
|
+
return stats.shipped?.items?.length ?? stats.legacyStats?.summary?.totalShipsEver ?? 0
|
|
125
165
|
}
|
|
126
166
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
167
|
+
function getTasksCompleted(stats: StatsResult): number {
|
|
168
|
+
return stats.metrics?.currentSprint?.tasksCompleted ?? stats.legacyStats?.metrics?.tasksCompleted ?? 0
|
|
169
|
+
}
|
|
130
170
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
171
|
+
interface PageProps {
|
|
172
|
+
params: Promise<{ id: string }>
|
|
173
|
+
}
|
|
134
174
|
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
const insightMessage = useMemo(() => getInsightMessage(stats, streak), [stats, streak])
|
|
138
|
-
const velocity = stats?.metrics?.velocity?.tasksPerDay || 0
|
|
139
|
-
const velocityChange = useMemo(() => getVelocityChange(velocity), [velocity])
|
|
140
|
-
const weeklyVelocityData = useMemo(() => getWeeklyVelocityData(stats?.timeline || []), [stats?.timeline])
|
|
175
|
+
export default async function ProjectStatsPage({ params }: PageProps) {
|
|
176
|
+
const { id: projectId } = await params
|
|
141
177
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
178
|
+
// Fetch data directly on server - no API calls
|
|
179
|
+
const [project, stats] = await Promise.all([
|
|
180
|
+
getProject(projectId),
|
|
181
|
+
getStats(projectId)
|
|
182
|
+
])
|
|
145
183
|
|
|
146
|
-
if (!
|
|
147
|
-
|
|
148
|
-
<div className="flex items-center justify-center h-full">
|
|
149
|
-
<div className="text-center space-y-4">
|
|
150
|
-
<p className="text-4xl text-muted-foreground">404</p>
|
|
151
|
-
<Button variant="ghost" onClick={() => router.back()}>
|
|
152
|
-
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
153
|
-
Back
|
|
154
|
-
</Button>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
)
|
|
184
|
+
if (!stats.hasData) {
|
|
185
|
+
notFound()
|
|
158
186
|
}
|
|
159
187
|
|
|
188
|
+
// Compute derived values using service functions
|
|
189
|
+
const streak = calculateStreak(stats.metrics)
|
|
190
|
+
const healthScore = calculateHealthScore(stats)
|
|
191
|
+
const velocity = getVelocity(stats)
|
|
192
|
+
const velocityChange = getVelocityChange(velocity)
|
|
193
|
+
const insightMessage = getInsightMessage(stats, streak)
|
|
194
|
+
const weeklyVelocityData = getWeeklyVelocityData(stats.metrics)
|
|
195
|
+
|
|
196
|
+
// Normalize data for components
|
|
197
|
+
const currentTask = normalizeCurrentTask(stats)
|
|
198
|
+
const queue = normalizeQueue(stats)
|
|
199
|
+
const roadmap = normalizeRoadmap(stats)
|
|
200
|
+
const shipped = normalizeShipped(stats)
|
|
201
|
+
const ideas = normalizeIdeas(stats)
|
|
202
|
+
const agents = normalizeAgents(stats)
|
|
203
|
+
const timeline = normalizeTimeline(stats)
|
|
204
|
+
const totalShips = getTotalShips(stats)
|
|
205
|
+
const tasksCompleted = getTasksCompleted(stats)
|
|
206
|
+
|
|
160
207
|
return (
|
|
161
|
-
<div className="flex h-full flex-col p-8 overflow-auto">
|
|
162
|
-
{/*
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
208
|
+
<div className="flex h-full flex-col p-4 md:p-8 overflow-auto">
|
|
209
|
+
{/* Mobile: Add padding for hamburger menu */}
|
|
210
|
+
<div className="pl-10 md:pl-0">
|
|
211
|
+
<HeroSection
|
|
212
|
+
projectId={projectId}
|
|
213
|
+
projectName={project?.name ?? projectId}
|
|
214
|
+
tasksCompleted={tasksCompleted}
|
|
215
|
+
healthScore={healthScore}
|
|
216
|
+
velocity={velocity}
|
|
217
|
+
velocityChange={velocityChange}
|
|
218
|
+
insightMessage={insightMessage}
|
|
219
|
+
timeline={timeline}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{/* Bento Grid - Server Components */}
|
|
224
|
+
<BentoGrid className="mt-6 md:mt-8">
|
|
225
|
+
<NowCard currentTask={currentTask} />
|
|
178
226
|
<VelocityCard
|
|
179
227
|
tasksPerDay={velocity}
|
|
180
228
|
weeklyData={weeklyVelocityData}
|
|
181
229
|
change={velocityChange}
|
|
182
230
|
/>
|
|
183
|
-
<RoadmapCard roadmap={
|
|
184
|
-
|
|
185
|
-
{/* STREAK under VELOCITY */}
|
|
231
|
+
<RoadmapCard roadmap={roadmap} />
|
|
186
232
|
<StreakCard streak={streak} />
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
<
|
|
190
|
-
<
|
|
191
|
-
ships={stats.shipped || []}
|
|
192
|
-
totalShips={stats.summary?.totalShipsEver || 0}
|
|
193
|
-
/>
|
|
194
|
-
<IdeasCard ideas={stats.ideas?.pending || []} />
|
|
195
|
-
<AgentsCard agents={stats.agents || []} />
|
|
233
|
+
<QueueCard queue={queue} />
|
|
234
|
+
<ShipsCard ships={shipped} totalShips={totalShips} />
|
|
235
|
+
<IdeasCard ideas={ideas} />
|
|
236
|
+
<AgentsCard agents={agents} />
|
|
196
237
|
</BentoGrid>
|
|
197
238
|
|
|
198
|
-
{/* Activity Timeline */}
|
|
199
|
-
<div className="mt-8">
|
|
200
|
-
<ActivityTimeline timeline={
|
|
239
|
+
{/* Activity Timeline - Client Component */}
|
|
240
|
+
<div className="mt-6 md:mt-8">
|
|
241
|
+
<ActivityTimeline timeline={timeline} />
|
|
201
242
|
</div>
|
|
202
243
|
|
|
203
|
-
<div className="h-8" />
|
|
244
|
+
<div className="h-6 md:h-8" />
|
|
204
245
|
</div>
|
|
205
246
|
)
|
|
206
247
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
4
5
|
import { useTheme } from 'next-themes'
|
|
5
|
-
import { Settings as SettingsIcon, CheckCircle, XCircle, Terminal, Sun, Moon, Monitor } from 'lucide-react'
|
|
6
|
+
import { Settings as SettingsIcon, CheckCircle, XCircle, Terminal, Sun, Moon, Monitor, Key, Eye, EyeOff, Loader2, Trash2, RefreshCw, Database } from 'lucide-react'
|
|
6
7
|
import { Button } from '@/components/ui/button'
|
|
8
|
+
import { Input } from '@/components/ui/input'
|
|
7
9
|
|
|
8
10
|
export default function Settings() {
|
|
9
11
|
const { theme, setTheme } = useTheme()
|
|
12
|
+
const queryClient = useQueryClient()
|
|
13
|
+
const [apiKey, setApiKey] = useState('')
|
|
14
|
+
const [showKey, setShowKey] = useState(false)
|
|
15
|
+
|
|
10
16
|
const { data: claudeStatus } = useQuery({
|
|
11
17
|
queryKey: ['claude-status'],
|
|
12
18
|
queryFn: async () => {
|
|
@@ -16,6 +22,79 @@ export default function Settings() {
|
|
|
16
22
|
}
|
|
17
23
|
})
|
|
18
24
|
|
|
25
|
+
const { data: settings } = useQuery({
|
|
26
|
+
queryKey: ['settings'],
|
|
27
|
+
queryFn: async () => {
|
|
28
|
+
const res = await fetch('/api/settings')
|
|
29
|
+
const json = await res.json()
|
|
30
|
+
return json.data
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const saveKeyMutation = useMutation({
|
|
35
|
+
mutationFn: async (key: string) => {
|
|
36
|
+
const res = await fetch('/api/settings', {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({ openRouterApiKey: key })
|
|
40
|
+
})
|
|
41
|
+
if (!res.ok) throw new Error('Failed to save')
|
|
42
|
+
return res.json()
|
|
43
|
+
},
|
|
44
|
+
onSuccess: () => {
|
|
45
|
+
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
|
46
|
+
setApiKey('')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const deleteKeyMutation = useMutation({
|
|
51
|
+
mutationFn: async () => {
|
|
52
|
+
const res = await fetch('/api/settings', { method: 'DELETE' })
|
|
53
|
+
if (!res.ok) throw new Error('Failed to delete')
|
|
54
|
+
return res.json()
|
|
55
|
+
},
|
|
56
|
+
onSuccess: () => {
|
|
57
|
+
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const { data: projects } = useQuery({
|
|
62
|
+
queryKey: ['migrate-projects'],
|
|
63
|
+
queryFn: async () => {
|
|
64
|
+
const res = await fetch('/api/migrate')
|
|
65
|
+
const json = await res.json()
|
|
66
|
+
return json.data?.projects || [] as { id: string; name: string }[]
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const [selectedProject, setSelectedProject] = useState<string>('')
|
|
71
|
+
const [migrationResults, setMigrationResults] = useState<{
|
|
72
|
+
success: boolean
|
|
73
|
+
results: Array<{ file: string; success: boolean; error?: string }>
|
|
74
|
+
} | null>(null)
|
|
75
|
+
|
|
76
|
+
const migrateMutation = useMutation({
|
|
77
|
+
mutationFn: async (projectId: string) => {
|
|
78
|
+
const res = await fetch('/api/migrate', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({ projectId })
|
|
82
|
+
})
|
|
83
|
+
const json = await res.json()
|
|
84
|
+
if (!res.ok) throw new Error(json.error || 'Migration failed')
|
|
85
|
+
return json.data
|
|
86
|
+
},
|
|
87
|
+
onSuccess: (data) => {
|
|
88
|
+
setMigrationResults(data)
|
|
89
|
+
},
|
|
90
|
+
onError: (error) => {
|
|
91
|
+
setMigrationResults({
|
|
92
|
+
success: false,
|
|
93
|
+
results: [{ file: 'migration', success: false, error: error.message }]
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
19
98
|
return (
|
|
20
99
|
<div className="p-6 h-full overflow-auto">
|
|
21
100
|
<header className="mb-6">
|
|
@@ -71,6 +150,147 @@ export default function Settings() {
|
|
|
71
150
|
</div>
|
|
72
151
|
</section>
|
|
73
152
|
|
|
153
|
+
{/* OpenRouter API Key */}
|
|
154
|
+
<section className="bg-card border border-border rounded-lg p-6">
|
|
155
|
+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
156
|
+
<Key className="w-5 h-5" />
|
|
157
|
+
OpenRouter API Key
|
|
158
|
+
</h2>
|
|
159
|
+
|
|
160
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
161
|
+
Required for AI-powered features like MD→JSON migration. Get your key at{' '}
|
|
162
|
+
<a
|
|
163
|
+
href="https://openrouter.ai/keys"
|
|
164
|
+
target="_blank"
|
|
165
|
+
rel="noopener noreferrer"
|
|
166
|
+
className="text-primary underline"
|
|
167
|
+
>
|
|
168
|
+
openrouter.ai/keys
|
|
169
|
+
</a>
|
|
170
|
+
</p>
|
|
171
|
+
|
|
172
|
+
{settings?.hasApiKey ? (
|
|
173
|
+
<div className="space-y-3">
|
|
174
|
+
<div className="flex items-center gap-3 p-3 bg-muted/50 rounded-md">
|
|
175
|
+
<CheckCircle className="w-5 h-5 text-green-500 shrink-0" />
|
|
176
|
+
<span className="text-sm font-mono">{settings.maskedKey}</span>
|
|
177
|
+
<Button
|
|
178
|
+
variant="ghost"
|
|
179
|
+
size="icon-sm"
|
|
180
|
+
onClick={() => deleteKeyMutation.mutate()}
|
|
181
|
+
disabled={deleteKeyMutation.isPending}
|
|
182
|
+
className="ml-auto text-muted-foreground hover:text-destructive"
|
|
183
|
+
>
|
|
184
|
+
{deleteKeyMutation.isPending ? (
|
|
185
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
186
|
+
) : (
|
|
187
|
+
<Trash2 className="w-4 h-4" />
|
|
188
|
+
)}
|
|
189
|
+
</Button>
|
|
190
|
+
</div>
|
|
191
|
+
<p className="text-xs text-muted-foreground mb-4">
|
|
192
|
+
API key is stored locally in ~/.prjct-cli/settings.json
|
|
193
|
+
</p>
|
|
194
|
+
|
|
195
|
+
{/* Data Migration Section - only shown when API key exists */}
|
|
196
|
+
<div className="pt-4 border-t border-border">
|
|
197
|
+
<h3 className="text-sm font-medium mb-3 flex items-center gap-2">
|
|
198
|
+
<Database className="w-4 h-4" />
|
|
199
|
+
Data Migration (MD → JSON)
|
|
200
|
+
</h3>
|
|
201
|
+
<p className="text-xs text-muted-foreground mb-3">
|
|
202
|
+
Convert markdown files to structured JSON using AI. This enables better parsing and querying.
|
|
203
|
+
</p>
|
|
204
|
+
|
|
205
|
+
<div className="flex gap-2 mb-3">
|
|
206
|
+
<select
|
|
207
|
+
value={selectedProject}
|
|
208
|
+
onChange={(e) => {
|
|
209
|
+
setSelectedProject(e.target.value)
|
|
210
|
+
setMigrationResults(null)
|
|
211
|
+
}}
|
|
212
|
+
className="flex-1 h-9 rounded-md border border-input bg-transparent px-3 text-sm"
|
|
213
|
+
>
|
|
214
|
+
<option value="">Select a project...</option>
|
|
215
|
+
{(projects as { id: string; name: string }[] || []).map((p) => (
|
|
216
|
+
<option key={p.id} value={p.id}>{p.name}</option>
|
|
217
|
+
))}
|
|
218
|
+
</select>
|
|
219
|
+
<Button
|
|
220
|
+
onClick={() => selectedProject && migrateMutation.mutate(selectedProject)}
|
|
221
|
+
disabled={!selectedProject || migrateMutation.isPending}
|
|
222
|
+
size="sm"
|
|
223
|
+
>
|
|
224
|
+
{migrateMutation.isPending ? (
|
|
225
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
226
|
+
) : (
|
|
227
|
+
<>
|
|
228
|
+
<RefreshCw className="w-4 h-4" />
|
|
229
|
+
Sync
|
|
230
|
+
</>
|
|
231
|
+
)}
|
|
232
|
+
</Button>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
{migrationResults && (
|
|
236
|
+
<div className={`p-3 rounded-md text-sm ${migrationResults.success ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}`}>
|
|
237
|
+
<p className={`font-medium mb-2 ${migrationResults.success ? 'text-green-500' : 'text-red-500'}`}>
|
|
238
|
+
{migrationResults.success ? 'Migration complete!' : 'Migration had errors'}
|
|
239
|
+
</p>
|
|
240
|
+
<ul className="space-y-1 text-xs">
|
|
241
|
+
{migrationResults.results.map((r, i) => (
|
|
242
|
+
<li key={i} className="flex items-center gap-2">
|
|
243
|
+
{r.success ? (
|
|
244
|
+
<CheckCircle className="w-3 h-3 text-green-500" />
|
|
245
|
+
) : (
|
|
246
|
+
<XCircle className="w-3 h-3 text-red-500" />
|
|
247
|
+
)}
|
|
248
|
+
<span>{r.file}</span>
|
|
249
|
+
{r.error && <span className="text-muted-foreground">- {r.error}</span>}
|
|
250
|
+
</li>
|
|
251
|
+
))}
|
|
252
|
+
</ul>
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
) : (
|
|
258
|
+
<div className="space-y-3">
|
|
259
|
+
<div className="flex gap-2">
|
|
260
|
+
<div className="relative flex-1">
|
|
261
|
+
<Input
|
|
262
|
+
type={showKey ? 'text' : 'password'}
|
|
263
|
+
placeholder="sk-or-v1-..."
|
|
264
|
+
value={apiKey}
|
|
265
|
+
onChange={(e) => setApiKey(e.target.value)}
|
|
266
|
+
className="pr-10 font-mono"
|
|
267
|
+
/>
|
|
268
|
+
<button
|
|
269
|
+
type="button"
|
|
270
|
+
onClick={() => setShowKey(!showKey)}
|
|
271
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
|
272
|
+
>
|
|
273
|
+
{showKey ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
|
274
|
+
</button>
|
|
275
|
+
</div>
|
|
276
|
+
<Button
|
|
277
|
+
onClick={() => saveKeyMutation.mutate(apiKey)}
|
|
278
|
+
disabled={!apiKey || saveKeyMutation.isPending}
|
|
279
|
+
>
|
|
280
|
+
{saveKeyMutation.isPending ? (
|
|
281
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
282
|
+
) : (
|
|
283
|
+
'Save'
|
|
284
|
+
)}
|
|
285
|
+
</Button>
|
|
286
|
+
</div>
|
|
287
|
+
{saveKeyMutation.isError && (
|
|
288
|
+
<p className="text-sm text-destructive">Failed to save API key</p>
|
|
289
|
+
)}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</section>
|
|
293
|
+
|
|
74
294
|
{/* Claude Code Status */}
|
|
75
295
|
<section className="bg-card border border-border rounded-lg p-6">
|
|
76
296
|
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|