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,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Compaction
|
|
3
|
-
*
|
|
4
|
-
* Compresses conversation context while preserving semantics.
|
|
5
|
-
* Useful for long sessions to prevent context overflow.
|
|
6
|
-
*
|
|
7
|
-
* Inspired by opencode's context management system.
|
|
8
|
-
*
|
|
9
|
-
* @version 1.0.0
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import fs from 'node:fs/promises'
|
|
13
|
-
import path from 'node:path'
|
|
14
|
-
import pathManager from '../infrastructure/path-manager'
|
|
15
|
-
import type { CompactedContext, CompactionConfig, ConversationTurn } from '../types'
|
|
16
|
-
import { getTimestamp } from '../utils/date-helper'
|
|
17
|
-
|
|
18
|
-
export type { CompactedContext, CompactionConfig, ConversationTurn } from '../types'
|
|
19
|
-
|
|
20
|
-
const DEFAULT_CONFIG: Required<CompactionConfig> = {
|
|
21
|
-
maxTurns: 50,
|
|
22
|
-
maxTokens: 160000,
|
|
23
|
-
preserveRecent: 10,
|
|
24
|
-
summaryMaxLength: 2000,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Estimate token count (rough approximation)
|
|
29
|
-
*/
|
|
30
|
-
function estimateTokens(text: string): number {
|
|
31
|
-
// Rough estimate: ~4 chars per token
|
|
32
|
-
return Math.ceil(text.length / 4)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract key information from conversation
|
|
37
|
-
*/
|
|
38
|
-
function extractKeyInfo(turns: ConversationTurn[]): {
|
|
39
|
-
decisions: string[]
|
|
40
|
-
filesModified: string[]
|
|
41
|
-
tasksCompleted: string[]
|
|
42
|
-
} {
|
|
43
|
-
const decisions: string[] = []
|
|
44
|
-
const filesModified = new Set<string>()
|
|
45
|
-
const tasksCompleted: string[] = []
|
|
46
|
-
|
|
47
|
-
for (const turn of turns) {
|
|
48
|
-
const content = turn.content
|
|
49
|
-
|
|
50
|
-
// Extract decisions (patterns like "decided to", "will use", "choosing")
|
|
51
|
-
const decisionPatterns = [
|
|
52
|
-
/decided to ([^.]+)/gi,
|
|
53
|
-
/will use ([^.]+)/gi,
|
|
54
|
-
/choosing ([^.]+)/gi,
|
|
55
|
-
/going with ([^.]+)/gi,
|
|
56
|
-
]
|
|
57
|
-
|
|
58
|
-
for (const pattern of decisionPatterns) {
|
|
59
|
-
const matches = content.matchAll(pattern)
|
|
60
|
-
for (const match of matches) {
|
|
61
|
-
decisions.push(match[1].trim())
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Extract file modifications
|
|
66
|
-
const filePatterns = [
|
|
67
|
-
/(?:created|modified|updated|edited|wrote to)\s+[`"]?([a-zA-Z0-9_\-./]+\.[a-zA-Z]+)[`"]?/gi,
|
|
68
|
-
/File (?:created|updated).*?:\s*([a-zA-Z0-9_\-./]+\.[a-zA-Z]+)/gi,
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
for (const pattern of filePatterns) {
|
|
72
|
-
const matches = content.matchAll(pattern)
|
|
73
|
-
for (const match of matches) {
|
|
74
|
-
filesModified.add(match[1])
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Extract completed tasks
|
|
79
|
-
const taskPatterns = [/✅\s*(.+)/g, /completed[:\s]+(.+)/gi, /finished[:\s]+(.+)/gi]
|
|
80
|
-
|
|
81
|
-
for (const pattern of taskPatterns) {
|
|
82
|
-
const matches = content.matchAll(pattern)
|
|
83
|
-
for (const match of matches) {
|
|
84
|
-
const task = match[1].trim()
|
|
85
|
-
if (task.length < 100) {
|
|
86
|
-
// Avoid capturing large blocks
|
|
87
|
-
tasksCompleted.push(task)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
decisions: [...new Set(decisions)].slice(0, 10),
|
|
95
|
-
filesModified: [...filesModified].slice(0, 20),
|
|
96
|
-
tasksCompleted: [...new Set(tasksCompleted)].slice(0, 10),
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Generate summary from conversation turns
|
|
102
|
-
*/
|
|
103
|
-
function generateSummary(turns: ConversationTurn[], maxLength: number): string {
|
|
104
|
-
// Get key user requests
|
|
105
|
-
const userRequests = turns
|
|
106
|
-
.filter((t) => t.role === 'user')
|
|
107
|
-
.map((t) => t.content.slice(0, 200))
|
|
108
|
-
.slice(0, 5)
|
|
109
|
-
|
|
110
|
-
// Get key assistant actions
|
|
111
|
-
const assistantActions = turns
|
|
112
|
-
.filter((t) => t.role === 'assistant')
|
|
113
|
-
.map((t) => {
|
|
114
|
-
// Extract first meaningful sentence
|
|
115
|
-
const firstLine = t.content.split('\n')[0]
|
|
116
|
-
return firstLine.slice(0, 150)
|
|
117
|
-
})
|
|
118
|
-
.filter((a) => a.length > 10)
|
|
119
|
-
.slice(0, 5)
|
|
120
|
-
|
|
121
|
-
const summary = [
|
|
122
|
-
'## Session Summary',
|
|
123
|
-
'',
|
|
124
|
-
'### User Requests:',
|
|
125
|
-
...userRequests.map((r, i) => `${i + 1}. ${r.slice(0, 100)}...`),
|
|
126
|
-
'',
|
|
127
|
-
'### Key Actions:',
|
|
128
|
-
...assistantActions.map((a, i) => `${i + 1}. ${a}`),
|
|
129
|
-
].join('\n')
|
|
130
|
-
|
|
131
|
-
return summary.slice(0, maxLength)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Compact conversation context
|
|
136
|
-
*/
|
|
137
|
-
export function compactContext(
|
|
138
|
-
turns: ConversationTurn[],
|
|
139
|
-
config: CompactionConfig = {}
|
|
140
|
-
): CompactedContext {
|
|
141
|
-
const cfg = { ...DEFAULT_CONFIG, ...config }
|
|
142
|
-
|
|
143
|
-
const { decisions, filesModified, tasksCompleted } = extractKeyInfo(turns)
|
|
144
|
-
const summary = generateSummary(turns, cfg.summaryMaxLength)
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
summary,
|
|
148
|
-
keyPoints: decisions.slice(0, 5),
|
|
149
|
-
decisions,
|
|
150
|
-
filesModified,
|
|
151
|
-
tasksCompleted,
|
|
152
|
-
originalTurns: turns.length,
|
|
153
|
-
compactedAt: getTimestamp(),
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Check if compaction is needed
|
|
159
|
-
*/
|
|
160
|
-
export function needsCompaction(turns: ConversationTurn[], config: CompactionConfig = {}): boolean {
|
|
161
|
-
const cfg = { ...DEFAULT_CONFIG, ...config }
|
|
162
|
-
|
|
163
|
-
// Check turn count
|
|
164
|
-
if (turns.length > cfg.maxTurns) {
|
|
165
|
-
return true
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Check token count
|
|
169
|
-
const totalTokens = turns.reduce((sum, t) => sum + estimateTokens(t.content), 0)
|
|
170
|
-
if (totalTokens > cfg.maxTokens) {
|
|
171
|
-
return true
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return false
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Save compacted context to file
|
|
179
|
-
*/
|
|
180
|
-
export async function saveCompactedContext(
|
|
181
|
-
projectId: string,
|
|
182
|
-
context: CompactedContext
|
|
183
|
-
): Promise<string> {
|
|
184
|
-
const dirPath = path.join(pathManager.getGlobalProjectPath(projectId), 'memory')
|
|
185
|
-
const filePath = path.join(dirPath, 'compacted.jsonl')
|
|
186
|
-
|
|
187
|
-
await fs.mkdir(dirPath, { recursive: true })
|
|
188
|
-
|
|
189
|
-
const line = `${JSON.stringify(context)}\n`
|
|
190
|
-
await fs.appendFile(filePath, line, 'utf-8')
|
|
191
|
-
|
|
192
|
-
return filePath
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Load recent compacted contexts
|
|
197
|
-
*/
|
|
198
|
-
export async function loadCompactedContexts(
|
|
199
|
-
projectId: string,
|
|
200
|
-
limit = 5
|
|
201
|
-
): Promise<CompactedContext[]> {
|
|
202
|
-
const filePath = path.join(
|
|
203
|
-
pathManager.getGlobalProjectPath(projectId),
|
|
204
|
-
'memory',
|
|
205
|
-
'compacted.jsonl'
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
const content = await fs.readFile(filePath, 'utf-8')
|
|
210
|
-
const lines = content.trim().split('\n').filter(Boolean)
|
|
211
|
-
const contexts = lines.map((line) => JSON.parse(line) as CompactedContext)
|
|
212
|
-
|
|
213
|
-
// Return most recent
|
|
214
|
-
return contexts.slice(-limit)
|
|
215
|
-
} catch (_error) {
|
|
216
|
-
return []
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Format compacted context for prompt injection
|
|
222
|
-
*/
|
|
223
|
-
export function formatCompactedForPrompt(context: CompactedContext): string {
|
|
224
|
-
const lines = ['<compacted-context>', context.summary, '']
|
|
225
|
-
|
|
226
|
-
if (context.filesModified.length > 0) {
|
|
227
|
-
lines.push('### Files Modified:')
|
|
228
|
-
lines.push(context.filesModified.map((f) => `- ${f}`).join('\n'))
|
|
229
|
-
lines.push('')
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (context.tasksCompleted.length > 0) {
|
|
233
|
-
lines.push('### Tasks Completed:')
|
|
234
|
-
lines.push(context.tasksCompleted.map((t) => `- ${t}`).join('\n'))
|
|
235
|
-
lines.push('')
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (context.decisions.length > 0) {
|
|
239
|
-
lines.push('### Decisions Made:')
|
|
240
|
-
lines.push(context.decisions.map((d) => `- ${d}`).join('\n'))
|
|
241
|
-
lines.push('')
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
lines.push(`*Compacted from ${context.originalTurns} turns at ${context.compactedAt}*`)
|
|
245
|
-
lines.push('</compacted-context>')
|
|
246
|
-
|
|
247
|
-
return lines.join('\n')
|
|
248
|
-
}
|
package/core/session/index.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Management - Consolidated Module
|
|
3
|
-
*
|
|
4
|
-
* Two managers with distinct purposes:
|
|
5
|
-
* - TaskSessionManager: Task lifecycle (start, pause, complete)
|
|
6
|
-
* - SessionLogManager: Log fragmentation (JSONL temporal)
|
|
7
|
-
*
|
|
8
|
-
* Storage: ~/.prjct-cli/projects/{projectId}/sessions/
|
|
9
|
-
*
|
|
10
|
-
* @version 2.0.0
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// Task session types and utilities
|
|
14
|
-
// Log session types
|
|
15
|
-
export type {
|
|
16
|
-
MigrationResult,
|
|
17
|
-
Session,
|
|
18
|
-
SessionEntry,
|
|
19
|
-
SessionLogMetadata,
|
|
20
|
-
SessionMetrics,
|
|
21
|
-
SessionStats,
|
|
22
|
-
TimelineEvent,
|
|
23
|
-
} from '../types'
|
|
24
|
-
export { SessionLogManager } from './session-log-manager'
|
|
25
|
-
|
|
26
|
-
// Main exports
|
|
27
|
-
export { TaskSessionManager } from './task-session-manager'
|
|
28
|
-
export { calculateDuration, formatDuration, generateId } from './utils'
|
|
29
|
-
|
|
30
|
-
// Default: TaskSessionManager for backward compatibility
|
|
31
|
-
import { TaskSessionManager } from './task-session-manager'
|
|
32
|
-
export default TaskSessionManager
|
|
33
|
-
|
|
34
|
-
// Alias for backward compatibility
|
|
35
|
-
export { TaskSessionManager as SessionManager } from './task-session-manager'
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Migration
|
|
3
|
-
* Migrate legacy single-file logs to session structure
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import path from 'node:path'
|
|
7
|
-
import pathManager from '../infrastructure/path-manager'
|
|
8
|
-
import type { SessionEntry, SessionLogMetadata, SessionMigrationResult } from '../types'
|
|
9
|
-
import * as dateHelper from '../utils/date-helper'
|
|
10
|
-
import * as fileHelper from '../utils/file-helper'
|
|
11
|
-
import * as jsonlHelper from '../utils/jsonl-helper'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Migrate legacy JSONL file
|
|
15
|
-
*/
|
|
16
|
-
export async function migrateLegacyJsonl(
|
|
17
|
-
projectId: string,
|
|
18
|
-
content: string,
|
|
19
|
-
sessionFilename: string,
|
|
20
|
-
updateMetadata: (sessionPath: string, updates: Partial<SessionLogMetadata>) => Promise<void>,
|
|
21
|
-
ensureMetadata: (sessionPath: string) => Promise<void>
|
|
22
|
-
): Promise<SessionMigrationResult> {
|
|
23
|
-
const entries = jsonlHelper.parseJsonLines(content) as SessionEntry[]
|
|
24
|
-
const sessionGroups = new Map<string, SessionEntry[]>()
|
|
25
|
-
|
|
26
|
-
for (const entry of entries) {
|
|
27
|
-
const date = new Date(entry.timestamp || entry.data?.timestamp || Date.now())
|
|
28
|
-
const dateKey = dateHelper.getDateKey(date)
|
|
29
|
-
|
|
30
|
-
if (!sessionGroups.has(dateKey)) {
|
|
31
|
-
sessionGroups.set(dateKey, [])
|
|
32
|
-
}
|
|
33
|
-
sessionGroups.get(dateKey)!.push(entry)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let migratedCount = 0
|
|
37
|
-
for (const [dateKey, groupEntries] of sessionGroups) {
|
|
38
|
-
const [year, monthStr, day] = dateKey.split('-')
|
|
39
|
-
const month = parseInt(monthStr, 10)
|
|
40
|
-
const date = new Date(parseInt(year, 10), month - 1, parseInt(day, 10))
|
|
41
|
-
const sessionPath = await pathManager.ensureSessionPath(projectId, date)
|
|
42
|
-
const filePath = path.join(sessionPath, sessionFilename)
|
|
43
|
-
|
|
44
|
-
await jsonlHelper.writeJsonLines(filePath, groupEntries)
|
|
45
|
-
|
|
46
|
-
migratedCount += groupEntries.length
|
|
47
|
-
|
|
48
|
-
await ensureMetadata(sessionPath)
|
|
49
|
-
await updateMetadata(sessionPath, {
|
|
50
|
-
entryCount: groupEntries.length,
|
|
51
|
-
migrated: true,
|
|
52
|
-
migratedAt: dateHelper.getTimestamp(),
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
success: true,
|
|
58
|
-
message: `Migrated ${migratedCount} entries to ${sessionGroups.size} sessions`,
|
|
59
|
-
entriesMigrated: migratedCount,
|
|
60
|
-
sessionsCreated: sessionGroups.size,
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Migrate legacy markdown file
|
|
66
|
-
*/
|
|
67
|
-
export async function migrateLegacyMarkdown(
|
|
68
|
-
sessionPath: string,
|
|
69
|
-
content: string,
|
|
70
|
-
sessionFilename: string,
|
|
71
|
-
updateMetadata: (sessionPath: string, updates: Partial<SessionLogMetadata>) => Promise<void>
|
|
72
|
-
): Promise<SessionMigrationResult> {
|
|
73
|
-
const filePath = path.join(sessionPath, sessionFilename)
|
|
74
|
-
|
|
75
|
-
await fileHelper.writeFile(filePath, content)
|
|
76
|
-
|
|
77
|
-
await updateMetadata(sessionPath, {
|
|
78
|
-
migrated: true,
|
|
79
|
-
migratedAt: dateHelper.getTimestamp(),
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
success: true,
|
|
84
|
-
message: 'Migrated markdown content to current session',
|
|
85
|
-
entriesMigrated: 1,
|
|
86
|
-
sessionsCreated: 1,
|
|
87
|
-
}
|
|
88
|
-
}
|
package/core/session/metrics.ts
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SessionMetrics - Metrics Calculation and Aggregation
|
|
3
|
-
*
|
|
4
|
-
* Calculates productivity metrics from session data.
|
|
5
|
-
*
|
|
6
|
-
* @version 1.0.0
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs/promises'
|
|
10
|
-
import path from 'node:path'
|
|
11
|
-
import pathManager from '../infrastructure/path-manager'
|
|
12
|
-
import { getErrorMessage, isNotFoundError } from '../types/fs'
|
|
13
|
-
|
|
14
|
-
interface Session {
|
|
15
|
-
id: string
|
|
16
|
-
status: 'active' | 'paused' | 'completed'
|
|
17
|
-
startedAt: string
|
|
18
|
-
completedAt?: string
|
|
19
|
-
duration: number
|
|
20
|
-
metrics?: {
|
|
21
|
-
filesChanged?: number
|
|
22
|
-
linesAdded?: number
|
|
23
|
-
linesRemoved?: number
|
|
24
|
-
commits?: number
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface DayMetrics {
|
|
29
|
-
sessions: number
|
|
30
|
-
duration: number
|
|
31
|
-
commits: number
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface AggregatedMetrics {
|
|
35
|
-
period: string
|
|
36
|
-
totalSessions: number
|
|
37
|
-
totalDuration: number
|
|
38
|
-
totalDurationFormatted: string
|
|
39
|
-
averageDuration: number
|
|
40
|
-
averageDurationFormatted: string
|
|
41
|
-
tasksCompleted: number
|
|
42
|
-
filesChanged: number
|
|
43
|
-
linesAdded: number
|
|
44
|
-
linesRemoved: number
|
|
45
|
-
commits: number
|
|
46
|
-
productivityScore: number
|
|
47
|
-
streak: number
|
|
48
|
-
byDay: Record<string, DayMetrics>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
class SessionMetrics {
|
|
52
|
-
private projectId: string
|
|
53
|
-
|
|
54
|
-
constructor(projectId: string) {
|
|
55
|
-
this.projectId = projectId
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get aggregated metrics for a time period
|
|
60
|
-
*/
|
|
61
|
-
async getMetrics(period: 'day' | 'week' | 'month' | 'all' = 'week'): Promise<AggregatedMetrics> {
|
|
62
|
-
const sessions = await this.getSessionsForPeriod(period)
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
period,
|
|
66
|
-
totalSessions: sessions.length,
|
|
67
|
-
totalDuration: this.sumDurations(sessions),
|
|
68
|
-
totalDurationFormatted: this.formatDuration(this.sumDurations(sessions)),
|
|
69
|
-
averageDuration: this.averageDuration(sessions),
|
|
70
|
-
averageDurationFormatted: this.formatDuration(this.averageDuration(sessions)),
|
|
71
|
-
tasksCompleted: sessions.filter((s) => s.status === 'completed').length,
|
|
72
|
-
filesChanged: this.sumMetric(sessions, 'filesChanged'),
|
|
73
|
-
linesAdded: this.sumMetric(sessions, 'linesAdded'),
|
|
74
|
-
linesRemoved: this.sumMetric(sessions, 'linesRemoved'),
|
|
75
|
-
commits: this.sumMetric(sessions, 'commits'),
|
|
76
|
-
productivityScore: this.calculateProductivityScore(sessions),
|
|
77
|
-
streak: await this.calculateStreak(),
|
|
78
|
-
byDay: this.groupByDay(sessions),
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get sessions for a given period
|
|
84
|
-
*/
|
|
85
|
-
async getSessionsForPeriod(period: string): Promise<Session[]> {
|
|
86
|
-
const globalPath = pathManager.getGlobalProjectPath(this.projectId)
|
|
87
|
-
const archiveDir = path.join(globalPath, 'sessions', 'archive')
|
|
88
|
-
|
|
89
|
-
const sessions: Session[] = []
|
|
90
|
-
const cutoffDate = this.getCutoffDate(period)
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const months = await fs.readdir(archiveDir)
|
|
94
|
-
|
|
95
|
-
for (const month of months) {
|
|
96
|
-
const monthDir = path.join(archiveDir, month)
|
|
97
|
-
const files = await fs.readdir(monthDir)
|
|
98
|
-
|
|
99
|
-
for (const file of files) {
|
|
100
|
-
if (!file.endsWith('.json')) continue
|
|
101
|
-
|
|
102
|
-
const content = await fs.readFile(path.join(monthDir, file), 'utf-8')
|
|
103
|
-
const session: Session = JSON.parse(content)
|
|
104
|
-
|
|
105
|
-
if (session.completedAt && new Date(session.completedAt) >= cutoffDate) {
|
|
106
|
-
sessions.push(session)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
} catch (error) {
|
|
111
|
-
// Archive might not exist - expected for new projects
|
|
112
|
-
if (!isNotFoundError(error)) {
|
|
113
|
-
console.error(`Metrics archive read error: ${getErrorMessage(error)}`)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Also check current session
|
|
118
|
-
try {
|
|
119
|
-
const currentPath = path.join(globalPath, 'sessions', 'current.json')
|
|
120
|
-
const content = await fs.readFile(currentPath, 'utf-8')
|
|
121
|
-
const current: Session = JSON.parse(content)
|
|
122
|
-
if (new Date(current.startedAt) >= cutoffDate) {
|
|
123
|
-
sessions.push(current)
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
// No current session - expected
|
|
127
|
-
if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
|
|
128
|
-
console.error(`Current session read error: ${getErrorMessage(error)}`)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return sessions
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get cutoff date for period
|
|
137
|
-
*/
|
|
138
|
-
getCutoffDate(period: string): Date {
|
|
139
|
-
const now = new Date()
|
|
140
|
-
|
|
141
|
-
switch (period) {
|
|
142
|
-
case 'day':
|
|
143
|
-
return new Date(now.setHours(0, 0, 0, 0))
|
|
144
|
-
case 'week': {
|
|
145
|
-
const weekAgo = new Date(now)
|
|
146
|
-
weekAgo.setDate(weekAgo.getDate() - 7)
|
|
147
|
-
return weekAgo
|
|
148
|
-
}
|
|
149
|
-
case 'month': {
|
|
150
|
-
const monthAgo = new Date(now)
|
|
151
|
-
monthAgo.setMonth(monthAgo.getMonth() - 1)
|
|
152
|
-
return monthAgo
|
|
153
|
-
}
|
|
154
|
-
default:
|
|
155
|
-
return new Date(0) // Beginning of time
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Sum durations from sessions
|
|
161
|
-
*/
|
|
162
|
-
sumDurations(sessions: Session[]): number {
|
|
163
|
-
return sessions.reduce((sum, s) => sum + (s.duration || 0), 0)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Calculate average duration
|
|
168
|
-
*/
|
|
169
|
-
averageDuration(sessions: Session[]): number {
|
|
170
|
-
if (sessions.length === 0) return 0
|
|
171
|
-
return Math.round(this.sumDurations(sessions) / sessions.length)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Sum a specific metric
|
|
176
|
-
*/
|
|
177
|
-
sumMetric(
|
|
178
|
-
sessions: Session[],
|
|
179
|
-
metric: 'filesChanged' | 'linesAdded' | 'linesRemoved' | 'commits'
|
|
180
|
-
): number {
|
|
181
|
-
return sessions.reduce((sum, s) => {
|
|
182
|
-
return sum + (s.metrics?.[metric] || 0)
|
|
183
|
-
}, 0)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Calculate productivity score (0-100)
|
|
188
|
-
* Based on consistency, duration, and output
|
|
189
|
-
*/
|
|
190
|
-
calculateProductivityScore(sessions: Session[]): number {
|
|
191
|
-
if (sessions.length === 0) return 0
|
|
192
|
-
|
|
193
|
-
// Factors:
|
|
194
|
-
// 1. Session count (more = better, up to a point)
|
|
195
|
-
const sessionScore = Math.min(sessions.length / 10, 1) * 30
|
|
196
|
-
|
|
197
|
-
// 2. Average duration (25-90 min is optimal)
|
|
198
|
-
const avgMins = this.averageDuration(sessions) / 60
|
|
199
|
-
let durationScore = 0
|
|
200
|
-
if (avgMins >= 25 && avgMins <= 90) {
|
|
201
|
-
durationScore = 30
|
|
202
|
-
} else if (avgMins > 0) {
|
|
203
|
-
durationScore = Math.max(0, 30 - Math.abs(avgMins - 57.5) / 2)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 3. Completion rate
|
|
207
|
-
const completedCount = sessions.filter((s) => s.status === 'completed').length
|
|
208
|
-
const completionScore = (completedCount / sessions.length) * 20
|
|
209
|
-
|
|
210
|
-
// 4. Output (commits + files changed)
|
|
211
|
-
const totalOutput =
|
|
212
|
-
this.sumMetric(sessions, 'commits') + this.sumMetric(sessions, 'filesChanged')
|
|
213
|
-
const outputScore = Math.min(totalOutput / 50, 1) * 20
|
|
214
|
-
|
|
215
|
-
return Math.round(sessionScore + durationScore + completionScore + outputScore)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Calculate current streak (consecutive days with sessions)
|
|
220
|
-
*/
|
|
221
|
-
async calculateStreak(): Promise<number> {
|
|
222
|
-
const sessions = await this.getSessionsForPeriod('month')
|
|
223
|
-
|
|
224
|
-
// Get unique dates with sessions
|
|
225
|
-
const dates = new Set(
|
|
226
|
-
sessions.map((s) => {
|
|
227
|
-
const d = new Date(s.completedAt || s.startedAt)
|
|
228
|
-
return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`
|
|
229
|
-
})
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
// Count consecutive days from today
|
|
233
|
-
let streak = 0
|
|
234
|
-
const today = new Date()
|
|
235
|
-
|
|
236
|
-
for (let i = 0; i < 30; i++) {
|
|
237
|
-
const checkDate = new Date(today)
|
|
238
|
-
checkDate.setDate(checkDate.getDate() - i)
|
|
239
|
-
const key = `${checkDate.getFullYear()}-${checkDate.getMonth()}-${checkDate.getDate()}`
|
|
240
|
-
|
|
241
|
-
if (dates.has(key)) {
|
|
242
|
-
streak++
|
|
243
|
-
} else if (i > 0) {
|
|
244
|
-
break // Streak broken
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return streak
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Group sessions by day
|
|
253
|
-
*/
|
|
254
|
-
groupByDay(sessions: Session[]): Record<string, DayMetrics> {
|
|
255
|
-
const byDay: Record<string, DayMetrics> = {}
|
|
256
|
-
|
|
257
|
-
for (const session of sessions) {
|
|
258
|
-
const date = new Date(session.completedAt || session.startedAt)
|
|
259
|
-
const key = date.toISOString().split('T')[0]
|
|
260
|
-
|
|
261
|
-
if (!byDay[key]) {
|
|
262
|
-
byDay[key] = {
|
|
263
|
-
sessions: 0,
|
|
264
|
-
duration: 0,
|
|
265
|
-
commits: 0,
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
byDay[key].sessions++
|
|
270
|
-
byDay[key].duration += session.duration || 0
|
|
271
|
-
byDay[key].commits += session.metrics?.commits || 0
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return byDay
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Format duration as human readable
|
|
279
|
-
*/
|
|
280
|
-
formatDuration(seconds: number): string {
|
|
281
|
-
if (seconds < 60) return `${seconds}s`
|
|
282
|
-
if (seconds < 3600) return `${Math.round(seconds / 60)}m`
|
|
283
|
-
|
|
284
|
-
const hours = Math.floor(seconds / 3600)
|
|
285
|
-
const minutes = Math.round((seconds % 3600) / 60)
|
|
286
|
-
|
|
287
|
-
if (minutes === 0) return `${hours}h`
|
|
288
|
-
return `${hours}h ${minutes}m`
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Generate metrics summary for display
|
|
293
|
-
*/
|
|
294
|
-
async generateSummary(period: 'day' | 'week' | 'month' = 'week'): Promise<string> {
|
|
295
|
-
const m = await this.getMetrics(period)
|
|
296
|
-
|
|
297
|
-
const periodLabel: Record<string, string> = {
|
|
298
|
-
day: 'Today',
|
|
299
|
-
week: 'This Week',
|
|
300
|
-
month: 'This Month',
|
|
301
|
-
all: 'All Time',
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return `
|
|
305
|
-
## ${periodLabel[period]}
|
|
306
|
-
|
|
307
|
-
| Metric | Value |
|
|
308
|
-
|--------|-------|
|
|
309
|
-
| Sessions | ${m.totalSessions} |
|
|
310
|
-
| Total Time | ${m.totalDurationFormatted} |
|
|
311
|
-
| Avg Session | ${m.averageDurationFormatted} |
|
|
312
|
-
| Tasks Done | ${m.tasksCompleted} |
|
|
313
|
-
| Files Changed | ${m.filesChanged} |
|
|
314
|
-
| Lines +/- | +${m.linesAdded} / -${m.linesRemoved} |
|
|
315
|
-
| Commits | ${m.commits} |
|
|
316
|
-
| Streak | ${m.streak} days |
|
|
317
|
-
| Score | ${m.productivityScore}/100 |
|
|
318
|
-
`.trim()
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export default SessionMetrics
|
|
323
|
-
export { SessionMetrics, type AggregatedMetrics, type DayMetrics }
|