specweave 0.30.19 → 0.32.2

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.
Files changed (242) hide show
  1. package/CLAUDE.md +176 -2
  2. package/README.md +22 -0
  3. package/bin/specweave.js +18 -1
  4. package/dist/src/cli/commands/cache.d.ts +17 -0
  5. package/dist/src/cli/commands/cache.d.ts.map +1 -0
  6. package/dist/src/cli/commands/cache.js +126 -0
  7. package/dist/src/cli/commands/cache.js.map +1 -0
  8. package/dist/src/cli/commands/init.js +1 -1
  9. package/dist/src/cli/commands/init.js.map +1 -1
  10. package/dist/src/cli/commands/plan/increment-detector.js +2 -2
  11. package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
  12. package/dist/src/cli/commands/sync-spec-commits.js +1 -1
  13. package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
  14. package/dist/src/cli/commands/sync-specs.js +2 -2
  15. package/dist/src/cli/commands/sync-specs.js.map +1 -1
  16. package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
  17. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  18. package/dist/src/cli/workers/living-docs-worker.js +66 -1
  19. package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
  20. package/dist/src/config/types.d.ts +203 -1208
  21. package/dist/src/config/types.d.ts.map +1 -1
  22. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
  23. package/dist/src/core/discrepancy/increment-generator.js +5 -2
  24. package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
  25. package/dist/src/core/external-tools/external-items-counter.d.ts +62 -0
  26. package/dist/src/core/external-tools/external-items-counter.d.ts.map +1 -0
  27. package/dist/src/core/external-tools/external-items-counter.js +206 -0
  28. package/dist/src/core/external-tools/external-items-counter.js.map +1 -0
  29. package/dist/src/core/external-tools/external-items-display.d.ts +39 -0
  30. package/dist/src/core/external-tools/external-items-display.d.ts.map +1 -0
  31. package/dist/src/core/external-tools/external-items-display.js +185 -0
  32. package/dist/src/core/external-tools/external-items-display.js.map +1 -0
  33. package/dist/src/core/external-tools/index.d.ts +8 -0
  34. package/dist/src/core/external-tools/index.d.ts.map +1 -0
  35. package/dist/src/core/external-tools/index.js +8 -0
  36. package/dist/src/core/external-tools/index.js.map +1 -0
  37. package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts +39 -0
  38. package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts.map +1 -0
  39. package/dist/src/core/external-tools/providers/ado-items-adapter.js +188 -0
  40. package/dist/src/core/external-tools/providers/ado-items-adapter.js.map +1 -0
  41. package/dist/src/core/external-tools/providers/github-items-adapter.d.ts +38 -0
  42. package/dist/src/core/external-tools/providers/github-items-adapter.d.ts.map +1 -0
  43. package/dist/src/core/external-tools/providers/github-items-adapter.js +136 -0
  44. package/dist/src/core/external-tools/providers/github-items-adapter.js.map +1 -0
  45. package/dist/src/core/external-tools/providers/index.d.ts +7 -0
  46. package/dist/src/core/external-tools/providers/index.d.ts.map +1 -0
  47. package/dist/src/core/external-tools/providers/index.js +7 -0
  48. package/dist/src/core/external-tools/providers/index.js.map +1 -0
  49. package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts +42 -0
  50. package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts.map +1 -0
  51. package/dist/src/core/external-tools/providers/jira-items-adapter.js +153 -0
  52. package/dist/src/core/external-tools/providers/jira-items-adapter.js.map +1 -0
  53. package/dist/src/core/external-tools/types.d.ts +78 -0
  54. package/dist/src/core/external-tools/types.d.ts.map +1 -0
  55. package/dist/src/core/external-tools/types.js +19 -0
  56. package/dist/src/core/external-tools/types.js.map +1 -0
  57. package/dist/src/core/increment/duplicate-detector.js +2 -2
  58. package/dist/src/core/increment/duplicate-detector.js.map +1 -1
  59. package/dist/src/core/increment/increment-archiver.d.ts +24 -0
  60. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  61. package/dist/src/core/increment/increment-archiver.js +59 -2
  62. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  63. package/dist/src/core/increment/increment-status.js +2 -2
  64. package/dist/src/core/increment/increment-status.js.map +1 -1
  65. package/dist/src/core/increment/increment-utils.d.ts +98 -37
  66. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  67. package/dist/src/core/increment/increment-utils.js +119 -68
  68. package/dist/src/core/increment/increment-utils.js.map +1 -1
  69. package/dist/src/core/increment/metadata-validator.js +1 -1
  70. package/dist/src/core/increment/metadata-validator.js.map +1 -1
  71. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  72. package/dist/src/core/increment/status-change-sync-trigger.js +4 -0
  73. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  74. package/dist/src/core/living-docs/feature-id-manager.js +1 -1
  75. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  76. package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
  77. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  78. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
  79. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
  80. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +247 -0
  81. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
  82. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
  83. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
  84. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
  85. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
  86. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
  87. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
  88. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
  89. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
  90. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
  91. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
  92. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
  93. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
  94. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
  95. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
  96. package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
  97. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
  98. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +18 -0
  99. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
  100. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +154 -0
  101. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
  102. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
  103. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
  104. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
  105. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
  106. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +146 -0
  107. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
  108. package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
  109. package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
  110. package/dist/src/core/living-docs/living-docs-sync.d.ts +5 -0
  111. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  112. package/dist/src/core/living-docs/living-docs-sync.js +36 -2
  113. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  114. package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
  115. package/dist/src/core/llm/providers/azure-openai-provider.js +1 -0
  116. package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
  117. package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
  118. package/dist/src/core/llm/providers/bedrock-provider.js +2 -0
  119. package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
  120. package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
  121. package/dist/src/core/llm/providers/openai-provider.js +1 -0
  122. package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
  123. package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
  124. package/dist/src/core/llm/providers/vertex-ai-provider.js +1 -0
  125. package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
  126. package/dist/src/core/sync/spec-increment-mapper.js +3 -3
  127. package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
  128. package/dist/src/importers/item-converter.d.ts +25 -0
  129. package/dist/src/importers/item-converter.d.ts.map +1 -1
  130. package/dist/src/importers/item-converter.js +135 -5
  131. package/dist/src/importers/item-converter.js.map +1 -1
  132. package/dist/src/init/architecture/types.d.ts +33 -140
  133. package/dist/src/init/architecture/types.d.ts.map +1 -1
  134. package/dist/src/init/compliance/types.d.ts +30 -27
  135. package/dist/src/init/compliance/types.d.ts.map +1 -1
  136. package/dist/src/init/repo/types.d.ts +11 -34
  137. package/dist/src/init/repo/types.d.ts.map +1 -1
  138. package/dist/src/init/research/src/config/types.d.ts +15 -82
  139. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  140. package/dist/src/init/research/types.d.ts +38 -93
  141. package/dist/src/init/research/types.d.ts.map +1 -1
  142. package/dist/src/init/team/types.d.ts +4 -42
  143. package/dist/src/init/team/types.d.ts.map +1 -1
  144. package/dist/src/types/dashboard-cache.d.ts +181 -0
  145. package/dist/src/types/dashboard-cache.d.ts.map +1 -0
  146. package/dist/src/types/dashboard-cache.js +65 -0
  147. package/dist/src/types/dashboard-cache.js.map +1 -0
  148. package/dist/src/utils/docs-validator.d.ts +131 -0
  149. package/dist/src/utils/docs-validator.d.ts.map +1 -0
  150. package/dist/src/utils/docs-validator.js +529 -0
  151. package/dist/src/utils/docs-validator.js.map +1 -0
  152. package/dist/src/utils/feature-id-collision.js +1 -1
  153. package/dist/src/utils/feature-id-collision.js.map +1 -1
  154. package/dist/src/utils/html-to-mdx.d.ts +1 -0
  155. package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
  156. package/dist/src/utils/html-to-mdx.js +43 -5
  157. package/dist/src/utils/html-to-mdx.js.map +1 -1
  158. package/package.json +1 -5
  159. package/plugins/specweave/agents/pm/AGENT.md +10 -7
  160. package/plugins/specweave/commands/specweave-archive-features.md +5 -7
  161. package/plugins/specweave/commands/specweave-archive.md +2 -1
  162. package/plugins/specweave/commands/specweave-do.md +35 -1
  163. package/plugins/specweave/commands/specweave-done.md +96 -0
  164. package/plugins/specweave/commands/specweave-external.md +150 -0
  165. package/plugins/specweave/commands/specweave-import-external.md +45 -18
  166. package/plugins/specweave/commands/specweave-increment.md +331 -33
  167. package/plugins/specweave/commands/specweave-jobs.md +2 -2
  168. package/plugins/specweave/commands/specweave-progress.md +4 -4
  169. package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
  170. package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
  171. package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
  172. package/plugins/specweave/commands/specweave-validate-features.md +13 -8
  173. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  174. package/plugins/specweave/hooks/hooks.json +33 -4
  175. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  176. package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
  177. package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
  178. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  179. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  180. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  181. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  182. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  183. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  184. package/plugins/specweave/hooks/post-task-completion.sh +4 -23
  185. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  186. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
  187. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  188. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  189. package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
  190. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  191. package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
  192. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  193. package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
  194. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
  195. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  196. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  197. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  198. package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
  199. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  200. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
  201. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
  202. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
  203. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
  204. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
  205. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
  206. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
  207. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
  208. package/plugins/specweave/scripts/README.md +166 -0
  209. package/plugins/specweave/scripts/cleanup-state.sh +142 -0
  210. package/plugins/specweave/scripts/force-kill.sh +142 -0
  211. package/plugins/specweave/scripts/jobs.js +171 -0
  212. package/plugins/specweave/scripts/progress.js +170 -0
  213. package/plugins/specweave/scripts/read-costs.sh +132 -0
  214. package/plugins/specweave/scripts/read-jobs.sh +324 -0
  215. package/plugins/specweave/scripts/read-progress.sh +185 -0
  216. package/plugins/specweave/scripts/read-status.sh +146 -0
  217. package/plugins/specweave/scripts/read-workflow.sh +173 -0
  218. package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
  219. package/plugins/specweave/scripts/session-watchdog.sh +192 -0
  220. package/plugins/specweave/scripts/status.js +154 -0
  221. package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
  222. package/plugins/specweave/skills/increment-planner/SKILL.md +333 -24
  223. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -9
  224. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -2
  225. package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
  226. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  227. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  228. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  229. package/plugins/specweave-docs/commands/build.md +32 -4
  230. package/plugins/specweave-docs/commands/preview.md +43 -1
  231. package/plugins/specweave-docs/commands/validate.md +250 -0
  232. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -626
  233. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  234. package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
  235. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  236. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  237. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -939
  238. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  239. package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
  240. package/plugins/specweave/hooks/post-write-spec.sh +0 -267
  241. package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
  242. package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Azure DevOps Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single WIQL query, returns count (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ *
8
+ * Requires ADO_ORG_URL, ADO_PROJECT, ADO_PAT in environment
9
+ */
10
+ import { ExternalItemsProvider, ExternalProvider, ItemsFilter, PaginatedItemsResult } from '../types.js';
11
+ import { Logger } from '../../../utils/logger.js';
12
+ export declare class AdoItemsAdapter implements ExternalItemsProvider {
13
+ private orgUrl;
14
+ private project;
15
+ private pat;
16
+ private logger;
17
+ constructor(options: {
18
+ orgUrl: string;
19
+ project: string;
20
+ pat: string;
21
+ logger?: Logger;
22
+ });
23
+ static fromEnv(options?: {
24
+ logger?: Logger;
25
+ }): AdoItemsAdapter | null;
26
+ getProviderName(): ExternalProvider;
27
+ isConfigured(): Promise<boolean>;
28
+ /**
29
+ * TIER 1: Fast count - single WIQL query, just count IDs
30
+ * WIQL returns workItem IDs, we just count them without fetching details
31
+ */
32
+ getOpenCount(): Promise<number>;
33
+ /**
34
+ * TIER 3: Paginated items - on-demand with filters
35
+ */
36
+ getOpenItems(filter?: ItemsFilter): Promise<PaginatedItemsResult>;
37
+ private emptyResult;
38
+ }
39
+ //# sourceMappingURL=ado-items-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ado-items-adapter.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/ado-items-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EAIrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAiB,MAAM,0BAA0B,CAAC;AAEjE,qBAAa,eAAgB,YAAW,qBAAqB;IAC3D,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAOD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe,GAAG,IAAI;IAiBrE,eAAe,IAAI,gBAAgB;IAI7B,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAItC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAyCrC;;OAEG;IACG,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmIvE,OAAO,CAAC,WAAW;CAUpB"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Azure DevOps Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single WIQL query, returns count (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ *
8
+ * Requires ADO_ORG_URL, ADO_PROJECT, ADO_PAT in environment
9
+ */
10
+ import { STALE_THRESHOLD_DAYS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '../types.js';
11
+ import { consoleLogger } from '../../../utils/logger.js';
12
+ export class AdoItemsAdapter {
13
+ constructor(options) {
14
+ this.orgUrl = options.orgUrl.replace(/\/$/, '');
15
+ this.project = options.project;
16
+ this.pat = options.pat;
17
+ this.logger = options.logger ?? consoleLogger;
18
+ }
19
+ static fromEnv(options) {
20
+ const orgUrl = process.env.ADO_ORG_URL || process.env.AZURE_DEVOPS_ORG_URL;
21
+ const project = process.env.ADO_PROJECT || process.env.AZURE_DEVOPS_PROJECT;
22
+ const pat = process.env.ADO_PAT || process.env.AZURE_DEVOPS_PAT;
23
+ if (!orgUrl || !project || !pat) {
24
+ return null;
25
+ }
26
+ return new AdoItemsAdapter({
27
+ orgUrl,
28
+ project,
29
+ pat,
30
+ logger: options?.logger,
31
+ });
32
+ }
33
+ getProviderName() {
34
+ return 'ado';
35
+ }
36
+ async isConfigured() {
37
+ return !!(this.orgUrl && this.project && this.pat);
38
+ }
39
+ /**
40
+ * TIER 1: Fast count - single WIQL query, just count IDs
41
+ * WIQL returns workItem IDs, we just count them without fetching details
42
+ */
43
+ async getOpenCount() {
44
+ if (!await this.isConfigured()) {
45
+ return 0;
46
+ }
47
+ try {
48
+ const wiql = {
49
+ query: `SELECT [System.Id]
50
+ FROM WorkItems
51
+ WHERE [System.TeamProject] = '${this.project}'
52
+ AND [System.State] NOT IN ('Done', 'Closed', 'Removed', 'Completed')`,
53
+ };
54
+ const queryUrl = `${this.orgUrl}/${this.project}/_apis/wit/wiql?api-version=7.0`;
55
+ const authString = Buffer.from(`:${this.pat}`).toString('base64');
56
+ const response = await fetch(queryUrl, {
57
+ method: 'POST',
58
+ headers: {
59
+ 'Authorization': `Basic ${authString}`,
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ body: JSON.stringify(wiql),
63
+ });
64
+ if (!response.ok) {
65
+ this.logger.error(`ADO count error: ${response.status} ${response.statusText}`);
66
+ return 0;
67
+ }
68
+ const data = await response.json();
69
+ return data.workItems?.length || 0;
70
+ }
71
+ catch (error) {
72
+ this.logger.error(`ADO count failed: ${error}`);
73
+ return 0;
74
+ }
75
+ }
76
+ /**
77
+ * TIER 3: Paginated items - on-demand with filters
78
+ */
79
+ async getOpenItems(filter) {
80
+ if (!await this.isConfigured()) {
81
+ return this.emptyResult(1, DEFAULT_PAGE_SIZE);
82
+ }
83
+ const limit = Math.min(filter?.limit || DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
84
+ const offset = filter?.offset || 0;
85
+ const page = Math.floor(offset / limit) + 1;
86
+ try {
87
+ // First get all IDs (for total count and pagination)
88
+ const wiql = {
89
+ query: `SELECT [System.Id]
90
+ FROM WorkItems
91
+ WHERE [System.TeamProject] = '${this.project}'
92
+ AND [System.State] NOT IN ('Done', 'Closed', 'Removed', 'Completed')
93
+ ORDER BY [System.CreatedDate] DESC`,
94
+ };
95
+ const queryUrl = `${this.orgUrl}/${this.project}/_apis/wit/wiql?api-version=7.0`;
96
+ const authString = Buffer.from(`:${this.pat}`).toString('base64');
97
+ const queryResponse = await fetch(queryUrl, {
98
+ method: 'POST',
99
+ headers: {
100
+ 'Authorization': `Basic ${authString}`,
101
+ 'Content-Type': 'application/json',
102
+ },
103
+ body: JSON.stringify(wiql),
104
+ });
105
+ if (!queryResponse.ok) {
106
+ this.logger.error(`ADO WIQL error: ${queryResponse.status} ${queryResponse.statusText}`);
107
+ return this.emptyResult(page, limit);
108
+ }
109
+ const queryData = await queryResponse.json();
110
+ const allIds = queryData.workItems || [];
111
+ const total = allIds.length;
112
+ const totalPages = Math.ceil(total / limit);
113
+ if (total === 0) {
114
+ return this.emptyResult(page, limit);
115
+ }
116
+ // Get IDs for current page
117
+ const pageIds = allIds.slice(offset, offset + limit).map(wi => wi.id);
118
+ if (pageIds.length === 0) {
119
+ return {
120
+ items: [],
121
+ total,
122
+ page,
123
+ pageSize: limit,
124
+ totalPages,
125
+ hasMore: false,
126
+ };
127
+ }
128
+ // Fetch details for page
129
+ const detailsUrl = `${this.orgUrl}/${this.project}/_apis/wit/workitems?ids=${pageIds.join(',')}&fields=System.Id,System.Title,System.CreatedDate,System.State,System.Tags&api-version=7.0`;
130
+ const detailsResponse = await fetch(detailsUrl, {
131
+ headers: {
132
+ 'Authorization': `Basic ${authString}`,
133
+ },
134
+ });
135
+ if (!detailsResponse.ok) {
136
+ this.logger.error(`ADO details error: ${detailsResponse.status}`);
137
+ return this.emptyResult(page, limit);
138
+ }
139
+ const detailsData = await detailsResponse.json();
140
+ const now = Date.now();
141
+ const items = detailsData.value.map(item => {
142
+ const createdDate = new Date(item.fields['System.CreatedDate']).getTime();
143
+ const ageMs = now - createdDate;
144
+ const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
145
+ const tags = item.fields['System.Tags']?.split(';').map(t => t.trim()) || [];
146
+ return {
147
+ id: `#${item.id}`,
148
+ title: item.fields['System.Title'],
149
+ url: item._links.html.href,
150
+ createdAt: item.fields['System.CreatedDate'],
151
+ age: ageDays,
152
+ labels: tags,
153
+ provider: 'ado',
154
+ project: this.project,
155
+ state: item.fields['System.State'],
156
+ isStale: ageDays >= STALE_THRESHOLD_DAYS,
157
+ };
158
+ });
159
+ // Filter stale if requested
160
+ const filteredItems = filter?.staleOnly
161
+ ? items.filter(i => i.isStale)
162
+ : items;
163
+ return {
164
+ items: filteredItems,
165
+ total,
166
+ page,
167
+ pageSize: limit,
168
+ totalPages,
169
+ hasMore: page < totalPages,
170
+ };
171
+ }
172
+ catch (error) {
173
+ this.logger.error(`Failed to fetch ADO work items: ${error}`);
174
+ return this.emptyResult(page, limit);
175
+ }
176
+ }
177
+ emptyResult(page, pageSize) {
178
+ return {
179
+ items: [],
180
+ total: 0,
181
+ page,
182
+ pageSize,
183
+ totalPages: 0,
184
+ hasMore: false,
185
+ };
186
+ }
187
+ }
188
+ //# sourceMappingURL=ado-items-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ado-items-adapter.js","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/ado-items-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * GitHub Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single API call, returns total (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ */
8
+ import { ExternalItemsProvider, ExternalProvider, ItemsFilter, PaginatedItemsResult } from '../types.js';
9
+ import { Logger } from '../../../utils/logger.js';
10
+ export declare class GitHubItemsAdapter implements ExternalItemsProvider {
11
+ private owner;
12
+ private repo;
13
+ private logger;
14
+ constructor(options: {
15
+ owner: string;
16
+ repo: string;
17
+ logger?: Logger;
18
+ });
19
+ /**
20
+ * Create adapter from auto-detected git remote
21
+ */
22
+ static fromGitRemote(options?: {
23
+ logger?: Logger;
24
+ }): Promise<GitHubItemsAdapter | null>;
25
+ getProviderName(): ExternalProvider;
26
+ isConfigured(): Promise<boolean>;
27
+ /**
28
+ * TIER 1: Fast count - single API call
29
+ * Uses GitHub search API to get total_count without fetching items
30
+ */
31
+ getOpenCount(): Promise<number>;
32
+ /**
33
+ * TIER 3: Paginated items - on-demand with filters
34
+ */
35
+ getOpenItems(filter?: ItemsFilter): Promise<PaginatedItemsResult>;
36
+ private emptyResult;
37
+ }
38
+ //# sourceMappingURL=github-items-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-items-adapter.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/github-items-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAEL,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EAIrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAiB,MAAM,0BAA0B,CAAC;AAgBjE,qBAAa,kBAAmB,YAAW,qBAAqB;IAC9D,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAMD;;OAEG;WACU,aAAa,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAqB7F,eAAe,IAAI,gBAAgB;IAI7B,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAKtC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAoBrC;;OAEG;IACG,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmEvE,OAAO,CAAC,WAAW;CAUpB"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * GitHub Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single API call, returns total (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ */
8
+ import { execFileNoThrow } from '../../../utils/execFileNoThrow.js';
9
+ import { STALE_THRESHOLD_DAYS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '../types.js';
10
+ import { consoleLogger } from '../../../utils/logger.js';
11
+ export class GitHubItemsAdapter {
12
+ constructor(options) {
13
+ this.owner = options.owner;
14
+ this.repo = options.repo;
15
+ this.logger = options.logger ?? consoleLogger;
16
+ }
17
+ /**
18
+ * Create adapter from auto-detected git remote
19
+ */
20
+ static async fromGitRemote(options) {
21
+ const result = await execFileNoThrow('git', ['remote', 'get-url', 'origin']);
22
+ if (result.exitCode !== 0) {
23
+ return null;
24
+ }
25
+ const remote = result.stdout.trim();
26
+ const match = remote.match(/github\.com[:/](.+)\/(.+?)(?:\.git)?$/);
27
+ if (!match) {
28
+ return null;
29
+ }
30
+ return new GitHubItemsAdapter({
31
+ owner: match[1],
32
+ repo: match[2],
33
+ logger: options?.logger,
34
+ });
35
+ }
36
+ getProviderName() {
37
+ return 'github';
38
+ }
39
+ async isConfigured() {
40
+ const result = await execFileNoThrow('gh', ['auth', 'status']);
41
+ return result.exitCode === 0;
42
+ }
43
+ /**
44
+ * TIER 1: Fast count - single API call
45
+ * Uses GitHub search API to get total_count without fetching items
46
+ */
47
+ async getOpenCount() {
48
+ // Use search API with per_page=1 to get total_count efficiently
49
+ const query = `repo:${this.owner}/${this.repo} is:issue is:open`;
50
+ const result = await execFileNoThrow('gh', [
51
+ 'api',
52
+ 'search/issues',
53
+ '-X', 'GET',
54
+ '-f', `q=${query}`,
55
+ '-f', 'per_page=1',
56
+ '--jq', '.total_count',
57
+ ]);
58
+ if (result.exitCode !== 0) {
59
+ this.logger.error(`GitHub count failed: ${result.stderr}`);
60
+ return 0;
61
+ }
62
+ return parseInt(result.stdout.trim(), 10) || 0;
63
+ }
64
+ /**
65
+ * TIER 3: Paginated items - on-demand with filters
66
+ */
67
+ async getOpenItems(filter) {
68
+ const limit = Math.min(filter?.limit || DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
69
+ const offset = filter?.offset || 0;
70
+ const page = Math.floor(offset / limit) + 1;
71
+ // First get total count
72
+ const total = await this.getOpenCount();
73
+ // Then fetch page of items
74
+ const result = await execFileNoThrow('gh', [
75
+ 'issue',
76
+ 'list',
77
+ '--repo', `${this.owner}/${this.repo}`,
78
+ '--state', 'open',
79
+ '--limit', String(limit),
80
+ '--json', 'number,title,url,createdAt,labels,state',
81
+ ]);
82
+ if (result.exitCode !== 0) {
83
+ this.logger.error(`GitHub items failed: ${result.stderr}`);
84
+ return this.emptyResult(page, limit);
85
+ }
86
+ try {
87
+ const issues = JSON.parse(result.stdout);
88
+ const now = Date.now();
89
+ const items = issues.map(issue => {
90
+ const createdDate = new Date(issue.createdAt).getTime();
91
+ const ageMs = now - createdDate;
92
+ const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
93
+ return {
94
+ id: `#${issue.number}`,
95
+ title: issue.title,
96
+ url: issue.url,
97
+ createdAt: issue.createdAt,
98
+ age: ageDays,
99
+ labels: issue.labels.map(l => l.name),
100
+ provider: 'github',
101
+ project: `${this.owner}/${this.repo}`,
102
+ state: issue.state,
103
+ isStale: ageDays >= STALE_THRESHOLD_DAYS,
104
+ };
105
+ });
106
+ // Filter stale if requested
107
+ const filteredItems = filter?.staleOnly
108
+ ? items.filter(i => i.isStale)
109
+ : items;
110
+ const totalPages = Math.ceil(total / limit);
111
+ return {
112
+ items: filteredItems,
113
+ total,
114
+ page,
115
+ pageSize: limit,
116
+ totalPages,
117
+ hasMore: page < totalPages,
118
+ };
119
+ }
120
+ catch (error) {
121
+ this.logger.error(`GitHub parse failed: ${error}`);
122
+ return this.emptyResult(page, limit);
123
+ }
124
+ }
125
+ emptyResult(page, pageSize) {
126
+ return {
127
+ items: [],
128
+ total: 0,
129
+ page,
130
+ pageSize,
131
+ totalPages: 0,
132
+ hasMore: false,
133
+ };
134
+ }
135
+ }
136
+ //# sourceMappingURL=github-items-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-items-adapter.js","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/github-items-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * External Items Providers - Re-exports
3
+ */
4
+ export { GitHubItemsAdapter } from './github-items-adapter.js';
5
+ export { JiraItemsAdapter } from './jira-items-adapter.js';
6
+ export { AdoItemsAdapter } from './ado-items-adapter.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * External Items Providers - Re-exports
3
+ */
4
+ export { GitHubItemsAdapter } from './github-items-adapter.js';
5
+ export { JiraItemsAdapter } from './jira-items-adapter.js';
6
+ export { AdoItemsAdapter } from './ado-items-adapter.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * JIRA Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single API call, returns total (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ *
8
+ * Requires JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN in environment
9
+ */
10
+ import { ExternalItemsProvider, ExternalProvider, ItemsFilter, PaginatedItemsResult } from '../types.js';
11
+ import { Logger } from '../../../utils/logger.js';
12
+ export declare class JiraItemsAdapter implements ExternalItemsProvider {
13
+ private baseUrl;
14
+ private email;
15
+ private apiToken;
16
+ private projectKey;
17
+ private logger;
18
+ constructor(options: {
19
+ baseUrl: string;
20
+ email: string;
21
+ apiToken: string;
22
+ projectKey: string;
23
+ logger?: Logger;
24
+ });
25
+ static fromEnv(options?: {
26
+ projectKey?: string;
27
+ logger?: Logger;
28
+ }): JiraItemsAdapter | null;
29
+ getProviderName(): ExternalProvider;
30
+ isConfigured(): Promise<boolean>;
31
+ /**
32
+ * TIER 1: Fast count - single API call with maxResults=0
33
+ * JIRA returns total even when maxResults=0, so we get count without fetching items
34
+ */
35
+ getOpenCount(): Promise<number>;
36
+ /**
37
+ * TIER 3: Paginated items - on-demand with filters
38
+ */
39
+ getOpenItems(filter?: ItemsFilter): Promise<PaginatedItemsResult>;
40
+ private emptyResult;
41
+ }
42
+ //# sourceMappingURL=jira-items-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-items-adapter.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-tools/providers/jira-items-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EAIrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAiB,MAAM,0BAA0B,CAAC;AAEjE,qBAAa,gBAAiB,YAAW,qBAAqB;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAQD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,gBAAgB,GAAG,IAAI;IAmB3F,eAAe,IAAI,gBAAgB;IAI7B,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAItC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAmCrC;;OAEG;IACG,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAqFvE,OAAO,CAAC,WAAW;CAUpB"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * JIRA Items Adapter - OPTIMIZED FOR SCALE
3
+ *
4
+ * Three-tier fetching:
5
+ * 1. getOpenCount() - Single API call, returns total (for status)
6
+ * 2. getOpenItems(filter) - Paginated, filtered (for details)
7
+ *
8
+ * Requires JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN in environment
9
+ */
10
+ import { STALE_THRESHOLD_DAYS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '../types.js';
11
+ import { consoleLogger } from '../../../utils/logger.js';
12
+ export class JiraItemsAdapter {
13
+ constructor(options) {
14
+ this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash
15
+ this.email = options.email;
16
+ this.apiToken = options.apiToken;
17
+ this.projectKey = options.projectKey;
18
+ this.logger = options.logger ?? consoleLogger;
19
+ }
20
+ static fromEnv(options) {
21
+ const baseUrl = process.env.JIRA_BASE_URL;
22
+ const email = process.env.JIRA_EMAIL;
23
+ const apiToken = process.env.JIRA_API_TOKEN;
24
+ const projectKey = options?.projectKey || process.env.JIRA_PROJECT_KEY;
25
+ if (!baseUrl || !email || !apiToken) {
26
+ return null;
27
+ }
28
+ return new JiraItemsAdapter({
29
+ baseUrl,
30
+ email,
31
+ apiToken,
32
+ projectKey: projectKey || '',
33
+ logger: options?.logger,
34
+ });
35
+ }
36
+ getProviderName() {
37
+ return 'jira';
38
+ }
39
+ async isConfigured() {
40
+ return !!(this.baseUrl && this.email && this.apiToken);
41
+ }
42
+ /**
43
+ * TIER 1: Fast count - single API call with maxResults=0
44
+ * JIRA returns total even when maxResults=0, so we get count without fetching items
45
+ */
46
+ async getOpenCount() {
47
+ if (!await this.isConfigured()) {
48
+ return 0;
49
+ }
50
+ try {
51
+ const jql = this.projectKey
52
+ ? `project = "${this.projectKey}" AND status NOT IN (Done, Closed)`
53
+ : 'status NOT IN (Done, Closed)';
54
+ const encodedJql = encodeURIComponent(jql);
55
+ // maxResults=0 returns just the total count without fetching items
56
+ const url = `${this.baseUrl}/rest/api/3/search?jql=${encodedJql}&maxResults=0`;
57
+ const authString = Buffer.from(`${this.email}:${this.apiToken}`).toString('base64');
58
+ const response = await fetch(url, {
59
+ headers: {
60
+ 'Authorization': `Basic ${authString}`,
61
+ 'Accept': 'application/json',
62
+ },
63
+ });
64
+ if (!response.ok) {
65
+ this.logger.error(`JIRA count error: ${response.status} ${response.statusText}`);
66
+ return 0;
67
+ }
68
+ const data = await response.json();
69
+ return data.total || 0;
70
+ }
71
+ catch (error) {
72
+ this.logger.error(`JIRA count failed: ${error}`);
73
+ return 0;
74
+ }
75
+ }
76
+ /**
77
+ * TIER 3: Paginated items - on-demand with filters
78
+ */
79
+ async getOpenItems(filter) {
80
+ if (!await this.isConfigured()) {
81
+ return this.emptyResult(1, DEFAULT_PAGE_SIZE);
82
+ }
83
+ const limit = Math.min(filter?.limit || DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
84
+ const offset = filter?.offset || 0;
85
+ const page = Math.floor(offset / limit) + 1;
86
+ try {
87
+ const jql = this.projectKey
88
+ ? `project = "${this.projectKey}" AND status NOT IN (Done, Closed) ORDER BY created DESC`
89
+ : 'status NOT IN (Done, Closed) ORDER BY created DESC';
90
+ const encodedJql = encodeURIComponent(jql);
91
+ const url = `${this.baseUrl}/rest/api/3/search?jql=${encodedJql}&startAt=${offset}&maxResults=${limit}&fields=key,summary,created,labels,status`;
92
+ const authString = Buffer.from(`${this.email}:${this.apiToken}`).toString('base64');
93
+ const response = await fetch(url, {
94
+ headers: {
95
+ 'Authorization': `Basic ${authString}`,
96
+ 'Accept': 'application/json',
97
+ },
98
+ });
99
+ if (!response.ok) {
100
+ this.logger.error(`JIRA API error: ${response.status} ${response.statusText}`);
101
+ return this.emptyResult(page, limit);
102
+ }
103
+ const data = await response.json();
104
+ const now = Date.now();
105
+ const total = data.total;
106
+ const totalPages = Math.ceil(total / limit);
107
+ const items = data.issues.map(issue => {
108
+ const createdDate = new Date(issue.fields.created).getTime();
109
+ const ageMs = now - createdDate;
110
+ const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
111
+ return {
112
+ id: issue.key,
113
+ title: issue.fields.summary,
114
+ url: `${this.baseUrl}/browse/${issue.key}`,
115
+ createdAt: issue.fields.created,
116
+ age: ageDays,
117
+ labels: issue.fields.labels || [],
118
+ provider: 'jira',
119
+ project: this.projectKey || 'default',
120
+ state: issue.fields.status.name,
121
+ isStale: ageDays >= STALE_THRESHOLD_DAYS,
122
+ };
123
+ });
124
+ // Filter stale if requested
125
+ const filteredItems = filter?.staleOnly
126
+ ? items.filter(i => i.isStale)
127
+ : items;
128
+ return {
129
+ items: filteredItems,
130
+ total,
131
+ page,
132
+ pageSize: limit,
133
+ totalPages,
134
+ hasMore: page < totalPages,
135
+ };
136
+ }
137
+ catch (error) {
138
+ this.logger.error(`Failed to fetch JIRA issues: ${error}`);
139
+ return this.emptyResult(page, limit);
140
+ }
141
+ }
142
+ emptyResult(page, pageSize) {
143
+ return {
144
+ items: [],
145
+ total: 0,
146
+ page,
147
+ pageSize,
148
+ totalPages: 0,
149
+ hasMore: false,
150
+ };
151
+ }
152
+ }
153
+ //# sourceMappingURL=jira-items-adapter.js.map