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,41 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import fsSync from 'fs'
|
|
3
|
+
import readline from 'readline'
|
|
4
|
+
import path from 'path'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* JSONL Helper - Centralized JSONL parsing and writing
|
|
8
8
|
*
|
|
9
9
|
* Eliminates duplicated JSONL logic across:
|
|
10
|
-
* - session-manager.
|
|
11
|
-
* - commands.
|
|
12
|
-
* - analyzer.
|
|
10
|
+
* - session-manager.ts (_parseJsonLines)
|
|
11
|
+
* - commands.ts (inline parsing)
|
|
12
|
+
* - analyzer.ts (inline parsing)
|
|
13
13
|
*
|
|
14
14
|
* JSONL Format: One JSON object per line, newline-separated
|
|
15
15
|
* Example:
|
|
16
16
|
* {"ts":"2025-10-04T14:30:00Z","type":"feature_add","name":"auth"}
|
|
17
17
|
* {"ts":"2025-10-04T15:00:00Z","type":"task_start","task":"JWT"}
|
|
18
|
-
*
|
|
19
|
-
* @module jsonl-helper
|
|
20
18
|
*/
|
|
21
19
|
|
|
20
|
+
interface NodeError extends Error {
|
|
21
|
+
code?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface FileSizeWarning {
|
|
25
|
+
sizeMB: number
|
|
26
|
+
isLarge: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
/**
|
|
23
30
|
* Parse JSONL content into array of objects
|
|
24
31
|
* Handles malformed lines gracefully (skips them)
|
|
25
|
-
*
|
|
26
|
-
* @param {string} content - JSONL content
|
|
27
|
-
* @returns {Array<Object>} - Array of parsed objects
|
|
28
32
|
*/
|
|
29
|
-
function parseJsonLines(content) {
|
|
33
|
+
export function parseJsonLines<T = Record<string, unknown>>(content: string): T[] {
|
|
30
34
|
const lines = content.split('\n').filter((line) => line.trim())
|
|
31
|
-
const entries = []
|
|
35
|
+
const entries: T[] = []
|
|
32
36
|
|
|
33
37
|
for (const line of lines) {
|
|
34
38
|
try {
|
|
35
|
-
entries.push(JSON.parse(line))
|
|
36
|
-
} catch
|
|
39
|
+
entries.push(JSON.parse(line) as T)
|
|
40
|
+
} catch {
|
|
37
41
|
// Skip malformed lines silently
|
|
38
|
-
// Could optionally log warning
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -44,26 +47,20 @@ function parseJsonLines(content) {
|
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
49
|
* Convert array of objects to JSONL string
|
|
47
|
-
*
|
|
48
|
-
* @param {Array<Object>} objects - Array of objects to stringify
|
|
49
|
-
* @returns {string} - JSONL formatted string
|
|
50
50
|
*/
|
|
51
|
-
function stringifyJsonLines(objects) {
|
|
51
|
+
export function stringifyJsonLines(objects: unknown[]): string {
|
|
52
52
|
return objects.map((obj) => JSON.stringify(obj)).join('\n') + '\n'
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Read and parse JSONL file
|
|
57
|
-
*
|
|
58
|
-
* @param {string} filePath - Path to JSONL file
|
|
59
|
-
* @returns {Promise<Array<Object>>} - Array of parsed objects
|
|
60
57
|
*/
|
|
61
|
-
async function readJsonLines(filePath) {
|
|
58
|
+
export async function readJsonLines<T = Record<string, unknown>>(filePath: string): Promise<T[]> {
|
|
62
59
|
try {
|
|
63
60
|
const content = await fs.readFile(filePath, 'utf-8')
|
|
64
|
-
return parseJsonLines(content)
|
|
61
|
+
return parseJsonLines<T>(content)
|
|
65
62
|
} catch (error) {
|
|
66
|
-
if (error.code === 'ENOENT') {
|
|
63
|
+
if ((error as NodeError).code === 'ENOENT') {
|
|
67
64
|
return [] // File doesn't exist, return empty array
|
|
68
65
|
}
|
|
69
66
|
throw error
|
|
@@ -72,12 +69,8 @@ async function readJsonLines(filePath) {
|
|
|
72
69
|
|
|
73
70
|
/**
|
|
74
71
|
* Write array of objects to JSONL file (overwrites)
|
|
75
|
-
*
|
|
76
|
-
* @param {string} filePath - Path to JSONL file
|
|
77
|
-
* @param {Array<Object>} objects - Array of objects to write
|
|
78
|
-
* @returns {Promise<void>}
|
|
79
72
|
*/
|
|
80
|
-
async function writeJsonLines(filePath, objects) {
|
|
73
|
+
export async function writeJsonLines(filePath: string, objects: unknown[]): Promise<void> {
|
|
81
74
|
const content = stringifyJsonLines(objects)
|
|
82
75
|
await fs.writeFile(filePath, content, 'utf-8')
|
|
83
76
|
}
|
|
@@ -85,24 +78,16 @@ async function writeJsonLines(filePath, objects) {
|
|
|
85
78
|
/**
|
|
86
79
|
* Append a single object to JSONL file
|
|
87
80
|
* Uses append mode for efficiency (no full file read/write)
|
|
88
|
-
*
|
|
89
|
-
* @param {string} filePath - Path to JSONL file
|
|
90
|
-
* @param {Object} object - Object to append
|
|
91
|
-
* @returns {Promise<void>}
|
|
92
81
|
*/
|
|
93
|
-
async function appendJsonLine(filePath, object) {
|
|
82
|
+
export async function appendJsonLine(filePath: string, object: unknown): Promise<void> {
|
|
94
83
|
const line = JSON.stringify(object) + '\n'
|
|
95
84
|
await fs.appendFile(filePath, line, 'utf-8')
|
|
96
85
|
}
|
|
97
86
|
|
|
98
87
|
/**
|
|
99
88
|
* Append multiple objects to JSONL file
|
|
100
|
-
*
|
|
101
|
-
* @param {string} filePath - Path to JSONL file
|
|
102
|
-
* @param {Array<Object>} objects - Objects to append
|
|
103
|
-
* @returns {Promise<void>}
|
|
104
89
|
*/
|
|
105
|
-
async function appendJsonLines(filePath, objects) {
|
|
90
|
+
export async function appendJsonLines(filePath: string, objects: unknown[]): Promise<void> {
|
|
106
91
|
const content = stringifyJsonLines(objects)
|
|
107
92
|
await fs.appendFile(filePath, content, 'utf-8')
|
|
108
93
|
}
|
|
@@ -110,29 +95,25 @@ async function appendJsonLines(filePath, objects) {
|
|
|
110
95
|
/**
|
|
111
96
|
* Filter JSONL file entries by predicate
|
|
112
97
|
* Reads all entries, filters, returns matching ones
|
|
113
|
-
*
|
|
114
|
-
* @param {string} filePath - Path to JSONL file
|
|
115
|
-
* @param {Function} predicate - Filter function (entry => boolean)
|
|
116
|
-
* @returns {Promise<Array<Object>>} - Filtered entries
|
|
117
98
|
*/
|
|
118
|
-
async function filterJsonLines
|
|
119
|
-
|
|
99
|
+
export async function filterJsonLines<T = Record<string, unknown>>(
|
|
100
|
+
filePath: string,
|
|
101
|
+
predicate: (entry: T) => boolean
|
|
102
|
+
): Promise<T[]> {
|
|
103
|
+
const entries = await readJsonLines<T>(filePath)
|
|
120
104
|
return entries.filter(predicate)
|
|
121
105
|
}
|
|
122
106
|
|
|
123
107
|
/**
|
|
124
108
|
* Count lines in JSONL file (non-empty, parseable lines)
|
|
125
|
-
*
|
|
126
|
-
* @param {string} filePath - Path to JSONL file
|
|
127
|
-
* @returns {Promise<number>} - Number of valid lines
|
|
128
109
|
*/
|
|
129
|
-
async function countJsonLines(filePath) {
|
|
110
|
+
export async function countJsonLines(filePath: string): Promise<number> {
|
|
130
111
|
try {
|
|
131
112
|
const content = await fs.readFile(filePath, 'utf-8')
|
|
132
113
|
const lines = content.split('\n').filter((line) => line.trim())
|
|
133
114
|
return lines.length
|
|
134
115
|
} catch (error) {
|
|
135
|
-
if (error.code === 'ENOENT') {
|
|
116
|
+
if ((error as NodeError).code === 'ENOENT') {
|
|
136
117
|
return 0
|
|
137
118
|
}
|
|
138
119
|
throw error
|
|
@@ -142,40 +123,35 @@ async function countJsonLines(filePath) {
|
|
|
142
123
|
/**
|
|
143
124
|
* Get last N entries from JSONL file
|
|
144
125
|
* Efficient for large files (reads whole file but only returns last N)
|
|
145
|
-
*
|
|
146
|
-
* @param {string} filePath - Path to JSONL file
|
|
147
|
-
* @param {number} n - Number of entries to return
|
|
148
|
-
* @returns {Promise<Array<Object>>} - Last N entries
|
|
149
126
|
*/
|
|
150
|
-
async function getLastJsonLines
|
|
151
|
-
|
|
127
|
+
export async function getLastJsonLines<T = Record<string, unknown>>(
|
|
128
|
+
filePath: string,
|
|
129
|
+
n: number
|
|
130
|
+
): Promise<T[]> {
|
|
131
|
+
const entries = await readJsonLines<T>(filePath)
|
|
152
132
|
return entries.slice(-n)
|
|
153
133
|
}
|
|
154
134
|
|
|
155
135
|
/**
|
|
156
136
|
* Get first N entries from JSONL file
|
|
157
|
-
*
|
|
158
|
-
* @param {string} filePath - Path to JSONL file
|
|
159
|
-
* @param {number} n - Number of entries to return
|
|
160
|
-
* @returns {Promise<Array<Object>>} - First N entries
|
|
161
137
|
*/
|
|
162
|
-
async function getFirstJsonLines
|
|
163
|
-
|
|
138
|
+
export async function getFirstJsonLines<T = Record<string, unknown>>(
|
|
139
|
+
filePath: string,
|
|
140
|
+
n: number
|
|
141
|
+
): Promise<T[]> {
|
|
142
|
+
const entries = await readJsonLines<T>(filePath)
|
|
164
143
|
return entries.slice(0, n)
|
|
165
144
|
}
|
|
166
145
|
|
|
167
146
|
/**
|
|
168
147
|
* Merge multiple JSONL files into one array
|
|
169
148
|
* Useful for reading multiple sessions
|
|
170
|
-
*
|
|
171
|
-
* @param {Array<string>} filePaths - Array of JSONL file paths
|
|
172
|
-
* @returns {Promise<Array<Object>>} - Merged entries from all files
|
|
173
149
|
*/
|
|
174
|
-
async function mergeJsonLines(filePaths) {
|
|
175
|
-
const allEntries = []
|
|
150
|
+
export async function mergeJsonLines<T = Record<string, unknown>>(filePaths: string[]): Promise<T[]> {
|
|
151
|
+
const allEntries: T[] = []
|
|
176
152
|
|
|
177
153
|
for (const filePath of filePaths) {
|
|
178
|
-
const entries = await readJsonLines(filePath)
|
|
154
|
+
const entries = await readJsonLines<T>(filePath)
|
|
179
155
|
allEntries.push(...entries)
|
|
180
156
|
}
|
|
181
157
|
|
|
@@ -184,11 +160,8 @@ async function mergeJsonLines(filePaths) {
|
|
|
184
160
|
|
|
185
161
|
/**
|
|
186
162
|
* Check if JSONL file is empty or doesn't exist
|
|
187
|
-
*
|
|
188
|
-
* @param {string} filePath - Path to JSONL file
|
|
189
|
-
* @returns {Promise<boolean>} - True if empty or non-existent
|
|
190
163
|
*/
|
|
191
|
-
async function isJsonLinesEmpty(filePath) {
|
|
164
|
+
export async function isJsonLinesEmpty(filePath: string): Promise<boolean> {
|
|
192
165
|
const count = await countJsonLines(filePath)
|
|
193
166
|
return count === 0
|
|
194
167
|
}
|
|
@@ -196,12 +169,11 @@ async function isJsonLinesEmpty(filePath) {
|
|
|
196
169
|
/**
|
|
197
170
|
* Read JSONL file with streaming (memory-efficient for large files)
|
|
198
171
|
* Only reads last N lines instead of loading entire file
|
|
199
|
-
*
|
|
200
|
-
* @param {string} filePath - Path to JSONL file
|
|
201
|
-
* @param {number} maxLines - Maximum lines to read (default: 1000)
|
|
202
|
-
* @returns {Promise<Array<Object>>} - Array of parsed objects (last N lines)
|
|
203
172
|
*/
|
|
204
|
-
async function readJsonLinesStreaming
|
|
173
|
+
export async function readJsonLinesStreaming<T = Record<string, unknown>>(
|
|
174
|
+
filePath: string,
|
|
175
|
+
maxLines = 1000
|
|
176
|
+
): Promise<T[]> {
|
|
205
177
|
try {
|
|
206
178
|
const fileStream = fsSync.createReadStream(filePath)
|
|
207
179
|
const rl = readline.createInterface({
|
|
@@ -209,12 +181,12 @@ async function readJsonLinesStreaming(filePath, maxLines = 1000) {
|
|
|
209
181
|
crlfDelay: Infinity,
|
|
210
182
|
})
|
|
211
183
|
|
|
212
|
-
const lines = []
|
|
184
|
+
const lines: T[] = []
|
|
213
185
|
|
|
214
186
|
for await (const line of rl) {
|
|
215
187
|
if (line.trim()) {
|
|
216
188
|
try {
|
|
217
|
-
lines.push(JSON.parse(line))
|
|
189
|
+
lines.push(JSON.parse(line) as T)
|
|
218
190
|
} catch {
|
|
219
191
|
// Skip malformed lines
|
|
220
192
|
}
|
|
@@ -228,7 +200,7 @@ async function readJsonLinesStreaming(filePath, maxLines = 1000) {
|
|
|
228
200
|
|
|
229
201
|
return lines
|
|
230
202
|
} catch (error) {
|
|
231
|
-
if (error.code === 'ENOENT') {
|
|
203
|
+
if ((error as NodeError).code === 'ENOENT') {
|
|
232
204
|
return []
|
|
233
205
|
}
|
|
234
206
|
throw error
|
|
@@ -237,16 +209,13 @@ async function readJsonLinesStreaming(filePath, maxLines = 1000) {
|
|
|
237
209
|
|
|
238
210
|
/**
|
|
239
211
|
* Get file size in MB
|
|
240
|
-
*
|
|
241
|
-
* @param {string} filePath - Path to file
|
|
242
|
-
* @returns {Promise<number>} - File size in MB
|
|
243
212
|
*/
|
|
244
|
-
async function getFileSizeMB(filePath) {
|
|
213
|
+
export async function getFileSizeMB(filePath: string): Promise<number> {
|
|
245
214
|
try {
|
|
246
215
|
const stats = await fs.stat(filePath)
|
|
247
216
|
return stats.size / (1024 * 1024)
|
|
248
217
|
} catch (error) {
|
|
249
|
-
if (error.code === 'ENOENT') {
|
|
218
|
+
if ((error as NodeError).code === 'ENOENT') {
|
|
250
219
|
return 0
|
|
251
220
|
}
|
|
252
221
|
throw error
|
|
@@ -256,12 +225,8 @@ async function getFileSizeMB(filePath) {
|
|
|
256
225
|
/**
|
|
257
226
|
* Rotate JSONL file if it exceeds size limit
|
|
258
227
|
* Moves large file to archive with timestamp
|
|
259
|
-
*
|
|
260
|
-
* @param {string} filePath - Path to JSONL file
|
|
261
|
-
* @param {number} maxSizeMB - Maximum size in MB before rotation (default: 10)
|
|
262
|
-
* @returns {Promise<boolean>} - True if rotated, false if not needed
|
|
263
228
|
*/
|
|
264
|
-
async function rotateJsonLinesIfNeeded(filePath, maxSizeMB = 10) {
|
|
229
|
+
export async function rotateJsonLinesIfNeeded(filePath: string, maxSizeMB = 10): Promise<boolean> {
|
|
265
230
|
const sizeMB = await getFileSizeMB(filePath)
|
|
266
231
|
|
|
267
232
|
if (sizeMB < maxSizeMB) {
|
|
@@ -286,13 +251,12 @@ async function rotateJsonLinesIfNeeded(filePath, maxSizeMB = 10) {
|
|
|
286
251
|
/**
|
|
287
252
|
* Append JSON line with automatic rotation
|
|
288
253
|
* Checks file size before append and rotates if needed
|
|
289
|
-
*
|
|
290
|
-
* @param {string} filePath - Path to JSONL file
|
|
291
|
-
* @param {Object} object - Object to append
|
|
292
|
-
* @param {number} maxSizeMB - Maximum size before rotation (default: 10)
|
|
293
|
-
* @returns {Promise<void>}
|
|
294
254
|
*/
|
|
295
|
-
async function appendJsonLineWithRotation(
|
|
255
|
+
export async function appendJsonLineWithRotation(
|
|
256
|
+
filePath: string,
|
|
257
|
+
object: unknown,
|
|
258
|
+
maxSizeMB = 10
|
|
259
|
+
): Promise<void> {
|
|
296
260
|
// Rotate if needed (before appending)
|
|
297
261
|
await rotateJsonLinesIfNeeded(filePath, maxSizeMB)
|
|
298
262
|
|
|
@@ -303,12 +267,11 @@ async function appendJsonLineWithRotation(filePath, object, maxSizeMB = 10) {
|
|
|
303
267
|
/**
|
|
304
268
|
* Warn if file is large before reading
|
|
305
269
|
* Returns size and whether it's considered large
|
|
306
|
-
*
|
|
307
|
-
* @param {string} filePath - Path to file
|
|
308
|
-
* @param {number} warnThresholdMB - Threshold in MB to warn (default: 50)
|
|
309
|
-
* @returns {Promise<{sizeMB: number, isLarge: boolean}>}
|
|
310
270
|
*/
|
|
311
|
-
async function checkFileSizeWarning(
|
|
271
|
+
export async function checkFileSizeWarning(
|
|
272
|
+
filePath: string,
|
|
273
|
+
warnThresholdMB = 50
|
|
274
|
+
): Promise<FileSizeWarning> {
|
|
312
275
|
const sizeMB = await getFileSizeMB(filePath)
|
|
313
276
|
const isLarge = sizeMB > warnThresholdMB
|
|
314
277
|
|
|
@@ -321,7 +284,8 @@ async function checkFileSizeWarning(filePath, warnThresholdMB = 50) {
|
|
|
321
284
|
return { sizeMB, isLarge }
|
|
322
285
|
}
|
|
323
286
|
|
|
324
|
-
|
|
287
|
+
// Default export for CommonJS compatibility
|
|
288
|
+
export default {
|
|
325
289
|
parseJsonLines,
|
|
326
290
|
stringifyJsonLines,
|
|
327
291
|
readJsonLines,
|
|
@@ -334,10 +298,10 @@ module.exports = {
|
|
|
334
298
|
getFirstJsonLines,
|
|
335
299
|
mergeJsonLines,
|
|
336
300
|
isJsonLinesEmpty,
|
|
337
|
-
// NEW: Memory-efficient functions
|
|
338
301
|
readJsonLinesStreaming,
|
|
339
302
|
getFileSizeMB,
|
|
340
303
|
rotateJsonLinesIfNeeded,
|
|
341
304
|
appendJsonLineWithRotation,
|
|
342
|
-
checkFileSizeWarning
|
|
305
|
+
checkFileSizeWarning
|
|
343
306
|
}
|
|
307
|
+
|
|
@@ -9,14 +9,26 @@
|
|
|
9
9
|
* PRJCT_DEBUG=debug (everything)
|
|
10
10
|
*
|
|
11
11
|
* Usage:
|
|
12
|
-
*
|
|
12
|
+
* import log from './utils/logger'
|
|
13
13
|
* log.debug('Processing files...')
|
|
14
14
|
* log.info('Task started')
|
|
15
15
|
* log.warn('Cache miss')
|
|
16
16
|
* log.error('Failed to load', error.message)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
type LogLevel = 'error' | 'warn' | 'info' | 'debug'
|
|
20
|
+
type LogFunction = (...args: unknown[]) => void
|
|
21
|
+
|
|
22
|
+
interface Logger {
|
|
23
|
+
error: LogFunction
|
|
24
|
+
warn: LogFunction
|
|
25
|
+
info: LogFunction
|
|
26
|
+
debug: LogFunction
|
|
27
|
+
isEnabled: () => boolean
|
|
28
|
+
level: () => LogLevel | 'disabled'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const LEVELS: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 }
|
|
20
32
|
|
|
21
33
|
const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || ''
|
|
22
34
|
const isEnabled = debugEnv === '1' || debugEnv === 'true' || debugEnv.includes('prjct')
|
|
@@ -26,39 +38,39 @@ let currentLevel = -1 // disabled by default
|
|
|
26
38
|
if (isEnabled) {
|
|
27
39
|
if (debugEnv === '1' || debugEnv === 'true' || debugEnv === 'prjct') {
|
|
28
40
|
currentLevel = LEVELS.debug // all logs
|
|
29
|
-
} else if (LEVELS[debugEnv] !== undefined) {
|
|
30
|
-
currentLevel = LEVELS[debugEnv]
|
|
41
|
+
} else if (LEVELS[debugEnv as LogLevel] !== undefined) {
|
|
42
|
+
currentLevel = LEVELS[debugEnv as LogLevel]
|
|
31
43
|
} else {
|
|
32
44
|
currentLevel = LEVELS.debug
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
// No-op function for disabled logs
|
|
37
|
-
const noop = () => {}
|
|
49
|
+
const noop: LogFunction = () => {}
|
|
38
50
|
|
|
39
51
|
// Create logger methods
|
|
40
|
-
const logger = {
|
|
52
|
+
const logger: Logger = {
|
|
41
53
|
error: currentLevel >= LEVELS.error
|
|
42
|
-
? (...args) => console.error('[prjct:error]', ...args)
|
|
54
|
+
? (...args: unknown[]) => console.error('[prjct:error]', ...args)
|
|
43
55
|
: noop,
|
|
44
56
|
|
|
45
57
|
warn: currentLevel >= LEVELS.warn
|
|
46
|
-
? (...args) => console.warn('[prjct:warn]', ...args)
|
|
58
|
+
? (...args: unknown[]) => console.warn('[prjct:warn]', ...args)
|
|
47
59
|
: noop,
|
|
48
60
|
|
|
49
61
|
info: currentLevel >= LEVELS.info
|
|
50
|
-
? (...args) => console.log('[prjct:info]', ...args)
|
|
62
|
+
? (...args: unknown[]) => console.log('[prjct:info]', ...args)
|
|
51
63
|
: noop,
|
|
52
64
|
|
|
53
65
|
debug: currentLevel >= LEVELS.debug
|
|
54
|
-
? (...args) => console.log('[prjct:debug]', ...args)
|
|
66
|
+
? (...args: unknown[]) => console.log('[prjct:debug]', ...args)
|
|
55
67
|
: noop,
|
|
56
68
|
|
|
57
69
|
// Check if logging is enabled (useful for expensive log prep)
|
|
58
70
|
isEnabled: () => currentLevel >= 0,
|
|
59
71
|
|
|
60
72
|
// Get current level name
|
|
61
|
-
level: () => Object.keys(LEVELS).find(k => LEVELS[k] === currentLevel) || 'disabled'
|
|
73
|
+
level: () => (Object.keys(LEVELS) as LogLevel[]).find(k => LEVELS[k] === currentLevel) || 'disabled'
|
|
62
74
|
}
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
export default logger
|
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal Output System for prjct-cli
|
|
3
3
|
* Spinner while working → Single line result
|
|
4
|
-
* With
|
|
4
|
+
* With prjct branding
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import branding from './branding'
|
|
9
9
|
|
|
10
10
|
const FRAMES = branding.spinner.frames
|
|
11
11
|
const SPEED = branding.spinner.speed
|
|
12
12
|
|
|
13
|
-
let interval = null
|
|
13
|
+
let interval: ReturnType<typeof setInterval> | null = null
|
|
14
14
|
let frame = 0
|
|
15
15
|
|
|
16
|
-
const truncate = (s
|
|
17
|
-
|
|
16
|
+
const truncate = (s: string | undefined | null, max = 50): string =>
|
|
17
|
+
s && s.length > max ? s.slice(0, max - 1) + '…' : s || ''
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const clear = (): boolean => process.stdout.write('\r' + ' '.repeat(80) + '\r')
|
|
20
|
+
|
|
21
|
+
interface Output {
|
|
22
|
+
start(): Output
|
|
23
|
+
end(): Output
|
|
24
|
+
spin(msg: string): Output
|
|
25
|
+
done(msg: string): Output
|
|
26
|
+
fail(msg: string): Output
|
|
27
|
+
warn(msg: string): Output
|
|
28
|
+
stop(): Output
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const out: Output = {
|
|
20
32
|
// Branding: Show header at start
|
|
21
33
|
start() {
|
|
22
34
|
console.log(branding.cli.header())
|
|
@@ -29,8 +41,8 @@ const out = {
|
|
|
29
41
|
return this
|
|
30
42
|
},
|
|
31
43
|
|
|
32
|
-
// Branded spinner:
|
|
33
|
-
spin(msg) {
|
|
44
|
+
// Branded spinner: prjct message...
|
|
45
|
+
spin(msg: string) {
|
|
34
46
|
this.stop()
|
|
35
47
|
interval = setInterval(() => {
|
|
36
48
|
process.stdout.write(`\r${branding.cli.spin(frame++, truncate(msg, 45))}`)
|
|
@@ -38,19 +50,19 @@ const out = {
|
|
|
38
50
|
return this
|
|
39
51
|
},
|
|
40
52
|
|
|
41
|
-
done(msg) {
|
|
53
|
+
done(msg: string) {
|
|
42
54
|
this.stop()
|
|
43
55
|
console.log(`${chalk.green('✓')} ${truncate(msg, 65)}`)
|
|
44
56
|
return this
|
|
45
57
|
},
|
|
46
58
|
|
|
47
|
-
fail(msg) {
|
|
59
|
+
fail(msg: string) {
|
|
48
60
|
this.stop()
|
|
49
61
|
console.log(`${chalk.red('✗')} ${truncate(msg, 65)}`)
|
|
50
62
|
return this
|
|
51
63
|
},
|
|
52
64
|
|
|
53
|
-
warn(msg) {
|
|
65
|
+
warn(msg: string) {
|
|
54
66
|
this.stop()
|
|
55
67
|
console.log(`${chalk.yellow('⚠')} ${truncate(msg, 65)}`)
|
|
56
68
|
return this
|
|
@@ -66,4 +78,4 @@ const out = {
|
|
|
66
78
|
}
|
|
67
79
|
}
|
|
68
80
|
|
|
69
|
-
|
|
81
|
+
export default out
|
|
@@ -3,16 +3,25 @@
|
|
|
3
3
|
* Detects ONLY what exists - no assumptions, no hallucinations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import fs from 'fs/promises'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
|
|
9
|
+
interface Capabilities {
|
|
10
|
+
design: boolean
|
|
11
|
+
test: boolean
|
|
12
|
+
docs: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PackageJson {
|
|
16
|
+
dependencies?: Record<string, string>
|
|
17
|
+
devDependencies?: Record<string, string>
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
class ProjectCapabilities {
|
|
10
21
|
/**
|
|
11
22
|
* Detect project capabilities
|
|
12
|
-
* @param {string} projectPath - Project root path
|
|
13
|
-
* @returns {Promise<Object>} Capabilities object
|
|
14
23
|
*/
|
|
15
|
-
async detect(projectPath = process.cwd()) {
|
|
24
|
+
async detect(projectPath = process.cwd()): Promise<Capabilities> {
|
|
16
25
|
return {
|
|
17
26
|
design: await this.hasDesign(projectPath),
|
|
18
27
|
test: await this.hasTest(projectPath),
|
|
@@ -23,7 +32,7 @@ class ProjectCapabilities {
|
|
|
23
32
|
/**
|
|
24
33
|
* Check if project has design system
|
|
25
34
|
*/
|
|
26
|
-
async hasDesign(projectPath) {
|
|
35
|
+
async hasDesign(projectPath: string): Promise<boolean> {
|
|
27
36
|
return (
|
|
28
37
|
(await this.hasFolder(projectPath, 'design')) ||
|
|
29
38
|
(await this.hasFolder(projectPath, 'designs')) ||
|
|
@@ -37,7 +46,7 @@ class ProjectCapabilities {
|
|
|
37
46
|
/**
|
|
38
47
|
* Check if project has test framework
|
|
39
48
|
*/
|
|
40
|
-
async hasTest(projectPath) {
|
|
49
|
+
async hasTest(projectPath: string): Promise<boolean> {
|
|
41
50
|
return (
|
|
42
51
|
(await this.hasDep(projectPath, 'jest')) ||
|
|
43
52
|
(await this.hasDep(projectPath, 'vitest')) ||
|
|
@@ -52,7 +61,7 @@ class ProjectCapabilities {
|
|
|
52
61
|
/**
|
|
53
62
|
* Check if project has documentation system
|
|
54
63
|
*/
|
|
55
|
-
async hasDocs(projectPath) {
|
|
64
|
+
async hasDocs(projectPath: string): Promise<boolean> {
|
|
56
65
|
return (
|
|
57
66
|
(await this.hasFolder(projectPath, 'docs')) ||
|
|
58
67
|
(await this.hasFolder(projectPath, 'documentation')) ||
|
|
@@ -65,7 +74,7 @@ class ProjectCapabilities {
|
|
|
65
74
|
/**
|
|
66
75
|
* Check if folder exists
|
|
67
76
|
*/
|
|
68
|
-
async hasFolder(projectPath, folderName) {
|
|
77
|
+
async hasFolder(projectPath: string, folderName: string): Promise<boolean> {
|
|
69
78
|
try {
|
|
70
79
|
const folderPath = path.join(projectPath, folderName)
|
|
71
80
|
const stat = await fs.stat(folderPath)
|
|
@@ -78,7 +87,7 @@ class ProjectCapabilities {
|
|
|
78
87
|
/**
|
|
79
88
|
* Check if file exists
|
|
80
89
|
*/
|
|
81
|
-
async hasFile(projectPath, fileName) {
|
|
90
|
+
async hasFile(projectPath: string, fileName: string): Promise<boolean> {
|
|
82
91
|
try {
|
|
83
92
|
const filePath = path.join(projectPath, fileName)
|
|
84
93
|
await fs.access(filePath)
|
|
@@ -91,7 +100,7 @@ class ProjectCapabilities {
|
|
|
91
100
|
/**
|
|
92
101
|
* Check if files matching pattern exist
|
|
93
102
|
*/
|
|
94
|
-
async hasFiles(projectPath, pattern) {
|
|
103
|
+
async hasFiles(projectPath: string, pattern: string): Promise<boolean> {
|
|
95
104
|
try {
|
|
96
105
|
// Convert glob pattern to regex
|
|
97
106
|
const regex = this.globToRegex(pattern)
|
|
@@ -99,9 +108,12 @@ class ProjectCapabilities {
|
|
|
99
108
|
|
|
100
109
|
// Filter by pattern and ignore node_modules, dist, build
|
|
101
110
|
return files.some((file) => {
|
|
111
|
+
const fileName = typeof file === 'string' ? file : file.toString()
|
|
102
112
|
const skip =
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
fileName.includes('node_modules/') ||
|
|
114
|
+
fileName.includes('dist/') ||
|
|
115
|
+
fileName.includes('build/')
|
|
116
|
+
return !skip && regex.test(fileName)
|
|
105
117
|
})
|
|
106
118
|
} catch {
|
|
107
119
|
return false
|
|
@@ -111,24 +123,24 @@ class ProjectCapabilities {
|
|
|
111
123
|
/**
|
|
112
124
|
* Convert simple glob pattern to regex
|
|
113
125
|
*/
|
|
114
|
-
globToRegex(pattern) {
|
|
126
|
+
globToRegex(pattern: string): RegExp {
|
|
115
127
|
// Convert **/*.{test,spec}.{js,ts,jsx,tsx} to regex
|
|
116
128
|
const escaped = pattern
|
|
117
129
|
.replace(/\./g, '\\.')
|
|
118
130
|
.replace(/\*\*/g, '.*')
|
|
119
131
|
.replace(/\*/g, '[^/]*')
|
|
120
|
-
.replace(/\{([^}]+)\}/g, (_, group) => `(${group.split(',').join('|')})`)
|
|
132
|
+
.replace(/\{([^}]+)\}/g, (_, group: string) => `(${group.split(',').join('|')})`)
|
|
121
133
|
return new RegExp(escaped)
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
/**
|
|
125
137
|
* Check if dependency exists in package.json
|
|
126
138
|
*/
|
|
127
|
-
async hasDep(projectPath, depName) {
|
|
139
|
+
async hasDep(projectPath: string, depName: string): Promise<boolean> {
|
|
128
140
|
try {
|
|
129
141
|
const pkgPath = path.join(projectPath, 'package.json')
|
|
130
142
|
const content = await fs.readFile(pkgPath, 'utf8')
|
|
131
|
-
const pkg = JSON.parse(content)
|
|
143
|
+
const pkg: PackageJson = JSON.parse(content)
|
|
132
144
|
|
|
133
145
|
return !!(
|
|
134
146
|
(pkg.dependencies && pkg.dependencies[depName]) ||
|
|
@@ -140,4 +152,5 @@ class ProjectCapabilities {
|
|
|
140
152
|
}
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
|
|
155
|
+
const projectCapabilities = new ProjectCapabilities()
|
|
156
|
+
export default projectCapabilities
|