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
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* OPTIMIZED: Tests updated to match compressed prompt structure
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { describe, it, expect, beforeEach } from '
|
|
9
|
-
import promptBuilder from '../../agentic/prompt-builder
|
|
8
|
+
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
9
|
+
import promptBuilder from '../../agentic/prompt-builder'
|
|
10
10
|
|
|
11
11
|
describe('PromptBuilder', () => {
|
|
12
|
-
let builder
|
|
12
|
+
let builder: typeof promptBuilder
|
|
13
13
|
|
|
14
14
|
beforeEach(() => {
|
|
15
15
|
builder = promptBuilder
|
|
@@ -122,7 +122,6 @@ describe('PromptBuilder', () => {
|
|
|
122
122
|
|
|
123
123
|
const prompt = builder.build(template, context, state)
|
|
124
124
|
|
|
125
|
-
// Non-code commands should NOT include patterns section
|
|
126
125
|
expect(prompt).not.toContain('## PATTERNS')
|
|
127
126
|
})
|
|
128
127
|
})
|
|
@@ -143,7 +142,6 @@ describe('PromptBuilder', () => {
|
|
|
143
142
|
|
|
144
143
|
const prompt = builder.build(template, context, state)
|
|
145
144
|
|
|
146
|
-
// OPTIMIZED: New compressed format uses ## FILES:
|
|
147
145
|
expect(prompt).toContain('## FILES:')
|
|
148
146
|
expect(prompt).toContain('3 available')
|
|
149
147
|
expect(prompt).toContain('file1.js')
|
|
@@ -161,7 +159,6 @@ describe('PromptBuilder', () => {
|
|
|
161
159
|
|
|
162
160
|
const prompt = builder.build(template, context, state)
|
|
163
161
|
|
|
164
|
-
// OPTIMIZED: New compressed format uses ## PROJECT:
|
|
165
162
|
expect(prompt).toContain('## PROJECT:')
|
|
166
163
|
expect(prompt).toContain('/test/project')
|
|
167
164
|
})
|
|
@@ -191,7 +188,6 @@ describe('PromptBuilder', () => {
|
|
|
191
188
|
expect(prompt).toContain('TOOLS:')
|
|
192
189
|
expect(prompt).toContain('Flow')
|
|
193
190
|
expect(prompt).toContain('RULES (CRITICAL)')
|
|
194
|
-
// OPTIMIZED: New compressed format uses ## FILES:
|
|
195
191
|
expect(prompt).toContain('## FILES:')
|
|
196
192
|
})
|
|
197
193
|
|
|
@@ -206,7 +202,6 @@ describe('PromptBuilder', () => {
|
|
|
206
202
|
|
|
207
203
|
const prompt = builder.build(template, context, state)
|
|
208
204
|
|
|
209
|
-
// Optimized prompts should be under 2000 chars for simple cases
|
|
210
205
|
expect(prompt.length).toBeLessThan(2000)
|
|
211
206
|
})
|
|
212
207
|
})
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date Helper Tests
|
|
3
|
+
* Tests for centralized date operations and formatting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, setSystemTime } from 'bun:test'
|
|
7
|
+
import {
|
|
8
|
+
formatDate,
|
|
9
|
+
formatMonth,
|
|
10
|
+
getTodayKey,
|
|
11
|
+
getDateKey,
|
|
12
|
+
getYearMonthDay,
|
|
13
|
+
parseDate,
|
|
14
|
+
getTimestamp,
|
|
15
|
+
getDaysAgo,
|
|
16
|
+
getDaysFromNow,
|
|
17
|
+
getDateRange,
|
|
18
|
+
isToday,
|
|
19
|
+
isWithinLastDays,
|
|
20
|
+
formatDuration,
|
|
21
|
+
calculateDuration,
|
|
22
|
+
getStartOfDay,
|
|
23
|
+
getEndOfDay,
|
|
24
|
+
} from '../../utils/date-helper'
|
|
25
|
+
|
|
26
|
+
describe('DateHelper', () => {
|
|
27
|
+
describe('formatDate', () => {
|
|
28
|
+
it('should format date to YYYY-MM-DD', () => {
|
|
29
|
+
const date = new Date(2025, 9, 4) // Oct 4, 2025
|
|
30
|
+
expect(formatDate(date)).toBe('2025-10-04')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should pad single digit months', () => {
|
|
34
|
+
const date = new Date(2025, 0, 15) // Jan 15, 2025
|
|
35
|
+
expect(formatDate(date)).toBe('2025-01-15')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should pad single digit days', () => {
|
|
39
|
+
const date = new Date(2025, 11, 5) // Dec 5, 2025
|
|
40
|
+
expect(formatDate(date)).toBe('2025-12-05')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should handle year boundaries', () => {
|
|
44
|
+
const date = new Date(2024, 11, 31) // Dec 31, 2024
|
|
45
|
+
expect(formatDate(date)).toBe('2024-12-31')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('formatMonth', () => {
|
|
50
|
+
it('should format date to YYYY-MM', () => {
|
|
51
|
+
const date = new Date(2025, 9, 15) // Oct 15, 2025
|
|
52
|
+
expect(formatMonth(date)).toBe('2025-10')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should pad single digit months', () => {
|
|
56
|
+
const date = new Date(2025, 2, 1) // Mar 1, 2025
|
|
57
|
+
expect(formatMonth(date)).toBe('2025-03')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should handle December', () => {
|
|
61
|
+
const date = new Date(2025, 11, 25) // Dec 25, 2025
|
|
62
|
+
expect(formatMonth(date)).toBe('2025-12')
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('getTodayKey', () => {
|
|
67
|
+
it('should return today in YYYY-MM-DD format', () => {
|
|
68
|
+
setSystemTime(new Date(2025, 5, 15)) // June 15, 2025
|
|
69
|
+
expect(getTodayKey()).toBe('2025-06-15')
|
|
70
|
+
setSystemTime()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('getDateKey', () => {
|
|
75
|
+
it('should return date in YYYY-MM-DD format (alias for formatDate)', () => {
|
|
76
|
+
const date = new Date(2025, 7, 20) // Aug 20, 2025
|
|
77
|
+
expect(getDateKey(date)).toBe('2025-08-20')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe('getYearMonthDay', () => {
|
|
82
|
+
it('should return separate year, month, day strings', () => {
|
|
83
|
+
const date = new Date(2025, 9, 4) // Oct 4, 2025
|
|
84
|
+
const result = getYearMonthDay(date)
|
|
85
|
+
|
|
86
|
+
expect(result.year).toBe('2025')
|
|
87
|
+
expect(result.month).toBe('10')
|
|
88
|
+
expect(result.day).toBe('04')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should pad month values', () => {
|
|
92
|
+
const date = new Date(2025, 0, 15) // Jan 15, 2025
|
|
93
|
+
const result = getYearMonthDay(date)
|
|
94
|
+
expect(result.month).toBe('01')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should pad day values', () => {
|
|
98
|
+
const date = new Date(2025, 5, 7) // June 7, 2025
|
|
99
|
+
const result = getYearMonthDay(date)
|
|
100
|
+
expect(result.day).toBe('07')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('parseDate', () => {
|
|
105
|
+
it('should parse YYYY-MM-DD format', () => {
|
|
106
|
+
const result = parseDate('2025-10-04')
|
|
107
|
+
expect(result.getFullYear()).toBe(2025)
|
|
108
|
+
expect(result.getMonth()).toBe(9) // 0-indexed
|
|
109
|
+
expect(result.getDate()).toBe(4)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should parse ISO strings', () => {
|
|
113
|
+
const result = parseDate('2025-10-04T14:30:00.000Z')
|
|
114
|
+
expect(result.getFullYear()).toBe(2025)
|
|
115
|
+
expect(result.getMonth()).toBe(9)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('getTimestamp', () => {
|
|
120
|
+
it('should return ISO timestamp', () => {
|
|
121
|
+
setSystemTime(new Date('2025-10-04T14:30:00.000Z'))
|
|
122
|
+
expect(getTimestamp()).toBe('2025-10-04T14:30:00.000Z')
|
|
123
|
+
setSystemTime()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('should include milliseconds', () => {
|
|
127
|
+
const timestamp = getTimestamp()
|
|
128
|
+
expect(timestamp).toMatch(/\.\d{3}Z$/)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('getDaysAgo', () => {
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
afterEach(() => {
|
|
138
|
+
setSystemTime()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should calculate past dates correctly', () => {
|
|
142
|
+
const result = getDaysAgo(5)
|
|
143
|
+
expect(formatDate(result)).toBe('2025-10-10')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should handle month boundaries', () => {
|
|
147
|
+
const result = getDaysAgo(20)
|
|
148
|
+
expect(formatDate(result)).toBe('2025-09-25')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should return today for 0 days ago', () => {
|
|
152
|
+
const result = getDaysAgo(0)
|
|
153
|
+
expect(formatDate(result)).toBe('2025-10-15')
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('getDaysFromNow', () => {
|
|
158
|
+
beforeEach(() => {
|
|
159
|
+
setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
afterEach(() => {
|
|
163
|
+
setSystemTime()
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('should calculate future dates correctly', () => {
|
|
167
|
+
const result = getDaysFromNow(5)
|
|
168
|
+
expect(formatDate(result)).toBe('2025-10-20')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should handle month boundaries', () => {
|
|
172
|
+
const result = getDaysFromNow(20)
|
|
173
|
+
expect(formatDate(result)).toBe('2025-11-04')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should return today for 0 days from now', () => {
|
|
177
|
+
const result = getDaysFromNow(0)
|
|
178
|
+
expect(formatDate(result)).toBe('2025-10-15')
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('getDateRange', () => {
|
|
183
|
+
it('should return array of dates in range', () => {
|
|
184
|
+
const from = new Date(2025, 9, 1) // Oct 1
|
|
185
|
+
const to = new Date(2025, 9, 5) // Oct 5
|
|
186
|
+
|
|
187
|
+
const result = getDateRange(from, to)
|
|
188
|
+
|
|
189
|
+
expect(result.length).toBe(5)
|
|
190
|
+
expect(formatDate(result[0])).toBe('2025-10-01')
|
|
191
|
+
expect(formatDate(result[4])).toBe('2025-10-05')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('should include start and end dates', () => {
|
|
195
|
+
const from = new Date(2025, 9, 10)
|
|
196
|
+
const to = new Date(2025, 9, 12)
|
|
197
|
+
|
|
198
|
+
const result = getDateRange(from, to)
|
|
199
|
+
|
|
200
|
+
expect(formatDate(result[0])).toBe('2025-10-10')
|
|
201
|
+
expect(formatDate(result[result.length - 1])).toBe('2025-10-12')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('should return single date if from equals to', () => {
|
|
205
|
+
const date = new Date(2025, 9, 15)
|
|
206
|
+
const result = getDateRange(date, date)
|
|
207
|
+
|
|
208
|
+
expect(result.length).toBe(1)
|
|
209
|
+
expect(formatDate(result[0])).toBe('2025-10-15')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should handle month boundaries', () => {
|
|
213
|
+
const from = new Date(2025, 9, 30) // Oct 30
|
|
214
|
+
const to = new Date(2025, 10, 2) // Nov 2
|
|
215
|
+
|
|
216
|
+
const result = getDateRange(from, to)
|
|
217
|
+
|
|
218
|
+
expect(result.length).toBe(4)
|
|
219
|
+
expect(formatDate(result[0])).toBe('2025-10-30')
|
|
220
|
+
expect(formatDate(result[3])).toBe('2025-11-02')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should return empty array if from is after to', () => {
|
|
224
|
+
const from = new Date(2025, 9, 15)
|
|
225
|
+
const to = new Date(2025, 9, 10)
|
|
226
|
+
|
|
227
|
+
const result = getDateRange(from, to)
|
|
228
|
+
|
|
229
|
+
expect(result.length).toBe(0)
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
describe('isToday', () => {
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
afterEach(() => {
|
|
239
|
+
setSystemTime()
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('should return true for today', () => {
|
|
243
|
+
const today = new Date(2025, 9, 15)
|
|
244
|
+
expect(isToday(today)).toBe(true)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('should return false for yesterday', () => {
|
|
248
|
+
const yesterday = new Date(2025, 9, 14)
|
|
249
|
+
expect(isToday(yesterday)).toBe(false)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('should return false for tomorrow', () => {
|
|
253
|
+
const tomorrow = new Date(2025, 9, 16)
|
|
254
|
+
expect(isToday(tomorrow)).toBe(false)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('should ignore time component', () => {
|
|
258
|
+
const todayLate = new Date(2025, 9, 15, 23, 59, 59)
|
|
259
|
+
expect(isToday(todayLate)).toBe(true)
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
describe('isWithinLastDays', () => {
|
|
264
|
+
beforeEach(() => {
|
|
265
|
+
setSystemTime(new Date(2025, 9, 15, 12, 0, 0)) // Oct 15, 2025 at noon
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
afterEach(() => {
|
|
269
|
+
setSystemTime()
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('should return true for dates within range', () => {
|
|
273
|
+
const recent = new Date(2025, 9, 12) // 3 days ago
|
|
274
|
+
expect(isWithinLastDays(recent, 7)).toBe(true)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('should return false for dates outside range', () => {
|
|
278
|
+
const old = new Date(2025, 9, 1) // 14 days ago
|
|
279
|
+
expect(isWithinLastDays(old, 7)).toBe(false)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('should include today', () => {
|
|
283
|
+
const today = new Date(2025, 9, 15)
|
|
284
|
+
expect(isWithinLastDays(today, 7)).toBe(true)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('should include boundary date', () => {
|
|
288
|
+
// Oct 8 at noon is exactly 7 days before Oct 15 at noon
|
|
289
|
+
const boundary = new Date(2025, 9, 8, 12, 0, 0)
|
|
290
|
+
expect(isWithinLastDays(boundary, 7)).toBe(true)
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe('formatDuration', () => {
|
|
295
|
+
it('should format seconds', () => {
|
|
296
|
+
expect(formatDuration(5000)).toBe('5s')
|
|
297
|
+
expect(formatDuration(45000)).toBe('45s')
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('should format minutes', () => {
|
|
301
|
+
expect(formatDuration(60000)).toBe('1m')
|
|
302
|
+
expect(formatDuration(120000)).toBe('2m')
|
|
303
|
+
expect(formatDuration(90000)).toBe('1m') // 1.5 min rounds down
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should format hours and minutes', () => {
|
|
307
|
+
expect(formatDuration(3600000)).toBe('1h 0m')
|
|
308
|
+
expect(formatDuration(5400000)).toBe('1h 30m')
|
|
309
|
+
expect(formatDuration(7200000)).toBe('2h 0m')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('should format days and hours', () => {
|
|
313
|
+
expect(formatDuration(86400000)).toBe('1d 0h')
|
|
314
|
+
expect(formatDuration(90000000)).toBe('1d 1h')
|
|
315
|
+
expect(formatDuration(172800000)).toBe('2d 0h')
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
it('should handle zero', () => {
|
|
319
|
+
expect(formatDuration(0)).toBe('0s')
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
describe('calculateDuration', () => {
|
|
324
|
+
it('should calculate duration between two dates', () => {
|
|
325
|
+
const start = new Date('2025-10-15T10:00:00.000Z')
|
|
326
|
+
const end = new Date('2025-10-15T12:30:00.000Z')
|
|
327
|
+
|
|
328
|
+
expect(calculateDuration(start, end)).toBe('2h 30m')
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it('should default to now if no end date', () => {
|
|
332
|
+
setSystemTime(new Date('2025-10-15T12:00:00.000Z'))
|
|
333
|
+
|
|
334
|
+
const start = new Date('2025-10-15T10:00:00.000Z')
|
|
335
|
+
expect(calculateDuration(start)).toBe('2h 0m')
|
|
336
|
+
|
|
337
|
+
setSystemTime()
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('should handle short durations', () => {
|
|
341
|
+
const start = new Date('2025-10-15T10:00:00.000Z')
|
|
342
|
+
const end = new Date('2025-10-15T10:00:30.000Z')
|
|
343
|
+
|
|
344
|
+
expect(calculateDuration(start, end)).toBe('30s')
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
describe('getStartOfDay', () => {
|
|
349
|
+
it('should set time to 00:00:00.000', () => {
|
|
350
|
+
const date = new Date(2025, 9, 15, 14, 30, 45, 500)
|
|
351
|
+
const result = getStartOfDay(date)
|
|
352
|
+
|
|
353
|
+
expect(result.getHours()).toBe(0)
|
|
354
|
+
expect(result.getMinutes()).toBe(0)
|
|
355
|
+
expect(result.getSeconds()).toBe(0)
|
|
356
|
+
expect(result.getMilliseconds()).toBe(0)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
it('should preserve the date', () => {
|
|
360
|
+
const date = new Date(2025, 9, 15, 23, 59, 59)
|
|
361
|
+
const result = getStartOfDay(date)
|
|
362
|
+
|
|
363
|
+
expect(result.getFullYear()).toBe(2025)
|
|
364
|
+
expect(result.getMonth()).toBe(9)
|
|
365
|
+
expect(result.getDate()).toBe(15)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('should not mutate original date', () => {
|
|
369
|
+
const original = new Date(2025, 9, 15, 14, 30)
|
|
370
|
+
getStartOfDay(original)
|
|
371
|
+
|
|
372
|
+
expect(original.getHours()).toBe(14)
|
|
373
|
+
expect(original.getMinutes()).toBe(30)
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
describe('getEndOfDay', () => {
|
|
378
|
+
it('should set time to 23:59:59.999', () => {
|
|
379
|
+
const date = new Date(2025, 9, 15, 10, 0, 0, 0)
|
|
380
|
+
const result = getEndOfDay(date)
|
|
381
|
+
|
|
382
|
+
expect(result.getHours()).toBe(23)
|
|
383
|
+
expect(result.getMinutes()).toBe(59)
|
|
384
|
+
expect(result.getSeconds()).toBe(59)
|
|
385
|
+
expect(result.getMilliseconds()).toBe(999)
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it('should preserve the date', () => {
|
|
389
|
+
const date = new Date(2025, 9, 15, 0, 0, 0)
|
|
390
|
+
const result = getEndOfDay(date)
|
|
391
|
+
|
|
392
|
+
expect(result.getFullYear()).toBe(2025)
|
|
393
|
+
expect(result.getMonth()).toBe(9)
|
|
394
|
+
expect(result.getDate()).toBe(15)
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
it('should not mutate original date', () => {
|
|
398
|
+
const original = new Date(2025, 9, 15, 10, 30)
|
|
399
|
+
getEndOfDay(original)
|
|
400
|
+
|
|
401
|
+
expect(original.getHours()).toBe(10)
|
|
402
|
+
expect(original.getMinutes()).toBe(30)
|
|
403
|
+
})
|
|
404
|
+
})
|
|
405
|
+
})
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
* Minimal output system for prjct-cli
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, spyOn } from 'bun:test'
|
|
7
|
+
import out from '../../utils/output'
|
|
7
8
|
|
|
8
9
|
describe('Output Module', () => {
|
|
9
|
-
let consoleLogSpy
|
|
10
|
-
let stdoutWriteSpy
|
|
10
|
+
let consoleLogSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let stdoutWriteSpy: ReturnType<typeof spyOn>
|
|
11
12
|
|
|
12
13
|
beforeEach(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Mock process.stdout.write
|
|
16
|
-
stdoutWriteSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => {})
|
|
17
|
-
// Ensure spinner is stopped before each test
|
|
14
|
+
consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
15
|
+
stdoutWriteSpy = spyOn(process.stdout, 'write').mockImplementation(() => true)
|
|
18
16
|
out.stop()
|
|
19
17
|
})
|
|
20
18
|
|
|
@@ -39,7 +37,6 @@ describe('Output Module', () => {
|
|
|
39
37
|
out.done(longMessage)
|
|
40
38
|
|
|
41
39
|
const output = consoleLogSpy.mock.calls[0][0]
|
|
42
|
-
// Should be truncated to ~65 chars + checkmark
|
|
43
40
|
expect(output.length).toBeLessThan(80)
|
|
44
41
|
})
|
|
45
42
|
|
|
@@ -90,20 +87,16 @@ describe('Output Module', () => {
|
|
|
90
87
|
})
|
|
91
88
|
|
|
92
89
|
describe('spin()', () => {
|
|
93
|
-
it('should start spinner with message', () => {
|
|
94
|
-
vi.useFakeTimers()
|
|
95
|
-
|
|
90
|
+
it('should start spinner with message', async () => {
|
|
96
91
|
out.spin('loading')
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
vi.advanceTimersByTime(100)
|
|
93
|
+
await new Promise(resolve => setTimeout(resolve, 150))
|
|
100
94
|
|
|
101
95
|
expect(stdoutWriteSpy).toHaveBeenCalled()
|
|
102
96
|
const output = stdoutWriteSpy.mock.calls[0][0]
|
|
103
97
|
expect(output).toContain('loading')
|
|
104
98
|
|
|
105
99
|
out.stop()
|
|
106
|
-
vi.useRealTimers()
|
|
107
100
|
})
|
|
108
101
|
|
|
109
102
|
it('should return self for chaining', () => {
|
|
@@ -114,19 +107,14 @@ describe('Output Module', () => {
|
|
|
114
107
|
})
|
|
115
108
|
|
|
116
109
|
describe('stop()', () => {
|
|
117
|
-
it('should stop spinner and clear line', () => {
|
|
118
|
-
vi.useFakeTimers()
|
|
119
|
-
|
|
110
|
+
it('should stop spinner and clear line', async () => {
|
|
120
111
|
out.spin('loading')
|
|
121
|
-
|
|
112
|
+
await new Promise(resolve => setTimeout(resolve, 150))
|
|
122
113
|
|
|
123
114
|
stdoutWriteSpy.mockClear()
|
|
124
115
|
out.stop()
|
|
125
116
|
|
|
126
|
-
// Should write clearing spaces
|
|
127
117
|
expect(stdoutWriteSpy).toHaveBeenCalled()
|
|
128
|
-
|
|
129
|
-
vi.useRealTimers()
|
|
130
118
|
})
|
|
131
119
|
|
|
132
120
|
it('should be safe to call multiple times', () => {
|
|
@@ -151,8 +139,8 @@ describe('Output Module', () => {
|
|
|
151
139
|
})
|
|
152
140
|
|
|
153
141
|
it('should handle null/undefined messages', () => {
|
|
154
|
-
expect(() => out.done(null)).not.toThrow()
|
|
155
|
-
expect(() => out.done(undefined)).not.toThrow()
|
|
142
|
+
expect(() => out.done(null as unknown as string)).not.toThrow()
|
|
143
|
+
expect(() => out.done(undefined as unknown as string)).not.toThrow()
|
|
156
144
|
})
|
|
157
145
|
|
|
158
146
|
it('should handle messages with special characters', () => {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Router
|
|
3
|
+
* Orchestrates agent loading and context building for Claude delegation.
|
|
4
|
+
*
|
|
5
|
+
* @module agentic/agent-router
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs/promises'
|
|
10
|
+
import path from 'path'
|
|
11
|
+
import configManager from '../infrastructure/config-manager'
|
|
12
|
+
import pathManager from '../infrastructure/path-manager'
|
|
13
|
+
|
|
14
|
+
interface Agent {
|
|
15
|
+
name: string
|
|
16
|
+
content: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AssignmentContext {
|
|
20
|
+
task: string
|
|
21
|
+
availableAgents: string[]
|
|
22
|
+
projectPath: string
|
|
23
|
+
projectId: string | null
|
|
24
|
+
_template: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Routes tasks to specialized agents based on Claude's decisions.
|
|
29
|
+
* Handles agent loading, context building, and usage logging.
|
|
30
|
+
*/
|
|
31
|
+
class AgentRouter {
|
|
32
|
+
projectId: string | null = null
|
|
33
|
+
agentsPath: string | null = null
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize router with project context
|
|
37
|
+
*/
|
|
38
|
+
async initialize(projectPath: string): Promise<void> {
|
|
39
|
+
this.projectId = await configManager.getProjectId(projectPath)
|
|
40
|
+
this.agentsPath = pathManager.getPath(this.projectId!, 'agents')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load all available agents from project
|
|
45
|
+
*/
|
|
46
|
+
async loadAvailableAgents(): Promise<Agent[]> {
|
|
47
|
+
try {
|
|
48
|
+
const files = await fs.readdir(this.agentsPath!)
|
|
49
|
+
const agents: Agent[] = []
|
|
50
|
+
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
if (file.endsWith('.md')) {
|
|
53
|
+
const name = file.replace('.md', '')
|
|
54
|
+
const content = await fs.readFile(path.join(this.agentsPath!, file), 'utf-8')
|
|
55
|
+
agents.push({ name, content })
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return agents
|
|
60
|
+
} catch {
|
|
61
|
+
return []
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get list of available agent names
|
|
67
|
+
*/
|
|
68
|
+
async getAgentNames(): Promise<string[]> {
|
|
69
|
+
const agents = await this.loadAvailableAgents()
|
|
70
|
+
return agents.map((a) => a.name)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Load a specific agent by name
|
|
75
|
+
*/
|
|
76
|
+
async loadAgent(name: string): Promise<Agent | null> {
|
|
77
|
+
try {
|
|
78
|
+
const filePath = path.join(this.agentsPath!, `${name}.md`)
|
|
79
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
80
|
+
return { name, content }
|
|
81
|
+
} catch {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build context for Claude to decide agent assignment
|
|
88
|
+
*/
|
|
89
|
+
async buildAssignmentContext(
|
|
90
|
+
task: string | { description?: string },
|
|
91
|
+
projectPath: string
|
|
92
|
+
): Promise<AssignmentContext> {
|
|
93
|
+
const agents = await this.getAgentNames()
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
task: typeof task === 'string' ? task : task.description || '',
|
|
97
|
+
availableAgents: agents,
|
|
98
|
+
projectPath,
|
|
99
|
+
projectId: this.projectId,
|
|
100
|
+
// Claude reads this and decides via template
|
|
101
|
+
_template: 'templates/agent-assignment.md',
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Log agent usage to JSONL file
|
|
107
|
+
*/
|
|
108
|
+
async logUsage(
|
|
109
|
+
task: string | { description?: string },
|
|
110
|
+
agent: string | { name?: string },
|
|
111
|
+
_projectPath: string
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
try {
|
|
114
|
+
const logPath = path.join(
|
|
115
|
+
process.env.HOME || '',
|
|
116
|
+
'.prjct-cli',
|
|
117
|
+
'projects',
|
|
118
|
+
this.projectId || '',
|
|
119
|
+
'agent-usage.jsonl'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const entry =
|
|
123
|
+
JSON.stringify({
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
task: typeof task === 'string' ? task : task.description,
|
|
126
|
+
agent: typeof agent === 'string' ? agent : agent.name,
|
|
127
|
+
projectId: this.projectId,
|
|
128
|
+
}) + '\n'
|
|
129
|
+
|
|
130
|
+
await fs.appendFile(logPath, entry)
|
|
131
|
+
} catch {
|
|
132
|
+
// Silent fail for logging
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default AgentRouter
|