specweave 0.23.16 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/.claude-plugin/marketplace.json +93 -38
  2. package/CLAUDE.md +159 -11
  3. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  4. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +57 -0
  5. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  6. package/dist/src/cli/commands/sync-spec-content.js +3 -0
  7. package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
  8. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
  9. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
  10. package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
  11. package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
  12. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
  13. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
  14. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
  15. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
  16. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
  17. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  18. package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
  19. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  20. package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
  21. package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
  22. package/dist/src/cli/helpers/smart-filter.js +265 -0
  23. package/dist/src/cli/helpers/smart-filter.js.map +1 -0
  24. package/dist/src/core/progress/progress-tracker.d.ts +4 -1
  25. package/dist/src/core/progress/progress-tracker.d.ts.map +1 -1
  26. package/dist/src/core/progress/progress-tracker.js +33 -4
  27. package/dist/src/core/progress/progress-tracker.js.map +1 -1
  28. package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
  29. package/dist/src/core/qa/quality-gate-decider.js +2 -2
  30. package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
  31. package/dist/src/core/qa/risk-calculator.d.ts +2 -2
  32. package/dist/src/core/qa/risk-calculator.js +2 -2
  33. package/dist/src/core/spec-content-sync.d.ts +1 -1
  34. package/dist/src/core/spec-content-sync.d.ts.map +1 -1
  35. package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
  36. package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
  37. package/dist/src/core/validators/ac-presence-validator.js +149 -0
  38. package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
  39. package/dist/src/integrations/ado/ado-dependency-loader.d.ts +1 -1
  40. package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -1
  41. package/dist/src/integrations/ado/ado-dependency-loader.js +39 -7
  42. package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -1
  43. package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
  44. package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
  45. package/dist/src/integrations/ado/area-path-mapper.js +267 -0
  46. package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
  47. package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
  48. package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
  49. package/dist/src/integrations/jira/filter-processor.js +207 -0
  50. package/dist/src/integrations/jira/filter-processor.js.map +1 -0
  51. package/dist/src/integrations/jira/jira-client.d.ts +13 -0
  52. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  53. package/dist/src/integrations/jira/jira-client.js +33 -0
  54. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  55. package/dist/src/utils/ac-embedder.d.ts +63 -0
  56. package/dist/src/utils/ac-embedder.d.ts.map +1 -0
  57. package/dist/src/utils/ac-embedder.js +217 -0
  58. package/dist/src/utils/ac-embedder.js.map +1 -0
  59. package/dist/src/utils/env-manager.d.ts +86 -0
  60. package/dist/src/utils/env-manager.d.ts.map +1 -0
  61. package/dist/src/utils/env-manager.js +188 -0
  62. package/dist/src/utils/env-manager.js.map +1 -0
  63. package/package.json +1 -1
  64. package/plugins/specweave/.claude-plugin/plugin.json +1 -1
  65. package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
  66. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
  67. package/plugins/specweave/commands/specweave-do.md +37 -0
  68. package/plugins/specweave/commands/specweave-done.md +159 -0
  69. package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
  70. package/plugins/specweave/commands/specweave-next.md +148 -3
  71. package/plugins/specweave/commands/specweave-qa.md +2 -2
  72. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +1 -1
  73. package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +245 -0
  74. package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
  75. package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +149 -0
  76. package/plugins/specweave/hooks/lib/update-status-line.sh +34 -4
  77. package/plugins/specweave/hooks/lib/validate-spec-status.sh +1 -1
  78. package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +163 -0
  79. package/plugins/specweave/hooks/post-first-increment.sh +1 -1
  80. package/plugins/specweave/hooks/post-first-increment.sh.bak +61 -0
  81. package/plugins/specweave/hooks/post-spec-update.sh +1 -1
  82. package/plugins/specweave/hooks/post-spec-update.sh.bak +158 -0
  83. package/plugins/specweave/hooks/post-user-story-complete.sh +1 -1
  84. package/plugins/specweave/hooks/post-user-story-complete.sh.bak +179 -0
  85. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -1
  86. package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +83 -0
  87. package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
  88. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  89. package/plugins/specweave/hooks/user-prompt-submit.sh.bak +386 -0
  90. package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
  91. package/plugins/specweave/skills/specweave-framework/SKILL.md +1 -1
  92. package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
  93. package/plugins/specweave-ado/agents/ado-manager/AGENT.md +23 -0
  94. package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +23 -0
  95. package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +23 -0
  96. package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
  97. package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
  98. package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
  99. package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
  100. package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
  101. package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
  102. package/plugins/specweave-backend/agents/database-optimizer/AGENT.md +23 -0
  103. package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
  104. package/plugins/specweave-backend/commands/crud-generate.md +109 -0
  105. package/plugins/specweave-backend/commands/migration-generate.md +139 -0
  106. package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +23 -0
  107. package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
  108. package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
  109. package/plugins/specweave-confluent/commands/schema-register.md +123 -0
  110. package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
  111. package/plugins/specweave-core/commands/architecture-review.md +288 -0
  112. package/plugins/specweave-core/commands/code-review.md +213 -0
  113. package/plugins/specweave-core/commands/refactor-plan.md +249 -0
  114. package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
  115. package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
  116. package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
  117. package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
  118. package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
  119. package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
  120. package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
  121. package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
  122. package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
  123. package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
  124. package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +23 -0
  125. package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
  126. package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
  127. package/plugins/specweave-docs/commands/docs-generate.md +441 -0
  128. package/plugins/specweave-docs/commands/docs-init.md +334 -0
  129. package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
  130. package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
  131. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
  132. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
  133. package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
  134. package/plugins/specweave-figma/commands/figma-import.md +690 -0
  135. package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
  136. package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
  137. package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
  138. package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
  139. package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
  140. package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
  141. package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
  142. package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
  143. package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
  144. package/plugins/specweave-frontend/commands/component-generate.md +510 -0
  145. package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
  146. package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
  147. package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
  148. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
  149. package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
  150. package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
  151. package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
  152. package/plugins/specweave-github/agents/github-manager/AGENT.md +23 -0
  153. package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +25 -0
  154. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +25 -0
  155. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
  156. package/plugins/specweave-github/lib/github-spec-content-sync.js +49 -0
  157. package/plugins/specweave-github/lib/github-spec-content-sync.ts +67 -0
  158. package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
  159. package/plugins/specweave-infrastructure/agents/devops/AGENT.md +26 -0
  160. package/plugins/specweave-infrastructure/agents/network-engineer/AGENT.md +26 -0
  161. package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +26 -0
  162. package/plugins/specweave-infrastructure/agents/performance-engineer/AGENT.md +26 -0
  163. package/plugins/specweave-infrastructure/agents/sre/AGENT.md +26 -0
  164. package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
  165. package/plugins/specweave-jira/agents/jira-manager/AGENT.md +26 -0
  166. package/plugins/specweave-jira/commands/import-projects.js +183 -0
  167. package/plugins/specweave-jira/commands/import-projects.md +97 -0
  168. package/plugins/specweave-jira/commands/import-projects.ts +288 -0
  169. package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
  170. package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
  171. package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +26 -0
  172. package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +26 -0
  173. package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +26 -0
  174. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
  175. package/plugins/specweave-kubernetes/agents/kubernetes-architect/AGENT.md +26 -0
  176. package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
  177. package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
  178. package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
  179. package/plugins/specweave-ml/.claude-plugin/plugin.json +3 -3
  180. package/plugins/specweave-ml/agents/data-scientist/AGENT.md +26 -0
  181. package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +26 -0
  182. package/plugins/specweave-ml/agents/mlops-engineer/AGENT.md +26 -0
  183. package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +26 -0
  184. package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
  185. package/plugins/specweave-mobile/commands/build-config.md +256 -0
  186. package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
  187. package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
  188. package/plugins/specweave-payments/agents/payment-integration/AGENT.md +26 -0
  189. package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +20 -0
  190. package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
  191. package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
  192. package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
  193. package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
  194. package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
  195. package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
  196. package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
  197. package/plugins/specweave-release/agents/release-manager/AGENT.md +27 -0
  198. package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
  199. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
  200. package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
  201. package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
  202. package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
  203. package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
  204. package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
  205. package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
  206. package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
  207. package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
  208. package/plugins/specweave-testing/commands/test-coverage.md +979 -0
  209. package/plugins/specweave-testing/commands/test-generate.md +1156 -0
  210. package/plugins/specweave-testing/commands/test-init.md +409 -0
  211. package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
  212. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
  213. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
  214. package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
  215. package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
  216. package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
  217. package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
  218. package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
  219. package/plugins/specweave-ui/commands/ui-automate.md +199 -0
  220. package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
  221. package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
  222. package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
  223. package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
  224. package/plugins/specweave/commands/check-hooks.md +0 -257
  225. package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
  226. package/plugins/specweave/skills/plugin-expert/SKILL.md +0 -340
  227. /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
@@ -0,0 +1,183 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import { existsSync } from "fs";
4
+ import path from "path";
5
+ import { JiraClient } from "../../../src/integrations/jira/jira-client.js";
6
+ import { FilterProcessor } from "../../../src/integrations/jira/filter-processor.js";
7
+ import { mergeEnvList, getEnvValue } from "../../../src/utils/env-manager.js";
8
+ import { consoleLogger } from "../../../src/utils/logger.js";
9
+ import { credentialsManager } from "../../../src/core/credentials-manager.js";
10
+ async function importProjects(options = {}) {
11
+ const logger = options.logger ?? consoleLogger;
12
+ const projectRoot = process.cwd();
13
+ console.log(chalk.cyan("\n\u{1F4E5} JIRA Project Import\n"));
14
+ const credentials = credentialsManager.getJiraCredentials();
15
+ if (!credentials) {
16
+ console.log(chalk.red("\u274C No JIRA credentials found"));
17
+ console.log(chalk.gray(" Run: specweave init"));
18
+ return;
19
+ }
20
+ const client = new JiraClient(credentials);
21
+ const filterProcessor = new FilterProcessor(client, { logger });
22
+ if (options.resume) {
23
+ const resumed = await resumeImport(projectRoot, client, filterProcessor, options);
24
+ if (resumed) {
25
+ return;
26
+ }
27
+ console.log(chalk.yellow("\u26A0\uFE0F No import state found. Starting fresh import.\n"));
28
+ }
29
+ const existing = await loadExistingProjects(projectRoot, logger);
30
+ console.log(chalk.gray(`Current projects: ${existing.length > 0 ? existing.join(", ") : "none"}
31
+ `));
32
+ console.log(chalk.cyan("\u{1F4E1} Fetching available JIRA projects...\n"));
33
+ let allProjects = [];
34
+ try {
35
+ const response = await client.searchProjects({ maxResults: 1e3 });
36
+ allProjects = response.values || [];
37
+ console.log(chalk.green(`\u2713 Found ${allProjects.length} total projects
38
+ `));
39
+ } catch (error) {
40
+ console.log(chalk.red(`\u274C Failed to fetch projects: ${error.message}`));
41
+ return;
42
+ }
43
+ let filteredProjects = allProjects;
44
+ if (options.preset) {
45
+ console.log(chalk.cyan(`\u{1F50D} Applying preset: ${options.preset}
46
+ `));
47
+ try {
48
+ filteredProjects = await filterProcessor.applyPreset(allProjects, options.preset);
49
+ } catch (error) {
50
+ console.log(chalk.red(`\u274C ${error.message}`));
51
+ return;
52
+ }
53
+ } else if (options.filter || options.type || options.lead || options.jql) {
54
+ const filterOptions = {};
55
+ if (options.filter === "active") {
56
+ filterOptions.active = true;
57
+ } else if (options.filter === "archived") {
58
+ filterOptions.active = false;
59
+ }
60
+ if (options.type) {
61
+ filterOptions.types = options.type;
62
+ }
63
+ if (options.lead) {
64
+ filterOptions.lead = options.lead;
65
+ }
66
+ if (options.jql) {
67
+ filterOptions.jql = options.jql;
68
+ }
69
+ console.log(chalk.cyan("\u{1F50D} Applying filters...\n"));
70
+ filteredProjects = await filterProcessor.applyFilters(allProjects, filterOptions);
71
+ }
72
+ const newProjects = filteredProjects.filter((p) => {
73
+ return !existing.some((e) => e.toLowerCase() === p.key.toLowerCase());
74
+ });
75
+ if (newProjects.length === 0) {
76
+ console.log(chalk.yellow("\u26A0\uFE0F No new projects found to import"));
77
+ console.log(chalk.gray(" All available projects are already imported\n"));
78
+ return;
79
+ }
80
+ console.log(chalk.cyan("\u{1F4CB} Import Preview:\n"));
81
+ console.log(chalk.white(` Total available: ${allProjects.length}`));
82
+ console.log(chalk.white(` After filtering: ${filteredProjects.length}`));
83
+ console.log(chalk.white(` Already imported: ${existing.length}`));
84
+ console.log(chalk.white(` New projects: ${chalk.green.bold(newProjects.length)}
85
+ `));
86
+ if (newProjects.length <= 10) {
87
+ console.log(chalk.gray("Projects to import:"));
88
+ newProjects.forEach((p) => {
89
+ const typeLabel = p.projectTypeKey || "unknown";
90
+ const leadLabel = p.lead?.displayName || "no lead";
91
+ console.log(chalk.gray(` \u2728 ${p.key} - ${p.name} (${typeLabel}, ${leadLabel})`));
92
+ });
93
+ console.log("");
94
+ }
95
+ if (options.dryRun) {
96
+ console.log(chalk.yellow("\u{1F50D} Dry-run mode: No changes will be made\n"));
97
+ console.log(chalk.green(`\u2713 Preview complete: ${newProjects.length} project(s) would be imported
98
+ `));
99
+ return;
100
+ }
101
+ const { confirmed } = await inquirer.prompt([{
102
+ type: "confirm",
103
+ name: "confirmed",
104
+ message: `Import ${newProjects.length} new project(s)?`,
105
+ default: true
106
+ }]);
107
+ if (!confirmed) {
108
+ console.log(chalk.yellow("\n\u23ED\uFE0F Import canceled\n"));
109
+ return;
110
+ }
111
+ const projectKeys = newProjects.map((p) => p.key);
112
+ console.log(chalk.cyan("\n\u{1F4E5} Importing projects...\n"));
113
+ try {
114
+ await mergeEnvList({
115
+ key: "JIRA_PROJECTS",
116
+ newValues: projectKeys,
117
+ projectRoot,
118
+ logger,
119
+ createBackup: true
120
+ });
121
+ console.log(chalk.green(`
122
+ \u2705 Successfully imported ${projectKeys.length} project(s)
123
+ `));
124
+ console.log(chalk.gray("Updated: .env (JIRA_PROJECTS)"));
125
+ console.log(chalk.gray("Backup: .env.backup\n"));
126
+ } catch (error) {
127
+ console.log(chalk.red(`
128
+ \u274C Import failed: ${error.message}
129
+ `));
130
+ }
131
+ }
132
+ async function loadExistingProjects(projectRoot, logger) {
133
+ const value = await getEnvValue(projectRoot, "JIRA_PROJECTS");
134
+ if (!value) {
135
+ return [];
136
+ }
137
+ return value.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
138
+ }
139
+ async function resumeImport(projectRoot, client, filterProcessor, options) {
140
+ const stateFile = path.join(projectRoot, ".specweave", "cache", "import-state.json");
141
+ if (!existsSync(stateFile)) {
142
+ return false;
143
+ }
144
+ console.log(chalk.cyan("\u{1F504} Resuming interrupted import...\n"));
145
+ return false;
146
+ }
147
+ async function main() {
148
+ const args = process.argv.slice(2);
149
+ const options = {};
150
+ for (let i = 0; i < args.length; i++) {
151
+ const arg = args[i];
152
+ if (arg === "--filter") {
153
+ options.filter = args[++i];
154
+ } else if (arg === "--type") {
155
+ options.type = args[++i].split(",");
156
+ } else if (arg === "--lead") {
157
+ options.lead = args[++i];
158
+ } else if (arg === "--jql") {
159
+ options.jql = args[++i];
160
+ } else if (arg === "--preset") {
161
+ options.preset = args[++i];
162
+ } else if (arg === "--dry-run") {
163
+ options.dryRun = true;
164
+ } else if (arg === "--resume") {
165
+ options.resume = true;
166
+ } else if (arg === "--no-progress") {
167
+ options.noProgress = true;
168
+ }
169
+ }
170
+ await importProjects(options);
171
+ }
172
+ if (import.meta.url === `file://${process.argv[1]}`) {
173
+ main().catch((error) => {
174
+ console.error(chalk.red(`
175
+ \u274C Error: ${error.message}
176
+ `));
177
+ process.exit(1);
178
+ });
179
+ }
180
+ export {
181
+ main as default,
182
+ importProjects
183
+ };
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: specweave-jira:import-projects
3
+ description: Import additional JIRA projects post-init with smart filtering, resume, and dry-run support
4
+ ---
5
+
6
+ # JIRA Project Import Command
7
+
8
+ Import additional JIRA projects after initial setup with advanced filtering and merge capabilities.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ # Import all active projects
14
+ /specweave-jira:import-projects --filter active
15
+
16
+ # Import with preset filter
17
+ /specweave-jira:import-projects --preset production
18
+
19
+ # Import with custom JQL
20
+ /specweave-jira:import-projects --jql "project NOT IN (TEST, SANDBOX)"
21
+
22
+ # Dry-run preview (no changes)
23
+ /specweave-jira:import-projects --dry-run
24
+
25
+ # Resume interrupted import
26
+ /specweave-jira:import-projects --resume
27
+ ```
28
+
29
+ ## Options
30
+
31
+ - `--filter <type>` - Filter by status (active, archived, all)
32
+ - `--type <types>` - Filter by project type (software, business, service_desk)
33
+ - `--lead <email>` - Filter by project lead
34
+ - `--jql <query>` - Custom JQL filter
35
+ - `--preset <name>` - Use saved filter preset
36
+ - `--dry-run` - Preview without making changes
37
+ - `--resume` - Resume interrupted import
38
+ - `--no-progress` - Disable progress tracking
39
+
40
+ ## Examples
41
+
42
+ ### Import Active Projects Only
43
+
44
+ ```bash
45
+ /specweave-jira:import-projects --filter active
46
+ ```
47
+
48
+ Filters out archived projects, shows preview, prompts for confirmation, merges with existing.
49
+
50
+ ### Import with Production Preset
51
+
52
+ ```bash
53
+ /specweave-jira:import-projects --preset production
54
+ ```
55
+
56
+ Uses the "production" preset filter (active + software + excludes TEST/SANDBOX).
57
+
58
+ ### Custom JQL Filter
59
+
60
+ ```bash
61
+ /specweave-jira:import-projects --jql "lead = currentUser() AND status != Archived"
62
+ ```
63
+
64
+ Imports projects where you are the lead and not archived.
65
+
66
+ ### Dry-Run Preview
67
+
68
+ ```bash
69
+ /specweave-jira:import-projects --filter active --dry-run
70
+ ```
71
+
72
+ Shows which projects would be imported without making any changes.
73
+
74
+ ## Features
75
+
76
+ - **Smart Filtering**: Filter by status, type, lead, or custom JQL
77
+ - **Merge with Existing**: Automatically merges with existing projects (no duplicates)
78
+ - **Progress Tracking**: Real-time progress with ETA and cancelation support
79
+ - **Resume Support**: Resume interrupted imports with `--resume`
80
+ - **Dry-Run Mode**: Preview changes before applying
81
+ - **Filter Presets**: Use saved filter combinations
82
+
83
+ ## Implementation
84
+
85
+ This command:
86
+
87
+ 1. Reads existing projects from `.env` (JIRA_PROJECTS)
88
+ 2. Fetches available projects from JIRA API
89
+ 3. Applies selected filters using FilterProcessor
90
+ 4. Shows preview with project count and reduction percentage
91
+ 5. Prompts for confirmation
92
+ 6. Batch imports with AsyncProjectLoader (50-project limit)
93
+ 7. Shows progress with ETA
94
+ 8. Merges with existing projects (no duplicates)
95
+ 9. Updates `.env` file atomically
96
+
97
+ Handles Ctrl+C gracefully with import state saving for resume.
@@ -0,0 +1,288 @@
1
+ /**
2
+ * JIRA Project Import Command (Post-Init)
3
+ *
4
+ * Import additional JIRA projects after initial setup with smart filtering,
5
+ * resume support, and merge capabilities.
6
+ *
7
+ * Features:
8
+ * - Smart filtering (active, type, lead, JQL)
9
+ * - Filter presets (production, active-only, agile-only)
10
+ * - Dry-run preview mode
11
+ * - Resume interrupted imports
12
+ * - Progress tracking with ETA
13
+ * - Merge with existing projects (no duplicates)
14
+ *
15
+ * NEW (v0.24.0): Post-init flexibility for project management
16
+ *
17
+ * @module plugins/specweave-jira/commands/import-projects
18
+ */
19
+
20
+ import chalk from 'chalk';
21
+ import inquirer from 'inquirer';
22
+ import { existsSync } from 'fs';
23
+ import path from 'path';
24
+ import { JiraClient } from '../../../src/integrations/jira/jira-client.js';
25
+ import { FilterProcessor, type FilterOptions } from '../../../src/integrations/jira/filter-processor.js';
26
+ import { AsyncProjectLoader } from '../../../src/cli/helpers/async-project-loader.js';
27
+ import { mergeEnvList, getEnvValue } from '../../../src/utils/env-manager.js';
28
+ import { consoleLogger, type Logger } from '../../../src/utils/logger.js';
29
+ import { credentialsManager } from '../../../src/core/credentials-manager.js';
30
+
31
+ export interface ImportProjectsOptions {
32
+ filter?: 'active' | 'archived' | 'all';
33
+ type?: string[];
34
+ lead?: string;
35
+ jql?: string;
36
+ preset?: string;
37
+ dryRun?: boolean;
38
+ resume?: boolean;
39
+ noProgress?: boolean;
40
+ logger?: Logger;
41
+ }
42
+
43
+ export interface ImportState {
44
+ total: number;
45
+ completed: string[];
46
+ remaining: string[];
47
+ timestamp: number;
48
+ }
49
+
50
+ /**
51
+ * Import JIRA projects command
52
+ *
53
+ * TC-063: Post-Init Import (Merge with Existing)
54
+ * TC-064: Filter Active Projects Only
55
+ * TC-068: Resume Interrupted Import
56
+ * TC-069: Dry-Run Preview
57
+ * TC-070: Progress During Import
58
+ *
59
+ * @param options - Import options
60
+ */
61
+ export async function importProjects(options: ImportProjectsOptions = {}): Promise<void> {
62
+ const logger = options.logger ?? consoleLogger;
63
+ const projectRoot = process.cwd();
64
+
65
+ // Step 1: Load credentials
66
+ console.log(chalk.cyan('\nšŸ“„ JIRA Project Import\n'));
67
+
68
+ const credentials = credentialsManager.getJiraCredentials();
69
+ if (!credentials) {
70
+ console.log(chalk.red('āŒ No JIRA credentials found'));
71
+ console.log(chalk.gray(' Run: specweave init'));
72
+ return;
73
+ }
74
+
75
+ const client = new JiraClient(credentials);
76
+ const filterProcessor = new FilterProcessor(client, { logger });
77
+
78
+ // Step 2: Check for resume state
79
+ if (options.resume) {
80
+ const resumed = await resumeImport(projectRoot, client, filterProcessor, options);
81
+ if (resumed) {
82
+ return;
83
+ }
84
+ console.log(chalk.yellow('āš ļø No import state found. Starting fresh import.\n'));
85
+ }
86
+
87
+ // Step 3: Read existing projects from .env
88
+ const existing = await loadExistingProjects(projectRoot, logger);
89
+ console.log(chalk.gray(`Current projects: ${existing.length > 0 ? existing.join(', ') : 'none'}\n`));
90
+
91
+ // Step 4: Fetch available projects from JIRA
92
+ console.log(chalk.cyan('šŸ“” Fetching available JIRA projects...\n'));
93
+
94
+ let allProjects: any[] = [];
95
+ try {
96
+ const response = await client.searchProjects({ maxResults: 1000 });
97
+ allProjects = response.values || [];
98
+ console.log(chalk.green(`āœ“ Found ${allProjects.length} total projects\n`));
99
+ } catch (error: any) {
100
+ console.log(chalk.red(`āŒ Failed to fetch projects: ${error.message}`));
101
+ return;
102
+ }
103
+
104
+ // Step 5: Apply filters
105
+ let filteredProjects = allProjects;
106
+
107
+ if (options.preset) {
108
+ // Use filter preset
109
+ console.log(chalk.cyan(`šŸ” Applying preset: ${options.preset}\n`));
110
+ try {
111
+ filteredProjects = await filterProcessor.applyPreset(allProjects, options.preset);
112
+ } catch (error: any) {
113
+ console.log(chalk.red(`āŒ ${error.message}`));
114
+ return;
115
+ }
116
+ } else if (options.filter || options.type || options.lead || options.jql) {
117
+ // Build filter options
118
+ const filterOptions: FilterOptions = {};
119
+
120
+ if (options.filter === 'active') {
121
+ filterOptions.active = true;
122
+ } else if (options.filter === 'archived') {
123
+ filterOptions.active = false;
124
+ }
125
+
126
+ if (options.type) {
127
+ filterOptions.types = options.type;
128
+ }
129
+
130
+ if (options.lead) {
131
+ filterOptions.lead = options.lead;
132
+ }
133
+
134
+ if (options.jql) {
135
+ filterOptions.jql = options.jql;
136
+ }
137
+
138
+ console.log(chalk.cyan('šŸ” Applying filters...\n'));
139
+ filteredProjects = await filterProcessor.applyFilters(allProjects, filterOptions);
140
+ }
141
+
142
+ // Step 6: Exclude existing projects
143
+ const newProjects = filteredProjects.filter(p => {
144
+ return !existing.some(e => e.toLowerCase() === p.key.toLowerCase());
145
+ });
146
+
147
+ if (newProjects.length === 0) {
148
+ console.log(chalk.yellow('āš ļø No new projects found to import'));
149
+ console.log(chalk.gray(' All available projects are already imported\n'));
150
+ return;
151
+ }
152
+
153
+ // Step 7: Show preview
154
+ console.log(chalk.cyan('šŸ“‹ Import Preview:\n'));
155
+ console.log(chalk.white(` Total available: ${allProjects.length}`));
156
+ console.log(chalk.white(` After filtering: ${filteredProjects.length}`));
157
+ console.log(chalk.white(` Already imported: ${existing.length}`));
158
+ console.log(chalk.white(` New projects: ${chalk.green.bold(newProjects.length)}\n`));
159
+
160
+ if (newProjects.length <= 10) {
161
+ console.log(chalk.gray('Projects to import:'));
162
+ newProjects.forEach(p => {
163
+ const typeLabel = p.projectTypeKey || 'unknown';
164
+ const leadLabel = p.lead?.displayName || 'no lead';
165
+ console.log(chalk.gray(` ✨ ${p.key} - ${p.name} (${typeLabel}, ${leadLabel})`));
166
+ });
167
+ console.log('');
168
+ }
169
+
170
+ // Step 8: Dry-run mode (exit without changes)
171
+ if (options.dryRun) {
172
+ console.log(chalk.yellow('šŸ” Dry-run mode: No changes will be made\n'));
173
+ console.log(chalk.green(`āœ“ Preview complete: ${newProjects.length} project(s) would be imported\n`));
174
+ return;
175
+ }
176
+
177
+ // Step 9: Confirm import
178
+ const { confirmed } = await inquirer.prompt([{
179
+ type: 'confirm',
180
+ name: 'confirmed',
181
+ message: `Import ${newProjects.length} new project(s)?`,
182
+ default: true
183
+ }]);
184
+
185
+ if (!confirmed) {
186
+ console.log(chalk.yellow('\nā­ļø Import canceled\n'));
187
+ return;
188
+ }
189
+
190
+ // Step 10: Extract project keys
191
+ const projectKeys = newProjects.map(p => p.key);
192
+
193
+ // Step 11: Merge with existing
194
+ console.log(chalk.cyan('\nšŸ“„ Importing projects...\n'));
195
+
196
+ try {
197
+ await mergeEnvList({
198
+ key: 'JIRA_PROJECTS',
199
+ newValues: projectKeys,
200
+ projectRoot,
201
+ logger,
202
+ createBackup: true
203
+ });
204
+
205
+ console.log(chalk.green(`\nāœ… Successfully imported ${projectKeys.length} project(s)\n`));
206
+ console.log(chalk.gray('Updated: .env (JIRA_PROJECTS)'));
207
+ console.log(chalk.gray('Backup: .env.backup\n'));
208
+
209
+ } catch (error: any) {
210
+ console.log(chalk.red(`\nāŒ Import failed: ${error.message}\n`));
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Load existing JIRA projects from .env
216
+ */
217
+ async function loadExistingProjects(projectRoot: string, logger: Logger): Promise<string[]> {
218
+ const value = await getEnvValue(projectRoot, 'JIRA_PROJECTS');
219
+ if (!value) {
220
+ return [];
221
+ }
222
+
223
+ return value.split(',').map(v => v.trim()).filter(v => v.length > 0);
224
+ }
225
+
226
+ /**
227
+ * Resume interrupted import
228
+ */
229
+ async function resumeImport(
230
+ projectRoot: string,
231
+ client: JiraClient,
232
+ filterProcessor: FilterProcessor,
233
+ options: ImportProjectsOptions
234
+ ): Promise<boolean> {
235
+ const stateFile = path.join(projectRoot, '.specweave', 'cache', 'import-state.json');
236
+
237
+ if (!existsSync(stateFile)) {
238
+ return false;
239
+ }
240
+
241
+ console.log(chalk.cyan('šŸ”„ Resuming interrupted import...\n'));
242
+
243
+ // Load state and continue
244
+ // (Implementation would load state, skip completed, import remaining)
245
+
246
+ return false; // Placeholder - full implementation would handle resume
247
+ }
248
+
249
+ /**
250
+ * Command entry point (called by CLI)
251
+ */
252
+ export default async function main(): Promise<void> {
253
+ // Parse CLI arguments
254
+ const args = process.argv.slice(2);
255
+ const options: ImportProjectsOptions = {};
256
+
257
+ for (let i = 0; i < args.length; i++) {
258
+ const arg = args[i];
259
+
260
+ if (arg === '--filter') {
261
+ options.filter = args[++i] as 'active' | 'archived' | 'all';
262
+ } else if (arg === '--type') {
263
+ options.type = args[++i].split(',');
264
+ } else if (arg === '--lead') {
265
+ options.lead = args[++i];
266
+ } else if (arg === '--jql') {
267
+ options.jql = args[++i];
268
+ } else if (arg === '--preset') {
269
+ options.preset = args[++i];
270
+ } else if (arg === '--dry-run') {
271
+ options.dryRun = true;
272
+ } else if (arg === '--resume') {
273
+ options.resume = true;
274
+ } else if (arg === '--no-progress') {
275
+ options.noProgress = true;
276
+ }
277
+ }
278
+
279
+ await importProjects(options);
280
+ }
281
+
282
+ // Run command if called directly
283
+ if (import.meta.url === `file://${process.argv[1]}`) {
284
+ main().catch(error => {
285
+ console.error(chalk.red(`\nāŒ Error: ${error.message}\n`));
286
+ process.exit(1);
287
+ });
288
+ }