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
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linear Sync Layer
|
|
3
|
-
*
|
|
4
|
-
* Bidirectional sync between Linear and local prjct storage.
|
|
5
|
-
* Uses issues.json as local cache with 30-minute staleness.
|
|
6
|
-
*
|
|
7
|
-
* Architecture:
|
|
8
|
-
* Linear (source of truth)
|
|
9
|
-
* ↕
|
|
10
|
-
* Sync Layer (this file)
|
|
11
|
-
* ↕
|
|
12
|
-
* storage/issues.json ← FULL COPY of Linear issues
|
|
13
|
-
* ↕
|
|
14
|
-
* state.json.currentTask.linearId ← DIRECT LINK
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
18
|
-
import { join } from 'node:path'
|
|
19
|
-
import {
|
|
20
|
-
type CachedIssue,
|
|
21
|
-
createEmptyIssues,
|
|
22
|
-
type IssuesJson,
|
|
23
|
-
parseIssues,
|
|
24
|
-
type SyncResult,
|
|
25
|
-
} from '../../schemas/issues'
|
|
26
|
-
import { getProjectPath } from '../../schemas/schemas'
|
|
27
|
-
import { getErrorMessage } from '../../types/fs'
|
|
28
|
-
import { fileExists } from '../../utils/fs-helpers'
|
|
29
|
-
import type { Issue } from '../issue-tracker/types'
|
|
30
|
-
import { linearService } from './service'
|
|
31
|
-
|
|
32
|
-
// Default staleness threshold: 30 minutes
|
|
33
|
-
const DEFAULT_STALE_AFTER = 30 * 60 * 1000
|
|
34
|
-
|
|
35
|
-
export class LinearSync {
|
|
36
|
-
/**
|
|
37
|
-
* Pull all assigned issues from Linear and store in issues.json
|
|
38
|
-
* This is the main sync operation - call on `p. sync`
|
|
39
|
-
*/
|
|
40
|
-
async pullAll(projectId: string): Promise<SyncResult> {
|
|
41
|
-
const storagePath = join(getProjectPath(projectId), 'storage')
|
|
42
|
-
const issuesPath = join(storagePath, 'issues.json')
|
|
43
|
-
|
|
44
|
-
// Ensure storage directory exists
|
|
45
|
-
if (!(await fileExists(storagePath))) {
|
|
46
|
-
await mkdir(storagePath, { recursive: true })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const timestamp = new Date().toISOString()
|
|
50
|
-
const errors: Array<{ issueId: string; error: string }> = []
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
// Fetch all assigned issues from Linear
|
|
54
|
-
const issues = await linearService.fetchAssignedIssues({ limit: 100 })
|
|
55
|
-
|
|
56
|
-
// Convert to cached format
|
|
57
|
-
const issuesMap: Record<string, CachedIssue> = {}
|
|
58
|
-
for (const issue of issues) {
|
|
59
|
-
try {
|
|
60
|
-
issuesMap[issue.externalId] = this.toCachedIssue(issue, timestamp)
|
|
61
|
-
} catch (err) {
|
|
62
|
-
errors.push({
|
|
63
|
-
issueId: issue.externalId || issue.id,
|
|
64
|
-
error: getErrorMessage(err),
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Write to issues.json
|
|
70
|
-
const issuesJson: IssuesJson = {
|
|
71
|
-
provider: 'linear',
|
|
72
|
-
lastSync: timestamp,
|
|
73
|
-
staleAfter: DEFAULT_STALE_AFTER,
|
|
74
|
-
issues: issuesMap,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
await writeFile(issuesPath, JSON.stringify(issuesJson, null, 2))
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
provider: 'linear',
|
|
81
|
-
fetched: issues.length,
|
|
82
|
-
updated: Object.keys(issuesMap).length,
|
|
83
|
-
errors,
|
|
84
|
-
timestamp,
|
|
85
|
-
}
|
|
86
|
-
} catch (err) {
|
|
87
|
-
errors.push({
|
|
88
|
-
issueId: 'all',
|
|
89
|
-
error: getErrorMessage(err),
|
|
90
|
-
})
|
|
91
|
-
return {
|
|
92
|
-
provider: 'linear',
|
|
93
|
-
fetched: 0,
|
|
94
|
-
updated: 0,
|
|
95
|
-
errors,
|
|
96
|
-
timestamp,
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Get issue from local cache, fetch from API if not found or stale
|
|
103
|
-
* Local-first approach for performance
|
|
104
|
-
*/
|
|
105
|
-
async getIssue(projectId: string, identifier: string): Promise<CachedIssue | null> {
|
|
106
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
107
|
-
|
|
108
|
-
// Check local cache first
|
|
109
|
-
if (issuesJson?.issues[identifier]) {
|
|
110
|
-
const cachedIssue = issuesJson.issues[identifier]
|
|
111
|
-
|
|
112
|
-
// Check if cached issue is still fresh (within fetchedAt + some grace period)
|
|
113
|
-
const fetchedAt = new Date(cachedIssue.fetchedAt).getTime()
|
|
114
|
-
const now = Date.now()
|
|
115
|
-
const issueStaleness = 10 * 60 * 1000 // 10 minutes for individual issues
|
|
116
|
-
|
|
117
|
-
if (now - fetchedAt < issueStaleness) {
|
|
118
|
-
return cachedIssue
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Not in cache or stale - fetch from API and update cache
|
|
123
|
-
try {
|
|
124
|
-
const issue = await linearService.fetchIssue(identifier)
|
|
125
|
-
if (!issue) return null
|
|
126
|
-
|
|
127
|
-
const timestamp = new Date().toISOString()
|
|
128
|
-
const cachedIssue = this.toCachedIssue(issue, timestamp)
|
|
129
|
-
|
|
130
|
-
// Update cache with this single issue
|
|
131
|
-
await this.updateIssueInCache(projectId, identifier, cachedIssue)
|
|
132
|
-
|
|
133
|
-
return cachedIssue
|
|
134
|
-
} catch {
|
|
135
|
-
// API failed, return cached version if available (even if stale)
|
|
136
|
-
if (issuesJson?.issues[identifier]) {
|
|
137
|
-
return issuesJson.issues[identifier]
|
|
138
|
-
}
|
|
139
|
-
return null
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Get issue from local cache ONLY (no API call)
|
|
145
|
-
* Use for fast lookups when you know the issue should be cached
|
|
146
|
-
*/
|
|
147
|
-
async getIssueLocal(projectId: string, identifier: string): Promise<CachedIssue | null> {
|
|
148
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
149
|
-
return issuesJson?.issues[identifier] || null
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Push local status change to Linear
|
|
154
|
-
* Called when task status changes (in_progress, done)
|
|
155
|
-
*/
|
|
156
|
-
async pushStatus(
|
|
157
|
-
projectId: string,
|
|
158
|
-
identifier: string,
|
|
159
|
-
status: 'in_progress' | 'done'
|
|
160
|
-
): Promise<void> {
|
|
161
|
-
// Update Linear
|
|
162
|
-
if (status === 'in_progress') {
|
|
163
|
-
await linearService.markInProgress(identifier)
|
|
164
|
-
} else if (status === 'done') {
|
|
165
|
-
await linearService.markDone(identifier)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Update local cache to reflect the change
|
|
169
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
170
|
-
if (issuesJson?.issues[identifier]) {
|
|
171
|
-
const cachedStatus = status === 'done' ? 'done' : 'in_progress'
|
|
172
|
-
issuesJson.issues[identifier].status = cachedStatus
|
|
173
|
-
issuesJson.issues[identifier].fetchedAt = new Date().toISOString()
|
|
174
|
-
|
|
175
|
-
await this.saveIssues(projectId, issuesJson)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Check if the local issues cache is stale
|
|
181
|
-
* Staleness = lastSync is older than staleAfter threshold
|
|
182
|
-
*/
|
|
183
|
-
async isStale(projectId: string): Promise<boolean> {
|
|
184
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
185
|
-
|
|
186
|
-
if (!issuesJson || !issuesJson.lastSync) {
|
|
187
|
-
return true // No cache = stale
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const lastSyncTime = new Date(issuesJson.lastSync).getTime()
|
|
191
|
-
const now = Date.now()
|
|
192
|
-
const staleAfter = issuesJson.staleAfter || DEFAULT_STALE_AFTER
|
|
193
|
-
|
|
194
|
-
return now - lastSyncTime > staleAfter
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Get sync status for display
|
|
199
|
-
*/
|
|
200
|
-
async getSyncStatus(projectId: string): Promise<{
|
|
201
|
-
hasCache: boolean
|
|
202
|
-
lastSync: string | null
|
|
203
|
-
issueCount: number
|
|
204
|
-
isStale: boolean
|
|
205
|
-
}> {
|
|
206
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
207
|
-
|
|
208
|
-
if (!issuesJson) {
|
|
209
|
-
return {
|
|
210
|
-
hasCache: false,
|
|
211
|
-
lastSync: null,
|
|
212
|
-
issueCount: 0,
|
|
213
|
-
isStale: true,
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
hasCache: true,
|
|
219
|
-
lastSync: issuesJson.lastSync || null,
|
|
220
|
-
issueCount: Object.keys(issuesJson.issues).length,
|
|
221
|
-
isStale: await this.isStale(projectId),
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* List all cached issues
|
|
227
|
-
*/
|
|
228
|
-
async listCachedIssues(projectId: string): Promise<CachedIssue[]> {
|
|
229
|
-
const issuesJson = await this.loadIssues(projectId)
|
|
230
|
-
if (!issuesJson) return []
|
|
231
|
-
|
|
232
|
-
return Object.values(issuesJson.issues)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// =============================================================================
|
|
236
|
-
// Private Helpers
|
|
237
|
-
// =============================================================================
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Load issues.json from disk
|
|
241
|
-
*/
|
|
242
|
-
private async loadIssues(projectId: string): Promise<IssuesJson | null> {
|
|
243
|
-
const issuesPath = join(getProjectPath(projectId), 'storage', 'issues.json')
|
|
244
|
-
|
|
245
|
-
if (!(await fileExists(issuesPath))) {
|
|
246
|
-
return null
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const content = await readFile(issuesPath, 'utf-8')
|
|
251
|
-
return parseIssues(JSON.parse(content))
|
|
252
|
-
} catch {
|
|
253
|
-
return null
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Save issues.json to disk
|
|
259
|
-
*/
|
|
260
|
-
private async saveIssues(projectId: string, issuesJson: IssuesJson): Promise<void> {
|
|
261
|
-
const storagePath = join(getProjectPath(projectId), 'storage')
|
|
262
|
-
const issuesPath = join(storagePath, 'issues.json')
|
|
263
|
-
|
|
264
|
-
if (!(await fileExists(storagePath))) {
|
|
265
|
-
await mkdir(storagePath, { recursive: true })
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
await writeFile(issuesPath, JSON.stringify(issuesJson, null, 2))
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Update a single issue in the cache
|
|
273
|
-
*/
|
|
274
|
-
private async updateIssueInCache(
|
|
275
|
-
projectId: string,
|
|
276
|
-
identifier: string,
|
|
277
|
-
issue: CachedIssue
|
|
278
|
-
): Promise<void> {
|
|
279
|
-
let issuesJson = await this.loadIssues(projectId)
|
|
280
|
-
|
|
281
|
-
if (!issuesJson) {
|
|
282
|
-
issuesJson = createEmptyIssues('linear')
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
issuesJson.issues[identifier] = issue
|
|
286
|
-
await this.saveIssues(projectId, issuesJson)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Convert API Issue to CachedIssue format
|
|
291
|
-
*/
|
|
292
|
-
private toCachedIssue(issue: Issue, timestamp: string): CachedIssue {
|
|
293
|
-
return {
|
|
294
|
-
id: issue.id,
|
|
295
|
-
identifier: issue.externalId,
|
|
296
|
-
title: issue.title,
|
|
297
|
-
description: issue.description,
|
|
298
|
-
status: issue.status,
|
|
299
|
-
priority: issue.priority,
|
|
300
|
-
type: issue.type,
|
|
301
|
-
assignee: issue.assignee,
|
|
302
|
-
labels: issue.labels,
|
|
303
|
-
team: issue.team,
|
|
304
|
-
project: issue.project,
|
|
305
|
-
url: issue.url,
|
|
306
|
-
createdAt: issue.createdAt,
|
|
307
|
-
updatedAt: issue.updatedAt,
|
|
308
|
-
fetchedAt: timestamp,
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Singleton instance
|
|
314
|
-
export const linearSync = new LinearSync()
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Outcome Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes outcomes to extract patterns and insights.
|
|
5
|
-
* Powers the learning loop for better estimates and agent selection.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AgentMetrics, DetectedPattern, Outcome, OutcomeSummary } from '../types'
|
|
9
|
-
import outcomeRecorder from './recorder'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* OutcomeAnalyzer - Extracts insights from outcomes.
|
|
13
|
-
*/
|
|
14
|
-
export class OutcomeAnalyzer {
|
|
15
|
-
/**
|
|
16
|
-
* Generate summary of all outcomes.
|
|
17
|
-
*/
|
|
18
|
-
async summarize(projectId: string): Promise<OutcomeSummary> {
|
|
19
|
-
const outcomes = await outcomeRecorder.getAll(projectId)
|
|
20
|
-
|
|
21
|
-
if (outcomes.length === 0) {
|
|
22
|
-
return {
|
|
23
|
-
totalOutcomes: 0,
|
|
24
|
-
avgQualityScore: 0,
|
|
25
|
-
estimateAccuracy: 0,
|
|
26
|
-
topBlockers: [],
|
|
27
|
-
topAgents: [],
|
|
28
|
-
patternsDetected: [],
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Calculate average quality
|
|
33
|
-
const avgQuality = outcomes.reduce((sum, o) => sum + o.qualityScore, 0) / outcomes.length
|
|
34
|
-
|
|
35
|
-
// Calculate estimate accuracy
|
|
36
|
-
const estimateAccuracy = await outcomeRecorder.getEstimateAccuracy(projectId)
|
|
37
|
-
|
|
38
|
-
// Find top blockers
|
|
39
|
-
const blockerCounts = new Map<string, number>()
|
|
40
|
-
for (const outcome of outcomes) {
|
|
41
|
-
for (const blocker of outcome.blockers || []) {
|
|
42
|
-
blockerCounts.set(blocker, (blockerCounts.get(blocker) || 0) + 1)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const topBlockers = [...blockerCounts.entries()]
|
|
46
|
-
.sort((a, b) => b[1] - a[1])
|
|
47
|
-
.slice(0, 5)
|
|
48
|
-
.map(([blocker]) => blocker)
|
|
49
|
-
|
|
50
|
-
// Find top agents
|
|
51
|
-
const agentMetrics = await this.getAgentMetrics(projectId)
|
|
52
|
-
const topAgents = agentMetrics
|
|
53
|
-
.sort((a, b) => b.successRate - a.successRate)
|
|
54
|
-
.slice(0, 3)
|
|
55
|
-
.map((m) => m.agent)
|
|
56
|
-
|
|
57
|
-
// Detect patterns
|
|
58
|
-
const patterns = await this.detectPatterns(projectId)
|
|
59
|
-
const patternsDetected = patterns.map((p) => p.description)
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
totalOutcomes: outcomes.length,
|
|
63
|
-
avgQualityScore: Math.round(avgQuality * 10) / 10,
|
|
64
|
-
estimateAccuracy,
|
|
65
|
-
topBlockers,
|
|
66
|
-
topAgents,
|
|
67
|
-
patternsDetected,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Get metrics for each agent.
|
|
73
|
-
*/
|
|
74
|
-
async getAgentMetrics(projectId: string): Promise<AgentMetrics[]> {
|
|
75
|
-
const outcomes = await outcomeRecorder.getAll(projectId)
|
|
76
|
-
|
|
77
|
-
// Group by agent
|
|
78
|
-
const byAgent = new Map<string, Outcome[]>()
|
|
79
|
-
for (const outcome of outcomes) {
|
|
80
|
-
const agent = outcome.agentUsed || 'unknown'
|
|
81
|
-
if (!byAgent.has(agent)) {
|
|
82
|
-
byAgent.set(agent, [])
|
|
83
|
-
}
|
|
84
|
-
byAgent.get(agent)!.push(outcome)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const metrics: AgentMetrics[] = []
|
|
88
|
-
|
|
89
|
-
for (const [agent, agentOutcomes] of byAgent) {
|
|
90
|
-
const tasksCompleted = agentOutcomes.length
|
|
91
|
-
const successful = agentOutcomes.filter((o) => o.completedAsPlanned)
|
|
92
|
-
const successRate = Math.round((successful.length / tasksCompleted) * 100)
|
|
93
|
-
|
|
94
|
-
const avgQuality = agentOutcomes.reduce((sum, o) => sum + o.qualityScore, 0) / tasksCompleted
|
|
95
|
-
|
|
96
|
-
// Calculate estimate accuracy for this agent
|
|
97
|
-
const accurateEstimates = agentOutcomes.filter((o) => {
|
|
98
|
-
if (!o.variance) return false
|
|
99
|
-
const variance = this.parseVariance(o.variance)
|
|
100
|
-
const estimated = this.parseDuration(o.estimatedDuration)
|
|
101
|
-
if (estimated === 0) return false
|
|
102
|
-
return Math.abs(variance) / estimated <= 0.2
|
|
103
|
-
})
|
|
104
|
-
const estimateAccuracy = Math.round((accurateEstimates.length / tasksCompleted) * 100)
|
|
105
|
-
|
|
106
|
-
// Find best task types
|
|
107
|
-
const taskTypes = new Map<string, number>()
|
|
108
|
-
for (const o of agentOutcomes.filter((o) => o.completedAsPlanned)) {
|
|
109
|
-
for (const tag of o.tags || []) {
|
|
110
|
-
taskTypes.set(tag, (taskTypes.get(tag) || 0) + 1)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const bestFor = [...taskTypes.entries()]
|
|
114
|
-
.sort((a, b) => b[1] - a[1])
|
|
115
|
-
.slice(0, 3)
|
|
116
|
-
.map(([type]) => type)
|
|
117
|
-
|
|
118
|
-
metrics.push({
|
|
119
|
-
agent,
|
|
120
|
-
tasksCompleted,
|
|
121
|
-
successRate,
|
|
122
|
-
avgQualityScore: Math.round(avgQuality * 10) / 10,
|
|
123
|
-
estimateAccuracy,
|
|
124
|
-
bestFor,
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return metrics
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Detect patterns from outcomes.
|
|
133
|
-
*/
|
|
134
|
-
async detectPatterns(projectId: string): Promise<DetectedPattern[]> {
|
|
135
|
-
const outcomes = await outcomeRecorder.getAll(projectId)
|
|
136
|
-
const patterns: DetectedPattern[] = []
|
|
137
|
-
|
|
138
|
-
if (outcomes.length < 3) {
|
|
139
|
-
return patterns // Need at least 3 outcomes to detect patterns
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Pattern: Consistent underestimation
|
|
143
|
-
const underestimated = outcomes.filter((o) => {
|
|
144
|
-
const variance = this.parseVariance(o.variance)
|
|
145
|
-
return variance > 0
|
|
146
|
-
})
|
|
147
|
-
if (underestimated.length / outcomes.length > 0.6) {
|
|
148
|
-
patterns.push({
|
|
149
|
-
description: 'Tasks consistently take longer than estimated',
|
|
150
|
-
confidence: underestimated.length / outcomes.length,
|
|
151
|
-
occurrences: underestimated.length,
|
|
152
|
-
suggestedAction: 'Add 30% buffer to estimates',
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Pattern: Consistent overestimation
|
|
157
|
-
const overestimated = outcomes.filter((o) => {
|
|
158
|
-
const variance = this.parseVariance(o.variance)
|
|
159
|
-
return variance < 0
|
|
160
|
-
})
|
|
161
|
-
if (overestimated.length / outcomes.length > 0.6) {
|
|
162
|
-
patterns.push({
|
|
163
|
-
description: 'Tasks consistently finish faster than estimated',
|
|
164
|
-
confidence: overestimated.length / outcomes.length,
|
|
165
|
-
occurrences: overestimated.length,
|
|
166
|
-
suggestedAction: 'Reduce estimates by 20%',
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Pattern: Common blockers
|
|
171
|
-
const blockerCounts = new Map<string, number>()
|
|
172
|
-
for (const outcome of outcomes) {
|
|
173
|
-
for (const blocker of outcome.blockers || []) {
|
|
174
|
-
blockerCounts.set(blocker, (blockerCounts.get(blocker) || 0) + 1)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
for (const [blocker, count] of blockerCounts) {
|
|
178
|
-
if (count >= 3) {
|
|
179
|
-
patterns.push({
|
|
180
|
-
description: `Recurring blocker: ${blocker}`,
|
|
181
|
-
confidence: count / outcomes.length,
|
|
182
|
-
occurrences: count,
|
|
183
|
-
suggestedAction: `Address root cause of "${blocker}"`,
|
|
184
|
-
})
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Pattern: Agent performance
|
|
189
|
-
const agentMetrics = await this.getAgentMetrics(projectId)
|
|
190
|
-
for (const metrics of agentMetrics) {
|
|
191
|
-
if (metrics.tasksCompleted >= 5 && metrics.successRate > 90) {
|
|
192
|
-
patterns.push({
|
|
193
|
-
description: `${metrics.agent} has high success rate (${metrics.successRate}%)`,
|
|
194
|
-
confidence: 0.9,
|
|
195
|
-
occurrences: metrics.tasksCompleted,
|
|
196
|
-
suggestedAction: `Prefer ${metrics.agent} for similar tasks`,
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return patterns.sort((a, b) => b.confidence - a.confidence)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Suggest estimate for a task type based on history.
|
|
206
|
-
*/
|
|
207
|
-
async suggestEstimate(projectId: string, taskType: string): Promise<string | null> {
|
|
208
|
-
const outcomes = await outcomeRecorder.getAll(projectId)
|
|
209
|
-
|
|
210
|
-
// Filter by task type (using tags)
|
|
211
|
-
const relevant = outcomes.filter((o) => o.tags?.includes(taskType))
|
|
212
|
-
|
|
213
|
-
if (relevant.length < 2) {
|
|
214
|
-
return null // Not enough data
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Calculate average actual duration
|
|
218
|
-
const totalMinutes = relevant.reduce((sum, o) => {
|
|
219
|
-
return sum + this.parseDuration(o.actualDuration)
|
|
220
|
-
}, 0)
|
|
221
|
-
|
|
222
|
-
const avgMinutes = Math.round(totalMinutes / relevant.length)
|
|
223
|
-
|
|
224
|
-
// Format as duration string
|
|
225
|
-
if (avgMinutes >= 60) {
|
|
226
|
-
const hours = Math.floor(avgMinutes / 60)
|
|
227
|
-
const mins = avgMinutes % 60
|
|
228
|
-
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`
|
|
229
|
-
}
|
|
230
|
-
return `${avgMinutes}m`
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Suggest best agent for a task type.
|
|
235
|
-
*/
|
|
236
|
-
async suggestAgent(projectId: string, taskType: string): Promise<string | null> {
|
|
237
|
-
const agentMetrics = await this.getAgentMetrics(projectId)
|
|
238
|
-
|
|
239
|
-
// Find agents good at this task type
|
|
240
|
-
const suitable = agentMetrics.filter((m) => m.bestFor.includes(taskType))
|
|
241
|
-
|
|
242
|
-
if (suitable.length === 0) {
|
|
243
|
-
return null
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Return the one with highest success rate
|
|
247
|
-
return suitable.sort((a, b) => b.successRate - a.successRate)[0].agent
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Parse variance string to minutes.
|
|
252
|
-
*/
|
|
253
|
-
private parseVariance(variance: string): number {
|
|
254
|
-
const match = variance.match(/^([+-])(\d+)([mh])$/)
|
|
255
|
-
if (!match) return 0
|
|
256
|
-
|
|
257
|
-
const sign = match[1] === '-' ? -1 : 1
|
|
258
|
-
const value = parseInt(match[2], 10)
|
|
259
|
-
const unit = match[3]
|
|
260
|
-
|
|
261
|
-
return sign * (unit === 'h' ? value * 60 : value)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Parse duration string to minutes.
|
|
266
|
-
*/
|
|
267
|
-
private parseDuration(duration: string): number {
|
|
268
|
-
let minutes = 0
|
|
269
|
-
|
|
270
|
-
const hourMatch = duration.match(/(\d+)h/)
|
|
271
|
-
if (hourMatch) {
|
|
272
|
-
minutes += parseInt(hourMatch[1], 10) * 60
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const minMatch = duration.match(/(\d+)m/)
|
|
276
|
-
if (minMatch) {
|
|
277
|
-
minutes += parseInt(minMatch[1], 10)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return minutes
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Singleton instance
|
|
285
|
-
const outcomeAnalyzer = new OutcomeAnalyzer()
|
|
286
|
-
export default outcomeAnalyzer
|
package/core/outcomes/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Outcomes Module
|
|
3
|
-
*
|
|
4
|
-
* Records and analyzes execution outcomes for learning.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { outcomeRecorder, outcomeAnalyzer } from './outcomes'
|
|
9
|
-
*
|
|
10
|
-
* // Record an outcome
|
|
11
|
-
* await outcomeRecorder.record(projectId, {
|
|
12
|
-
* sessionId: 'session_123',
|
|
13
|
-
* command: '/p:now',
|
|
14
|
-
* task: 'implement auth',
|
|
15
|
-
* startedAt: '2025-12-09T14:00:00Z',
|
|
16
|
-
* completedAt: '2025-12-09T16:30:00Z',
|
|
17
|
-
* estimatedDuration: '2h',
|
|
18
|
-
* actualDuration: '2h 30m',
|
|
19
|
-
* variance: '+30m',
|
|
20
|
-
* completedAsPlanned: true,
|
|
21
|
-
* qualityScore: 4,
|
|
22
|
-
* agentUsed: 'backend-specialist',
|
|
23
|
-
* tags: ['auth', 'backend']
|
|
24
|
-
* })
|
|
25
|
-
*
|
|
26
|
-
* // Analyze outcomes
|
|
27
|
-
* const summary = await outcomeAnalyzer.summarize(projectId)
|
|
28
|
-
* const patterns = await outcomeAnalyzer.detectPatterns(projectId)
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
export * from '../types'
|
|
33
|
-
export { default as outcomeAnalyzer, OutcomeAnalyzer } from './analyzer'
|
|
34
|
-
export { default as outcomeRecorder, OutcomeRecorder } from './recorder'
|