claude-autopm 2.7.0 → 2.8.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 (281) hide show
  1. package/README.md +307 -56
  2. package/autopm/.claude/.env +158 -0
  3. package/autopm/.claude/settings.local.json +9 -0
  4. package/bin/autopm.js +11 -2
  5. package/bin/commands/epic.js +23 -3
  6. package/bin/commands/plugin.js +395 -0
  7. package/bin/commands/team.js +184 -10
  8. package/install/install.js +223 -4
  9. package/lib/cli/commands/issue.js +360 -20
  10. package/lib/plugins/PluginManager.js +1328 -0
  11. package/lib/plugins/PluginManager.old.js +400 -0
  12. package/lib/providers/AzureDevOpsProvider.js +575 -0
  13. package/lib/providers/GitHubProvider.js +475 -0
  14. package/lib/services/EpicService.js +1092 -3
  15. package/lib/services/IssueService.js +991 -0
  16. package/package.json +9 -1
  17. package/scripts/publish-plugins.sh +166 -0
  18. package/autopm/.claude/agents/cloud/README.md +0 -55
  19. package/autopm/.claude/agents/cloud/aws-cloud-architect.md +0 -521
  20. package/autopm/.claude/agents/cloud/azure-cloud-architect.md +0 -436
  21. package/autopm/.claude/agents/cloud/gcp-cloud-architect.md +0 -385
  22. package/autopm/.claude/agents/cloud/gcp-cloud-functions-engineer.md +0 -306
  23. package/autopm/.claude/agents/cloud/gemini-api-expert.md +0 -880
  24. package/autopm/.claude/agents/cloud/kubernetes-orchestrator.md +0 -566
  25. package/autopm/.claude/agents/cloud/openai-python-expert.md +0 -1087
  26. package/autopm/.claude/agents/cloud/terraform-infrastructure-expert.md +0 -454
  27. package/autopm/.claude/agents/core/agent-manager.md +0 -296
  28. package/autopm/.claude/agents/core/code-analyzer.md +0 -131
  29. package/autopm/.claude/agents/core/file-analyzer.md +0 -162
  30. package/autopm/.claude/agents/core/test-runner.md +0 -200
  31. package/autopm/.claude/agents/data/airflow-orchestration-expert.md +0 -52
  32. package/autopm/.claude/agents/data/kedro-pipeline-expert.md +0 -50
  33. package/autopm/.claude/agents/data/langgraph-workflow-expert.md +0 -520
  34. package/autopm/.claude/agents/databases/README.md +0 -50
  35. package/autopm/.claude/agents/databases/bigquery-expert.md +0 -392
  36. package/autopm/.claude/agents/databases/cosmosdb-expert.md +0 -368
  37. package/autopm/.claude/agents/databases/mongodb-expert.md +0 -398
  38. package/autopm/.claude/agents/databases/postgresql-expert.md +0 -321
  39. package/autopm/.claude/agents/databases/redis-expert.md +0 -52
  40. package/autopm/.claude/agents/devops/README.md +0 -52
  41. package/autopm/.claude/agents/devops/azure-devops-specialist.md +0 -308
  42. package/autopm/.claude/agents/devops/docker-containerization-expert.md +0 -298
  43. package/autopm/.claude/agents/devops/github-operations-specialist.md +0 -335
  44. package/autopm/.claude/agents/devops/mcp-context-manager.md +0 -319
  45. package/autopm/.claude/agents/devops/observability-engineer.md +0 -574
  46. package/autopm/.claude/agents/devops/ssh-operations-expert.md +0 -1093
  47. package/autopm/.claude/agents/devops/traefik-proxy-expert.md +0 -444
  48. package/autopm/.claude/agents/frameworks/README.md +0 -64
  49. package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +0 -360
  50. package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +0 -254
  51. package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +0 -217
  52. package/autopm/.claude/agents/frameworks/react-ui-expert.md +0 -226
  53. package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +0 -770
  54. package/autopm/.claude/agents/frameworks/ux-design-expert.md +0 -244
  55. package/autopm/.claude/agents/integration/message-queue-engineer.md +0 -794
  56. package/autopm/.claude/agents/languages/README.md +0 -50
  57. package/autopm/.claude/agents/languages/bash-scripting-expert.md +0 -541
  58. package/autopm/.claude/agents/languages/javascript-frontend-engineer.md +0 -197
  59. package/autopm/.claude/agents/languages/nodejs-backend-engineer.md +0 -226
  60. package/autopm/.claude/agents/languages/python-backend-engineer.md +0 -214
  61. package/autopm/.claude/agents/languages/python-backend-expert.md +0 -289
  62. package/autopm/.claude/agents/testing/frontend-testing-engineer.md +0 -395
  63. package/autopm/.claude/commands/ai/langgraph-workflow.md +0 -65
  64. package/autopm/.claude/commands/ai/openai-chat.md +0 -65
  65. package/autopm/.claude/commands/azure/COMMANDS.md +0 -107
  66. package/autopm/.claude/commands/azure/COMMAND_MAPPING.md +0 -252
  67. package/autopm/.claude/commands/azure/INTEGRATION_FIX.md +0 -103
  68. package/autopm/.claude/commands/azure/README.md +0 -246
  69. package/autopm/.claude/commands/azure/active-work.md +0 -198
  70. package/autopm/.claude/commands/azure/aliases.md +0 -143
  71. package/autopm/.claude/commands/azure/blocked-items.md +0 -287
  72. package/autopm/.claude/commands/azure/clean.md +0 -93
  73. package/autopm/.claude/commands/azure/docs-query.md +0 -48
  74. package/autopm/.claude/commands/azure/feature-decompose.md +0 -380
  75. package/autopm/.claude/commands/azure/feature-list.md +0 -61
  76. package/autopm/.claude/commands/azure/feature-new.md +0 -115
  77. package/autopm/.claude/commands/azure/feature-show.md +0 -205
  78. package/autopm/.claude/commands/azure/feature-start.md +0 -130
  79. package/autopm/.claude/commands/azure/fix-integration-example.md +0 -93
  80. package/autopm/.claude/commands/azure/help.md +0 -150
  81. package/autopm/.claude/commands/azure/import-us.md +0 -269
  82. package/autopm/.claude/commands/azure/init.md +0 -211
  83. package/autopm/.claude/commands/azure/next-task.md +0 -262
  84. package/autopm/.claude/commands/azure/search.md +0 -160
  85. package/autopm/.claude/commands/azure/sprint-status.md +0 -235
  86. package/autopm/.claude/commands/azure/standup.md +0 -260
  87. package/autopm/.claude/commands/azure/sync-all.md +0 -99
  88. package/autopm/.claude/commands/azure/task-analyze.md +0 -186
  89. package/autopm/.claude/commands/azure/task-close.md +0 -329
  90. package/autopm/.claude/commands/azure/task-edit.md +0 -145
  91. package/autopm/.claude/commands/azure/task-list.md +0 -263
  92. package/autopm/.claude/commands/azure/task-new.md +0 -84
  93. package/autopm/.claude/commands/azure/task-reopen.md +0 -79
  94. package/autopm/.claude/commands/azure/task-show.md +0 -126
  95. package/autopm/.claude/commands/azure/task-start.md +0 -301
  96. package/autopm/.claude/commands/azure/task-status.md +0 -65
  97. package/autopm/.claude/commands/azure/task-sync.md +0 -67
  98. package/autopm/.claude/commands/azure/us-edit.md +0 -164
  99. package/autopm/.claude/commands/azure/us-list.md +0 -202
  100. package/autopm/.claude/commands/azure/us-new.md +0 -265
  101. package/autopm/.claude/commands/azure/us-parse.md +0 -253
  102. package/autopm/.claude/commands/azure/us-show.md +0 -188
  103. package/autopm/.claude/commands/azure/us-status.md +0 -320
  104. package/autopm/.claude/commands/azure/validate.md +0 -86
  105. package/autopm/.claude/commands/azure/work-item-sync.md +0 -47
  106. package/autopm/.claude/commands/cloud/infra-deploy.md +0 -38
  107. package/autopm/.claude/commands/github/workflow-create.md +0 -42
  108. package/autopm/.claude/commands/infrastructure/ssh-security.md +0 -65
  109. package/autopm/.claude/commands/infrastructure/traefik-setup.md +0 -65
  110. package/autopm/.claude/commands/kubernetes/deploy.md +0 -37
  111. package/autopm/.claude/commands/playwright/test-scaffold.md +0 -38
  112. package/autopm/.claude/commands/pm/blocked.md +0 -28
  113. package/autopm/.claude/commands/pm/clean.md +0 -119
  114. package/autopm/.claude/commands/pm/context-create.md +0 -136
  115. package/autopm/.claude/commands/pm/context-prime.md +0 -170
  116. package/autopm/.claude/commands/pm/context-update.md +0 -292
  117. package/autopm/.claude/commands/pm/context.md +0 -28
  118. package/autopm/.claude/commands/pm/epic-close.md +0 -86
  119. package/autopm/.claude/commands/pm/epic-decompose.md +0 -370
  120. package/autopm/.claude/commands/pm/epic-edit.md +0 -83
  121. package/autopm/.claude/commands/pm/epic-list.md +0 -30
  122. package/autopm/.claude/commands/pm/epic-merge.md +0 -222
  123. package/autopm/.claude/commands/pm/epic-oneshot.md +0 -119
  124. package/autopm/.claude/commands/pm/epic-refresh.md +0 -119
  125. package/autopm/.claude/commands/pm/epic-show.md +0 -28
  126. package/autopm/.claude/commands/pm/epic-split.md +0 -120
  127. package/autopm/.claude/commands/pm/epic-start.md +0 -195
  128. package/autopm/.claude/commands/pm/epic-status.md +0 -28
  129. package/autopm/.claude/commands/pm/epic-sync-modular.md +0 -338
  130. package/autopm/.claude/commands/pm/epic-sync-original.md +0 -473
  131. package/autopm/.claude/commands/pm/epic-sync.md +0 -486
  132. package/autopm/.claude/commands/pm/help.md +0 -28
  133. package/autopm/.claude/commands/pm/import.md +0 -115
  134. package/autopm/.claude/commands/pm/in-progress.md +0 -28
  135. package/autopm/.claude/commands/pm/init.md +0 -28
  136. package/autopm/.claude/commands/pm/issue-analyze.md +0 -202
  137. package/autopm/.claude/commands/pm/issue-close.md +0 -119
  138. package/autopm/.claude/commands/pm/issue-edit.md +0 -93
  139. package/autopm/.claude/commands/pm/issue-reopen.md +0 -87
  140. package/autopm/.claude/commands/pm/issue-show.md +0 -41
  141. package/autopm/.claude/commands/pm/issue-start.md +0 -234
  142. package/autopm/.claude/commands/pm/issue-status.md +0 -95
  143. package/autopm/.claude/commands/pm/issue-sync.md +0 -411
  144. package/autopm/.claude/commands/pm/next.md +0 -28
  145. package/autopm/.claude/commands/pm/prd-edit.md +0 -82
  146. package/autopm/.claude/commands/pm/prd-list.md +0 -28
  147. package/autopm/.claude/commands/pm/prd-new.md +0 -55
  148. package/autopm/.claude/commands/pm/prd-parse.md +0 -42
  149. package/autopm/.claude/commands/pm/prd-status.md +0 -28
  150. package/autopm/.claude/commands/pm/search.md +0 -28
  151. package/autopm/.claude/commands/pm/standup.md +0 -28
  152. package/autopm/.claude/commands/pm/status.md +0 -28
  153. package/autopm/.claude/commands/pm/sync.md +0 -99
  154. package/autopm/.claude/commands/pm/test-reference-update.md +0 -151
  155. package/autopm/.claude/commands/pm/validate.md +0 -28
  156. package/autopm/.claude/commands/pm/what-next.md +0 -28
  157. package/autopm/.claude/commands/python/api-scaffold.md +0 -50
  158. package/autopm/.claude/commands/python/docs-query.md +0 -48
  159. package/autopm/.claude/commands/react/app-scaffold.md +0 -50
  160. package/autopm/.claude/commands/testing/prime.md +0 -314
  161. package/autopm/.claude/commands/testing/run.md +0 -125
  162. package/autopm/.claude/commands/ui/bootstrap-scaffold.md +0 -65
  163. package/autopm/.claude/commands/ui/tailwind-system.md +0 -64
  164. package/autopm/.claude/rules/ai-integration-patterns.md +0 -219
  165. package/autopm/.claude/rules/ci-cd-kubernetes-strategy.md +0 -25
  166. package/autopm/.claude/rules/database-management-strategy.md +0 -17
  167. package/autopm/.claude/rules/database-pipeline.md +0 -94
  168. package/autopm/.claude/rules/devops-troubleshooting-playbook.md +0 -450
  169. package/autopm/.claude/rules/docker-first-development.md +0 -404
  170. package/autopm/.claude/rules/infrastructure-pipeline.md +0 -128
  171. package/autopm/.claude/rules/performance-guidelines.md +0 -403
  172. package/autopm/.claude/rules/ui-development-standards.md +0 -281
  173. package/autopm/.claude/rules/ui-framework-rules.md +0 -151
  174. package/autopm/.claude/rules/ux-design-rules.md +0 -209
  175. package/autopm/.claude/rules/visual-testing.md +0 -223
  176. package/autopm/.claude/scripts/azure/README.md +0 -192
  177. package/autopm/.claude/scripts/azure/active-work.js +0 -524
  178. package/autopm/.claude/scripts/azure/active-work.sh +0 -20
  179. package/autopm/.claude/scripts/azure/blocked.js +0 -520
  180. package/autopm/.claude/scripts/azure/blocked.sh +0 -20
  181. package/autopm/.claude/scripts/azure/daily.js +0 -533
  182. package/autopm/.claude/scripts/azure/daily.sh +0 -20
  183. package/autopm/.claude/scripts/azure/dashboard.js +0 -970
  184. package/autopm/.claude/scripts/azure/dashboard.sh +0 -20
  185. package/autopm/.claude/scripts/azure/feature-list.js +0 -254
  186. package/autopm/.claude/scripts/azure/feature-list.sh +0 -20
  187. package/autopm/.claude/scripts/azure/feature-show.js +0 -7
  188. package/autopm/.claude/scripts/azure/feature-show.sh +0 -20
  189. package/autopm/.claude/scripts/azure/feature-status.js +0 -604
  190. package/autopm/.claude/scripts/azure/feature-status.sh +0 -20
  191. package/autopm/.claude/scripts/azure/help.js +0 -342
  192. package/autopm/.claude/scripts/azure/help.sh +0 -20
  193. package/autopm/.claude/scripts/azure/next-task.js +0 -508
  194. package/autopm/.claude/scripts/azure/next-task.sh +0 -20
  195. package/autopm/.claude/scripts/azure/search.js +0 -469
  196. package/autopm/.claude/scripts/azure/search.sh +0 -20
  197. package/autopm/.claude/scripts/azure/setup.js +0 -745
  198. package/autopm/.claude/scripts/azure/setup.sh +0 -20
  199. package/autopm/.claude/scripts/azure/sprint-report.js +0 -1012
  200. package/autopm/.claude/scripts/azure/sprint-report.sh +0 -20
  201. package/autopm/.claude/scripts/azure/sync.js +0 -563
  202. package/autopm/.claude/scripts/azure/sync.sh +0 -20
  203. package/autopm/.claude/scripts/azure/us-list.js +0 -210
  204. package/autopm/.claude/scripts/azure/us-list.sh +0 -20
  205. package/autopm/.claude/scripts/azure/us-status.js +0 -238
  206. package/autopm/.claude/scripts/azure/us-status.sh +0 -20
  207. package/autopm/.claude/scripts/azure/validate.js +0 -626
  208. package/autopm/.claude/scripts/azure/validate.sh +0 -20
  209. package/autopm/.claude/scripts/azure/wrapper-template.sh +0 -20
  210. package/autopm/.claude/scripts/github/dependency-tracker.js +0 -554
  211. package/autopm/.claude/scripts/github/dependency-validator.js +0 -545
  212. package/autopm/.claude/scripts/github/dependency-visualizer.js +0 -477
  213. package/autopm/.claude/scripts/pm/analytics.js +0 -425
  214. package/autopm/.claude/scripts/pm/blocked.js +0 -164
  215. package/autopm/.claude/scripts/pm/blocked.sh +0 -78
  216. package/autopm/.claude/scripts/pm/clean.js +0 -464
  217. package/autopm/.claude/scripts/pm/context-create.js +0 -216
  218. package/autopm/.claude/scripts/pm/context-prime.js +0 -335
  219. package/autopm/.claude/scripts/pm/context-update.js +0 -344
  220. package/autopm/.claude/scripts/pm/context.js +0 -338
  221. package/autopm/.claude/scripts/pm/epic-close.js +0 -347
  222. package/autopm/.claude/scripts/pm/epic-edit.js +0 -382
  223. package/autopm/.claude/scripts/pm/epic-list.js +0 -273
  224. package/autopm/.claude/scripts/pm/epic-list.sh +0 -109
  225. package/autopm/.claude/scripts/pm/epic-show.js +0 -291
  226. package/autopm/.claude/scripts/pm/epic-show.sh +0 -105
  227. package/autopm/.claude/scripts/pm/epic-split.js +0 -522
  228. package/autopm/.claude/scripts/pm/epic-start/epic-start.js +0 -183
  229. package/autopm/.claude/scripts/pm/epic-start/epic-start.sh +0 -94
  230. package/autopm/.claude/scripts/pm/epic-status.js +0 -291
  231. package/autopm/.claude/scripts/pm/epic-status.sh +0 -104
  232. package/autopm/.claude/scripts/pm/epic-sync/README.md +0 -208
  233. package/autopm/.claude/scripts/pm/epic-sync/create-epic-issue.sh +0 -77
  234. package/autopm/.claude/scripts/pm/epic-sync/create-task-issues.sh +0 -86
  235. package/autopm/.claude/scripts/pm/epic-sync/update-epic-file.sh +0 -79
  236. package/autopm/.claude/scripts/pm/epic-sync/update-references.sh +0 -89
  237. package/autopm/.claude/scripts/pm/epic-sync.sh +0 -137
  238. package/autopm/.claude/scripts/pm/help.js +0 -92
  239. package/autopm/.claude/scripts/pm/help.sh +0 -90
  240. package/autopm/.claude/scripts/pm/in-progress.js +0 -178
  241. package/autopm/.claude/scripts/pm/in-progress.sh +0 -93
  242. package/autopm/.claude/scripts/pm/init.js +0 -321
  243. package/autopm/.claude/scripts/pm/init.sh +0 -178
  244. package/autopm/.claude/scripts/pm/issue-close.js +0 -232
  245. package/autopm/.claude/scripts/pm/issue-edit.js +0 -310
  246. package/autopm/.claude/scripts/pm/issue-show.js +0 -272
  247. package/autopm/.claude/scripts/pm/issue-start.js +0 -181
  248. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +0 -468
  249. package/autopm/.claude/scripts/pm/issue-sync/gather-updates.sh +0 -460
  250. package/autopm/.claude/scripts/pm/issue-sync/post-comment.sh +0 -330
  251. package/autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh +0 -348
  252. package/autopm/.claude/scripts/pm/issue-sync/update-frontmatter.sh +0 -387
  253. package/autopm/.claude/scripts/pm/lib/README.md +0 -85
  254. package/autopm/.claude/scripts/pm/lib/epic-discovery.js +0 -119
  255. package/autopm/.claude/scripts/pm/lib/logger.js +0 -78
  256. package/autopm/.claude/scripts/pm/next.js +0 -189
  257. package/autopm/.claude/scripts/pm/next.sh +0 -72
  258. package/autopm/.claude/scripts/pm/optimize.js +0 -407
  259. package/autopm/.claude/scripts/pm/pr-create.js +0 -337
  260. package/autopm/.claude/scripts/pm/pr-list.js +0 -257
  261. package/autopm/.claude/scripts/pm/prd-list.js +0 -242
  262. package/autopm/.claude/scripts/pm/prd-list.sh +0 -103
  263. package/autopm/.claude/scripts/pm/prd-new.js +0 -684
  264. package/autopm/.claude/scripts/pm/prd-parse.js +0 -547
  265. package/autopm/.claude/scripts/pm/prd-status.js +0 -152
  266. package/autopm/.claude/scripts/pm/prd-status.sh +0 -63
  267. package/autopm/.claude/scripts/pm/release.js +0 -460
  268. package/autopm/.claude/scripts/pm/search.js +0 -192
  269. package/autopm/.claude/scripts/pm/search.sh +0 -89
  270. package/autopm/.claude/scripts/pm/standup.js +0 -362
  271. package/autopm/.claude/scripts/pm/standup.sh +0 -95
  272. package/autopm/.claude/scripts/pm/status.js +0 -148
  273. package/autopm/.claude/scripts/pm/status.sh +0 -59
  274. package/autopm/.claude/scripts/pm/sync-batch.js +0 -337
  275. package/autopm/.claude/scripts/pm/sync.js +0 -343
  276. package/autopm/.claude/scripts/pm/template-list.js +0 -141
  277. package/autopm/.claude/scripts/pm/template-new.js +0 -366
  278. package/autopm/.claude/scripts/pm/validate.js +0 -274
  279. package/autopm/.claude/scripts/pm/validate.sh +0 -106
  280. package/autopm/.claude/scripts/pm/what-next.js +0 -660
  281. package/bin/node/azure-feature-show.js +0 -7
@@ -1,545 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * GitHub Issue Dependency Validator
4
- *
5
- * Validates dependency relationships and checks if issues can be closed:
6
- * - Checks if all dependencies are resolved
7
- * - Detects circular dependencies
8
- * - Validates issue closure readiness
9
- * - Provides blocker status and progress tracking
10
- *
11
- * @module dependency-validator
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const DependencyTracker = require('./dependency-tracker.js');
17
-
18
- /**
19
- * DependencyValidator class for validating GitHub issue dependencies
20
- */
21
- class DependencyValidator {
22
- /**
23
- * Initialize dependency validator
24
- *
25
- * @param {Object} config - Configuration options
26
- * @param {string} config.owner - GitHub repository owner
27
- * @param {string} config.repo - GitHub repository name
28
- * @param {string} config.token - GitHub personal access token
29
- * @param {boolean} config.localMode - Enable local mode
30
- * @param {string} config.cacheDir - Directory for local cache
31
- */
32
- constructor(config = {}) {
33
- this.localMode = config.localMode || false;
34
- this.cacheDir = config.cacheDir || path.join(process.cwd(), '.claude', 'cache');
35
- this.cacheTimeout = config.cacheTimeout || 300000; // 5 minutes default
36
- this.validationCache = new Map();
37
-
38
- // Initialize dependency tracker
39
- this.tracker = new DependencyTracker(config);
40
-
41
- if (this.localMode) {
42
- this._ensureCacheDir();
43
- }
44
- }
45
-
46
- /**
47
- * Ensure cache directory exists
48
- * @private
49
- */
50
- _ensureCacheDir() {
51
- if (!fs.existsSync(this.cacheDir)) {
52
- fs.mkdirSync(this.cacheDir, { recursive: true });
53
- }
54
- }
55
-
56
- /**
57
- * Get cached validation result
58
- * @private
59
- */
60
- _getCached(issueNumber) {
61
- const cached = this.validationCache.get(issueNumber);
62
- if (!cached) return null;
63
-
64
- const now = Date.now();
65
- if (now - cached.timestamp > this.cacheTimeout) {
66
- this.validationCache.delete(issueNumber);
67
- return null;
68
- }
69
-
70
- return cached.data;
71
- }
72
-
73
- /**
74
- * Set cached validation result
75
- * @private
76
- */
77
- _setCached(issueNumber, data) {
78
- this.validationCache.set(issueNumber, {
79
- data,
80
- timestamp: Date.now()
81
- });
82
- }
83
-
84
- /**
85
- * Validate all dependencies for an issue
86
- *
87
- * @param {number} issueNumber - Issue to validate
88
- * @param {Object} options - Validation options
89
- * @param {boolean} options.recursive - Check nested dependencies
90
- * @param {boolean} options.useCache - Use cached results
91
- * @returns {Promise<Object>} Validation result
92
- */
93
- async validateDependencies(issueNumber, options = {}) {
94
- try {
95
- // Check cache
96
- if (options.useCache !== false) {
97
- const cached = this._getCached(issueNumber);
98
- if (cached) return cached;
99
- }
100
-
101
- if (this.localMode) {
102
- return await this._validateLocal(issueNumber, options);
103
- }
104
-
105
- // Get dependencies
106
- const dependencies = await this.tracker.getDependencies(issueNumber);
107
-
108
- if (dependencies.length === 0) {
109
- const result = {
110
- valid: true,
111
- unresolvedDependencies: [],
112
- message: 'No dependencies to validate'
113
- };
114
- this._setCached(issueNumber, result);
115
- return result;
116
- }
117
-
118
- // Check status of each dependency
119
- const unresolvedDependencies = [];
120
-
121
- for (const depNumber of dependencies) {
122
- try {
123
- const depIssue = await this.tracker.octokit.rest.issues.get({
124
- owner: this.tracker.owner,
125
- repo: this.tracker.repo,
126
- issue_number: depNumber
127
- });
128
-
129
- if (depIssue.data.state !== 'closed') {
130
- unresolvedDependencies.push({
131
- number: depIssue.data.number,
132
- title: depIssue.data.title,
133
- state: depIssue.data.state,
134
- url: depIssue.data.html_url
135
- });
136
- }
137
-
138
- // Recursively check nested dependencies if requested
139
- if (options.recursive && depIssue.data.state !== 'closed') {
140
- const nestedValidation = await this.validateDependencies(depNumber, {
141
- ...options,
142
- useCache: true
143
- });
144
- if (!nestedValidation.valid) {
145
- unresolvedDependencies.push(...nestedValidation.unresolvedDependencies);
146
- }
147
- }
148
- } catch (error) {
149
- if (error.status === 404) {
150
- unresolvedDependencies.push({
151
- number: depNumber,
152
- error: 'Issue not found',
153
- state: 'unknown'
154
- });
155
- } else {
156
- throw error;
157
- }
158
- }
159
- }
160
-
161
- const result = {
162
- valid: unresolvedDependencies.length === 0,
163
- unresolvedDependencies,
164
- message: unresolvedDependencies.length === 0
165
- ? 'All dependencies resolved'
166
- : `${unresolvedDependencies.length} unresolved dependencies`
167
- };
168
-
169
- this._setCached(issueNumber, result);
170
- return result;
171
-
172
- } catch (error) {
173
- return {
174
- valid: false,
175
- error: error.message,
176
- unresolvedDependencies: []
177
- };
178
- }
179
- }
180
-
181
- /**
182
- * Validate dependencies in local mode
183
- * @private
184
- */
185
- async _validateLocal(issueNumber, options) {
186
- const depsFile = path.join(this.cacheDir, 'dependencies.json');
187
- const statesFile = path.join(this.cacheDir, 'issue-states.json');
188
-
189
- if (!fs.existsSync(depsFile)) {
190
- return {
191
- valid: true,
192
- unresolvedDependencies: [],
193
- message: 'No dependencies found (local mode)'
194
- };
195
- }
196
-
197
- const dependencies = JSON.parse(fs.readFileSync(depsFile, 'utf8'));
198
- const issueKey = issueNumber.toString();
199
- const issueDeps = dependencies[issueKey] || [];
200
-
201
- if (issueDeps.length === 0) {
202
- return {
203
- valid: true,
204
- unresolvedDependencies: [],
205
- message: 'No dependencies'
206
- };
207
- }
208
-
209
- // Load issue states if available
210
- let states = {};
211
- if (fs.existsSync(statesFile)) {
212
- states = JSON.parse(fs.readFileSync(statesFile, 'utf8'));
213
- }
214
-
215
- const unresolvedDependencies = issueDeps
216
- .filter(dep => states[dep.toString()] !== 'closed')
217
- .map(dep => ({ number: dep, state: states[dep.toString()] || 'unknown' }));
218
-
219
- return {
220
- valid: unresolvedDependencies.length === 0,
221
- unresolvedDependencies,
222
- message: unresolvedDependencies.length === 0
223
- ? 'All dependencies resolved'
224
- : `${unresolvedDependencies.length} unresolved dependencies`
225
- };
226
- }
227
-
228
- /**
229
- * Detect circular dependencies in dependency chain
230
- *
231
- * @param {number} issueNumber - Starting issue
232
- * @param {Object} options - Detection options
233
- * @param {number} options.maxDepth - Maximum depth to search
234
- * @returns {Promise<Object>} Detection result with cycles found
235
- */
236
- async detectCircularDependencies(issueNumber, options = {}) {
237
- const maxDepth = options.maxDepth || 50;
238
- const visited = new Set();
239
- const path = [];
240
- const cycles = [];
241
-
242
- /**
243
- * DFS to detect cycles
244
- */
245
- const detectCycle = async (current, depth = 0) => {
246
- if (depth > maxDepth) {
247
- return; // Prevent infinite loops
248
- }
249
-
250
- if (path.includes(current)) {
251
- // Found a cycle
252
- const cycleStart = path.indexOf(current);
253
- const cycle = [...path.slice(cycleStart), current];
254
- cycles.push(cycle);
255
- return;
256
- }
257
-
258
- if (visited.has(current)) {
259
- return; // Already explored this path
260
- }
261
-
262
- visited.add(current);
263
- path.push(current);
264
-
265
- try {
266
- const dependencies = await this.tracker.getDependencies(current);
267
-
268
- for (const dep of dependencies) {
269
- await detectCycle(dep, depth + 1);
270
- }
271
- } catch (error) {
272
- // Silently ignore errors in cycle detection
273
- }
274
-
275
- path.pop();
276
- };
277
-
278
- await detectCycle(issueNumber);
279
-
280
- return {
281
- hasCircular: cycles.length > 0,
282
- cycles,
283
- message: cycles.length > 0
284
- ? `Found ${cycles.length} circular dependency cycle(s)`
285
- : 'No circular dependencies detected'
286
- };
287
- }
288
-
289
- /**
290
- * Check if an issue can be closed
291
- *
292
- * @param {number} issueNumber - Issue to check
293
- * @returns {Promise<Object>} Result indicating if issue can be closed
294
- */
295
- async canClose(issueNumber) {
296
- try {
297
- // Check if dependencies are resolved
298
- const validation = await this.validateDependencies(issueNumber);
299
-
300
- if (!validation.valid) {
301
- return {
302
- canClose: false,
303
- reason: `Issue #${issueNumber} has unresolved dependencies`,
304
- blockingIssues: validation.unresolvedDependencies
305
- };
306
- }
307
-
308
- // Check if closing would block other issues
309
- const blockedIssues = await this.tracker.getBlockedIssues(issueNumber);
310
-
311
- if (blockedIssues.length > 0) {
312
- // Get details of blocked issues
313
- const openBlockedIssues = [];
314
-
315
- for (const blockedNum of blockedIssues) {
316
- try {
317
- const blockedIssue = await this.tracker.octokit.rest.issues.get({
318
- owner: this.tracker.owner,
319
- repo: this.tracker.repo,
320
- issue_number: blockedNum
321
- });
322
-
323
- if (blockedIssue.data.state === 'open') {
324
- openBlockedIssues.push({
325
- number: blockedIssue.data.number,
326
- title: blockedIssue.data.title,
327
- url: blockedIssue.data.html_url
328
- });
329
- }
330
- } catch {
331
- // Ignore errors for individual issues
332
- }
333
- }
334
-
335
- const result = {
336
- canClose: true,
337
- reason: 'All dependencies resolved',
338
- affectedIssues: openBlockedIssues
339
- };
340
-
341
- if (openBlockedIssues.length > 0) {
342
- result.warning = `Closing this issue will block ${openBlockedIssues.length} open issue(s)`;
343
- }
344
-
345
- return result;
346
- }
347
-
348
- // Check for circular dependencies
349
- const circularCheck = await this.detectCircularDependencies(issueNumber);
350
- const result = {
351
- canClose: true,
352
- reason: 'No dependencies blocking closure'
353
- };
354
-
355
- if (circularCheck.hasCircular) {
356
- result.warning = 'Issue is part of a circular dependency chain';
357
- result.cycles = circularCheck.cycles;
358
- }
359
-
360
- return result;
361
-
362
- } catch (error) {
363
- return {
364
- canClose: false,
365
- reason: 'Error checking closure status',
366
- error: error.message
367
- };
368
- }
369
- }
370
-
371
- /**
372
- * Get status of all blocking issues (dependencies)
373
- *
374
- * @param {number} issueNumber - Issue to check blockers for
375
- * @returns {Promise<Object>} Status of all blockers with progress
376
- */
377
- async getBlockerStatus(issueNumber) {
378
- try {
379
- const dependencies = await this.tracker.getDependencies(issueNumber);
380
-
381
- if (dependencies.length === 0) {
382
- return {
383
- total: 0,
384
- resolved: 0,
385
- unresolved: 0,
386
- progress: 100,
387
- blockers: []
388
- };
389
- }
390
-
391
- const blockers = [];
392
- let resolved = 0;
393
-
394
- for (const depNumber of dependencies) {
395
- try {
396
- const depIssue = await this.tracker.octokit.rest.issues.get({
397
- owner: this.tracker.owner,
398
- repo: this.tracker.repo,
399
- issue_number: depNumber
400
- });
401
-
402
- const blocker = {
403
- number: depIssue.data.number,
404
- title: depIssue.data.title,
405
- state: depIssue.data.state,
406
- url: depIssue.data.html_url,
407
- assignees: depIssue.data.assignees,
408
- labels: depIssue.data.labels
409
- };
410
-
411
- if (depIssue.data.state === 'closed') {
412
- resolved++;
413
- blocker.resolved = true;
414
- } else {
415
- blocker.resolved = false;
416
- }
417
-
418
- blockers.push(blocker);
419
- } catch (error) {
420
- blockers.push({
421
- number: depNumber,
422
- error: 'Failed to fetch issue details',
423
- resolved: false
424
- });
425
- }
426
- }
427
-
428
- const total = dependencies.length;
429
- const progress = (resolved / total) * 100;
430
-
431
- return {
432
- total,
433
- resolved,
434
- unresolved: total - resolved,
435
- progress: Math.round(progress * 100) / 100,
436
- blockers
437
- };
438
-
439
- } catch (error) {
440
- return {
441
- total: 0,
442
- resolved: 0,
443
- unresolved: 0,
444
- progress: 0,
445
- error: error.message,
446
- blockers: []
447
- };
448
- }
449
- }
450
- }
451
-
452
- // CLI interface
453
- if (require.main === module) {
454
- const command = process.argv[2];
455
- const issueNumber = parseInt(process.argv[3], 10);
456
-
457
- const validator = new DependencyValidator();
458
-
459
- (async () => {
460
- switch (command) {
461
- case 'validate':
462
- const validation = await validator.validateDependencies(issueNumber);
463
- console.log('\nDependency Validation:');
464
- console.log('─'.repeat(50));
465
- console.log(`Valid: ${validation.valid ? '✓' : '✗'}`);
466
- console.log(`Message: ${validation.message}`);
467
-
468
- if (validation.unresolvedDependencies.length > 0) {
469
- console.log('\nUnresolved Dependencies:');
470
- validation.unresolvedDependencies.forEach(dep => {
471
- console.log(` • #${dep.number}: ${dep.title || 'Unknown'} (${dep.state})`);
472
- });
473
- }
474
- break;
475
-
476
- case 'circular':
477
- const circular = await validator.detectCircularDependencies(issueNumber);
478
- console.log('\nCircular Dependency Check:');
479
- console.log('─'.repeat(50));
480
- console.log(`Has Circular: ${circular.hasCircular ? 'YES' : 'NO'}`);
481
-
482
- if (circular.hasCircular) {
483
- console.log('\nCycles Found:');
484
- circular.cycles.forEach((cycle, i) => {
485
- console.log(` ${i + 1}. ${cycle.join(' → ')}`);
486
- });
487
- }
488
- break;
489
-
490
- case 'can-close':
491
- const canClose = await validator.canClose(issueNumber);
492
- console.log('\nClosure Check:');
493
- console.log('─'.repeat(50));
494
- console.log(`Can Close: ${canClose.canClose ? '✓ YES' : '✗ NO'}`);
495
- console.log(`Reason: ${canClose.reason}`);
496
-
497
- if (canClose.warning) {
498
- console.log(`\n⚠ Warning: ${canClose.warning}`);
499
- }
500
-
501
- if (canClose.blockingIssues && canClose.blockingIssues.length > 0) {
502
- console.log('\nBlocking Issues:');
503
- canClose.blockingIssues.forEach(issue => {
504
- console.log(` • #${issue.number}: ${issue.title}`);
505
- });
506
- }
507
-
508
- if (canClose.affectedIssues && canClose.affectedIssues.length > 0) {
509
- console.log('\nAffected Issues:');
510
- canClose.affectedIssues.forEach(issue => {
511
- console.log(` • #${issue.number}: ${issue.title}`);
512
- });
513
- }
514
- break;
515
-
516
- case 'status':
517
- case 'blockers':
518
- const status = await validator.getBlockerStatus(issueNumber);
519
- console.log('\nBlocker Status:');
520
- console.log('─'.repeat(50));
521
- console.log(`Total Dependencies: ${status.total}`);
522
- console.log(`Resolved: ${status.resolved}`);
523
- console.log(`Unresolved: ${status.unresolved}`);
524
- console.log(`Progress: ${status.progress}%`);
525
-
526
- if (status.blockers.length > 0) {
527
- console.log('\nBlockers:');
528
- status.blockers.forEach(blocker => {
529
- const icon = blocker.resolved ? '✓' : '○';
530
- console.log(` ${icon} #${blocker.number}: ${blocker.title || 'Unknown'} (${blocker.state})`);
531
- });
532
- }
533
- break;
534
-
535
- default:
536
- console.error('Usage: dependency-validator.js <validate|circular|can-close|status> <issue>');
537
- process.exit(1);
538
- }
539
- })().catch(error => {
540
- console.error('Error:', error.message);
541
- process.exit(1);
542
- });
543
- }
544
-
545
- module.exports = DependencyValidator;