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,21 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SmartCache - Intelligent Persistent Cache for Agents
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Cache with specific keys: {projectId}-{domain}-{techStack}
|
|
5
5
|
* Persists to disk for cross-session caching
|
|
6
6
|
* Invalidates only when stack changes
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* @version 1.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
import fs from 'fs/promises'
|
|
12
|
+
import path from 'path'
|
|
13
|
+
import os from 'os'
|
|
14
|
+
import crypto from 'crypto'
|
|
15
|
+
import log from '../utils/logger'
|
|
16
|
+
|
|
17
|
+
interface CacheStats {
|
|
18
|
+
size: number
|
|
19
|
+
keys: string[]
|
|
20
|
+
}
|
|
16
21
|
|
|
17
22
|
class SmartCache {
|
|
18
|
-
|
|
23
|
+
projectId: string | null
|
|
24
|
+
memoryCache: Map<string, unknown>
|
|
25
|
+
cacheDir: string
|
|
26
|
+
cacheFile: string
|
|
27
|
+
|
|
28
|
+
constructor(projectId: string | null = null) {
|
|
19
29
|
this.projectId = projectId
|
|
20
30
|
this.memoryCache = new Map()
|
|
21
31
|
this.cacheDir = path.join(os.homedir(), '.prjct-cli', 'cache')
|
|
@@ -27,11 +37,11 @@ class SmartCache {
|
|
|
27
37
|
/**
|
|
28
38
|
* Initialize cache - load from disk
|
|
29
39
|
*/
|
|
30
|
-
async initialize() {
|
|
40
|
+
async initialize(): Promise<void> {
|
|
31
41
|
try {
|
|
32
42
|
await fs.mkdir(this.cacheDir, { recursive: true })
|
|
33
43
|
await this.loadFromDisk()
|
|
34
|
-
} catch
|
|
44
|
+
} catch {
|
|
35
45
|
// Cache file doesn't exist yet - that's ok
|
|
36
46
|
this.memoryCache = new Map()
|
|
37
47
|
}
|
|
@@ -41,7 +51,7 @@ class SmartCache {
|
|
|
41
51
|
* Generate cache key
|
|
42
52
|
* Format: {projectId}-{domain}-{techStackHash}
|
|
43
53
|
*/
|
|
44
|
-
generateKey(projectId, domain, techStack = {}) {
|
|
54
|
+
generateKey(projectId: string | null, domain: string, techStack: Record<string, unknown> = {}): string {
|
|
45
55
|
const techString = JSON.stringify(techStack)
|
|
46
56
|
const techHash = crypto.createHash('md5').update(techString).digest('hex').substring(0, 8)
|
|
47
57
|
return `${projectId || 'global'}-${domain}-${techHash}`
|
|
@@ -50,26 +60,26 @@ class SmartCache {
|
|
|
50
60
|
/**
|
|
51
61
|
* Get from cache
|
|
52
62
|
*/
|
|
53
|
-
async get(key) {
|
|
63
|
+
async get<T = unknown>(key: string): Promise<T | null> {
|
|
54
64
|
// Check memory first
|
|
55
65
|
if (this.memoryCache.has(key)) {
|
|
56
|
-
return this.memoryCache.get(key)
|
|
66
|
+
return this.memoryCache.get(key) as T
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
// Load from disk if not in memory
|
|
60
70
|
await this.loadFromDisk()
|
|
61
|
-
return this.memoryCache.get(key) || null
|
|
71
|
+
return (this.memoryCache.get(key) as T) || null
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
/**
|
|
65
75
|
* Set in cache
|
|
66
76
|
*/
|
|
67
|
-
async set(key, value) {
|
|
77
|
+
async set(key: string, value: unknown): Promise<void> {
|
|
68
78
|
// Set in memory
|
|
69
79
|
this.memoryCache.set(key, value)
|
|
70
80
|
|
|
71
81
|
// Persist to disk (async, don't wait)
|
|
72
|
-
this.persistToDisk().catch(err => {
|
|
82
|
+
this.persistToDisk().catch((err) => {
|
|
73
83
|
log.error('Cache persist error:', err.message)
|
|
74
84
|
})
|
|
75
85
|
}
|
|
@@ -77,7 +87,7 @@ class SmartCache {
|
|
|
77
87
|
/**
|
|
78
88
|
* Check if key exists
|
|
79
89
|
*/
|
|
80
|
-
async has(key) {
|
|
90
|
+
async has(key: string): Promise<boolean> {
|
|
81
91
|
if (this.memoryCache.has(key)) {
|
|
82
92
|
return true
|
|
83
93
|
}
|
|
@@ -89,7 +99,7 @@ class SmartCache {
|
|
|
89
99
|
/**
|
|
90
100
|
* Clear cache
|
|
91
101
|
*/
|
|
92
|
-
async clear() {
|
|
102
|
+
async clear(): Promise<void> {
|
|
93
103
|
this.memoryCache.clear()
|
|
94
104
|
try {
|
|
95
105
|
await fs.unlink(this.cacheFile)
|
|
@@ -101,26 +111,26 @@ class SmartCache {
|
|
|
101
111
|
/**
|
|
102
112
|
* Invalidate cache for a project (when stack changes)
|
|
103
113
|
*/
|
|
104
|
-
async invalidateProject(projectId) {
|
|
105
|
-
const keysToDelete = []
|
|
114
|
+
async invalidateProject(projectId: string): Promise<void> {
|
|
115
|
+
const keysToDelete: string[] = []
|
|
106
116
|
for (const key of this.memoryCache.keys()) {
|
|
107
117
|
if (key.startsWith(`${projectId}-`)) {
|
|
108
118
|
keysToDelete.push(key)
|
|
109
119
|
}
|
|
110
120
|
}
|
|
111
121
|
|
|
112
|
-
keysToDelete.forEach(key => this.memoryCache.delete(key))
|
|
122
|
+
keysToDelete.forEach((key) => this.memoryCache.delete(key))
|
|
113
123
|
await this.persistToDisk()
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
/**
|
|
117
127
|
* Load cache from disk
|
|
118
128
|
*/
|
|
119
|
-
async loadFromDisk() {
|
|
129
|
+
async loadFromDisk(): Promise<void> {
|
|
120
130
|
try {
|
|
121
131
|
const content = await fs.readFile(this.cacheFile, 'utf-8')
|
|
122
|
-
const data = JSON.parse(content)
|
|
123
|
-
|
|
132
|
+
const data = JSON.parse(content) as Record<string, unknown>
|
|
133
|
+
|
|
124
134
|
// Restore to memory cache
|
|
125
135
|
for (const [key, value] of Object.entries(data)) {
|
|
126
136
|
this.memoryCache.set(key, value)
|
|
@@ -134,11 +144,11 @@ class SmartCache {
|
|
|
134
144
|
/**
|
|
135
145
|
* Persist cache to disk
|
|
136
146
|
*/
|
|
137
|
-
async persistToDisk() {
|
|
147
|
+
async persistToDisk(): Promise<void> {
|
|
138
148
|
try {
|
|
139
149
|
const data = Object.fromEntries(this.memoryCache)
|
|
140
150
|
await fs.writeFile(this.cacheFile, JSON.stringify(data, null, 2), 'utf-8')
|
|
141
|
-
} catch
|
|
151
|
+
} catch {
|
|
142
152
|
// Fail silently - cache is best effort
|
|
143
153
|
}
|
|
144
154
|
}
|
|
@@ -146,13 +156,12 @@ class SmartCache {
|
|
|
146
156
|
/**
|
|
147
157
|
* Get cache statistics
|
|
148
158
|
*/
|
|
149
|
-
getStats() {
|
|
159
|
+
getStats(): CacheStats {
|
|
150
160
|
return {
|
|
151
161
|
size: this.memoryCache.size,
|
|
152
|
-
keys: Array.from(this.memoryCache.keys())
|
|
162
|
+
keys: Array.from(this.memoryCache.keys()),
|
|
153
163
|
}
|
|
154
164
|
}
|
|
155
165
|
}
|
|
156
166
|
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
export default SmartCache
|
|
@@ -9,28 +9,57 @@
|
|
|
9
9
|
* @version 1.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
import fs from 'fs/promises'
|
|
13
|
+
import path from 'path'
|
|
14
|
+
import { exec } from 'child_process'
|
|
15
|
+
import { promisify } from 'util'
|
|
16
|
+
import pathManager from '../infrastructure/path-manager'
|
|
17
|
+
import configManager from '../infrastructure/config-manager'
|
|
18
|
+
import { emit } from '../bus'
|
|
19
19
|
|
|
20
20
|
const execAsync = promisify(exec)
|
|
21
21
|
|
|
22
|
+
interface SnapshotInfo {
|
|
23
|
+
hash: string | null
|
|
24
|
+
message: string
|
|
25
|
+
timestamp: string
|
|
26
|
+
files: string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SnapshotListItem {
|
|
30
|
+
hash: string
|
|
31
|
+
short: string
|
|
32
|
+
message: string
|
|
33
|
+
date: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface RestoreResult {
|
|
37
|
+
hash: string
|
|
38
|
+
files: string[]
|
|
39
|
+
timestamp: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface RedoStackEntry {
|
|
43
|
+
hash: string
|
|
44
|
+
message: string
|
|
45
|
+
timestamp: string
|
|
46
|
+
files: string[]
|
|
47
|
+
}
|
|
48
|
+
|
|
22
49
|
class SnapshotManager {
|
|
23
|
-
|
|
50
|
+
projectPath: string
|
|
51
|
+
projectId: string | null = null
|
|
52
|
+
snapshotDir: string | null = null
|
|
53
|
+
initialized: boolean = false
|
|
54
|
+
|
|
55
|
+
constructor(projectPath: string) {
|
|
24
56
|
this.projectPath = projectPath
|
|
25
|
-
this.projectId = null
|
|
26
|
-
this.snapshotDir = null
|
|
27
|
-
this.initialized = false
|
|
28
57
|
}
|
|
29
58
|
|
|
30
59
|
/**
|
|
31
60
|
* Initialize snapshot system for project
|
|
32
61
|
*/
|
|
33
|
-
async initialize() {
|
|
62
|
+
async initialize(): Promise<void> {
|
|
34
63
|
this.projectId = await configManager.getProjectId(this.projectPath)
|
|
35
64
|
if (!this.projectId) {
|
|
36
65
|
throw new Error('No prjct project found. Run /p:init first.')
|
|
@@ -57,54 +86,48 @@ class SnapshotManager {
|
|
|
57
86
|
/**
|
|
58
87
|
* Initialize internal Git repository
|
|
59
88
|
*/
|
|
60
|
-
async initGitRepo() {
|
|
61
|
-
const gitDir = path.join(this.snapshotDir, '.git')
|
|
62
|
-
|
|
89
|
+
async initGitRepo(): Promise<void> {
|
|
63
90
|
// Create bare-ish repo structure
|
|
64
91
|
await execAsync(`git init "${this.snapshotDir}"`, { cwd: this.projectPath })
|
|
65
92
|
|
|
66
93
|
// Configure for snapshot use
|
|
67
|
-
await execAsync(`git config user.email "prjct@local"`, { cwd: this.snapshotDir })
|
|
68
|
-
await execAsync(`git config user.name "prjct-snapshots"`, { cwd: this.snapshotDir })
|
|
94
|
+
await execAsync(`git config user.email "prjct@local"`, { cwd: this.snapshotDir! })
|
|
95
|
+
await execAsync(`git config user.name "prjct-snapshots"`, { cwd: this.snapshotDir! })
|
|
69
96
|
|
|
70
97
|
// Create initial empty commit
|
|
71
|
-
await execAsync(`git commit --allow-empty -m "init: snapshot system"`, { cwd: this.snapshotDir })
|
|
98
|
+
await execAsync(`git commit --allow-empty -m "init: snapshot system"`, { cwd: this.snapshotDir! })
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
/**
|
|
75
102
|
* Create a snapshot of current project state
|
|
76
|
-
*
|
|
77
|
-
* @param {string} message - Snapshot description
|
|
78
|
-
* @param {string[]} files - Specific files to track (optional, defaults to all changed)
|
|
79
|
-
* @returns {Promise<Object>} Snapshot info {hash, message, timestamp, files}
|
|
80
103
|
*/
|
|
81
|
-
async create(message, files = null) {
|
|
104
|
+
async create(message: string, files: string[] | null = null): Promise<SnapshotInfo> {
|
|
82
105
|
if (!this.initialized) await this.initialize()
|
|
83
106
|
|
|
84
107
|
const timestamp = new Date().toISOString()
|
|
85
108
|
|
|
86
109
|
// Copy changed files to snapshot directory
|
|
87
|
-
const changedFiles = files || await this.getChangedFiles()
|
|
110
|
+
const changedFiles = files || (await this.getChangedFiles())
|
|
88
111
|
|
|
89
112
|
if (changedFiles.length === 0) {
|
|
90
113
|
return {
|
|
91
114
|
hash: null,
|
|
92
115
|
message: 'No changes to snapshot',
|
|
93
116
|
timestamp,
|
|
94
|
-
files: []
|
|
117
|
+
files: [],
|
|
95
118
|
}
|
|
96
119
|
}
|
|
97
120
|
|
|
98
121
|
// Copy files to snapshot dir maintaining structure
|
|
99
122
|
for (const file of changedFiles) {
|
|
100
123
|
const srcPath = path.join(this.projectPath, file)
|
|
101
|
-
const destPath = path.join(this.snapshotDir
|
|
124
|
+
const destPath = path.join(this.snapshotDir!, file)
|
|
102
125
|
|
|
103
126
|
try {
|
|
104
127
|
const content = await fs.readFile(srcPath, 'utf-8')
|
|
105
128
|
await fs.mkdir(path.dirname(destPath), { recursive: true })
|
|
106
129
|
await fs.writeFile(destPath, content)
|
|
107
|
-
} catch
|
|
130
|
+
} catch {
|
|
108
131
|
// File might be deleted, mark for removal
|
|
109
132
|
try {
|
|
110
133
|
await fs.unlink(destPath)
|
|
@@ -113,13 +136,13 @@ class SnapshotManager {
|
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
// Stage and commit in snapshot repo
|
|
116
|
-
await execAsync(`git add -A`, { cwd: this.snapshotDir })
|
|
139
|
+
await execAsync(`git add -A`, { cwd: this.snapshotDir! })
|
|
117
140
|
|
|
118
141
|
const commitMsg = `${message}\n\nFiles: ${changedFiles.length}\nTimestamp: ${timestamp}`
|
|
119
|
-
await execAsync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { cwd: this.snapshotDir })
|
|
142
|
+
await execAsync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { cwd: this.snapshotDir! })
|
|
120
143
|
|
|
121
144
|
// Get commit hash
|
|
122
|
-
const { stdout } = await execAsync(`git rev-parse HEAD`, { cwd: this.snapshotDir })
|
|
145
|
+
const { stdout } = await execAsync(`git rev-parse HEAD`, { cwd: this.snapshotDir! })
|
|
123
146
|
const hash = stdout.trim()
|
|
124
147
|
|
|
125
148
|
// Log to manifest
|
|
@@ -127,7 +150,7 @@ class SnapshotManager {
|
|
|
127
150
|
hash,
|
|
128
151
|
message,
|
|
129
152
|
timestamp,
|
|
130
|
-
files: changedFiles
|
|
153
|
+
files: changedFiles,
|
|
131
154
|
})
|
|
132
155
|
|
|
133
156
|
// Emit event for plugins
|
|
@@ -136,7 +159,7 @@ class SnapshotManager {
|
|
|
136
159
|
message,
|
|
137
160
|
timestamp,
|
|
138
161
|
filesCount: changedFiles.length,
|
|
139
|
-
projectId: this.projectId
|
|
162
|
+
projectId: this.projectId,
|
|
140
163
|
})
|
|
141
164
|
|
|
142
165
|
return { hash, message, timestamp, files: changedFiles }
|
|
@@ -145,18 +168,15 @@ class SnapshotManager {
|
|
|
145
168
|
/**
|
|
146
169
|
* Get list of changed files in project
|
|
147
170
|
*/
|
|
148
|
-
async getChangedFiles() {
|
|
171
|
+
async getChangedFiles(): Promise<string[]> {
|
|
149
172
|
try {
|
|
150
|
-
const { stdout } = await execAsync(
|
|
151
|
-
`git status --porcelain`,
|
|
152
|
-
{ cwd: this.projectPath }
|
|
153
|
-
)
|
|
173
|
+
const { stdout } = await execAsync(`git status --porcelain`, { cwd: this.projectPath })
|
|
154
174
|
|
|
155
175
|
return stdout
|
|
156
176
|
.split('\n')
|
|
157
177
|
.filter(Boolean)
|
|
158
|
-
.map(line => line.slice(3).trim())
|
|
159
|
-
.filter(file => !file.startsWith('.prjct/'))
|
|
178
|
+
.map((line) => line.slice(3).trim())
|
|
179
|
+
.filter((file) => !file.startsWith('.prjct/'))
|
|
160
180
|
} catch {
|
|
161
181
|
return []
|
|
162
182
|
}
|
|
@@ -164,30 +184,27 @@ class SnapshotManager {
|
|
|
164
184
|
|
|
165
185
|
/**
|
|
166
186
|
* List all snapshots
|
|
167
|
-
*
|
|
168
|
-
* @param {number} limit - Max snapshots to return
|
|
169
|
-
* @returns {Promise<Array>} List of snapshots
|
|
170
187
|
*/
|
|
171
|
-
async list(limit = 10) {
|
|
188
|
+
async list(limit: number = 10): Promise<SnapshotListItem[]> {
|
|
172
189
|
if (!this.initialized) await this.initialize()
|
|
173
190
|
|
|
174
191
|
try {
|
|
175
192
|
const { stdout } = await execAsync(
|
|
176
193
|
`git log --pretty=format:'{"hash":"%H","short":"%h","message":"%s","date":"%ai"}' -n ${limit}`,
|
|
177
|
-
{ cwd: this.snapshotDir }
|
|
194
|
+
{ cwd: this.snapshotDir! }
|
|
178
195
|
)
|
|
179
196
|
|
|
180
197
|
return stdout
|
|
181
198
|
.split('\n')
|
|
182
199
|
.filter(Boolean)
|
|
183
|
-
.map(line => {
|
|
200
|
+
.map((line) => {
|
|
184
201
|
try {
|
|
185
|
-
return JSON.parse(line)
|
|
202
|
+
return JSON.parse(line) as SnapshotListItem
|
|
186
203
|
} catch {
|
|
187
204
|
return null
|
|
188
205
|
}
|
|
189
206
|
})
|
|
190
|
-
.filter(
|
|
207
|
+
.filter((item): item is SnapshotListItem => item !== null)
|
|
191
208
|
} catch {
|
|
192
209
|
return []
|
|
193
210
|
}
|
|
@@ -195,34 +212,30 @@ class SnapshotManager {
|
|
|
195
212
|
|
|
196
213
|
/**
|
|
197
214
|
* Restore project to a specific snapshot
|
|
198
|
-
*
|
|
199
|
-
* @param {string} hash - Commit hash to restore
|
|
200
|
-
* @returns {Promise<Object>} Restore result
|
|
201
215
|
*/
|
|
202
|
-
async restore(hash) {
|
|
216
|
+
async restore(hash: string): Promise<RestoreResult> {
|
|
203
217
|
if (!this.initialized) await this.initialize()
|
|
204
218
|
|
|
205
219
|
// Get files changed in that commit
|
|
206
|
-
const { stdout: filesOutput } = await execAsync(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
220
|
+
const { stdout: filesOutput } = await execAsync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {
|
|
221
|
+
cwd: this.snapshotDir!,
|
|
222
|
+
})
|
|
210
223
|
|
|
211
224
|
const files = filesOutput.split('\n').filter(Boolean)
|
|
212
225
|
|
|
213
226
|
// Checkout files from that snapshot
|
|
214
|
-
await execAsync(`git checkout ${hash} -- .`, { cwd: this.snapshotDir })
|
|
227
|
+
await execAsync(`git checkout ${hash} -- .`, { cwd: this.snapshotDir! })
|
|
215
228
|
|
|
216
229
|
// Copy files back to project
|
|
217
230
|
for (const file of files) {
|
|
218
|
-
const srcPath = path.join(this.snapshotDir
|
|
231
|
+
const srcPath = path.join(this.snapshotDir!, file)
|
|
219
232
|
const destPath = path.join(this.projectPath, file)
|
|
220
233
|
|
|
221
234
|
try {
|
|
222
235
|
const content = await fs.readFile(srcPath, 'utf-8')
|
|
223
236
|
await fs.mkdir(path.dirname(destPath), { recursive: true })
|
|
224
237
|
await fs.writeFile(destPath, content)
|
|
225
|
-
} catch
|
|
238
|
+
} catch {
|
|
226
239
|
// File doesn't exist in snapshot, might need to delete from project
|
|
227
240
|
try {
|
|
228
241
|
await fs.unlink(destPath)
|
|
@@ -240,7 +253,7 @@ class SnapshotManager {
|
|
|
240
253
|
hash,
|
|
241
254
|
filesCount: files.length,
|
|
242
255
|
timestamp,
|
|
243
|
-
projectId: this.projectId
|
|
256
|
+
projectId: this.projectId,
|
|
244
257
|
})
|
|
245
258
|
|
|
246
259
|
return { hash, files, timestamp }
|
|
@@ -248,18 +261,12 @@ class SnapshotManager {
|
|
|
248
261
|
|
|
249
262
|
/**
|
|
250
263
|
* Get diff between current state and a snapshot
|
|
251
|
-
*
|
|
252
|
-
* @param {string} hash - Snapshot hash to compare
|
|
253
|
-
* @returns {Promise<string>} Diff output
|
|
254
264
|
*/
|
|
255
|
-
async diff(hash) {
|
|
265
|
+
async diff(hash: string): Promise<string> {
|
|
256
266
|
if (!this.initialized) await this.initialize()
|
|
257
267
|
|
|
258
268
|
try {
|
|
259
|
-
const { stdout } = await execAsync(
|
|
260
|
-
`git diff ${hash} --stat`,
|
|
261
|
-
{ cwd: this.snapshotDir }
|
|
262
|
-
)
|
|
269
|
+
const { stdout } = await execAsync(`git diff ${hash} --stat`, { cwd: this.snapshotDir! })
|
|
263
270
|
return stdout
|
|
264
271
|
} catch {
|
|
265
272
|
return ''
|
|
@@ -269,14 +276,11 @@ class SnapshotManager {
|
|
|
269
276
|
/**
|
|
270
277
|
* Get the most recent snapshot hash
|
|
271
278
|
*/
|
|
272
|
-
async getLatestHash() {
|
|
279
|
+
async getLatestHash(): Promise<string | null> {
|
|
273
280
|
if (!this.initialized) await this.initialize()
|
|
274
281
|
|
|
275
282
|
try {
|
|
276
|
-
const { stdout } = await execAsync(
|
|
277
|
-
`git rev-parse HEAD`,
|
|
278
|
-
{ cwd: this.snapshotDir }
|
|
279
|
-
)
|
|
283
|
+
const { stdout } = await execAsync(`git rev-parse HEAD`, { cwd: this.snapshotDir! })
|
|
280
284
|
return stdout.trim()
|
|
281
285
|
} catch {
|
|
282
286
|
return null
|
|
@@ -286,14 +290,11 @@ class SnapshotManager {
|
|
|
286
290
|
/**
|
|
287
291
|
* Get the hash before the current one (for undo)
|
|
288
292
|
*/
|
|
289
|
-
async getPreviousHash() {
|
|
293
|
+
async getPreviousHash(): Promise<string | null> {
|
|
290
294
|
if (!this.initialized) await this.initialize()
|
|
291
295
|
|
|
292
296
|
try {
|
|
293
|
-
const { stdout } = await execAsync(
|
|
294
|
-
`git rev-parse HEAD~1`,
|
|
295
|
-
{ cwd: this.snapshotDir }
|
|
296
|
-
)
|
|
297
|
+
const { stdout } = await execAsync(`git rev-parse HEAD~1`, { cwd: this.snapshotDir! })
|
|
297
298
|
return stdout.trim()
|
|
298
299
|
} catch {
|
|
299
300
|
return null
|
|
@@ -303,12 +304,13 @@ class SnapshotManager {
|
|
|
303
304
|
/**
|
|
304
305
|
* Log snapshot to manifest
|
|
305
306
|
*/
|
|
306
|
-
async logSnapshot(snapshot) {
|
|
307
|
-
const manifestPath = path.join(this.snapshotDir
|
|
308
|
-
const entry =
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
307
|
+
async logSnapshot(snapshot: SnapshotInfo): Promise<void> {
|
|
308
|
+
const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
|
|
309
|
+
const entry =
|
|
310
|
+
JSON.stringify({
|
|
311
|
+
type: 'snapshot',
|
|
312
|
+
...snapshot,
|
|
313
|
+
}) + '\n'
|
|
312
314
|
|
|
313
315
|
await fs.appendFile(manifestPath, entry)
|
|
314
316
|
}
|
|
@@ -316,14 +318,15 @@ class SnapshotManager {
|
|
|
316
318
|
/**
|
|
317
319
|
* Log restoration to manifest
|
|
318
320
|
*/
|
|
319
|
-
async logRestore(hash, files) {
|
|
320
|
-
const manifestPath = path.join(this.snapshotDir
|
|
321
|
-
const entry =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
async logRestore(hash: string, files: string[]): Promise<void> {
|
|
322
|
+
const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
|
|
323
|
+
const entry =
|
|
324
|
+
JSON.stringify({
|
|
325
|
+
type: 'restore',
|
|
326
|
+
hash,
|
|
327
|
+
files,
|
|
328
|
+
timestamp: new Date().toISOString(),
|
|
329
|
+
}) + '\n'
|
|
327
330
|
|
|
328
331
|
await fs.appendFile(manifestPath, entry)
|
|
329
332
|
}
|
|
@@ -332,8 +335,8 @@ class SnapshotManager {
|
|
|
332
335
|
* Get redo stack (snapshots after current position)
|
|
333
336
|
* This tracks undone snapshots that can be redone
|
|
334
337
|
*/
|
|
335
|
-
async getRedoStack() {
|
|
336
|
-
const stackPath = path.join(this.snapshotDir
|
|
338
|
+
async getRedoStack(): Promise<RedoStackEntry[]> {
|
|
339
|
+
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
337
340
|
try {
|
|
338
341
|
const content = await fs.readFile(stackPath, 'utf-8')
|
|
339
342
|
return JSON.parse(content)
|
|
@@ -345,20 +348,20 @@ class SnapshotManager {
|
|
|
345
348
|
/**
|
|
346
349
|
* Push to redo stack (when undoing)
|
|
347
350
|
*/
|
|
348
|
-
async pushToRedoStack(snapshot) {
|
|
351
|
+
async pushToRedoStack(snapshot: RedoStackEntry): Promise<void> {
|
|
349
352
|
const stack = await this.getRedoStack()
|
|
350
353
|
stack.push(snapshot)
|
|
351
|
-
const stackPath = path.join(this.snapshotDir
|
|
354
|
+
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
352
355
|
await fs.writeFile(stackPath, JSON.stringify(stack, null, 2))
|
|
353
356
|
}
|
|
354
357
|
|
|
355
358
|
/**
|
|
356
359
|
* Pop from redo stack (when redoing)
|
|
357
360
|
*/
|
|
358
|
-
async popFromRedoStack() {
|
|
361
|
+
async popFromRedoStack(): Promise<RedoStackEntry | undefined> {
|
|
359
362
|
const stack = await this.getRedoStack()
|
|
360
363
|
const snapshot = stack.pop()
|
|
361
|
-
const stackPath = path.join(this.snapshotDir
|
|
364
|
+
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
362
365
|
await fs.writeFile(stackPath, JSON.stringify(stack, null, 2))
|
|
363
366
|
return snapshot
|
|
364
367
|
}
|
|
@@ -366,10 +369,10 @@ class SnapshotManager {
|
|
|
366
369
|
/**
|
|
367
370
|
* Clear redo stack (when creating new snapshot after undo)
|
|
368
371
|
*/
|
|
369
|
-
async clearRedoStack() {
|
|
370
|
-
const stackPath = path.join(this.snapshotDir
|
|
372
|
+
async clearRedoStack(): Promise<void> {
|
|
373
|
+
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
371
374
|
await fs.writeFile(stackPath, '[]')
|
|
372
375
|
}
|
|
373
376
|
}
|
|
374
377
|
|
|
375
|
-
|
|
378
|
+
export default SnapshotManager
|