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,688 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JIRA Client
|
|
3
|
-
* Implements IssueTrackerProvider for Atlassian JIRA using REST API v3
|
|
4
|
-
*
|
|
5
|
-
* Authentication Methods:
|
|
6
|
-
* 1. API Token (default): Direct REST API calls
|
|
7
|
-
* - JIRA_BASE_URL: Your JIRA instance (e.g., https://company.atlassian.net)
|
|
8
|
-
* - JIRA_EMAIL: Your Atlassian account email
|
|
9
|
-
* - JIRA_API_TOKEN: API token from https://id.atlassian.com/manage-profile/security/api-tokens
|
|
10
|
-
*
|
|
11
|
-
* 2. MCP Mode (for corporate SSO): Uses Atlassian's official MCP Server
|
|
12
|
-
* - No API token needed
|
|
13
|
-
* - Authenticates via browser (OAuth 2.1, SSO compatible)
|
|
14
|
-
* - Requires MCP server configured in ~/.claude/mcp.json
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { getErrorMessage } from '../../types/fs'
|
|
18
|
-
import type { JiraAuthMode, JiraIssue, JiraProject, JiraSearchResponse } from '../../types/jira'
|
|
19
|
-
import type {
|
|
20
|
-
CreateIssueInput,
|
|
21
|
-
FetchOptions,
|
|
22
|
-
Issue,
|
|
23
|
-
IssuePriority,
|
|
24
|
-
IssueStatus,
|
|
25
|
-
IssueTrackerProvider,
|
|
26
|
-
IssueType,
|
|
27
|
-
JiraConfig,
|
|
28
|
-
UpdateIssueInput,
|
|
29
|
-
} from '../issue-tracker/types'
|
|
30
|
-
|
|
31
|
-
// =============================================================================
|
|
32
|
-
// Status/Priority Mapping
|
|
33
|
-
// =============================================================================
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Map JIRA status categories to normalized status
|
|
37
|
-
* JIRA uses statusCategory.key: 'new', 'indeterminate', 'done'
|
|
38
|
-
*/
|
|
39
|
-
const JIRA_STATUS_CATEGORY_MAP: Record<string, IssueStatus> = {
|
|
40
|
-
new: 'todo',
|
|
41
|
-
indeterminate: 'in_progress',
|
|
42
|
-
done: 'done',
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Common JIRA status names to normalized status
|
|
47
|
-
*/
|
|
48
|
-
const JIRA_STATUS_NAME_MAP: Record<string, IssueStatus> = {
|
|
49
|
-
// Backlog states
|
|
50
|
-
backlog: 'backlog',
|
|
51
|
-
open: 'backlog',
|
|
52
|
-
'to do': 'todo',
|
|
53
|
-
todo: 'todo',
|
|
54
|
-
new: 'todo',
|
|
55
|
-
|
|
56
|
-
// In Progress states
|
|
57
|
-
'in progress': 'in_progress',
|
|
58
|
-
'in development': 'in_progress',
|
|
59
|
-
'in review': 'in_review',
|
|
60
|
-
'code review': 'in_review',
|
|
61
|
-
review: 'in_review',
|
|
62
|
-
|
|
63
|
-
// Done states
|
|
64
|
-
done: 'done',
|
|
65
|
-
closed: 'done',
|
|
66
|
-
resolved: 'done',
|
|
67
|
-
complete: 'done',
|
|
68
|
-
completed: 'done',
|
|
69
|
-
|
|
70
|
-
// Cancelled states
|
|
71
|
-
cancelled: 'cancelled',
|
|
72
|
-
canceled: 'cancelled',
|
|
73
|
-
"won't do": 'cancelled',
|
|
74
|
-
'wont do': 'cancelled',
|
|
75
|
-
rejected: 'cancelled',
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* JIRA priorities: 1 = Highest, 5 = Lowest
|
|
80
|
-
*/
|
|
81
|
-
const JIRA_PRIORITY_MAP: Record<string, IssuePriority> = {
|
|
82
|
-
highest: 'urgent',
|
|
83
|
-
high: 'high',
|
|
84
|
-
medium: 'medium',
|
|
85
|
-
low: 'low',
|
|
86
|
-
lowest: 'low',
|
|
87
|
-
// Numeric fallbacks
|
|
88
|
-
'1': 'urgent',
|
|
89
|
-
'2': 'high',
|
|
90
|
-
'3': 'medium',
|
|
91
|
-
'4': 'low',
|
|
92
|
-
'5': 'low',
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const PRIORITY_TO_JIRA: Record<IssuePriority, string> = {
|
|
96
|
-
urgent: 'Highest',
|
|
97
|
-
high: 'High',
|
|
98
|
-
medium: 'Medium',
|
|
99
|
-
low: 'Low',
|
|
100
|
-
none: 'Medium',
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export type { JiraAuthMode } from '../../types/jira'
|
|
104
|
-
|
|
105
|
-
// =============================================================================
|
|
106
|
-
// JIRA Provider Implementation
|
|
107
|
-
// =============================================================================
|
|
108
|
-
|
|
109
|
-
export class JiraProvider implements IssueTrackerProvider {
|
|
110
|
-
readonly name = 'jira' as const
|
|
111
|
-
readonly displayName = 'JIRA'
|
|
112
|
-
|
|
113
|
-
private baseUrl: string = ''
|
|
114
|
-
private auth: string = ''
|
|
115
|
-
private config: JiraConfig | null = null
|
|
116
|
-
private currentUser: { accountId: string; displayName: string; email?: string } | null = null
|
|
117
|
-
private _authMode: JiraAuthMode = 'none'
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Get current authentication mode
|
|
121
|
-
*/
|
|
122
|
-
get authMode(): JiraAuthMode {
|
|
123
|
-
return this._authMode
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Check if using MCP mode (no direct API access)
|
|
128
|
-
*/
|
|
129
|
-
isMCPMode(): boolean {
|
|
130
|
-
return this._authMode === 'mcp'
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Check if provider is configured
|
|
135
|
-
* Returns true for both API token mode (ready to make calls)
|
|
136
|
-
* and MCP mode (ready to generate instructions)
|
|
137
|
-
*/
|
|
138
|
-
isConfigured(): boolean {
|
|
139
|
-
if (this._authMode === 'mcp') {
|
|
140
|
-
return this.config?.enabled === true
|
|
141
|
-
}
|
|
142
|
-
return this.baseUrl !== '' && this.auth !== '' && this.config?.enabled === true
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Initialize with config
|
|
147
|
-
* Supports two modes:
|
|
148
|
-
* 1. API Token mode: Direct REST API access (requires JIRA_API_TOKEN)
|
|
149
|
-
* 2. MCP mode: Via Atlassian MCP Server (for corporate SSO)
|
|
150
|
-
*/
|
|
151
|
-
async initialize(config: JiraConfig): Promise<void> {
|
|
152
|
-
this.config = config
|
|
153
|
-
|
|
154
|
-
// JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN: JIRA API credentials (fallback from config)
|
|
155
|
-
const baseUrl = config.baseUrl || process.env.JIRA_BASE_URL
|
|
156
|
-
const email = process.env.JIRA_EMAIL
|
|
157
|
-
const apiToken = config.apiKey || process.env.JIRA_API_TOKEN
|
|
158
|
-
|
|
159
|
-
// Check if we have API token credentials
|
|
160
|
-
const hasApiCredentials = baseUrl && email && apiToken
|
|
161
|
-
|
|
162
|
-
if (hasApiCredentials) {
|
|
163
|
-
// API Token mode - direct REST API access
|
|
164
|
-
this._authMode = 'api-token'
|
|
165
|
-
|
|
166
|
-
// Normalize base URL (remove trailing slash)
|
|
167
|
-
this.baseUrl = baseUrl!.replace(/\/$/, '')
|
|
168
|
-
|
|
169
|
-
// Create Basic Auth header
|
|
170
|
-
this.auth = Buffer.from(`${email}:${apiToken}`).toString('base64')
|
|
171
|
-
|
|
172
|
-
// Verify connection by fetching current user
|
|
173
|
-
try {
|
|
174
|
-
const response = await this.request<{
|
|
175
|
-
accountId: string
|
|
176
|
-
displayName: string
|
|
177
|
-
emailAddress?: string
|
|
178
|
-
}>('/rest/api/3/myself')
|
|
179
|
-
this.currentUser = {
|
|
180
|
-
accountId: response.accountId,
|
|
181
|
-
displayName: response.displayName,
|
|
182
|
-
email: response.emailAddress,
|
|
183
|
-
}
|
|
184
|
-
console.log(
|
|
185
|
-
`[jira] Connected as ${this.currentUser.displayName} (${this.currentUser.email || 'no email'})`
|
|
186
|
-
)
|
|
187
|
-
} catch (error) {
|
|
188
|
-
this.baseUrl = ''
|
|
189
|
-
this.auth = ''
|
|
190
|
-
this._authMode = 'none'
|
|
191
|
-
throw new Error(`JIRA connection failed: ${getErrorMessage(error)}`)
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
// MCP mode - no direct API access, Claude uses MCP tools
|
|
195
|
-
this._authMode = 'mcp'
|
|
196
|
-
|
|
197
|
-
// Store base URL if provided (for generating issue URLs)
|
|
198
|
-
if (baseUrl) {
|
|
199
|
-
this.baseUrl = baseUrl.replace(/\/$/, '')
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
console.log('[jira] Initialized in MCP mode (no API token)')
|
|
203
|
-
console.log('[jira] Claude will use Atlassian MCP tools for JIRA operations')
|
|
204
|
-
|
|
205
|
-
// Log what's missing if user might want API mode
|
|
206
|
-
if (!apiToken) {
|
|
207
|
-
console.log('[jira] Tip: Set JIRA_API_TOKEN for direct API access')
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Get auth mode information for templates
|
|
214
|
-
*/
|
|
215
|
-
getAuthInfo(): { mode: JiraAuthMode; baseUrl: string; user?: string } {
|
|
216
|
-
return {
|
|
217
|
-
mode: this._authMode,
|
|
218
|
-
baseUrl: this.baseUrl,
|
|
219
|
-
user: this.currentUser?.displayName,
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Get issues assigned to current user
|
|
225
|
-
*/
|
|
226
|
-
async fetchAssignedIssues(options?: FetchOptions): Promise<Issue[]> {
|
|
227
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
228
|
-
|
|
229
|
-
const maxResults = options?.limit || 50
|
|
230
|
-
const jql = options?.includeCompleted
|
|
231
|
-
? 'assignee = currentUser() ORDER BY updated DESC'
|
|
232
|
-
: 'assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC'
|
|
233
|
-
|
|
234
|
-
const response = await this.request<JiraSearchResponse>(
|
|
235
|
-
`/rest/api/3/search?jql=${encodeURIComponent(jql)}&maxResults=${maxResults}&fields=*all`
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
return response.issues.map((issue) => this.mapIssue(issue))
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Get issues from a team/project
|
|
243
|
-
*/
|
|
244
|
-
async fetchTeamIssues(projectKey: string, options?: FetchOptions): Promise<Issue[]> {
|
|
245
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
246
|
-
|
|
247
|
-
const maxResults = options?.limit || 50
|
|
248
|
-
const jql = options?.includeCompleted
|
|
249
|
-
? `project = ${projectKey} ORDER BY updated DESC`
|
|
250
|
-
: `project = ${projectKey} AND statusCategory != Done ORDER BY updated DESC`
|
|
251
|
-
|
|
252
|
-
const response = await this.request<JiraSearchResponse>(
|
|
253
|
-
`/rest/api/3/search?jql=${encodeURIComponent(jql)}&maxResults=${maxResults}&fields=*all`
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
return response.issues.map((issue) => this.mapIssue(issue))
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Get a single issue by key (e.g., "ENG-123") or ID
|
|
261
|
-
*/
|
|
262
|
-
async fetchIssue(id: string): Promise<Issue | null> {
|
|
263
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
const issue = await this.request<JiraIssue>(`/rest/api/3/issue/${id}?fields=*all`)
|
|
267
|
-
return this.mapIssue(issue)
|
|
268
|
-
} catch (error) {
|
|
269
|
-
// Issue not found
|
|
270
|
-
if (getErrorMessage(error).includes('404')) {
|
|
271
|
-
return null
|
|
272
|
-
}
|
|
273
|
-
throw error
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Create a new issue
|
|
279
|
-
*/
|
|
280
|
-
async createIssue(input: CreateIssueInput): Promise<Issue> {
|
|
281
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
282
|
-
|
|
283
|
-
const projectKey = input.teamId || this.config?.projectKey || this.config?.defaultTeamId
|
|
284
|
-
if (!projectKey) {
|
|
285
|
-
throw new Error('Project key required for creating issues')
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Build issue payload
|
|
289
|
-
const payload: Record<string, unknown> = {
|
|
290
|
-
fields: {
|
|
291
|
-
project: { key: projectKey },
|
|
292
|
-
summary: input.title,
|
|
293
|
-
issuetype: { name: this.mapTypeToJira(input.type) },
|
|
294
|
-
},
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Add optional fields
|
|
298
|
-
if (input.description) {
|
|
299
|
-
;(payload.fields as Record<string, unknown>).description = {
|
|
300
|
-
type: 'doc',
|
|
301
|
-
version: 1,
|
|
302
|
-
content: [
|
|
303
|
-
{
|
|
304
|
-
type: 'paragraph',
|
|
305
|
-
content: [{ type: 'text', text: input.description }],
|
|
306
|
-
},
|
|
307
|
-
],
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (input.priority) {
|
|
312
|
-
;(payload.fields as Record<string, unknown>).priority = {
|
|
313
|
-
name: PRIORITY_TO_JIRA[input.priority],
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (input.labels?.length) {
|
|
318
|
-
;(payload.fields as Record<string, unknown>).labels = input.labels
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (input.assigneeId) {
|
|
322
|
-
;(payload.fields as Record<string, unknown>).assignee = {
|
|
323
|
-
accountId: input.assigneeId,
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const created = await this.request<{ id: string; key: string }>('/rest/api/3/issue', {
|
|
328
|
-
method: 'POST',
|
|
329
|
-
body: JSON.stringify(payload),
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
// Fetch the full issue
|
|
333
|
-
const issue = await this.fetchIssue(created.key)
|
|
334
|
-
if (!issue) {
|
|
335
|
-
throw new Error('Failed to fetch created issue')
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return issue
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Update an issue (for enrichment - updates description)
|
|
343
|
-
*/
|
|
344
|
-
async updateIssue(id: string, input: UpdateIssueInput): Promise<Issue> {
|
|
345
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
346
|
-
|
|
347
|
-
const payload: Record<string, unknown> = { fields: {} }
|
|
348
|
-
|
|
349
|
-
if (input.description) {
|
|
350
|
-
;(payload.fields as Record<string, unknown>).description = {
|
|
351
|
-
type: 'doc',
|
|
352
|
-
version: 1,
|
|
353
|
-
content: this.markdownToADF(input.description),
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
await this.request(`/rest/api/3/issue/${id}`, {
|
|
358
|
-
method: 'PUT',
|
|
359
|
-
body: JSON.stringify(payload),
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
// Fetch updated issue
|
|
363
|
-
const updated = await this.fetchIssue(id)
|
|
364
|
-
if (!updated) {
|
|
365
|
-
throw new Error('Failed to fetch updated issue')
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return updated
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Mark issue as in progress
|
|
373
|
-
*/
|
|
374
|
-
async markInProgress(id: string): Promise<void> {
|
|
375
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
376
|
-
|
|
377
|
-
// Get available transitions
|
|
378
|
-
const transitions = await this.request<{
|
|
379
|
-
transitions: Array<{ id: string; name: string; to: { statusCategory: { key: string } } }>
|
|
380
|
-
}>(`/rest/api/3/issue/${id}/transitions`)
|
|
381
|
-
|
|
382
|
-
// Find transition to "in progress" state
|
|
383
|
-
const inProgressTransition = transitions.transitions.find(
|
|
384
|
-
(t) =>
|
|
385
|
-
t.to.statusCategory.key === 'indeterminate' ||
|
|
386
|
-
t.name.toLowerCase().includes('progress') ||
|
|
387
|
-
t.name.toLowerCase().includes('start')
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
if (inProgressTransition) {
|
|
391
|
-
await this.request(`/rest/api/3/issue/${id}/transitions`, {
|
|
392
|
-
method: 'POST',
|
|
393
|
-
body: JSON.stringify({ transition: { id: inProgressTransition.id } }),
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Mark issue as done
|
|
400
|
-
*/
|
|
401
|
-
async markDone(id: string): Promise<void> {
|
|
402
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
403
|
-
|
|
404
|
-
// Get available transitions
|
|
405
|
-
const transitions = await this.request<{
|
|
406
|
-
transitions: Array<{ id: string; name: string; to: { statusCategory: { key: string } } }>
|
|
407
|
-
}>(`/rest/api/3/issue/${id}/transitions`)
|
|
408
|
-
|
|
409
|
-
// Find transition to "done" state
|
|
410
|
-
const doneTransition = transitions.transitions.find(
|
|
411
|
-
(t) =>
|
|
412
|
-
t.to.statusCategory.key === 'done' ||
|
|
413
|
-
t.name.toLowerCase().includes('done') ||
|
|
414
|
-
t.name.toLowerCase().includes('complete') ||
|
|
415
|
-
t.name.toLowerCase().includes('resolve')
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
if (doneTransition) {
|
|
419
|
-
await this.request(`/rest/api/3/issue/${id}/transitions`, {
|
|
420
|
-
method: 'POST',
|
|
421
|
-
body: JSON.stringify({ transition: { id: doneTransition.id } }),
|
|
422
|
-
})
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Get available projects (teams in JIRA context)
|
|
428
|
-
*/
|
|
429
|
-
async getTeams(): Promise<Array<{ id: string; name: string; key?: string }>> {
|
|
430
|
-
if (!this.isConfigured()) throw new Error('JIRA not initialized')
|
|
431
|
-
|
|
432
|
-
const projects = await this.request<JiraProject[]>('/rest/api/3/project')
|
|
433
|
-
|
|
434
|
-
return projects.map((project) => ({
|
|
435
|
-
id: project.id,
|
|
436
|
-
name: project.name,
|
|
437
|
-
key: project.key,
|
|
438
|
-
}))
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Get available projects
|
|
443
|
-
*/
|
|
444
|
-
async getProjects(): Promise<Array<{ id: string; name: string }>> {
|
|
445
|
-
// In JIRA, teams = projects, so return same data
|
|
446
|
-
return this.getTeams()
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// =============================================================================
|
|
450
|
-
// Private Helpers
|
|
451
|
-
// =============================================================================
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Make authenticated request to JIRA API
|
|
455
|
-
*/
|
|
456
|
-
private async request<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
|
457
|
-
const url = `${this.baseUrl}${endpoint}`
|
|
458
|
-
|
|
459
|
-
const response = await fetch(url, {
|
|
460
|
-
...options,
|
|
461
|
-
headers: {
|
|
462
|
-
Authorization: `Basic ${this.auth}`,
|
|
463
|
-
'Content-Type': 'application/json',
|
|
464
|
-
Accept: 'application/json',
|
|
465
|
-
...options?.headers,
|
|
466
|
-
},
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
if (!response.ok) {
|
|
470
|
-
const errorText = await response.text()
|
|
471
|
-
throw new Error(`JIRA API error ${response.status}: ${errorText}`)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Handle empty responses (like successful PUT)
|
|
475
|
-
const text = await response.text()
|
|
476
|
-
if (!text) {
|
|
477
|
-
return {} as T
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
return JSON.parse(text) as T
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Map JIRA issue to normalized Issue
|
|
485
|
-
*/
|
|
486
|
-
private mapIssue(jiraIssue: JiraIssue): Issue {
|
|
487
|
-
const statusName = jiraIssue.fields.status.name.toLowerCase()
|
|
488
|
-
const statusCategory = jiraIssue.fields.status.statusCategory.key
|
|
489
|
-
|
|
490
|
-
// Try exact status name match first, then category
|
|
491
|
-
const status: IssueStatus =
|
|
492
|
-
JIRA_STATUS_NAME_MAP[statusName] || JIRA_STATUS_CATEGORY_MAP[statusCategory] || 'backlog'
|
|
493
|
-
|
|
494
|
-
const priorityName = jiraIssue.fields.priority?.name?.toLowerCase() || 'medium'
|
|
495
|
-
const priority: IssuePriority = JIRA_PRIORITY_MAP[priorityName] || 'medium'
|
|
496
|
-
|
|
497
|
-
return {
|
|
498
|
-
id: jiraIssue.id,
|
|
499
|
-
externalId: jiraIssue.key,
|
|
500
|
-
provider: 'jira',
|
|
501
|
-
title: jiraIssue.fields.summary,
|
|
502
|
-
description: this.extractDescription(jiraIssue.fields.description),
|
|
503
|
-
status,
|
|
504
|
-
priority,
|
|
505
|
-
type: this.inferType(jiraIssue.fields.issuetype.name, jiraIssue.fields.labels),
|
|
506
|
-
assignee: jiraIssue.fields.assignee
|
|
507
|
-
? {
|
|
508
|
-
id: jiraIssue.fields.assignee.accountId,
|
|
509
|
-
name: jiraIssue.fields.assignee.displayName,
|
|
510
|
-
email: jiraIssue.fields.assignee.emailAddress,
|
|
511
|
-
}
|
|
512
|
-
: undefined,
|
|
513
|
-
labels: jiraIssue.fields.labels || [],
|
|
514
|
-
team: {
|
|
515
|
-
id: jiraIssue.fields.project.id,
|
|
516
|
-
name: jiraIssue.fields.project.name,
|
|
517
|
-
key: jiraIssue.fields.project.key,
|
|
518
|
-
},
|
|
519
|
-
project: {
|
|
520
|
-
id: jiraIssue.fields.project.id,
|
|
521
|
-
name: jiraIssue.fields.project.name,
|
|
522
|
-
},
|
|
523
|
-
url: `${this.baseUrl}/browse/${jiraIssue.key}`,
|
|
524
|
-
createdAt: jiraIssue.fields.created,
|
|
525
|
-
updatedAt: jiraIssue.fields.updated,
|
|
526
|
-
raw: jiraIssue,
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* Extract plain text from JIRA description (ADF or string)
|
|
532
|
-
*/
|
|
533
|
-
private extractDescription(description: JiraIssue['fields']['description']): string | undefined {
|
|
534
|
-
if (!description) return undefined
|
|
535
|
-
|
|
536
|
-
// Handle string descriptions (older JIRA versions)
|
|
537
|
-
if (typeof description === 'string') {
|
|
538
|
-
return description
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Handle ADF (Atlassian Document Format)
|
|
542
|
-
try {
|
|
543
|
-
const texts: string[] = []
|
|
544
|
-
const extractText = (node: unknown): void => {
|
|
545
|
-
if (!node || typeof node !== 'object') return
|
|
546
|
-
const n = node as Record<string, unknown>
|
|
547
|
-
|
|
548
|
-
if (n.type === 'text' && typeof n.text === 'string') {
|
|
549
|
-
texts.push(n.text)
|
|
550
|
-
}
|
|
551
|
-
if (Array.isArray(n.content)) {
|
|
552
|
-
n.content.forEach(extractText)
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (Array.isArray(description.content)) {
|
|
557
|
-
description.content.forEach(extractText)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return texts.join('\n') || undefined
|
|
561
|
-
} catch (_error) {
|
|
562
|
-
return undefined
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Convert markdown to ADF (simplified)
|
|
568
|
-
*/
|
|
569
|
-
private markdownToADF(markdown: string): Array<Record<string, unknown>> {
|
|
570
|
-
const lines = markdown.split('\n')
|
|
571
|
-
const content: Array<Record<string, unknown>> = []
|
|
572
|
-
|
|
573
|
-
for (const line of lines) {
|
|
574
|
-
if (line.startsWith('## ')) {
|
|
575
|
-
// Heading 2
|
|
576
|
-
content.push({
|
|
577
|
-
type: 'heading',
|
|
578
|
-
attrs: { level: 2 },
|
|
579
|
-
content: [{ type: 'text', text: line.slice(3) }],
|
|
580
|
-
})
|
|
581
|
-
} else if (line.startsWith('### ')) {
|
|
582
|
-
// Heading 3
|
|
583
|
-
content.push({
|
|
584
|
-
type: 'heading',
|
|
585
|
-
attrs: { level: 3 },
|
|
586
|
-
content: [{ type: 'text', text: line.slice(4) }],
|
|
587
|
-
})
|
|
588
|
-
} else if (line.startsWith('- [ ] ')) {
|
|
589
|
-
// Checkbox unchecked
|
|
590
|
-
content.push({
|
|
591
|
-
type: 'taskList',
|
|
592
|
-
attrs: { localId: crypto.randomUUID() },
|
|
593
|
-
content: [
|
|
594
|
-
{
|
|
595
|
-
type: 'taskItem',
|
|
596
|
-
attrs: { localId: crypto.randomUUID(), state: 'TODO' },
|
|
597
|
-
content: [{ type: 'text', text: line.slice(6) }],
|
|
598
|
-
},
|
|
599
|
-
],
|
|
600
|
-
})
|
|
601
|
-
} else if (line.startsWith('- [x] ')) {
|
|
602
|
-
// Checkbox checked
|
|
603
|
-
content.push({
|
|
604
|
-
type: 'taskList',
|
|
605
|
-
attrs: { localId: crypto.randomUUID() },
|
|
606
|
-
content: [
|
|
607
|
-
{
|
|
608
|
-
type: 'taskItem',
|
|
609
|
-
attrs: { localId: crypto.randomUUID(), state: 'DONE' },
|
|
610
|
-
content: [{ type: 'text', text: line.slice(6) }],
|
|
611
|
-
},
|
|
612
|
-
],
|
|
613
|
-
})
|
|
614
|
-
} else if (line.startsWith('- ')) {
|
|
615
|
-
// Bullet point
|
|
616
|
-
content.push({
|
|
617
|
-
type: 'bulletList',
|
|
618
|
-
content: [
|
|
619
|
-
{
|
|
620
|
-
type: 'listItem',
|
|
621
|
-
content: [
|
|
622
|
-
{
|
|
623
|
-
type: 'paragraph',
|
|
624
|
-
content: [{ type: 'text', text: line.slice(2) }],
|
|
625
|
-
},
|
|
626
|
-
],
|
|
627
|
-
},
|
|
628
|
-
],
|
|
629
|
-
})
|
|
630
|
-
} else if (line.trim()) {
|
|
631
|
-
// Regular paragraph
|
|
632
|
-
content.push({
|
|
633
|
-
type: 'paragraph',
|
|
634
|
-
content: [{ type: 'text', text: line }],
|
|
635
|
-
})
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
return content
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Infer issue type from JIRA issue type and labels
|
|
644
|
-
*/
|
|
645
|
-
private inferType(issueTypeName: string, labels: string[]): IssueType {
|
|
646
|
-
const typeLower = issueTypeName.toLowerCase()
|
|
647
|
-
const labelsLower = labels.map((l) => l.toLowerCase())
|
|
648
|
-
|
|
649
|
-
if (typeLower === 'bug' || labelsLower.includes('bug')) {
|
|
650
|
-
return 'bug'
|
|
651
|
-
}
|
|
652
|
-
if (typeLower === 'story' || typeLower === 'feature' || labelsLower.includes('feature')) {
|
|
653
|
-
return 'feature'
|
|
654
|
-
}
|
|
655
|
-
if (typeLower === 'improvement' || labelsLower.includes('improvement')) {
|
|
656
|
-
return 'improvement'
|
|
657
|
-
}
|
|
658
|
-
if (typeLower === 'epic') {
|
|
659
|
-
return 'epic'
|
|
660
|
-
}
|
|
661
|
-
if (typeLower === 'sub-task' || typeLower === 'subtask') {
|
|
662
|
-
return 'task'
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
return 'task'
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Map prjct type to JIRA issue type name
|
|
670
|
-
*/
|
|
671
|
-
private mapTypeToJira(type?: IssueType): string {
|
|
672
|
-
switch (type) {
|
|
673
|
-
case 'bug':
|
|
674
|
-
return 'Bug'
|
|
675
|
-
case 'feature':
|
|
676
|
-
return 'Story'
|
|
677
|
-
case 'improvement':
|
|
678
|
-
return 'Improvement'
|
|
679
|
-
case 'epic':
|
|
680
|
-
return 'Epic'
|
|
681
|
-
default:
|
|
682
|
-
return 'Task'
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Singleton instance
|
|
688
|
-
export const jiraProvider = new JiraProvider()
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JIRA Integration
|
|
3
|
-
*
|
|
4
|
-
* Provides JIRA issue tracking integration for prjct-cli using REST API.
|
|
5
|
-
*
|
|
6
|
-
* Authentication (API Token Mode):
|
|
7
|
-
* - JIRA_BASE_URL: Your JIRA instance URL (e.g., https://company.atlassian.net)
|
|
8
|
-
* - JIRA_EMAIL: Your Atlassian account email
|
|
9
|
-
* - JIRA_API_TOKEN: API token from https://id.atlassian.com/manage-profile/security/api-tokens
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Cache utilities
|
|
13
|
-
export {
|
|
14
|
-
assignedIssuesCache,
|
|
15
|
-
clearJiraCache,
|
|
16
|
-
getJiraCacheStats,
|
|
17
|
-
issueCache,
|
|
18
|
-
projectsCache,
|
|
19
|
-
} from './cache'
|
|
20
|
-
// REST API client
|
|
21
|
-
export { type JiraAuthMode, JiraProvider, jiraProvider } from './client'
|
|
22
|
-
// Service layer with caching (preferred API)
|
|
23
|
-
export { JiraService, jiraService } from './service'
|