prjct-cli 1.22.0 → 1.23.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 +147 -0
- package/bin/prjct +30 -13
- package/dist/bin/prjct.mjs +917 -35845
- package/dist/bin/prjct.mjs.map +7 -0
- package/dist/cli/linear.mjs +16 -0
- package/dist/cli/linear.mjs.map +7 -0
- package/dist/templates.json +1 -0
- package/package.json +4 -5
- package/bin/prjct.ts +0 -342
- package/core/__tests__/agentic/analysis-injection.test.ts +0 -377
- package/core/__tests__/agentic/cache-eviction.test.ts +0 -294
- package/core/__tests__/agentic/command-context.test.ts +0 -281
- package/core/__tests__/agentic/command-executor.test.ts +0 -659
- package/core/__tests__/agentic/domain-classifier.test.ts +0 -330
- package/core/__tests__/agentic/injection-validator.test.ts +0 -255
- package/core/__tests__/agentic/memory-system.test.ts +0 -281
- package/core/__tests__/agentic/plan-mode.test.ts +0 -386
- package/core/__tests__/agentic/prompt-assembly.test.ts +0 -298
- package/core/__tests__/agentic/prompt-builder.test.ts +0 -243
- package/core/__tests__/agentic/response-validator.test.ts +0 -263
- package/core/__tests__/agentic/semantic-matching.test.ts +0 -131
- package/core/__tests__/agentic/smart-context.test.ts +0 -372
- package/core/__tests__/agentic/tech-normalizer.test.ts +0 -136
- package/core/__tests__/agentic/token-budget.test.ts +0 -294
- package/core/__tests__/ai-tools/formatters.test.ts +0 -476
- package/core/__tests__/domain/bm25.test.ts +0 -225
- package/core/__tests__/domain/change-propagator.test.ts +0 -100
- package/core/__tests__/domain/fibonacci.test.ts +0 -113
- package/core/__tests__/domain/file-hasher.test.ts +0 -146
- package/core/__tests__/domain/file-ranker.test.ts +0 -169
- package/core/__tests__/domain/git-cochange.test.ts +0 -121
- package/core/__tests__/domain/import-graph.test.ts +0 -156
- package/core/__tests__/domain/velocity.test.ts +0 -623
- package/core/__tests__/infrastructure/performance-tracker.test.ts +0 -328
- package/core/__tests__/schemas/model.test.ts +0 -272
- package/core/__tests__/services/dependency-validator.test.ts +0 -175
- package/core/__tests__/services/hierarchical-agent-resolver.test.ts +0 -359
- package/core/__tests__/services/nested-context-resolver.test.ts +0 -443
- package/core/__tests__/services/project-index.test.ts +0 -355
- package/core/__tests__/services/staleness-checker.test.ts +0 -204
- package/core/__tests__/storage/analysis-storage.test.ts +0 -641
- package/core/__tests__/storage/archive-storage.test.ts +0 -455
- package/core/__tests__/storage/safe-reader.test.ts +0 -262
- package/core/__tests__/storage/sqlite-migration.test.ts +0 -1016
- package/core/__tests__/storage/state-storage-feedback.test.ts +0 -463
- package/core/__tests__/storage/state-storage-history.test.ts +0 -469
- package/core/__tests__/storage/storage-manager.test.ts +0 -383
- package/core/__tests__/storage/subtask-handoff.test.ts +0 -237
- package/core/__tests__/types/fs.test.ts +0 -125
- package/core/__tests__/utils/date-helper.test.ts +0 -449
- package/core/__tests__/utils/output.test.ts +0 -278
- package/core/__tests__/utils/preserve-sections.test.ts +0 -216
- package/core/__tests__/utils/project-commands.test.ts +0 -71
- package/core/__tests__/utils/retry.test.ts +0 -381
- package/core/__tests__/workflow/state-machine.test.ts +0 -216
- package/core/agentic/agent-router.ts +0 -150
- package/core/agentic/anti-hallucination.ts +0 -141
- package/core/agentic/chain-of-thought.ts +0 -234
- package/core/agentic/command-classifier.ts +0 -141
- package/core/agentic/command-context.ts +0 -168
- package/core/agentic/command-executor.ts +0 -471
- package/core/agentic/context-builder.ts +0 -285
- package/core/agentic/domain-classifier.ts +0 -525
- package/core/agentic/environment-block.ts +0 -102
- package/core/agentic/ground-truth.ts +0 -706
- package/core/agentic/index.ts +0 -193
- package/core/agentic/injection-validator.ts +0 -208
- package/core/agentic/loop-detector.ts +0 -451
- package/core/agentic/memory-system.ts +0 -1547
- package/core/agentic/orchestrator-executor.ts +0 -579
- package/core/agentic/plan-mode.ts +0 -525
- package/core/agentic/prompt-builder.ts +0 -1069
- package/core/agentic/response-validator.ts +0 -98
- package/core/agentic/services.ts +0 -167
- package/core/agentic/skill-loader.ts +0 -106
- package/core/agentic/smart-context.ts +0 -393
- package/core/agentic/tech-normalizer.ts +0 -167
- package/core/agentic/template-executor.ts +0 -272
- package/core/agentic/template-loader.ts +0 -109
- package/core/agentic/token-budget.ts +0 -226
- package/core/agentic/tool-registry.ts +0 -146
- package/core/agents/index.ts +0 -28
- package/core/agents/performance.ts +0 -429
- package/core/ai-tools/formatters.ts +0 -341
- package/core/ai-tools/generator.ts +0 -144
- package/core/ai-tools/index.ts +0 -15
- package/core/ai-tools/registry.ts +0 -201
- package/core/bus/bus.ts +0 -314
- package/core/bus/index.ts +0 -8
- package/core/cli/linear.ts +0 -500
- package/core/cli/lint-meta-commentary.ts +0 -177
- package/core/cli/start.ts +0 -386
- package/core/commands/analysis.ts +0 -1274
- package/core/commands/analytics.ts +0 -342
- package/core/commands/base.ts +0 -118
- package/core/commands/cleanup.ts +0 -157
- package/core/commands/command-data.ts +0 -463
- package/core/commands/commands.ts +0 -306
- package/core/commands/context.ts +0 -238
- package/core/commands/design.ts +0 -77
- package/core/commands/index.ts +0 -19
- package/core/commands/maintenance.ts +0 -77
- package/core/commands/performance.ts +0 -114
- package/core/commands/planning.ts +0 -662
- package/core/commands/register.ts +0 -127
- package/core/commands/registry.ts +0 -444
- package/core/commands/setup.ts +0 -280
- package/core/commands/shipping.ts +0 -267
- package/core/commands/snapshots.ts +0 -297
- package/core/commands/uninstall.ts +0 -542
- package/core/commands/velocity.ts +0 -149
- package/core/commands/workflow.ts +0 -505
- package/core/config/command-context.config.json +0 -66
- package/core/constants/index.ts +0 -379
- package/core/context/generator.ts +0 -368
- package/core/context-tools/files-tool.ts +0 -577
- package/core/context-tools/imports-tool.ts +0 -400
- package/core/context-tools/index.ts +0 -434
- package/core/context-tools/recent-tool.ts +0 -301
- package/core/context-tools/signatures-tool.ts +0 -495
- package/core/context-tools/summary-tool.ts +0 -301
- package/core/context-tools/token-counter.ts +0 -273
- package/core/context-tools/types.ts +0 -253
- package/core/domain/agent-generator.ts +0 -186
- package/core/domain/agent-loader.ts +0 -419
- package/core/domain/analyzer.ts +0 -387
- package/core/domain/architecture-generator.ts +0 -108
- package/core/domain/bm25.ts +0 -525
- package/core/domain/change-propagator.ts +0 -162
- package/core/domain/context-estimator.ts +0 -175
- package/core/domain/fibonacci.ts +0 -128
- package/core/domain/file-hasher.ts +0 -296
- package/core/domain/file-ranker.ts +0 -151
- package/core/domain/git-cochange.ts +0 -250
- package/core/domain/import-graph.ts +0 -315
- package/core/domain/snapshot-manager.ts +0 -415
- package/core/domain/task-stack.ts +0 -578
- package/core/domain/velocity.ts +0 -470
- package/core/errors.ts +0 -335
- package/core/events/events.ts +0 -85
- package/core/events/index.ts +0 -8
- package/core/index.ts +0 -481
- package/core/infrastructure/agent-detector.ts +0 -135
- package/core/infrastructure/ai-provider.ts +0 -578
- package/core/infrastructure/author-detector.ts +0 -133
- package/core/infrastructure/capability-installer.ts +0 -76
- package/core/infrastructure/claude-agent.ts +0 -297
- package/core/infrastructure/command-installer.ts +0 -752
- package/core/infrastructure/config-manager.ts +0 -364
- package/core/infrastructure/editors-config.ts +0 -172
- package/core/infrastructure/path-manager.ts +0 -571
- package/core/infrastructure/performance-tracker.ts +0 -326
- package/core/infrastructure/permission-manager.ts +0 -289
- package/core/infrastructure/setup.ts +0 -1061
- package/core/infrastructure/update-checker.ts +0 -246
- package/core/integrations/issue-tracker/enricher.ts +0 -271
- package/core/integrations/issue-tracker/index.ts +0 -8
- package/core/integrations/issue-tracker/manager.ts +0 -286
- package/core/integrations/issue-tracker/types.ts +0 -310
- package/core/integrations/jira/cache.ts +0 -57
- package/core/integrations/jira/client.ts +0 -688
- package/core/integrations/jira/index.ts +0 -23
- package/core/integrations/jira/service.ts +0 -244
- package/core/integrations/linear/cache.ts +0 -68
- package/core/integrations/linear/client.ts +0 -436
- package/core/integrations/linear/index.ts +0 -20
- package/core/integrations/linear/service.ts +0 -260
- package/core/integrations/linear/sync.ts +0 -314
- package/core/outcomes/analyzer.ts +0 -286
- package/core/outcomes/index.ts +0 -34
- package/core/outcomes/recorder.ts +0 -195
- package/core/plugin/builtin/webhook.ts +0 -148
- package/core/plugin/hooks.ts +0 -315
- package/core/plugin/index.ts +0 -50
- package/core/plugin/loader.ts +0 -354
- package/core/plugin/registry.ts +0 -326
- package/core/schemas/agents.ts +0 -27
- package/core/schemas/analysis.ts +0 -530
- package/core/schemas/classification.ts +0 -91
- package/core/schemas/command-context.ts +0 -29
- package/core/schemas/enriched-task.ts +0 -291
- package/core/schemas/ideas.ts +0 -114
- package/core/schemas/index.ts +0 -53
- package/core/schemas/issues.ts +0 -159
- package/core/schemas/llm-output.ts +0 -170
- package/core/schemas/metrics.ts +0 -143
- package/core/schemas/model.ts +0 -153
- package/core/schemas/outcomes.ts +0 -487
- package/core/schemas/performance.ts +0 -128
- package/core/schemas/permissions.ts +0 -180
- package/core/schemas/prd.ts +0 -450
- package/core/schemas/project.ts +0 -57
- package/core/schemas/roadmap.ts +0 -322
- package/core/schemas/schemas.ts +0 -38
- package/core/schemas/shipped.ts +0 -109
- package/core/schemas/state.ts +0 -284
- package/core/schemas/velocity.ts +0 -103
- package/core/server/index.ts +0 -21
- package/core/server/routes-extended.ts +0 -566
- package/core/server/routes.ts +0 -176
- package/core/server/server.ts +0 -149
- package/core/server/sse.ts +0 -192
- package/core/services/agent-generator.ts +0 -385
- package/core/services/agent-service.ts +0 -168
- package/core/services/breakdown-service.ts +0 -124
- package/core/services/context-generator.ts +0 -445
- package/core/services/context-selector.ts +0 -429
- package/core/services/dependency-validator.ts +0 -318
- package/core/services/diff-generator.ts +0 -313
- package/core/services/doctor-service.ts +0 -423
- package/core/services/file-categorizer.ts +0 -448
- package/core/services/file-scorer.ts +0 -270
- package/core/services/git-analyzer.ts +0 -293
- package/core/services/hierarchical-agent-resolver.ts +0 -236
- package/core/services/hooks-service.ts +0 -685
- package/core/services/index.ts +0 -46
- package/core/services/local-state-generator.ts +0 -158
- package/core/services/memory-service.ts +0 -181
- package/core/services/nested-context-resolver.ts +0 -842
- package/core/services/project-index.ts +0 -911
- package/core/services/project-service.ts +0 -155
- package/core/services/session-tracker.ts +0 -287
- package/core/services/skill-installer.ts +0 -447
- package/core/services/skill-lock.ts +0 -132
- package/core/services/skill-service.ts +0 -306
- package/core/services/stack-detector.ts +0 -229
- package/core/services/staleness-checker.ts +0 -327
- package/core/services/sync-service.ts +0 -1515
- package/core/services/sync-verifier.ts +0 -253
- package/core/services/watch-service.ts +0 -312
- package/core/session/compaction.ts +0 -248
- package/core/session/index.ts +0 -35
- package/core/session/log-migration.ts +0 -88
- package/core/session/metrics.ts +0 -323
- package/core/session/session-log-manager.ts +0 -307
- package/core/session/task-session-manager.ts +0 -404
- package/core/session/utils.ts +0 -51
- package/core/storage/analysis-storage.ts +0 -373
- package/core/storage/archive-storage.ts +0 -205
- package/core/storage/database.ts +0 -575
- package/core/storage/ideas-storage.ts +0 -298
- package/core/storage/index-storage.ts +0 -523
- package/core/storage/index.ts +0 -79
- package/core/storage/metrics-storage.ts +0 -321
- package/core/storage/migrate-json.ts +0 -720
- package/core/storage/queue-storage.ts +0 -336
- package/core/storage/safe-reader.ts +0 -105
- package/core/storage/shipped-storage.ts +0 -253
- package/core/storage/state-storage.ts +0 -1035
- package/core/storage/storage-manager.ts +0 -205
- package/core/storage/storage.ts +0 -177
- package/core/storage/velocity-storage.ts +0 -149
- package/core/sync/auth-config.ts +0 -138
- package/core/sync/index.ts +0 -31
- package/core/sync/oauth-handler.ts +0 -143
- package/core/sync/sync-client.ts +0 -251
- package/core/sync/sync-manager.ts +0 -327
- package/core/tsconfig.json +0 -22
- package/core/types/agentic.ts +0 -760
- package/core/types/agents.ts +0 -150
- package/core/types/bus.ts +0 -193
- package/core/types/citations.ts +0 -22
- package/core/types/commands.ts +0 -399
- package/core/types/config.ts +0 -92
- package/core/types/core.ts +0 -96
- package/core/types/diff.ts +0 -41
- package/core/types/domain.ts +0 -71
- package/core/types/errors.ts +0 -111
- package/core/types/events.ts +0 -42
- package/core/types/fs.ts +0 -72
- package/core/types/index.ts +0 -510
- package/core/types/infrastructure.ts +0 -210
- package/core/types/integrations.ts +0 -31
- package/core/types/jira.ts +0 -51
- package/core/types/logger.ts +0 -17
- package/core/types/memory.ts +0 -313
- package/core/types/outcomes.ts +0 -190
- package/core/types/output.ts +0 -47
- package/core/types/plugin.ts +0 -25
- package/core/types/project-sync.ts +0 -129
- package/core/types/provider.ts +0 -163
- package/core/types/server.ts +0 -71
- package/core/types/services.ts +0 -84
- package/core/types/session.ts +0 -135
- package/core/types/stack.ts +0 -19
- package/core/types/storage.ts +0 -318
- package/core/types/sync-verifier.ts +0 -33
- package/core/types/sync.ts +0 -121
- package/core/types/task.ts +0 -72
- package/core/types/template.ts +0 -24
- package/core/types/utils.ts +0 -92
- package/core/types/workflow.ts +0 -23
- package/core/utils/agent-stream.ts +0 -140
- package/core/utils/animations.ts +0 -251
- package/core/utils/branding.ts +0 -88
- package/core/utils/cache.ts +0 -187
- package/core/utils/citations.ts +0 -39
- package/core/utils/collection-filters.ts +0 -209
- package/core/utils/date-helper.ts +0 -176
- package/core/utils/error-messages.ts +0 -38
- package/core/utils/file-helper.ts +0 -277
- package/core/utils/fs-helpers.ts +0 -14
- package/core/utils/help.ts +0 -314
- package/core/utils/jsonl-helper.ts +0 -290
- package/core/utils/keychain.ts +0 -127
- package/core/utils/logger.ts +0 -77
- package/core/utils/markdown-builder.ts +0 -280
- package/core/utils/next-steps.ts +0 -95
- package/core/utils/output.ts +0 -403
- package/core/utils/preserve-sections.ts +0 -218
- package/core/utils/project-commands.ts +0 -126
- package/core/utils/project-credentials.ts +0 -143
- package/core/utils/provider-cache.ts +0 -49
- package/core/utils/retry.ts +0 -318
- package/core/utils/runtime.ts +0 -108
- package/core/utils/session-helper.ts +0 -278
- package/core/utils/subtask-table.ts +0 -227
- package/core/utils/version.ts +0 -128
- package/core/wizard/index.ts +0 -13
- package/core/wizard/onboarding.ts +0 -633
- package/core/workflow/index.ts +0 -7
- package/core/workflow/state-machine.ts +0 -198
- package/core/workflow/workflow-preferences.ts +0 -294
- package/dist/core/infrastructure/command-installer.js +0 -1141
- package/dist/core/infrastructure/editors-config.js +0 -177
- package/dist/core/infrastructure/setup.js +0 -2244
- package/dist/core/utils/version.js +0 -141
- package/templates/agentic/agent-routing.md +0 -45
- package/templates/agentic/agents/uxui.md +0 -63
- package/templates/agentic/checklist-routing.md +0 -98
- package/templates/agentic/orchestrator.md +0 -68
- package/templates/agentic/task-fragmentation.md +0 -89
- package/templates/agents/AGENTS.md +0 -68
- package/templates/analysis/analyze.md +0 -84
- package/templates/analysis/patterns.md +0 -60
- package/templates/antigravity/SKILL.md +0 -39
- package/templates/architect/discovery.md +0 -67
- package/templates/architect/phases.md +0 -59
- package/templates/checklists/architecture.md +0 -28
- package/templates/checklists/code-quality.md +0 -28
- package/templates/checklists/data.md +0 -33
- package/templates/checklists/documentation.md +0 -33
- package/templates/checklists/infrastructure.md +0 -33
- package/templates/checklists/performance.md +0 -33
- package/templates/checklists/security.md +0 -33
- package/templates/checklists/testing.md +0 -33
- package/templates/checklists/ux-ui.md +0 -37
- package/templates/commands/analyze.md +0 -56
- package/templates/commands/auth.md +0 -234
- package/templates/commands/bug.md +0 -163
- package/templates/commands/cleanup.md +0 -19
- package/templates/commands/dash.md +0 -99
- package/templates/commands/design.md +0 -15
- package/templates/commands/done.md +0 -291
- package/templates/commands/enrich.md +0 -174
- package/templates/commands/git.md +0 -295
- package/templates/commands/history.md +0 -389
- package/templates/commands/idea.md +0 -88
- package/templates/commands/impact.md +0 -864
- package/templates/commands/init.md +0 -54
- package/templates/commands/jira.md +0 -278
- package/templates/commands/linear.md +0 -288
- package/templates/commands/merge.md +0 -206
- package/templates/commands/next.md +0 -80
- package/templates/commands/p.md +0 -67
- package/templates/commands/p.toml +0 -37
- package/templates/commands/pause.md +0 -136
- package/templates/commands/plan.md +0 -696
- package/templates/commands/prd.md +0 -356
- package/templates/commands/resume.md +0 -171
- package/templates/commands/review.md +0 -276
- package/templates/commands/serve.md +0 -118
- package/templates/commands/setup.md +0 -91
- package/templates/commands/ship.md +0 -475
- package/templates/commands/skill.md +0 -259
- package/templates/commands/spec.md +0 -218
- package/templates/commands/status.md +0 -207
- package/templates/commands/sync.md +0 -104
- package/templates/commands/task.md +0 -312
- package/templates/commands/test.md +0 -93
- package/templates/commands/update.md +0 -63
- package/templates/commands/verify.md +0 -204
- package/templates/commands/workflow.md +0 -150
- package/templates/config/skill-mappings.json +0 -82
- package/templates/context/dashboard.md +0 -256
- package/templates/context/roadmap.md +0 -221
- package/templates/cursor/commands/bug.md +0 -8
- package/templates/cursor/commands/done.md +0 -4
- package/templates/cursor/commands/pause.md +0 -6
- package/templates/cursor/commands/resume.md +0 -4
- package/templates/cursor/commands/ship.md +0 -8
- package/templates/cursor/commands/sync.md +0 -4
- package/templates/cursor/commands/task.md +0 -8
- package/templates/cursor/p.md +0 -29
- package/templates/cursor/router.mdc +0 -28
- package/templates/design/api.md +0 -95
- package/templates/design/architecture.md +0 -77
- package/templates/design/component.md +0 -89
- package/templates/design/database.md +0 -78
- package/templates/design/flow.md +0 -94
- package/templates/global/ANTIGRAVITY.md +0 -254
- package/templates/global/CLAUDE.md +0 -497
- package/templates/global/CURSOR.mdc +0 -266
- package/templates/global/GEMINI.md +0 -293
- package/templates/global/STORAGE-SPEC.md +0 -391
- package/templates/global/WINDSURF.md +0 -266
- package/templates/global/modules/CLAUDE-commands.md +0 -70
- package/templates/global/modules/CLAUDE-core.md +0 -105
- package/templates/global/modules/CLAUDE-git.md +0 -50
- package/templates/global/modules/CLAUDE-intelligence.md +0 -92
- package/templates/global/modules/CLAUDE-storage.md +0 -50
- package/templates/global/modules/module-config.json +0 -36
- package/templates/mcp-config.json +0 -19
- package/templates/permissions/default.jsonc +0 -60
- package/templates/permissions/permissive.jsonc +0 -49
- package/templates/permissions/strict.jsonc +0 -58
- package/templates/planning-methodology.md +0 -195
- package/templates/skills/code-review.md +0 -47
- package/templates/skills/debug.md +0 -61
- package/templates/skills/refactor.md +0 -47
- package/templates/subagents/agent-base.md +0 -20
- package/templates/subagents/domain/backend.md +0 -109
- package/templates/subagents/domain/database.md +0 -121
- package/templates/subagents/domain/devops.md +0 -152
- package/templates/subagents/domain/frontend.md +0 -103
- package/templates/subagents/domain/testing.md +0 -169
- package/templates/subagents/pm-expert.md +0 -366
- package/templates/subagents/workflow/chief-architect.md +0 -657
- package/templates/subagents/workflow/prjct-planner.md +0 -159
- package/templates/subagents/workflow/prjct-shipper.md +0 -188
- package/templates/subagents/workflow/prjct-workflow.md +0 -98
- package/templates/tools/bash.txt +0 -22
- package/templates/tools/edit.txt +0 -18
- package/templates/tools/glob.txt +0 -19
- package/templates/tools/grep.txt +0 -21
- package/templates/tools/read.txt +0 -14
- package/templates/tools/task.txt +0 -20
- package/templates/tools/webfetch.txt +0 -16
- package/templates/tools/websearch.txt +0 -18
- package/templates/tools/write.txt +0 -17
- package/templates/windsurf/router.md +0 -28
- package/templates/windsurf/workflows/bug.md +0 -8
- package/templates/windsurf/workflows/done.md +0 -4
- package/templates/windsurf/workflows/pause.md +0 -4
- package/templates/windsurf/workflows/resume.md +0 -4
- package/templates/windsurf/workflows/ship.md +0 -8
- package/templates/windsurf/workflows/sync.md +0 -4
- package/templates/windsurf/workflows/task.md +0 -8
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SnapshotManager - Git-based Undo/Redo System
|
|
3
|
-
*
|
|
4
|
-
* Uses Git internally to track file changes and enable undo/redo.
|
|
5
|
-
* Inspired by OpenCode's snapshot system.
|
|
6
|
-
*
|
|
7
|
-
* Storage: ~/.prjct-cli/projects/{projectId}/snapshots/
|
|
8
|
-
*
|
|
9
|
-
* @version 1.0.0
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { exec } from 'node:child_process'
|
|
13
|
-
import fs from 'node:fs/promises'
|
|
14
|
-
import path from 'node:path'
|
|
15
|
-
import { promisify } from 'node:util'
|
|
16
|
-
import { emit } from '../bus'
|
|
17
|
-
import configManager from '../infrastructure/config-manager'
|
|
18
|
-
import pathManager from '../infrastructure/path-manager'
|
|
19
|
-
import { isNotFoundError } from '../types/fs'
|
|
20
|
-
|
|
21
|
-
const execAsync = promisify(exec)
|
|
22
|
-
|
|
23
|
-
interface SnapshotInfo {
|
|
24
|
-
hash: string | null
|
|
25
|
-
message: string
|
|
26
|
-
timestamp: string
|
|
27
|
-
files: string[]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface SnapshotListItem {
|
|
31
|
-
hash: string
|
|
32
|
-
short: string
|
|
33
|
-
message: string
|
|
34
|
-
date: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
interface RestoreResult {
|
|
38
|
-
hash: string
|
|
39
|
-
files: string[]
|
|
40
|
-
timestamp: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface RedoStackEntry {
|
|
44
|
-
hash: string
|
|
45
|
-
message: string
|
|
46
|
-
timestamp: string
|
|
47
|
-
files: string[]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class SnapshotManager {
|
|
51
|
-
projectPath: string
|
|
52
|
-
projectId: string | null = null
|
|
53
|
-
snapshotDir: string | null = null
|
|
54
|
-
initialized: boolean = false
|
|
55
|
-
|
|
56
|
-
constructor(projectPath: string) {
|
|
57
|
-
this.projectPath = projectPath
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Initialize snapshot system for project
|
|
62
|
-
*/
|
|
63
|
-
async initialize(): Promise<void> {
|
|
64
|
-
this.projectId = await configManager.getProjectId(this.projectPath)
|
|
65
|
-
if (!this.projectId) {
|
|
66
|
-
throw new Error('No prjct project found. Run /p:init first.')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Snapshots live in global storage
|
|
70
|
-
const globalPath = pathManager.getGlobalProjectPath(this.projectId)
|
|
71
|
-
this.snapshotDir = path.join(globalPath, 'snapshots')
|
|
72
|
-
|
|
73
|
-
// Ensure directory exists
|
|
74
|
-
await fs.mkdir(this.snapshotDir, { recursive: true })
|
|
75
|
-
|
|
76
|
-
// Initialize bare git repo if not exists
|
|
77
|
-
const gitDir = path.join(this.snapshotDir, '.git')
|
|
78
|
-
try {
|
|
79
|
-
await fs.access(gitDir)
|
|
80
|
-
} catch (error) {
|
|
81
|
-
if (isNotFoundError(error)) {
|
|
82
|
-
await this.initGitRepo()
|
|
83
|
-
} else {
|
|
84
|
-
throw error
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
this.initialized = true
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Initialize internal Git repository
|
|
93
|
-
*/
|
|
94
|
-
async initGitRepo(): Promise<void> {
|
|
95
|
-
// Create bare-ish repo structure
|
|
96
|
-
await execAsync(`git init "${this.snapshotDir}"`, { cwd: this.projectPath })
|
|
97
|
-
|
|
98
|
-
// Configure for snapshot use
|
|
99
|
-
await execAsync(`git config user.email "prjct@local"`, { cwd: this.snapshotDir! })
|
|
100
|
-
await execAsync(`git config user.name "prjct-snapshots"`, { cwd: this.snapshotDir! })
|
|
101
|
-
|
|
102
|
-
// Create initial empty commit
|
|
103
|
-
await execAsync(`git commit --allow-empty -m "init: snapshot system"`, {
|
|
104
|
-
cwd: this.snapshotDir!,
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Create a snapshot of current project state
|
|
110
|
-
*/
|
|
111
|
-
async create(message: string, files: string[] | null = null): Promise<SnapshotInfo> {
|
|
112
|
-
if (!this.initialized) await this.initialize()
|
|
113
|
-
|
|
114
|
-
const timestamp = new Date().toISOString()
|
|
115
|
-
|
|
116
|
-
// Copy changed files to snapshot directory
|
|
117
|
-
const changedFiles = files || (await this.getChangedFiles())
|
|
118
|
-
|
|
119
|
-
if (changedFiles.length === 0) {
|
|
120
|
-
return {
|
|
121
|
-
hash: null,
|
|
122
|
-
message: 'No changes to snapshot',
|
|
123
|
-
timestamp,
|
|
124
|
-
files: [],
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Copy files to snapshot dir maintaining structure
|
|
129
|
-
for (const file of changedFiles) {
|
|
130
|
-
const srcPath = path.join(this.projectPath, file)
|
|
131
|
-
const destPath = path.join(this.snapshotDir!, file)
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const content = await fs.readFile(srcPath, 'utf-8')
|
|
135
|
-
await fs.mkdir(path.dirname(destPath), { recursive: true })
|
|
136
|
-
await fs.writeFile(destPath, content)
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (isNotFoundError(error)) {
|
|
139
|
-
// File might be deleted, mark for removal
|
|
140
|
-
try {
|
|
141
|
-
await fs.unlink(destPath)
|
|
142
|
-
} catch (unlinkError) {
|
|
143
|
-
// Ignore if dest doesn't exist either
|
|
144
|
-
if (!isNotFoundError(unlinkError)) {
|
|
145
|
-
throw unlinkError
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
throw error
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Stage and commit in snapshot repo
|
|
155
|
-
await execAsync(`git add -A`, { cwd: this.snapshotDir! })
|
|
156
|
-
|
|
157
|
-
const commitMsg = `${message}\n\nFiles: ${changedFiles.length}\nTimestamp: ${timestamp}`
|
|
158
|
-
await execAsync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { cwd: this.snapshotDir! })
|
|
159
|
-
|
|
160
|
-
// Get commit hash
|
|
161
|
-
const { stdout } = await execAsync(`git rev-parse HEAD`, { cwd: this.snapshotDir! })
|
|
162
|
-
const hash = stdout.trim()
|
|
163
|
-
|
|
164
|
-
// Log to manifest
|
|
165
|
-
await this.logSnapshot({
|
|
166
|
-
hash,
|
|
167
|
-
message,
|
|
168
|
-
timestamp,
|
|
169
|
-
files: changedFiles,
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
// Emit event for plugins
|
|
173
|
-
await emit.snapshotCreated({
|
|
174
|
-
hash,
|
|
175
|
-
message,
|
|
176
|
-
timestamp,
|
|
177
|
-
filesCount: changedFiles.length,
|
|
178
|
-
projectId: this.projectId,
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
return { hash, message, timestamp, files: changedFiles }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get list of changed files in project
|
|
186
|
-
*/
|
|
187
|
-
async getChangedFiles(): Promise<string[]> {
|
|
188
|
-
try {
|
|
189
|
-
const { stdout } = await execAsync(`git status --porcelain`, { cwd: this.projectPath })
|
|
190
|
-
|
|
191
|
-
return stdout
|
|
192
|
-
.split('\n')
|
|
193
|
-
.filter(Boolean)
|
|
194
|
-
.map((line) => line.slice(3).trim())
|
|
195
|
-
.filter((file) => !file.startsWith('.prjct/'))
|
|
196
|
-
} catch (_error) {
|
|
197
|
-
// Git status fails (not a git repo, etc.) - return empty (expected)
|
|
198
|
-
return []
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* List all snapshots
|
|
204
|
-
*/
|
|
205
|
-
async list(limit: number = 10): Promise<SnapshotListItem[]> {
|
|
206
|
-
if (!this.initialized) await this.initialize()
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
const { stdout } = await execAsync(
|
|
210
|
-
`git log --pretty=format:'{"hash":"%H","short":"%h","message":"%s","date":"%ai"}' -n ${limit}`,
|
|
211
|
-
{ cwd: this.snapshotDir! }
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
return stdout
|
|
215
|
-
.split('\n')
|
|
216
|
-
.filter(Boolean)
|
|
217
|
-
.map((line) => {
|
|
218
|
-
try {
|
|
219
|
-
return JSON.parse(line) as SnapshotListItem
|
|
220
|
-
} catch (error) {
|
|
221
|
-
if (error instanceof SyntaxError) {
|
|
222
|
-
return null
|
|
223
|
-
}
|
|
224
|
-
throw error
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
.filter((item): item is SnapshotListItem => item !== null)
|
|
228
|
-
} catch (_error) {
|
|
229
|
-
// Git log fails - return empty (expected in fresh repos)
|
|
230
|
-
return []
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Restore project to a specific snapshot
|
|
236
|
-
*/
|
|
237
|
-
async restore(hash: string): Promise<RestoreResult> {
|
|
238
|
-
if (!this.initialized) await this.initialize()
|
|
239
|
-
|
|
240
|
-
// Get files changed in that commit
|
|
241
|
-
const { stdout: filesOutput } = await execAsync(
|
|
242
|
-
`git diff-tree --no-commit-id --name-only -r ${hash}`,
|
|
243
|
-
{
|
|
244
|
-
cwd: this.snapshotDir!,
|
|
245
|
-
}
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
const files = filesOutput.split('\n').filter(Boolean)
|
|
249
|
-
|
|
250
|
-
// Checkout files from that snapshot
|
|
251
|
-
await execAsync(`git checkout ${hash} -- .`, { cwd: this.snapshotDir! })
|
|
252
|
-
|
|
253
|
-
// Copy files back to project
|
|
254
|
-
for (const file of files) {
|
|
255
|
-
const srcPath = path.join(this.snapshotDir!, file)
|
|
256
|
-
const destPath = path.join(this.projectPath, file)
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
const content = await fs.readFile(srcPath, 'utf-8')
|
|
260
|
-
await fs.mkdir(path.dirname(destPath), { recursive: true })
|
|
261
|
-
await fs.writeFile(destPath, content)
|
|
262
|
-
} catch (error) {
|
|
263
|
-
if (isNotFoundError(error)) {
|
|
264
|
-
// File doesn't exist in snapshot, might need to delete from project
|
|
265
|
-
try {
|
|
266
|
-
await fs.unlink(destPath)
|
|
267
|
-
} catch (unlinkError) {
|
|
268
|
-
// Ignore if dest doesn't exist either
|
|
269
|
-
if (!isNotFoundError(unlinkError)) {
|
|
270
|
-
throw unlinkError
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
} else {
|
|
274
|
-
throw error
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Log restoration
|
|
280
|
-
await this.logRestore(hash, files)
|
|
281
|
-
|
|
282
|
-
const timestamp = new Date().toISOString()
|
|
283
|
-
|
|
284
|
-
// Emit event for plugins
|
|
285
|
-
await emit.snapshotRestored({
|
|
286
|
-
hash,
|
|
287
|
-
filesCount: files.length,
|
|
288
|
-
timestamp,
|
|
289
|
-
projectId: this.projectId,
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
return { hash, files, timestamp }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Get diff between current state and a snapshot
|
|
297
|
-
*/
|
|
298
|
-
async diff(hash: string): Promise<string> {
|
|
299
|
-
if (!this.initialized) await this.initialize()
|
|
300
|
-
|
|
301
|
-
try {
|
|
302
|
-
const { stdout } = await execAsync(`git diff ${hash} --stat`, { cwd: this.snapshotDir! })
|
|
303
|
-
return stdout
|
|
304
|
-
} catch (_error) {
|
|
305
|
-
// Git diff fails - return empty (expected)
|
|
306
|
-
return ''
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Get the most recent snapshot hash
|
|
312
|
-
*/
|
|
313
|
-
async getLatestHash(): Promise<string | null> {
|
|
314
|
-
if (!this.initialized) await this.initialize()
|
|
315
|
-
|
|
316
|
-
try {
|
|
317
|
-
const { stdout } = await execAsync(`git rev-parse HEAD`, { cwd: this.snapshotDir! })
|
|
318
|
-
return stdout.trim()
|
|
319
|
-
} catch (_error) {
|
|
320
|
-
// Git rev-parse fails - no commits yet (expected)
|
|
321
|
-
return null
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Get the hash before the current one (for undo)
|
|
327
|
-
*/
|
|
328
|
-
async getPreviousHash(): Promise<string | null> {
|
|
329
|
-
if (!this.initialized) await this.initialize()
|
|
330
|
-
|
|
331
|
-
try {
|
|
332
|
-
const { stdout } = await execAsync(`git rev-parse HEAD~1`, { cwd: this.snapshotDir! })
|
|
333
|
-
return stdout.trim()
|
|
334
|
-
} catch (_error) {
|
|
335
|
-
// Git rev-parse fails - no previous commit (expected)
|
|
336
|
-
return null
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Log snapshot to manifest
|
|
342
|
-
*/
|
|
343
|
-
async logSnapshot(snapshot: SnapshotInfo): Promise<void> {
|
|
344
|
-
const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
|
|
345
|
-
const entry = `${JSON.stringify({
|
|
346
|
-
type: 'snapshot',
|
|
347
|
-
...snapshot,
|
|
348
|
-
})}\n`
|
|
349
|
-
|
|
350
|
-
await fs.appendFile(manifestPath, entry)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Log restoration to manifest
|
|
355
|
-
*/
|
|
356
|
-
async logRestore(hash: string, files: string[]): Promise<void> {
|
|
357
|
-
const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
|
|
358
|
-
const entry = `${JSON.stringify({
|
|
359
|
-
type: 'restore',
|
|
360
|
-
hash,
|
|
361
|
-
files,
|
|
362
|
-
timestamp: new Date().toISOString(),
|
|
363
|
-
})}\n`
|
|
364
|
-
|
|
365
|
-
await fs.appendFile(manifestPath, entry)
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Get redo stack (snapshots after current position)
|
|
370
|
-
* This tracks undone snapshots that can be redone
|
|
371
|
-
*/
|
|
372
|
-
async getRedoStack(): Promise<RedoStackEntry[]> {
|
|
373
|
-
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
374
|
-
try {
|
|
375
|
-
const content = await fs.readFile(stackPath, 'utf-8')
|
|
376
|
-
return JSON.parse(content)
|
|
377
|
-
} catch (error) {
|
|
378
|
-
if (isNotFoundError(error) || error instanceof SyntaxError) {
|
|
379
|
-
return []
|
|
380
|
-
}
|
|
381
|
-
throw error
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Push to redo stack (when undoing)
|
|
387
|
-
*/
|
|
388
|
-
async pushToRedoStack(snapshot: RedoStackEntry): Promise<void> {
|
|
389
|
-
const stack = await this.getRedoStack()
|
|
390
|
-
stack.push(snapshot)
|
|
391
|
-
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
392
|
-
await fs.writeFile(stackPath, JSON.stringify(stack, null, 2))
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Pop from redo stack (when redoing)
|
|
397
|
-
*/
|
|
398
|
-
async popFromRedoStack(): Promise<RedoStackEntry | undefined> {
|
|
399
|
-
const stack = await this.getRedoStack()
|
|
400
|
-
const snapshot = stack.pop()
|
|
401
|
-
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
402
|
-
await fs.writeFile(stackPath, JSON.stringify(stack, null, 2))
|
|
403
|
-
return snapshot
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Clear redo stack (when creating new snapshot after undo)
|
|
408
|
-
*/
|
|
409
|
-
async clearRedoStack(): Promise<void> {
|
|
410
|
-
const stackPath = path.join(this.snapshotDir!, 'redo-stack.json')
|
|
411
|
-
await fs.writeFile(stackPath, '[]')
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
export default SnapshotManager
|