prjct-cli 0.11.4 → 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 +72 -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 +246 -54
- 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.ts +405 -0
- 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} +99 -89
- 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} +35 -18
- 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} +62 -23
- 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 +203 -403
- 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/BentoGrid/BentoGrid.tsx +18 -0
- 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/EmptyState/EmptyState.tsx +58 -0
- 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/IdeasCard/IdeasCard.tsx +48 -0
- 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/ProgressRing/ProgressRing.tsx +51 -0
- 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/RoadmapCard/RoadmapCard.tsx +77 -0
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
- package/packages/web/components/RoadmapCard/index.ts +2 -0
- package/packages/web/components/ShipsCard/ShipsCard.tsx +52 -0
- 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/SparklineChart/SparklineChart.tsx +38 -0
- 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/StreakCard/StreakCard.tsx +53 -0
- 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/components/ui/tooltip.tsx +2 -2
- 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/next-env.d.ts +1 -1
- package/packages/web/package.json +10 -6
- 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 -128
- package/core/agentic/chain-of-thought.js +0 -578
- package/core/agentic/command-executor.js +0 -421
- 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 -850
- package/core/agentic/parallel-tools.js +0 -366
- package/core/agentic/plan-mode.js +0 -572
- package/core/agentic/prompt-builder.js +0 -338
- 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 -799
- 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/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,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Format Constants
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all file patterns and formats used in prjct.
|
|
5
|
+
* Use these constants instead of hardcoded strings.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { FORMATS, STATUS } from '../constants/formats'
|
|
10
|
+
*
|
|
11
|
+
* // Instead of:
|
|
12
|
+
* const nowContent = `# NOW\n\n**${task}**`
|
|
13
|
+
*
|
|
14
|
+
* // Use:
|
|
15
|
+
* const nowContent = FORMATS.NOW.header(task)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* NOW file format patterns.
|
|
21
|
+
*/
|
|
22
|
+
export const NOW = {
|
|
23
|
+
/** Header marker for NOW file */
|
|
24
|
+
HEADER: '# NOW',
|
|
25
|
+
|
|
26
|
+
/** Pattern to extract task from NOW content */
|
|
27
|
+
TASK_PATTERN: /\*\*(.+?)\*\*/,
|
|
28
|
+
|
|
29
|
+
/** Generate NOW file content */
|
|
30
|
+
content: (task: string, startedAt: string, agent?: string, confidence?: number): string => {
|
|
31
|
+
const lines = [
|
|
32
|
+
'# NOW',
|
|
33
|
+
'',
|
|
34
|
+
`**${task}**`,
|
|
35
|
+
'',
|
|
36
|
+
`Started: ${startedAt}`,
|
|
37
|
+
]
|
|
38
|
+
if (agent) {
|
|
39
|
+
lines.push(`Agent: ${agent}${confidence ? ` (${Math.round(confidence * 100)}% confidence)` : ''}`)
|
|
40
|
+
}
|
|
41
|
+
return lines.join('\n') + '\n'
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/** Extract task from NOW content */
|
|
45
|
+
extractTask: (content: string): string | null => {
|
|
46
|
+
const match = content.match(NOW.TASK_PATTERN)
|
|
47
|
+
return match ? match[1] : null
|
|
48
|
+
},
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* SHIPPED file format patterns.
|
|
53
|
+
*/
|
|
54
|
+
export const SHIPPED = {
|
|
55
|
+
/** Header marker for SHIPPED file */
|
|
56
|
+
HEADER: '# SHIPPED 🚀',
|
|
57
|
+
|
|
58
|
+
/** Generate ship entry */
|
|
59
|
+
entry: (feature: string, date: string, duration?: string): string => {
|
|
60
|
+
const lines = [
|
|
61
|
+
`## ${feature}`,
|
|
62
|
+
'',
|
|
63
|
+
`Shipped: ${date}`,
|
|
64
|
+
]
|
|
65
|
+
if (duration) {
|
|
66
|
+
lines.push(`Duration: ${duration}`)
|
|
67
|
+
}
|
|
68
|
+
return lines.join('\n') + '\n\n'
|
|
69
|
+
},
|
|
70
|
+
} as const
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* NEXT file format patterns.
|
|
74
|
+
*/
|
|
75
|
+
export const NEXT = {
|
|
76
|
+
/** Header marker for NEXT file */
|
|
77
|
+
HEADER: '# NEXT',
|
|
78
|
+
|
|
79
|
+
/** Pattern for task entries */
|
|
80
|
+
TASK_PATTERN: /^[-*]\s+\[([x ])\]\s+(.+)$/gm,
|
|
81
|
+
|
|
82
|
+
/** Generate task entry */
|
|
83
|
+
entry: (task: string, completed: boolean = false): string => {
|
|
84
|
+
return `- [${completed ? 'x' : ' '}] ${task}\n`
|
|
85
|
+
},
|
|
86
|
+
} as const
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* IDEAS file format patterns.
|
|
90
|
+
*/
|
|
91
|
+
export const IDEAS = {
|
|
92
|
+
/** Header marker for IDEAS file */
|
|
93
|
+
HEADER: '# IDEAS 💡',
|
|
94
|
+
|
|
95
|
+
/** Generate idea entry */
|
|
96
|
+
entry: (idea: string, date: string): string => {
|
|
97
|
+
return `- ${idea} _(${date})_\n`
|
|
98
|
+
},
|
|
99
|
+
} as const
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* ROADMAP file format patterns.
|
|
103
|
+
*/
|
|
104
|
+
export const ROADMAP = {
|
|
105
|
+
/** Header marker for ROADMAP file */
|
|
106
|
+
HEADER: '# ROADMAP 🗺️',
|
|
107
|
+
|
|
108
|
+
/** Status markers */
|
|
109
|
+
STATUS: {
|
|
110
|
+
PLANNED: '📋 Planned',
|
|
111
|
+
IN_PROGRESS: '🚧 In Progress',
|
|
112
|
+
COMPLETED: '✅ Completed',
|
|
113
|
+
BLOCKED: '🚫 Blocked',
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/** Generate feature entry */
|
|
117
|
+
entry: (feature: string, status: keyof typeof ROADMAP.STATUS, tasks?: string[]): string => {
|
|
118
|
+
const lines = [
|
|
119
|
+
`## ${feature}`,
|
|
120
|
+
'',
|
|
121
|
+
`Status: ${ROADMAP.STATUS[status]}`,
|
|
122
|
+
]
|
|
123
|
+
if (tasks && tasks.length > 0) {
|
|
124
|
+
lines.push('', '### Tasks', '')
|
|
125
|
+
tasks.forEach(task => lines.push(`- [ ] ${task}`))
|
|
126
|
+
}
|
|
127
|
+
return lines.join('\n') + '\n\n'
|
|
128
|
+
},
|
|
129
|
+
} as const
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Session file paths.
|
|
133
|
+
*/
|
|
134
|
+
export const SESSION = {
|
|
135
|
+
/** Date format for session directories */
|
|
136
|
+
DATE_FORMAT: 'YYYY-MM-DD',
|
|
137
|
+
|
|
138
|
+
/** Generate session path */
|
|
139
|
+
path: (year: string, month: string, day: string): string => {
|
|
140
|
+
return `sessions/${year}-${month}/${year}-${month}-${day}`
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/** Session metadata filename */
|
|
144
|
+
METADATA_FILE: 'session-meta.json',
|
|
145
|
+
|
|
146
|
+
/** Context log filename */
|
|
147
|
+
CONTEXT_FILE: 'context.jsonl',
|
|
148
|
+
} as const
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Status values used throughout prjct.
|
|
152
|
+
*/
|
|
153
|
+
export const STATUS = {
|
|
154
|
+
PENDING: 'pending',
|
|
155
|
+
IN_PROGRESS: 'in_progress',
|
|
156
|
+
COMPLETED: 'completed',
|
|
157
|
+
BLOCKED: 'blocked',
|
|
158
|
+
PAUSED: 'paused',
|
|
159
|
+
} as const
|
|
160
|
+
|
|
161
|
+
export type Status = typeof STATUS[keyof typeof STATUS]
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Priority levels.
|
|
165
|
+
*/
|
|
166
|
+
export const PRIORITY = {
|
|
167
|
+
LOW: 'low',
|
|
168
|
+
MEDIUM: 'medium',
|
|
169
|
+
HIGH: 'high',
|
|
170
|
+
CRITICAL: 'critical',
|
|
171
|
+
} as const
|
|
172
|
+
|
|
173
|
+
export type Priority = typeof PRIORITY[keyof typeof PRIORITY]
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Combined exports for easy import.
|
|
177
|
+
*/
|
|
178
|
+
export const FORMATS = {
|
|
179
|
+
NOW,
|
|
180
|
+
SHIPPED,
|
|
181
|
+
NEXT,
|
|
182
|
+
IDEAS,
|
|
183
|
+
ROADMAP,
|
|
184
|
+
SESSION,
|
|
185
|
+
STATUS,
|
|
186
|
+
PRIORITY,
|
|
187
|
+
} as const
|
|
@@ -13,24 +13,56 @@
|
|
|
13
13
|
* Called by: /p:sync, /p:analyze, /p:init
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
import fs from 'fs/promises'
|
|
17
|
+
import path from 'path'
|
|
18
|
+
import os from 'os'
|
|
19
|
+
|
|
20
|
+
interface StackInfo {
|
|
21
|
+
primary: string | null
|
|
22
|
+
full: string | null
|
|
23
|
+
languages: string[]
|
|
24
|
+
frameworks: string[]
|
|
25
|
+
dependencies: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ProjectStructure {
|
|
29
|
+
fileCount: number | null
|
|
30
|
+
directories: string[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface GitCommit {
|
|
34
|
+
hash: string
|
|
35
|
+
message: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GitStats {
|
|
39
|
+
commits: number | null
|
|
40
|
+
contributors: number | null
|
|
41
|
+
recentCommits: GitCommit[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface AgentExpertise {
|
|
45
|
+
name: string
|
|
46
|
+
role: string
|
|
47
|
+
expertise: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface ContextResult {
|
|
51
|
+
agents: string[]
|
|
52
|
+
stack: StackInfo
|
|
53
|
+
currentTask: string | null
|
|
54
|
+
}
|
|
19
55
|
|
|
20
56
|
/**
|
|
21
57
|
* Generate RICH project context file for Claude
|
|
22
58
|
* Embeds key information so Claude doesn't need to read multiple files
|
|
23
|
-
*
|
|
24
|
-
* @param {string} projectPath - Local project path
|
|
25
|
-
* @param {string} projectId - Project ID from config
|
|
26
|
-
* @returns {Promise<{agents: string[], stack: object, currentTask: string|null}>}
|
|
27
59
|
*/
|
|
28
|
-
async function generateLocalContext(projectPath, projectId) {
|
|
60
|
+
async function generateLocalContext(projectPath: string, projectId: string): Promise<ContextResult> {
|
|
29
61
|
const globalPath = path.join(os.homedir(), '.prjct-cli/projects', projectId)
|
|
30
62
|
const projectName = path.basename(projectPath)
|
|
31
63
|
|
|
32
64
|
// Helper to read files safely
|
|
33
|
-
const readSafe = async (p) => {
|
|
65
|
+
const readSafe = async (p: string): Promise<string | null> => {
|
|
34
66
|
try {
|
|
35
67
|
return await fs.readFile(p, 'utf-8')
|
|
36
68
|
} catch {
|
|
@@ -40,7 +72,7 @@ async function generateLocalContext(projectPath, projectId) {
|
|
|
40
72
|
|
|
41
73
|
// 1. Read ALL data sources in parallel
|
|
42
74
|
const agentsDir = path.join(globalPath, 'agents')
|
|
43
|
-
let agentFiles = []
|
|
75
|
+
let agentFiles: string[] = []
|
|
44
76
|
try {
|
|
45
77
|
agentFiles = await fs.readdir(agentsDir)
|
|
46
78
|
agentFiles = agentFiles.filter(f => f.endsWith('.md'))
|
|
@@ -152,10 +184,10 @@ For detailed information, read these files:
|
|
|
152
184
|
/**
|
|
153
185
|
* Extract FULL stack info from repo-summary
|
|
154
186
|
*/
|
|
155
|
-
function extractFullStack(summary) {
|
|
156
|
-
if (!summary) return { primary: null, full: null }
|
|
187
|
+
function extractFullStack(summary: string | null): StackInfo {
|
|
188
|
+
if (!summary) return { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
|
|
157
189
|
|
|
158
|
-
const result = { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
|
|
190
|
+
const result: StackInfo = { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
|
|
159
191
|
|
|
160
192
|
// Extract languages (e.g., "### JavaScript/TypeScript")
|
|
161
193
|
const langMatch = summary.match(/###\s*(JavaScript|TypeScript|Python|Go|Rust|Ruby|Java|C#|PHP)/i)
|
|
@@ -177,10 +209,10 @@ function extractFullStack(summary) {
|
|
|
177
209
|
/**
|
|
178
210
|
* Extract project structure from repo-summary
|
|
179
211
|
*/
|
|
180
|
-
function extractProjectStructure(summary) {
|
|
212
|
+
function extractProjectStructure(summary: string | null): ProjectStructure {
|
|
181
213
|
if (!summary) return { fileCount: null, directories: [] }
|
|
182
214
|
|
|
183
|
-
const result = { fileCount: null, directories: [] }
|
|
215
|
+
const result: ProjectStructure = { fileCount: null, directories: [] }
|
|
184
216
|
|
|
185
217
|
// Extract file count
|
|
186
218
|
const fileMatch = summary.match(/\*\*Total Files\*\*:\s*(\d+)/i)
|
|
@@ -196,10 +228,10 @@ function extractProjectStructure(summary) {
|
|
|
196
228
|
/**
|
|
197
229
|
* Extract git stats from repo-summary
|
|
198
230
|
*/
|
|
199
|
-
function extractGitStats(summary) {
|
|
231
|
+
function extractGitStats(summary: string | null): GitStats {
|
|
200
232
|
if (!summary) return { commits: null, contributors: null, recentCommits: [] }
|
|
201
233
|
|
|
202
|
-
const result = { commits: null, contributors: null, recentCommits: [] }
|
|
234
|
+
const result: GitStats = { commits: null, contributors: null, recentCommits: [] }
|
|
203
235
|
|
|
204
236
|
// Extract commit count
|
|
205
237
|
const commitMatch = summary.match(/\*\*Total Commits\*\*:\s*(\d+)/i)
|
|
@@ -217,7 +249,7 @@ function extractGitStats(summary) {
|
|
|
217
249
|
result.recentCommits = commitLines.slice(0, 5).map(line => {
|
|
218
250
|
const match = line.match(/- `([a-f0-9]+)` (.+?) \(/)
|
|
219
251
|
return match ? { hash: match[1], message: match[2] } : null
|
|
220
|
-
}).filter(
|
|
252
|
+
}).filter((c): c is GitCommit => c !== null)
|
|
221
253
|
}
|
|
222
254
|
}
|
|
223
255
|
|
|
@@ -227,8 +259,8 @@ function extractGitStats(summary) {
|
|
|
227
259
|
/**
|
|
228
260
|
* Extract agent expertise from agent files
|
|
229
261
|
*/
|
|
230
|
-
async function extractAgentExpertise(agentsDir, agentFiles) {
|
|
231
|
-
const expertise = []
|
|
262
|
+
async function extractAgentExpertise(agentsDir: string, agentFiles: string[]): Promise<AgentExpertise[]> {
|
|
263
|
+
const expertise: AgentExpertise[] = []
|
|
232
264
|
|
|
233
265
|
for (const file of agentFiles.slice(0, 6)) { // Max 6 agents to keep context small
|
|
234
266
|
try {
|
|
@@ -257,7 +289,7 @@ async function extractAgentExpertise(agentsDir, agentFiles) {
|
|
|
257
289
|
/**
|
|
258
290
|
* Extract current task from now.md
|
|
259
291
|
*/
|
|
260
|
-
function extractCurrentTask(now) {
|
|
292
|
+
function extractCurrentTask(now: string | null): string | null {
|
|
261
293
|
if (!now) return null
|
|
262
294
|
|
|
263
295
|
const lines = now.split('\n')
|
|
@@ -273,7 +305,7 @@ function extractCurrentTask(now) {
|
|
|
273
305
|
/**
|
|
274
306
|
* Extract top N tasks from next.md
|
|
275
307
|
*/
|
|
276
|
-
function extractTopTasks(next, count) {
|
|
308
|
+
function extractTopTasks(next: string | null, count: number): string[] {
|
|
277
309
|
if (!next) return []
|
|
278
310
|
|
|
279
311
|
return next
|
|
@@ -287,10 +319,10 @@ function extractTopTasks(next, count) {
|
|
|
287
319
|
/**
|
|
288
320
|
* Extract active features from roadmap.md
|
|
289
321
|
*/
|
|
290
|
-
function extractActiveFeatures(roadmap) {
|
|
322
|
+
function extractActiveFeatures(roadmap: string | null): string[] {
|
|
291
323
|
if (!roadmap) return []
|
|
292
324
|
|
|
293
|
-
const features = []
|
|
325
|
+
const features: string[] = []
|
|
294
326
|
const lines = roadmap.split('\n')
|
|
295
327
|
let inActive = false
|
|
296
328
|
|
|
@@ -314,7 +346,7 @@ function extractActiveFeatures(roadmap) {
|
|
|
314
346
|
/**
|
|
315
347
|
* Extract recent ideas from ideas.md
|
|
316
348
|
*/
|
|
317
|
-
function extractRecentIdeas(ideas) {
|
|
349
|
+
function extractRecentIdeas(ideas: string | null): string[] {
|
|
318
350
|
if (!ideas) return []
|
|
319
351
|
|
|
320
352
|
return ideas
|
|
@@ -325,4 +357,5 @@ function extractRecentIdeas(ideas) {
|
|
|
325
357
|
.filter(Boolean)
|
|
326
358
|
}
|
|
327
359
|
|
|
328
|
-
|
|
360
|
+
export { generateLocalContext }
|
|
361
|
+
export default { generateLocalContext }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages agents.json - specialized AI agents.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ArrayManager } from './base-manager'
|
|
8
|
+
import type { AgentSchema, AgentsSchema } from '../schemas'
|
|
9
|
+
import { DEFAULT_AGENT } from '../schemas'
|
|
10
|
+
|
|
11
|
+
class AgentsManager extends ArrayManager<AgentSchema> {
|
|
12
|
+
constructor() {
|
|
13
|
+
super('agents.json')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getAgents(projectId: string): Promise<AgentsSchema> {
|
|
17
|
+
return this.read(projectId)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async getAgent(projectId: string, name: string): Promise<AgentSchema | undefined> {
|
|
21
|
+
return this.find(projectId, (agent) => agent.name === name)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async addAgent(
|
|
25
|
+
projectId: string,
|
|
26
|
+
agent: Pick<AgentSchema, 'name' | 'description'> & Partial<AgentSchema>
|
|
27
|
+
): Promise<AgentsSchema> {
|
|
28
|
+
const fullAgent: AgentSchema = {
|
|
29
|
+
...DEFAULT_AGENT,
|
|
30
|
+
...agent
|
|
31
|
+
}
|
|
32
|
+
return this.add(projectId, fullAgent)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async updateAgent(
|
|
36
|
+
projectId: string,
|
|
37
|
+
name: string,
|
|
38
|
+
updates: Partial<AgentSchema>
|
|
39
|
+
): Promise<AgentsSchema> {
|
|
40
|
+
return this.updateItem(
|
|
41
|
+
projectId,
|
|
42
|
+
(agent) => agent.name === name,
|
|
43
|
+
(agent) => ({ ...agent, ...updates })
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async removeAgent(projectId: string, name: string): Promise<AgentsSchema> {
|
|
48
|
+
return this.remove(projectId, (agent) => agent.name === name)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async setAgents(projectId: string, agents: AgentsSchema): Promise<void> {
|
|
52
|
+
await this.write(projectId, agents)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async incrementTasksCompleted(projectId: string, name: string): Promise<AgentsSchema> {
|
|
56
|
+
return this.updateItem(
|
|
57
|
+
projectId,
|
|
58
|
+
(agent) => agent.name === name,
|
|
59
|
+
(agent) => ({
|
|
60
|
+
...agent,
|
|
61
|
+
tasksCompleted: (agent.tasksCompleted || 0) + 1
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async updateSuccessRate(
|
|
67
|
+
projectId: string,
|
|
68
|
+
name: string,
|
|
69
|
+
successRate: number
|
|
70
|
+
): Promise<AgentsSchema> {
|
|
71
|
+
return this.updateAgent(projectId, name, { successRate })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const agentsManager = new AgentsManager()
|
|
76
|
+
export default agentsManager
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analysis Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages analysis.json - repository analysis data.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BaseManager } from './base-manager'
|
|
8
|
+
import type { AnalysisSchema, CodePattern, AntiPattern } from '../schemas'
|
|
9
|
+
import { DEFAULT_ANALYSIS } from '../schemas'
|
|
10
|
+
|
|
11
|
+
class AnalysisManager extends BaseManager<AnalysisSchema> {
|
|
12
|
+
constructor() {
|
|
13
|
+
super('analysis.json')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected getDefault(projectId: string): AnalysisSchema {
|
|
17
|
+
return {
|
|
18
|
+
...DEFAULT_ANALYSIS,
|
|
19
|
+
projectId,
|
|
20
|
+
analyzedAt: new Date().toISOString()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getAnalysis(projectId: string): Promise<AnalysisSchema> {
|
|
25
|
+
return this.read(projectId)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async updateAnalysis(
|
|
29
|
+
projectId: string,
|
|
30
|
+
updates: Partial<Omit<AnalysisSchema, 'projectId'>>
|
|
31
|
+
): Promise<AnalysisSchema> {
|
|
32
|
+
return this.update(projectId, (analysis) => ({
|
|
33
|
+
...analysis,
|
|
34
|
+
...updates,
|
|
35
|
+
analyzedAt: new Date().toISOString()
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async setLanguages(projectId: string, languages: string[]): Promise<AnalysisSchema> {
|
|
40
|
+
return this.updateAnalysis(projectId, { languages })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async setFrameworks(projectId: string, frameworks: string[]): Promise<AnalysisSchema> {
|
|
44
|
+
return this.updateAnalysis(projectId, { frameworks })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async addPattern(projectId: string, pattern: CodePattern): Promise<AnalysisSchema> {
|
|
48
|
+
return this.update(projectId, (analysis) => ({
|
|
49
|
+
...analysis,
|
|
50
|
+
patterns: [...analysis.patterns, pattern],
|
|
51
|
+
analyzedAt: new Date().toISOString()
|
|
52
|
+
}))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async addAntiPattern(projectId: string, antiPattern: AntiPattern): Promise<AnalysisSchema> {
|
|
56
|
+
return this.update(projectId, (analysis) => ({
|
|
57
|
+
...analysis,
|
|
58
|
+
antiPatterns: [...analysis.antiPatterns, antiPattern],
|
|
59
|
+
analyzedAt: new Date().toISOString()
|
|
60
|
+
}))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async setPatterns(projectId: string, patterns: CodePattern[]): Promise<AnalysisSchema> {
|
|
64
|
+
return this.updateAnalysis(projectId, { patterns })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async setAntiPatterns(projectId: string, antiPatterns: AntiPattern[]): Promise<AnalysisSchema> {
|
|
68
|
+
return this.updateAnalysis(projectId, { antiPatterns })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getPatterns(projectId: string): Promise<CodePattern[]> {
|
|
72
|
+
const analysis = await this.read(projectId)
|
|
73
|
+
return analysis.patterns
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getAntiPatterns(projectId: string): Promise<AntiPattern[]> {
|
|
77
|
+
const analysis = await this.read(projectId)
|
|
78
|
+
return analysis.antiPatterns
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const analysisManager = new AnalysisManager()
|
|
83
|
+
export default analysisManager
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Manager
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for JSON file managers.
|
|
5
|
+
* Provides common CRUD operations for all data types.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path'
|
|
9
|
+
import * as fileHelper from '../utils/file-helper'
|
|
10
|
+
import pathManager from '../infrastructure/path-manager'
|
|
11
|
+
|
|
12
|
+
export abstract class BaseManager<T> {
|
|
13
|
+
protected filename: string
|
|
14
|
+
protected cache: Map<string, T> = new Map()
|
|
15
|
+
protected cacheTimeout = 5000 // 5 seconds
|
|
16
|
+
protected lastRead: Map<string, number> = new Map()
|
|
17
|
+
|
|
18
|
+
constructor(filename: string) {
|
|
19
|
+
this.filename = filename
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get file path for a project.
|
|
24
|
+
*/
|
|
25
|
+
protected getFilePath(projectId: string): string {
|
|
26
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
27
|
+
return path.join(globalPath, this.filename)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get default value for this data type.
|
|
32
|
+
*/
|
|
33
|
+
protected abstract getDefault(projectId: string): T
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Read data from JSON file.
|
|
37
|
+
*/
|
|
38
|
+
async read(projectId: string): Promise<T> {
|
|
39
|
+
const now = Date.now()
|
|
40
|
+
const lastReadTime = this.lastRead.get(projectId) || 0
|
|
41
|
+
|
|
42
|
+
// Return cached if fresh
|
|
43
|
+
if (now - lastReadTime < this.cacheTimeout && this.cache.has(projectId)) {
|
|
44
|
+
return this.cache.get(projectId)!
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const filePath = this.getFilePath(projectId)
|
|
48
|
+
const data = await fileHelper.readJson<T>(filePath, this.getDefault(projectId))
|
|
49
|
+
|
|
50
|
+
// Update cache
|
|
51
|
+
this.cache.set(projectId, data!)
|
|
52
|
+
this.lastRead.set(projectId, now)
|
|
53
|
+
|
|
54
|
+
return data!
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Write data to JSON file.
|
|
59
|
+
*/
|
|
60
|
+
async write(projectId: string, data: T): Promise<void> {
|
|
61
|
+
const filePath = this.getFilePath(projectId)
|
|
62
|
+
|
|
63
|
+
// Ensure directory exists
|
|
64
|
+
await fileHelper.ensureDir(path.dirname(filePath))
|
|
65
|
+
|
|
66
|
+
await fileHelper.writeJson(filePath, data)
|
|
67
|
+
|
|
68
|
+
// Update cache
|
|
69
|
+
this.cache.set(projectId, data)
|
|
70
|
+
this.lastRead.set(projectId, Date.now())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if file exists.
|
|
75
|
+
*/
|
|
76
|
+
async exists(projectId: string): Promise<boolean> {
|
|
77
|
+
const filePath = this.getFilePath(projectId)
|
|
78
|
+
return fileHelper.fileExists(filePath)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize with default data.
|
|
83
|
+
*/
|
|
84
|
+
async initialize(projectId: string): Promise<T> {
|
|
85
|
+
const data = this.getDefault(projectId)
|
|
86
|
+
await this.write(projectId, data)
|
|
87
|
+
return data
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clear cache.
|
|
92
|
+
*/
|
|
93
|
+
clearCache(projectId?: string): void {
|
|
94
|
+
if (projectId) {
|
|
95
|
+
this.cache.delete(projectId)
|
|
96
|
+
this.lastRead.delete(projectId)
|
|
97
|
+
} else {
|
|
98
|
+
this.cache.clear()
|
|
99
|
+
this.lastRead.clear()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Update data with a partial update.
|
|
105
|
+
*/
|
|
106
|
+
async update(projectId: string, updater: (data: T) => T): Promise<T> {
|
|
107
|
+
const current = await this.read(projectId)
|
|
108
|
+
const updated = updater(current)
|
|
109
|
+
await this.write(projectId, updated)
|
|
110
|
+
return updated
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Base manager for array-based JSON files.
|
|
116
|
+
*/
|
|
117
|
+
export abstract class ArrayManager<T> extends BaseManager<T[]> {
|
|
118
|
+
protected getDefault(): T[] {
|
|
119
|
+
return []
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Add item to array.
|
|
124
|
+
*/
|
|
125
|
+
async add(projectId: string, item: T): Promise<T[]> {
|
|
126
|
+
return this.update(projectId, (data) => [...data, item])
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Remove item by predicate.
|
|
131
|
+
*/
|
|
132
|
+
async remove(projectId: string, predicate: (item: T) => boolean): Promise<T[]> {
|
|
133
|
+
return this.update(projectId, (data) => data.filter((item) => !predicate(item)))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Find item by predicate.
|
|
138
|
+
*/
|
|
139
|
+
async find(projectId: string, predicate: (item: T) => boolean): Promise<T | undefined> {
|
|
140
|
+
const data = await this.read(projectId)
|
|
141
|
+
return data.find(predicate)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Update item by predicate.
|
|
146
|
+
*/
|
|
147
|
+
async updateItem(
|
|
148
|
+
projectId: string,
|
|
149
|
+
predicate: (item: T) => boolean,
|
|
150
|
+
updater: (item: T) => T
|
|
151
|
+
): Promise<T[]> {
|
|
152
|
+
return this.update(projectId, (data) =>
|
|
153
|
+
data.map((item) => (predicate(item) ? updater(item) : item))
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|