prjct-cli 1.22.0 → 1.24.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 +230 -0
- package/bin/prjct +30 -13
- package/dist/bin/prjct-core.mjs +1748 -0
- package/dist/bin/prjct.mjs +17 -36672
- package/dist/cli/linear.mjs +14 -0
- package/dist/daemon/entry.mjs +1429 -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
package/core/session/utils.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { generateUUID } from '../schemas'
|
|
6
|
-
import type { Session } from '../types'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Generate unique session ID (re-export from schemas)
|
|
10
|
-
*/
|
|
11
|
-
export const generateId = generateUUID
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Calculate total duration in seconds
|
|
15
|
-
*/
|
|
16
|
-
export function calculateDuration(session: Session): number {
|
|
17
|
-
let totalMs = 0
|
|
18
|
-
let lastStart: Date | null = null
|
|
19
|
-
|
|
20
|
-
for (const event of session.timeline) {
|
|
21
|
-
if (event.type === 'start' || event.type === 'resume') {
|
|
22
|
-
lastStart = new Date(event.at)
|
|
23
|
-
} else if (event.type === 'pause' || event.type === 'complete') {
|
|
24
|
-
if (lastStart) {
|
|
25
|
-
totalMs += new Date(event.at).getTime() - lastStart.getTime()
|
|
26
|
-
lastStart = null
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// If still active, count from last start to now
|
|
32
|
-
if (lastStart && session.status === 'active') {
|
|
33
|
-
totalMs += Date.now() - lastStart.getTime()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return Math.round(totalMs / 1000)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Format duration as human readable
|
|
41
|
-
*/
|
|
42
|
-
export function formatDuration(seconds: number): string {
|
|
43
|
-
if (seconds < 60) return `${seconds}s`
|
|
44
|
-
if (seconds < 3600) return `${Math.round(seconds / 60)}m`
|
|
45
|
-
|
|
46
|
-
const hours = Math.floor(seconds / 3600)
|
|
47
|
-
const minutes = Math.round((seconds % 3600) / 60)
|
|
48
|
-
|
|
49
|
-
if (minutes === 0) return `${hours}h`
|
|
50
|
-
return `${hours}h ${minutes}m`
|
|
51
|
-
}
|
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analysis Storage (PRJ-263)
|
|
3
|
-
*
|
|
4
|
-
* Manages sealable analysis with dual storage:
|
|
5
|
-
* - storage/analysis.json (current draft)
|
|
6
|
-
* - storage/analysis-sealed.json (locked sealed version)
|
|
7
|
-
*
|
|
8
|
-
* Lifecycle: DRAFT → VERIFIED → SEALED
|
|
9
|
-
* Re-sync creates a new draft WITHOUT destroying the sealed version.
|
|
10
|
-
* Only sealed analysis feeds task context.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createHash } from 'node:crypto'
|
|
14
|
-
import type { AnalysisSchema } from '../schemas/analysis'
|
|
15
|
-
import {
|
|
16
|
-
AnalysisItemSchema,
|
|
17
|
-
type SemanticVerificationReport,
|
|
18
|
-
semanticVerify,
|
|
19
|
-
} from '../schemas/analysis'
|
|
20
|
-
import { getTimestamp } from '../utils/date-helper'
|
|
21
|
-
import { StorageManager } from './storage-manager'
|
|
22
|
-
|
|
23
|
-
// =============================================================================
|
|
24
|
-
// Types
|
|
25
|
-
// =============================================================================
|
|
26
|
-
|
|
27
|
-
interface AnalysisStoreData {
|
|
28
|
-
draft: AnalysisSchema | null
|
|
29
|
-
sealed: AnalysisSchema | null
|
|
30
|
-
lastUpdated: string
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface SealResult {
|
|
34
|
-
success: boolean
|
|
35
|
-
signature?: string
|
|
36
|
-
error?: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface StalenessCheck {
|
|
40
|
-
isStale: boolean
|
|
41
|
-
sealedCommit: string | null
|
|
42
|
-
currentCommit: string | null
|
|
43
|
-
message: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// =============================================================================
|
|
47
|
-
// Analysis Storage
|
|
48
|
-
// =============================================================================
|
|
49
|
-
|
|
50
|
-
class AnalysisStorage extends StorageManager<AnalysisStoreData> {
|
|
51
|
-
constructor() {
|
|
52
|
-
super('analysis.json')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
protected getDefault(): AnalysisStoreData {
|
|
56
|
-
return {
|
|
57
|
-
draft: null,
|
|
58
|
-
sealed: null,
|
|
59
|
-
lastUpdated: '',
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
protected getMdFilename(): string {
|
|
64
|
-
return 'analysis.md'
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
protected getLayer(): string {
|
|
68
|
-
return 'analysis'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
protected getEventType(action: 'update' | 'create' | 'delete'): string {
|
|
72
|
-
return `analysis.${action}d`
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
protected toMarkdown(data: AnalysisStoreData): string {
|
|
76
|
-
const lines = ['# Analysis Status', '']
|
|
77
|
-
|
|
78
|
-
// Show sealed analysis if available
|
|
79
|
-
if (data.sealed) {
|
|
80
|
-
lines.push('## Sealed Analysis')
|
|
81
|
-
lines.push(`- **Status**: sealed`)
|
|
82
|
-
lines.push(`- **Commit**: \`${data.sealed.commitHash || 'unknown'}\``)
|
|
83
|
-
lines.push(`- **Sealed at**: ${data.sealed.sealedAt || 'unknown'}`)
|
|
84
|
-
lines.push(`- **Languages**: ${data.sealed.languages.join(', ') || 'none'}`)
|
|
85
|
-
lines.push(`- **Frameworks**: ${data.sealed.frameworks.join(', ') || 'none'}`)
|
|
86
|
-
lines.push(`- **Files**: ${data.sealed.fileCount}`)
|
|
87
|
-
if (data.sealed.patterns.length > 0) {
|
|
88
|
-
lines.push(`- **Patterns**: ${data.sealed.patterns.map((p) => p.name).join(', ')}`)
|
|
89
|
-
}
|
|
90
|
-
lines.push('')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Show draft if different from sealed
|
|
94
|
-
if (data.draft && data.draft.status === 'draft') {
|
|
95
|
-
lines.push('## Draft Analysis')
|
|
96
|
-
lines.push(`- **Status**: draft (not yet sealed)`)
|
|
97
|
-
lines.push(`- **Commit**: \`${data.draft.commitHash || 'unknown'}\``)
|
|
98
|
-
lines.push(`- **Analyzed at**: ${data.draft.analyzedAt}`)
|
|
99
|
-
lines.push(`- **Languages**: ${data.draft.languages.join(', ') || 'none'}`)
|
|
100
|
-
lines.push(`- **Frameworks**: ${data.draft.frameworks.join(', ') || 'none'}`)
|
|
101
|
-
lines.push(`- **Files**: ${data.draft.fileCount}`)
|
|
102
|
-
lines.push('')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!data.sealed && !data.draft) {
|
|
106
|
-
lines.push('_No analysis available. Run `p. sync` to generate._')
|
|
107
|
-
lines.push('')
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return lines.join('\n')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ===========================================================================
|
|
114
|
-
// Domain Methods
|
|
115
|
-
// ===========================================================================
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Save a new draft analysis (called by sync-service).
|
|
119
|
-
* Preserves existing sealed analysis.
|
|
120
|
-
*/
|
|
121
|
-
async saveDraft(projectId: string, analysis: AnalysisSchema): Promise<void> {
|
|
122
|
-
const draft: AnalysisSchema = {
|
|
123
|
-
...analysis,
|
|
124
|
-
status: 'draft',
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Validate with Zod
|
|
128
|
-
AnalysisItemSchema.parse(draft)
|
|
129
|
-
|
|
130
|
-
await this.update(projectId, (data) => ({
|
|
131
|
-
...data,
|
|
132
|
-
draft,
|
|
133
|
-
lastUpdated: getTimestamp(),
|
|
134
|
-
}))
|
|
135
|
-
|
|
136
|
-
await this.publishEntityEvent(projectId, 'analysis', 'drafted', {
|
|
137
|
-
commitHash: draft.commitHash,
|
|
138
|
-
fileCount: draft.fileCount,
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Seal the current draft analysis.
|
|
144
|
-
* Computes SHA-256 signature and locks the analysis.
|
|
145
|
-
*/
|
|
146
|
-
async seal(projectId: string): Promise<SealResult> {
|
|
147
|
-
const data = await this.read(projectId)
|
|
148
|
-
|
|
149
|
-
if (!data.draft) {
|
|
150
|
-
return { success: false, error: 'No draft analysis to seal. Run `p. sync` first.' }
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (data.draft.status === 'sealed') {
|
|
154
|
-
return { success: false, error: 'Draft is already sealed.' }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Compute signature
|
|
158
|
-
const signature = this.computeSignature(data.draft)
|
|
159
|
-
const now = getTimestamp()
|
|
160
|
-
|
|
161
|
-
const sealed: AnalysisSchema = {
|
|
162
|
-
...data.draft,
|
|
163
|
-
status: 'sealed',
|
|
164
|
-
signature,
|
|
165
|
-
sealedAt: now,
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Validate
|
|
169
|
-
AnalysisItemSchema.parse(sealed)
|
|
170
|
-
|
|
171
|
-
await this.write(projectId, {
|
|
172
|
-
draft: null, // Clear draft — it's now sealed
|
|
173
|
-
sealed,
|
|
174
|
-
lastUpdated: now,
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
await this.publishEntityEvent(projectId, 'analysis', 'sealed', {
|
|
178
|
-
commitHash: sealed.commitHash,
|
|
179
|
-
signature,
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
return { success: true, signature }
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get the sealed analysis (for task context injection).
|
|
187
|
-
* Returns null if no sealed analysis exists.
|
|
188
|
-
*/
|
|
189
|
-
async getSealed(projectId: string): Promise<AnalysisSchema | null> {
|
|
190
|
-
const data = await this.read(projectId)
|
|
191
|
-
return data.sealed
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Get the current draft analysis.
|
|
196
|
-
*/
|
|
197
|
-
async getDraft(projectId: string): Promise<AnalysisSchema | null> {
|
|
198
|
-
const data = await this.read(projectId)
|
|
199
|
-
return data.draft
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Get the active analysis (sealed if available, otherwise draft).
|
|
204
|
-
* This is what tasks should consume.
|
|
205
|
-
*/
|
|
206
|
-
async getActive(projectId: string): Promise<AnalysisSchema | null> {
|
|
207
|
-
const data = await this.read(projectId)
|
|
208
|
-
return data.sealed ?? data.draft
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Get the current analysis status.
|
|
213
|
-
*/
|
|
214
|
-
async getStatus(projectId: string): Promise<{
|
|
215
|
-
hasSealed: boolean
|
|
216
|
-
hasDraft: boolean
|
|
217
|
-
sealedCommit: string | null
|
|
218
|
-
draftCommit: string | null
|
|
219
|
-
sealedAt: string | null
|
|
220
|
-
}> {
|
|
221
|
-
const data = await this.read(projectId)
|
|
222
|
-
return {
|
|
223
|
-
hasSealed: data.sealed !== null,
|
|
224
|
-
hasDraft: data.draft !== null,
|
|
225
|
-
sealedCommit: data.sealed?.commitHash ?? null,
|
|
226
|
-
draftCommit: data.draft?.commitHash ?? null,
|
|
227
|
-
sealedAt: data.sealed?.sealedAt ?? null,
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Check if sealed analysis is stale (commit hash differs from current HEAD).
|
|
233
|
-
*/
|
|
234
|
-
checkStaleness(sealedCommit: string | null, currentCommit: string | null): StalenessCheck {
|
|
235
|
-
if (!sealedCommit) {
|
|
236
|
-
return {
|
|
237
|
-
isStale: false,
|
|
238
|
-
sealedCommit: null,
|
|
239
|
-
currentCommit,
|
|
240
|
-
message: 'No sealed analysis. Run `p. sync` then `p. seal`.',
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!currentCommit) {
|
|
245
|
-
return {
|
|
246
|
-
isStale: true,
|
|
247
|
-
sealedCommit,
|
|
248
|
-
currentCommit: null,
|
|
249
|
-
message: 'Cannot determine current commit. Analysis may be stale.',
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (sealedCommit !== currentCommit) {
|
|
254
|
-
return {
|
|
255
|
-
isStale: true,
|
|
256
|
-
sealedCommit,
|
|
257
|
-
currentCommit,
|
|
258
|
-
message: `Analysis is stale: sealed at ${sealedCommit}, HEAD is ${currentCommit}. Run \`p. sync\` + \`p. seal\` to update.`,
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
isStale: false,
|
|
264
|
-
sealedCommit,
|
|
265
|
-
currentCommit,
|
|
266
|
-
message: 'Analysis is current.',
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Verify the integrity of a sealed analysis by recomputing its signature.
|
|
272
|
-
*/
|
|
273
|
-
async verify(projectId: string): Promise<{ valid: boolean; message: string }> {
|
|
274
|
-
const data = await this.read(projectId)
|
|
275
|
-
|
|
276
|
-
if (!data.sealed) {
|
|
277
|
-
return { valid: false, message: 'No sealed analysis to verify.' }
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!data.sealed.signature) {
|
|
281
|
-
return { valid: false, message: 'Sealed analysis has no signature.' }
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const expected = this.computeSignature({
|
|
285
|
-
...data.sealed,
|
|
286
|
-
// Strip signature and sealedAt before recomputing — they weren't part of the original hash
|
|
287
|
-
signature: undefined,
|
|
288
|
-
sealedAt: undefined,
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
if (expected === data.sealed.signature) {
|
|
292
|
-
return { valid: true, message: 'Signature verified. Analysis integrity confirmed.' }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return {
|
|
296
|
-
valid: false,
|
|
297
|
-
message: `Signature mismatch. Expected ${expected}, got ${data.sealed.signature}. Analysis may have been modified.`,
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Perform semantic verification on analysis results (PRJ-270).
|
|
303
|
-
* Validates that analysis data matches actual project state:
|
|
304
|
-
* - Frameworks exist in package.json
|
|
305
|
-
* - Languages match file extensions
|
|
306
|
-
* - Pattern locations reference real files
|
|
307
|
-
* - File count is accurate
|
|
308
|
-
* - Anti-pattern files exist
|
|
309
|
-
*/
|
|
310
|
-
async semanticVerify(
|
|
311
|
-
projectId: string,
|
|
312
|
-
projectPath: string
|
|
313
|
-
): Promise<SemanticVerificationReport> {
|
|
314
|
-
const data = await this.read(projectId)
|
|
315
|
-
|
|
316
|
-
// Get the active analysis (sealed if available, otherwise draft)
|
|
317
|
-
const analysis = data.sealed ?? data.draft
|
|
318
|
-
|
|
319
|
-
if (!analysis) {
|
|
320
|
-
// No analysis to verify - return empty report
|
|
321
|
-
return {
|
|
322
|
-
passed: false,
|
|
323
|
-
checks: [
|
|
324
|
-
{
|
|
325
|
-
name: 'Analysis availability',
|
|
326
|
-
passed: false,
|
|
327
|
-
error: 'No analysis available. Run `p. sync` to generate.',
|
|
328
|
-
durationMs: 0,
|
|
329
|
-
},
|
|
330
|
-
],
|
|
331
|
-
totalMs: 0,
|
|
332
|
-
failedCount: 1,
|
|
333
|
-
passedCount: 0,
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Run semantic verification
|
|
338
|
-
return await semanticVerify(analysis, projectPath)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// ===========================================================================
|
|
342
|
-
// Private Helpers
|
|
343
|
-
// ===========================================================================
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Compute SHA-256 signature for analysis data.
|
|
347
|
-
* Deterministic: same input always produces same hash.
|
|
348
|
-
*/
|
|
349
|
-
private computeSignature(analysis: AnalysisSchema): string {
|
|
350
|
-
// Build a canonical representation (exclude volatile fields)
|
|
351
|
-
const canonical = {
|
|
352
|
-
projectId: analysis.projectId,
|
|
353
|
-
languages: analysis.languages,
|
|
354
|
-
frameworks: analysis.frameworks,
|
|
355
|
-
packageManager: analysis.packageManager,
|
|
356
|
-
sourceDir: analysis.sourceDir,
|
|
357
|
-
testDir: analysis.testDir,
|
|
358
|
-
configFiles: analysis.configFiles,
|
|
359
|
-
fileCount: analysis.fileCount,
|
|
360
|
-
patterns: analysis.patterns,
|
|
361
|
-
antiPatterns: analysis.antiPatterns,
|
|
362
|
-
analyzedAt: analysis.analyzedAt,
|
|
363
|
-
commitHash: analysis.commitHash,
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return createHash('sha256').update(JSON.stringify(canonical)).digest('hex')
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
export const analysisStorage = new AnalysisStorage()
|
|
371
|
-
export default analysisStorage
|
|
372
|
-
export type { AnalysisStoreData, SealResult, StalenessCheck }
|
|
373
|
-
export type { SemanticVerificationReport } from '../schemas/analysis'
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Archive Storage (PRJ-267)
|
|
3
|
-
*
|
|
4
|
-
* Manages archival of stale data from active storage.
|
|
5
|
-
* Works directly with the `archives` SQLite table (not kv_store).
|
|
6
|
-
*
|
|
7
|
-
* Archived data is accessible via explicit queries but never
|
|
8
|
-
* loaded into LLM context files.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { generateUUID } from '../schemas'
|
|
12
|
-
import { getTimestamp } from '../utils/date-helper'
|
|
13
|
-
import { prjctDb } from './database'
|
|
14
|
-
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// Archival Policy Constants
|
|
17
|
-
// =============================================================================
|
|
18
|
-
|
|
19
|
-
export const ARCHIVE_POLICIES = {
|
|
20
|
-
SHIPPED_RETENTION_DAYS: 90,
|
|
21
|
-
IDEA_DORMANT_DAYS: 180,
|
|
22
|
-
QUEUE_COMPLETED_DAYS: 7,
|
|
23
|
-
PAUSED_TASK_DAYS: 30,
|
|
24
|
-
MEMORY_MAX_ENTRIES: 500,
|
|
25
|
-
} as const
|
|
26
|
-
|
|
27
|
-
// =============================================================================
|
|
28
|
-
// Types
|
|
29
|
-
// =============================================================================
|
|
30
|
-
|
|
31
|
-
export type ArchiveEntityType = 'shipped' | 'idea' | 'queue_task' | 'paused_task' | 'memory_entry'
|
|
32
|
-
|
|
33
|
-
export interface ArchiveRecord {
|
|
34
|
-
id: string
|
|
35
|
-
entity_type: ArchiveEntityType
|
|
36
|
-
entity_id: string
|
|
37
|
-
entity_data: string
|
|
38
|
-
summary: string | null
|
|
39
|
-
archived_at: string
|
|
40
|
-
reason: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface ArchiveItem {
|
|
44
|
-
entityType: ArchiveEntityType
|
|
45
|
-
entityId: string
|
|
46
|
-
entityData: unknown
|
|
47
|
-
summary?: string
|
|
48
|
-
reason: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface ArchiveStats {
|
|
52
|
-
shipped: number
|
|
53
|
-
idea: number
|
|
54
|
-
queue_task: number
|
|
55
|
-
paused_task: number
|
|
56
|
-
memory_entry: number
|
|
57
|
-
total: number
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// =============================================================================
|
|
61
|
-
// Archive Storage
|
|
62
|
-
// =============================================================================
|
|
63
|
-
|
|
64
|
-
class ArchiveStorage {
|
|
65
|
-
/**
|
|
66
|
-
* Archive a single item
|
|
67
|
-
*/
|
|
68
|
-
archive(projectId: string, item: ArchiveItem): string {
|
|
69
|
-
const id = generateUUID()
|
|
70
|
-
const now = getTimestamp()
|
|
71
|
-
|
|
72
|
-
prjctDb.run(
|
|
73
|
-
projectId,
|
|
74
|
-
'INSERT INTO archives (id, entity_type, entity_id, entity_data, summary, archived_at, reason) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
|
75
|
-
id,
|
|
76
|
-
item.entityType,
|
|
77
|
-
item.entityId,
|
|
78
|
-
JSON.stringify(item.entityData),
|
|
79
|
-
item.summary ?? null,
|
|
80
|
-
now,
|
|
81
|
-
item.reason
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
return id
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Archive multiple items in a transaction
|
|
89
|
-
*/
|
|
90
|
-
archiveMany(projectId: string, items: ArchiveItem[]): number {
|
|
91
|
-
if (items.length === 0) return 0
|
|
92
|
-
|
|
93
|
-
const now = getTimestamp()
|
|
94
|
-
|
|
95
|
-
prjctDb.transaction(projectId, (db) => {
|
|
96
|
-
const stmt = db.prepare(
|
|
97
|
-
'INSERT INTO archives (id, entity_type, entity_id, entity_data, summary, archived_at, reason) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
for (const item of items) {
|
|
101
|
-
stmt.run(
|
|
102
|
-
generateUUID(),
|
|
103
|
-
item.entityType,
|
|
104
|
-
item.entityId,
|
|
105
|
-
JSON.stringify(item.entityData),
|
|
106
|
-
item.summary ?? null,
|
|
107
|
-
now,
|
|
108
|
-
item.reason
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
return items.length
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Query archived items by type
|
|
118
|
-
*/
|
|
119
|
-
getArchived(projectId: string, entityType?: ArchiveEntityType, limit = 50): ArchiveRecord[] {
|
|
120
|
-
if (entityType) {
|
|
121
|
-
return prjctDb.query<ArchiveRecord>(
|
|
122
|
-
projectId,
|
|
123
|
-
'SELECT * FROM archives WHERE entity_type = ? ORDER BY archived_at DESC LIMIT ?',
|
|
124
|
-
entityType,
|
|
125
|
-
limit
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
return prjctDb.query<ArchiveRecord>(
|
|
129
|
-
projectId,
|
|
130
|
-
'SELECT * FROM archives ORDER BY archived_at DESC LIMIT ?',
|
|
131
|
-
limit
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get count of archived items by type
|
|
137
|
-
*/
|
|
138
|
-
getStats(projectId: string): ArchiveStats {
|
|
139
|
-
const rows = prjctDb.query<{ entity_type: string; count: number }>(
|
|
140
|
-
projectId,
|
|
141
|
-
'SELECT entity_type, COUNT(*) as count FROM archives GROUP BY entity_type'
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
const stats: ArchiveStats = {
|
|
145
|
-
shipped: 0,
|
|
146
|
-
idea: 0,
|
|
147
|
-
queue_task: 0,
|
|
148
|
-
paused_task: 0,
|
|
149
|
-
memory_entry: 0,
|
|
150
|
-
total: 0,
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
for (const row of rows) {
|
|
154
|
-
const type = row.entity_type as ArchiveEntityType
|
|
155
|
-
if (type in stats) {
|
|
156
|
-
stats[type] = row.count
|
|
157
|
-
}
|
|
158
|
-
stats.total += row.count
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return stats
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Restore an item from archive (removes from archive table)
|
|
166
|
-
* Returns the entity data for the caller to re-insert into active storage.
|
|
167
|
-
*/
|
|
168
|
-
restore(projectId: string, archiveId: string): unknown | null {
|
|
169
|
-
const record = prjctDb.get<ArchiveRecord>(
|
|
170
|
-
projectId,
|
|
171
|
-
'SELECT * FROM archives WHERE id = ?',
|
|
172
|
-
archiveId
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
if (!record) return null
|
|
176
|
-
|
|
177
|
-
prjctDb.run(projectId, 'DELETE FROM archives WHERE id = ?', archiveId)
|
|
178
|
-
|
|
179
|
-
return JSON.parse(record.entity_data)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Permanently delete archives older than N days
|
|
184
|
-
*/
|
|
185
|
-
pruneOldArchives(projectId: string, olderThanDays: number): number {
|
|
186
|
-
const threshold = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000).toISOString()
|
|
187
|
-
|
|
188
|
-
const before = this.getTotalCount(projectId)
|
|
189
|
-
prjctDb.run(projectId, 'DELETE FROM archives WHERE archived_at < ?', threshold)
|
|
190
|
-
const after = this.getTotalCount(projectId)
|
|
191
|
-
|
|
192
|
-
return before - after
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get total count of archived items
|
|
197
|
-
*/
|
|
198
|
-
private getTotalCount(projectId: string): number {
|
|
199
|
-
const row = prjctDb.get<{ count: number }>(projectId, 'SELECT COUNT(*) as count FROM archives')
|
|
200
|
-
return row?.count ?? 0
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export const archiveStorage = new ArchiveStorage()
|
|
205
|
-
export default archiveStorage
|