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,436 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linear Client
|
|
3
|
-
* Implements IssueTrackerProvider for Linear using @linear/sdk
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { LinearClient as LinearSDK } from '@linear/sdk'
|
|
7
|
-
import { getErrorMessage } from '../../types/fs'
|
|
8
|
-
import { getCredential } from '../../utils/keychain'
|
|
9
|
-
import type {
|
|
10
|
-
CreateIssueInput,
|
|
11
|
-
FetchOptions,
|
|
12
|
-
Issue,
|
|
13
|
-
IssuePriority,
|
|
14
|
-
IssueStatus,
|
|
15
|
-
IssueTrackerProvider,
|
|
16
|
-
IssueType,
|
|
17
|
-
LinearConfig,
|
|
18
|
-
UpdateIssueInput,
|
|
19
|
-
} from '../issue-tracker/types'
|
|
20
|
-
|
|
21
|
-
// =============================================================================
|
|
22
|
-
// Status/Priority Mapping
|
|
23
|
-
// =============================================================================
|
|
24
|
-
|
|
25
|
-
const LINEAR_STATUS_MAP: Record<string, IssueStatus> = {
|
|
26
|
-
backlog: 'backlog',
|
|
27
|
-
unstarted: 'todo',
|
|
28
|
-
started: 'in_progress',
|
|
29
|
-
completed: 'done',
|
|
30
|
-
canceled: 'cancelled',
|
|
31
|
-
cancelled: 'cancelled',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const LINEAR_PRIORITY_MAP: Record<number, IssuePriority> = {
|
|
35
|
-
0: 'none',
|
|
36
|
-
1: 'urgent',
|
|
37
|
-
2: 'high',
|
|
38
|
-
3: 'medium',
|
|
39
|
-
4: 'low',
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const PRIORITY_TO_LINEAR: Record<IssuePriority, number> = {
|
|
43
|
-
none: 0,
|
|
44
|
-
urgent: 1,
|
|
45
|
-
high: 2,
|
|
46
|
-
medium: 3,
|
|
47
|
-
low: 4,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// =============================================================================
|
|
51
|
-
// Linear Provider Implementation
|
|
52
|
-
// =============================================================================
|
|
53
|
-
|
|
54
|
-
export class LinearProvider implements IssueTrackerProvider {
|
|
55
|
-
readonly name = 'linear' as const
|
|
56
|
-
readonly displayName = 'Linear'
|
|
57
|
-
|
|
58
|
-
private sdk: LinearSDK | null = null
|
|
59
|
-
private config: LinearConfig | null = null
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Check if provider is configured
|
|
63
|
-
*/
|
|
64
|
-
isConfigured(): boolean {
|
|
65
|
-
return this.sdk !== null && this.config?.enabled === true
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Initialize with config
|
|
70
|
-
* Looks for API key in: 1) config.apiKey, 2) macOS Keychain, 3) LINEAR_API_KEY env var
|
|
71
|
-
*/
|
|
72
|
-
async initialize(config: LinearConfig): Promise<void> {
|
|
73
|
-
this.config = config
|
|
74
|
-
|
|
75
|
-
// Try config first, then keychain (which falls back to env var)
|
|
76
|
-
const apiKey = config.apiKey || (await getCredential('linear-api-key'))
|
|
77
|
-
if (!apiKey) {
|
|
78
|
-
throw new Error('LINEAR_API_KEY not configured. Run `p. linear setup` to configure.')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const { LinearClient } = await import('@linear/sdk')
|
|
82
|
-
this.sdk = new LinearClient({ apiKey })
|
|
83
|
-
|
|
84
|
-
// Verify connection silently (no output noise)
|
|
85
|
-
try {
|
|
86
|
-
await this.sdk.viewer
|
|
87
|
-
} catch (error) {
|
|
88
|
-
this.sdk = null
|
|
89
|
-
throw new Error(`Linear connection failed: ${getErrorMessage(error)}`)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Get issues assigned to current user
|
|
95
|
-
* Filters by configured team if defaultTeamId is set
|
|
96
|
-
*/
|
|
97
|
-
async fetchAssignedIssues(options?: FetchOptions): Promise<Issue[]> {
|
|
98
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
99
|
-
|
|
100
|
-
const viewer = await this.sdk.viewer
|
|
101
|
-
|
|
102
|
-
// Build filter - always filter by team if configured
|
|
103
|
-
const filter: Record<string, unknown> = {}
|
|
104
|
-
|
|
105
|
-
if (!options?.includeCompleted) {
|
|
106
|
-
filter.state = { type: { nin: ['completed', 'canceled'] } }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Filter by configured team to only show relevant issues
|
|
110
|
-
if (this.config?.defaultTeamId) {
|
|
111
|
-
filter.team = { id: { eq: this.config.defaultTeamId } }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const assignedIssues = await viewer.assignedIssues({
|
|
115
|
-
first: options?.limit || 50,
|
|
116
|
-
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
return Promise.all(assignedIssues.nodes.map((issue) => this.mapIssue(issue)))
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get issues from a team
|
|
124
|
-
*/
|
|
125
|
-
async fetchTeamIssues(teamId: string, options?: FetchOptions): Promise<Issue[]> {
|
|
126
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
127
|
-
|
|
128
|
-
const team = await this.sdk.team(teamId)
|
|
129
|
-
const issues = await team.issues({
|
|
130
|
-
first: options?.limit || 50,
|
|
131
|
-
filter: options?.includeCompleted
|
|
132
|
-
? undefined
|
|
133
|
-
: { state: { type: { nin: ['completed', 'canceled'] } } },
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
return Promise.all(issues.nodes.map((issue) => this.mapIssue(issue)))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get a single issue by ID or identifier (e.g., "PRJ-123")
|
|
141
|
-
*/
|
|
142
|
-
async fetchIssue(id: string): Promise<Issue | null> {
|
|
143
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
// Check if it looks like an identifier (e.g., "PRJ-123")
|
|
147
|
-
if (id.includes('-') && /^[A-Z]+-\d+$/.test(id)) {
|
|
148
|
-
// Parse identifier into team key and issue number
|
|
149
|
-
const match = id.match(/^([A-Z]+)-(\d+)$/)
|
|
150
|
-
if (!match) return null
|
|
151
|
-
|
|
152
|
-
const [, teamKey, numberStr] = match
|
|
153
|
-
const issueNumber = parseInt(numberStr, 10)
|
|
154
|
-
|
|
155
|
-
// Find team by key
|
|
156
|
-
const teams = await this.sdk.teams({ first: 50 })
|
|
157
|
-
const team = teams.nodes.find((t) => t.key === teamKey)
|
|
158
|
-
if (!team) return null
|
|
159
|
-
|
|
160
|
-
// Query issue by team and number
|
|
161
|
-
const issues = await team.issues({
|
|
162
|
-
first: 1,
|
|
163
|
-
filter: { number: { eq: issueNumber } },
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
if (issues.nodes.length > 0) {
|
|
167
|
-
return this.mapIssue(issues.nodes[0])
|
|
168
|
-
}
|
|
169
|
-
return null
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Try by UUID directly
|
|
173
|
-
const issue = await this.sdk.issue(id)
|
|
174
|
-
return this.mapIssue(issue)
|
|
175
|
-
} catch (_error) {
|
|
176
|
-
return null
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Create a new issue
|
|
182
|
-
*/
|
|
183
|
-
async createIssue(input: CreateIssueInput): Promise<Issue> {
|
|
184
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
185
|
-
|
|
186
|
-
const teamId = input.teamId || this.config?.defaultTeamId
|
|
187
|
-
if (!teamId) {
|
|
188
|
-
throw new Error('Team ID required for creating issues')
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const payload = await this.sdk.createIssue({
|
|
192
|
-
teamId,
|
|
193
|
-
title: input.title,
|
|
194
|
-
description: input.description,
|
|
195
|
-
priority: input.priority ? PRIORITY_TO_LINEAR[input.priority] : undefined,
|
|
196
|
-
projectId: input.projectId || this.config?.defaultProjectId,
|
|
197
|
-
assigneeId: input.assigneeId,
|
|
198
|
-
labelIds: input.labels ? await this.resolveLabelIds(teamId, input.labels) : undefined,
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
const createdIssue = await payload.issue
|
|
202
|
-
if (!createdIssue) {
|
|
203
|
-
throw new Error('Failed to create issue')
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return this.mapIssue(createdIssue)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Update an issue
|
|
211
|
-
*/
|
|
212
|
-
async updateIssue(id: string, input: UpdateIssueInput): Promise<Issue> {
|
|
213
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
214
|
-
|
|
215
|
-
// Get the issue first to get UUID (if identifier like PRJ-123 was passed)
|
|
216
|
-
const issue = await this.fetchIssue(id)
|
|
217
|
-
if (!issue) {
|
|
218
|
-
throw new Error(`Issue ${id} not found`)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Build update payload with all supported fields
|
|
222
|
-
const updatePayload: Record<string, unknown> = {}
|
|
223
|
-
|
|
224
|
-
if (input.title !== undefined) updatePayload.title = input.title
|
|
225
|
-
if (input.description !== undefined) updatePayload.description = input.description
|
|
226
|
-
if (input.priority !== undefined) updatePayload.priority = PRIORITY_TO_LINEAR[input.priority]
|
|
227
|
-
if (input.assigneeId !== undefined) updatePayload.assigneeId = input.assigneeId
|
|
228
|
-
if (input.stateId !== undefined) updatePayload.stateId = input.stateId
|
|
229
|
-
if (input.projectId !== undefined) updatePayload.projectId = input.projectId
|
|
230
|
-
|
|
231
|
-
// Handle labels - need to resolve names to IDs
|
|
232
|
-
if (input.labels !== undefined && issue.team) {
|
|
233
|
-
updatePayload.labelIds = await this.resolveLabelIds(issue.team.id, input.labels)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
await this.sdk.updateIssue(issue.id, updatePayload)
|
|
237
|
-
|
|
238
|
-
// Fetch updated issue
|
|
239
|
-
const updated = await this.fetchIssue(issue.id)
|
|
240
|
-
if (!updated) {
|
|
241
|
-
throw new Error('Failed to fetch updated issue')
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return updated
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Mark issue as in progress
|
|
249
|
-
*/
|
|
250
|
-
async markInProgress(id: string): Promise<void> {
|
|
251
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
252
|
-
|
|
253
|
-
const issue = await this.fetchIssue(id)
|
|
254
|
-
if (!issue) throw new Error(`Issue ${id} not found`)
|
|
255
|
-
|
|
256
|
-
// Find "started" state for the team
|
|
257
|
-
const linearIssue = await this.sdk.issue(issue.id)
|
|
258
|
-
const team = await linearIssue.team
|
|
259
|
-
if (!team) throw new Error('Issue has no team')
|
|
260
|
-
|
|
261
|
-
const states = await team.states()
|
|
262
|
-
const startedState = states.nodes.find((s) => s.type === 'started')
|
|
263
|
-
|
|
264
|
-
if (startedState) {
|
|
265
|
-
await this.sdk.updateIssue(issue.id, { stateId: startedState.id })
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Mark issue as done
|
|
271
|
-
*/
|
|
272
|
-
async markDone(id: string): Promise<void> {
|
|
273
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
274
|
-
|
|
275
|
-
const issue = await this.fetchIssue(id)
|
|
276
|
-
if (!issue) throw new Error(`Issue ${id} not found`)
|
|
277
|
-
|
|
278
|
-
// Find "completed" state for the team
|
|
279
|
-
const linearIssue = await this.sdk.issue(issue.id)
|
|
280
|
-
const team = await linearIssue.team
|
|
281
|
-
if (!team) throw new Error('Issue has no team')
|
|
282
|
-
|
|
283
|
-
const states = await team.states()
|
|
284
|
-
const doneState = states.nodes.find((s) => s.type === 'completed')
|
|
285
|
-
|
|
286
|
-
if (doneState) {
|
|
287
|
-
await this.sdk.updateIssue(issue.id, { stateId: doneState.id })
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Add a comment to an issue
|
|
293
|
-
*/
|
|
294
|
-
async addComment(id: string, body: string): Promise<void> {
|
|
295
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
296
|
-
|
|
297
|
-
const issue = await this.fetchIssue(id)
|
|
298
|
-
if (!issue) throw new Error(`Issue ${id} not found`)
|
|
299
|
-
|
|
300
|
-
await this.sdk.createComment({
|
|
301
|
-
issueId: issue.id,
|
|
302
|
-
body,
|
|
303
|
-
})
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Get available teams
|
|
308
|
-
*/
|
|
309
|
-
async getTeams(): Promise<Array<{ id: string; name: string; key?: string }>> {
|
|
310
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
311
|
-
|
|
312
|
-
const teams = await this.sdk.teams({ first: 50 })
|
|
313
|
-
return teams.nodes.map((team) => ({
|
|
314
|
-
id: team.id,
|
|
315
|
-
name: team.name,
|
|
316
|
-
key: team.key,
|
|
317
|
-
}))
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Get available projects
|
|
322
|
-
*/
|
|
323
|
-
async getProjects(): Promise<Array<{ id: string; name: string }>> {
|
|
324
|
-
if (!this.sdk) throw new Error('Linear not initialized')
|
|
325
|
-
|
|
326
|
-
const projects = await this.sdk.projects({ first: 50 })
|
|
327
|
-
return projects.nodes.map((project) => ({
|
|
328
|
-
id: project.id,
|
|
329
|
-
name: project.name,
|
|
330
|
-
}))
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// =============================================================================
|
|
334
|
-
// Private Helpers
|
|
335
|
-
// =============================================================================
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Map Linear issue to normalized Issue
|
|
339
|
-
*/
|
|
340
|
-
private async mapIssue(linearIssue: Awaited<ReturnType<LinearSDK['issue']>>): Promise<Issue> {
|
|
341
|
-
const state = await linearIssue.state
|
|
342
|
-
const assignee = await linearIssue.assignee
|
|
343
|
-
const team = await linearIssue.team
|
|
344
|
-
const project = await linearIssue.project
|
|
345
|
-
const labels = await linearIssue.labels()
|
|
346
|
-
|
|
347
|
-
return {
|
|
348
|
-
id: linearIssue.id,
|
|
349
|
-
externalId: linearIssue.identifier,
|
|
350
|
-
provider: 'linear',
|
|
351
|
-
title: linearIssue.title,
|
|
352
|
-
description: linearIssue.description || undefined,
|
|
353
|
-
status: LINEAR_STATUS_MAP[state?.type || 'backlog'] || 'backlog',
|
|
354
|
-
priority: LINEAR_PRIORITY_MAP[linearIssue.priority] || 'none',
|
|
355
|
-
type: this.inferType(
|
|
356
|
-
linearIssue.title,
|
|
357
|
-
labels.nodes.map((l) => l.name)
|
|
358
|
-
),
|
|
359
|
-
assignee: assignee
|
|
360
|
-
? {
|
|
361
|
-
id: assignee.id,
|
|
362
|
-
name: assignee.name,
|
|
363
|
-
email: assignee.email,
|
|
364
|
-
}
|
|
365
|
-
: undefined,
|
|
366
|
-
labels: labels.nodes.map((l) => l.name),
|
|
367
|
-
team: team
|
|
368
|
-
? {
|
|
369
|
-
id: team.id,
|
|
370
|
-
name: team.name,
|
|
371
|
-
key: team.key,
|
|
372
|
-
}
|
|
373
|
-
: undefined,
|
|
374
|
-
project: project
|
|
375
|
-
? {
|
|
376
|
-
id: project.id,
|
|
377
|
-
name: project.name,
|
|
378
|
-
}
|
|
379
|
-
: undefined,
|
|
380
|
-
url: linearIssue.url,
|
|
381
|
-
createdAt: linearIssue.createdAt.toISOString(),
|
|
382
|
-
updatedAt: linearIssue.updatedAt.toISOString(),
|
|
383
|
-
raw: linearIssue,
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Infer issue type from title and labels
|
|
389
|
-
*/
|
|
390
|
-
private inferType(title: string, labels: string[]): IssueType {
|
|
391
|
-
const titleLower = title.toLowerCase()
|
|
392
|
-
const labelsLower = labels.map((l) => l.toLowerCase())
|
|
393
|
-
|
|
394
|
-
if (labelsLower.includes('bug') || titleLower.includes('fix') || titleLower.includes('bug')) {
|
|
395
|
-
return 'bug'
|
|
396
|
-
}
|
|
397
|
-
if (
|
|
398
|
-
labelsLower.includes('feature') ||
|
|
399
|
-
titleLower.includes('add') ||
|
|
400
|
-
titleLower.includes('implement')
|
|
401
|
-
) {
|
|
402
|
-
return 'feature'
|
|
403
|
-
}
|
|
404
|
-
if (
|
|
405
|
-
labelsLower.includes('improvement') ||
|
|
406
|
-
titleLower.includes('improve') ||
|
|
407
|
-
titleLower.includes('enhance')
|
|
408
|
-
) {
|
|
409
|
-
return 'improvement'
|
|
410
|
-
}
|
|
411
|
-
if (
|
|
412
|
-
labelsLower.includes('chore') ||
|
|
413
|
-
titleLower.includes('chore') ||
|
|
414
|
-
titleLower.includes('deps')
|
|
415
|
-
) {
|
|
416
|
-
return 'chore'
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return 'task'
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Resolve label names to IDs
|
|
424
|
-
*/
|
|
425
|
-
private async resolveLabelIds(teamId: string, labelNames: string[]): Promise<string[]> {
|
|
426
|
-
if (!this.sdk) return []
|
|
427
|
-
|
|
428
|
-
const team = await this.sdk.team(teamId)
|
|
429
|
-
const labels = await team.labels()
|
|
430
|
-
|
|
431
|
-
return labels.nodes.filter((label) => labelNames.includes(label.name)).map((label) => label.id)
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Singleton instance
|
|
436
|
-
export const linearProvider = new LinearProvider()
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linear Integration
|
|
3
|
-
* Issue tracker provider for Linear using @linear/sdk
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Cache utilities
|
|
7
|
-
export {
|
|
8
|
-
assignedIssuesCache,
|
|
9
|
-
clearLinearCache,
|
|
10
|
-
getLinearCacheStats,
|
|
11
|
-
issueCache,
|
|
12
|
-
projectsCache,
|
|
13
|
-
teamsCache,
|
|
14
|
-
} from './cache'
|
|
15
|
-
// Core provider
|
|
16
|
-
export { LinearProvider, linearProvider } from './client'
|
|
17
|
-
// Service layer with caching (preferred API)
|
|
18
|
-
export { LinearService, linearService } from './service'
|
|
19
|
-
// Sync layer for bidirectional sync with issues.json
|
|
20
|
-
export { LinearSync, linearSync } from './sync'
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linear Service Layer
|
|
3
|
-
* Wraps LinearProvider with caching for improved performance.
|
|
4
|
-
* All operations are cached with 5-minute TTL.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
CreateIssueInput,
|
|
9
|
-
FetchOptions,
|
|
10
|
-
Issue,
|
|
11
|
-
LinearConfig,
|
|
12
|
-
UpdateIssueInput,
|
|
13
|
-
} from '../issue-tracker/types'
|
|
14
|
-
import {
|
|
15
|
-
assignedIssuesCache,
|
|
16
|
-
clearLinearCache,
|
|
17
|
-
getLinearCacheStats,
|
|
18
|
-
issueCache,
|
|
19
|
-
projectsCache,
|
|
20
|
-
teamsCache,
|
|
21
|
-
} from './cache'
|
|
22
|
-
import { linearProvider } from './client'
|
|
23
|
-
|
|
24
|
-
export class LinearService {
|
|
25
|
-
private initialized = false
|
|
26
|
-
private userId: string | null = null
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Check if service is ready
|
|
30
|
-
*/
|
|
31
|
-
isReady(): boolean {
|
|
32
|
-
return this.initialized && linearProvider.isConfigured()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize the service with config
|
|
37
|
-
* Must be called before any operations
|
|
38
|
-
*/
|
|
39
|
-
async initialize(config: LinearConfig): Promise<void> {
|
|
40
|
-
if (this.initialized) return
|
|
41
|
-
|
|
42
|
-
await linearProvider.initialize(config)
|
|
43
|
-
this.initialized = true
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Initialize from API key directly
|
|
48
|
-
* Convenience method for simple setup
|
|
49
|
-
*/
|
|
50
|
-
async initializeFromApiKey(apiKey: string, teamId?: string): Promise<void> {
|
|
51
|
-
const config: LinearConfig = {
|
|
52
|
-
enabled: true,
|
|
53
|
-
provider: 'linear',
|
|
54
|
-
apiKey,
|
|
55
|
-
defaultTeamId: teamId,
|
|
56
|
-
syncOn: { task: true, done: true, ship: true },
|
|
57
|
-
enrichment: { enabled: true, updateProvider: true },
|
|
58
|
-
}
|
|
59
|
-
await this.initialize(config)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get issues assigned to current user (cached)
|
|
64
|
-
*/
|
|
65
|
-
async fetchAssignedIssues(options?: FetchOptions): Promise<Issue[]> {
|
|
66
|
-
this.ensureInitialized()
|
|
67
|
-
|
|
68
|
-
const cacheKey = `assigned:${this.userId || 'me'}`
|
|
69
|
-
const cached = assignedIssuesCache.get(cacheKey)
|
|
70
|
-
if (cached) {
|
|
71
|
-
return cached
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const issues = await linearProvider.fetchAssignedIssues(options)
|
|
75
|
-
assignedIssuesCache.set(cacheKey, issues)
|
|
76
|
-
|
|
77
|
-
// Also cache individual issues
|
|
78
|
-
for (const issue of issues) {
|
|
79
|
-
issueCache.set(`issue:${issue.id}`, issue)
|
|
80
|
-
issueCache.set(`issue:${issue.externalId}`, issue)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return issues
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get issues from a team (cached)
|
|
88
|
-
*/
|
|
89
|
-
async fetchTeamIssues(teamId: string, options?: FetchOptions): Promise<Issue[]> {
|
|
90
|
-
this.ensureInitialized()
|
|
91
|
-
|
|
92
|
-
const cacheKey = `team:${teamId}`
|
|
93
|
-
const cached = assignedIssuesCache.get(cacheKey)
|
|
94
|
-
if (cached) {
|
|
95
|
-
return cached
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const issues = await linearProvider.fetchTeamIssues(teamId, options)
|
|
99
|
-
assignedIssuesCache.set(cacheKey, issues)
|
|
100
|
-
|
|
101
|
-
// Also cache individual issues
|
|
102
|
-
for (const issue of issues) {
|
|
103
|
-
issueCache.set(`issue:${issue.id}`, issue)
|
|
104
|
-
issueCache.set(`issue:${issue.externalId}`, issue)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return issues
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get a single issue by ID or identifier (cached)
|
|
112
|
-
* Accepts UUID or identifier like "PRJ-123"
|
|
113
|
-
*/
|
|
114
|
-
async fetchIssue(id: string): Promise<Issue | null> {
|
|
115
|
-
this.ensureInitialized()
|
|
116
|
-
|
|
117
|
-
// Check cache first
|
|
118
|
-
const cacheKey = `issue:${id}`
|
|
119
|
-
const cached = issueCache.get(cacheKey)
|
|
120
|
-
if (cached) {
|
|
121
|
-
return cached
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const issue = await linearProvider.fetchIssue(id)
|
|
125
|
-
if (issue) {
|
|
126
|
-
// Cache by both ID and externalId
|
|
127
|
-
issueCache.set(`issue:${issue.id}`, issue)
|
|
128
|
-
issueCache.set(`issue:${issue.externalId}`, issue)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return issue
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Create a new issue (invalidates assigned cache)
|
|
136
|
-
*/
|
|
137
|
-
async createIssue(input: CreateIssueInput): Promise<Issue> {
|
|
138
|
-
this.ensureInitialized()
|
|
139
|
-
|
|
140
|
-
const issue = await linearProvider.createIssue(input)
|
|
141
|
-
|
|
142
|
-
// Cache the new issue
|
|
143
|
-
issueCache.set(`issue:${issue.id}`, issue)
|
|
144
|
-
issueCache.set(`issue:${issue.externalId}`, issue)
|
|
145
|
-
|
|
146
|
-
// Invalidate assigned issues cache (new issue may be assigned)
|
|
147
|
-
assignedIssuesCache.clear()
|
|
148
|
-
|
|
149
|
-
return issue
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Update an issue (invalidates cache for that issue)
|
|
154
|
-
*/
|
|
155
|
-
async updateIssue(id: string, input: UpdateIssueInput): Promise<Issue> {
|
|
156
|
-
this.ensureInitialized()
|
|
157
|
-
|
|
158
|
-
const issue = await linearProvider.updateIssue(id, input)
|
|
159
|
-
|
|
160
|
-
// Update cache
|
|
161
|
-
issueCache.set(`issue:${issue.id}`, issue)
|
|
162
|
-
issueCache.set(`issue:${issue.externalId}`, issue)
|
|
163
|
-
|
|
164
|
-
return issue
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Mark issue as in progress (invalidates cache)
|
|
169
|
-
*/
|
|
170
|
-
async markInProgress(id: string): Promise<void> {
|
|
171
|
-
this.ensureInitialized()
|
|
172
|
-
|
|
173
|
-
await linearProvider.markInProgress(id)
|
|
174
|
-
|
|
175
|
-
// Invalidate caches
|
|
176
|
-
issueCache.delete(`issue:${id}`)
|
|
177
|
-
assignedIssuesCache.clear()
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Mark issue as done (invalidates cache)
|
|
182
|
-
*/
|
|
183
|
-
async markDone(id: string): Promise<void> {
|
|
184
|
-
this.ensureInitialized()
|
|
185
|
-
|
|
186
|
-
await linearProvider.markDone(id)
|
|
187
|
-
|
|
188
|
-
// Invalidate caches
|
|
189
|
-
issueCache.delete(`issue:${id}`)
|
|
190
|
-
assignedIssuesCache.clear()
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Add a comment to an issue
|
|
195
|
-
*/
|
|
196
|
-
async addComment(id: string, body: string): Promise<void> {
|
|
197
|
-
this.ensureInitialized()
|
|
198
|
-
await linearProvider.addComment(id, body)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Get available teams (cached)
|
|
203
|
-
*/
|
|
204
|
-
async getTeams(): Promise<Array<{ id: string; name: string; key?: string }>> {
|
|
205
|
-
this.ensureInitialized()
|
|
206
|
-
|
|
207
|
-
const cached = teamsCache.get('teams')
|
|
208
|
-
if (cached) {
|
|
209
|
-
return cached
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const teams = await linearProvider.getTeams()
|
|
213
|
-
teamsCache.set('teams', teams)
|
|
214
|
-
return teams
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Get available projects (cached)
|
|
219
|
-
*/
|
|
220
|
-
async getProjects(): Promise<Array<{ id: string; name: string }>> {
|
|
221
|
-
this.ensureInitialized()
|
|
222
|
-
|
|
223
|
-
const cached = projectsCache.get('projects')
|
|
224
|
-
if (cached) {
|
|
225
|
-
return cached
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const projects = await linearProvider.getProjects()
|
|
229
|
-
projectsCache.set('projects', projects)
|
|
230
|
-
return projects
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Clear all caches
|
|
235
|
-
*/
|
|
236
|
-
clearCache(): void {
|
|
237
|
-
clearLinearCache()
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get cache statistics for debugging
|
|
242
|
-
*/
|
|
243
|
-
getCacheStats() {
|
|
244
|
-
return getLinearCacheStats()
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Ensure service is initialized
|
|
249
|
-
*/
|
|
250
|
-
private ensureInitialized(): void {
|
|
251
|
-
if (!this.initialized) {
|
|
252
|
-
throw new Error(
|
|
253
|
-
'Linear service not initialized. Call linearService.initialize() first or run `p. linear setup`.'
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Singleton instance
|
|
260
|
-
export const linearService = new LinearService()
|