specweave 0.23.18 → 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.
- package/.claude-plugin/marketplace.json +93 -49
- package/CLAUDE.md +137 -4
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
- package/dist/src/cli/helpers/smart-filter.js +265 -0
- package/dist/src/cli/helpers/smart-filter.js.map +1 -0
- package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
- package/dist/src/core/qa/quality-gate-decider.js +2 -2
- package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
- package/dist/src/core/qa/risk-calculator.d.ts +2 -2
- package/dist/src/core/qa/risk-calculator.js +2 -2
- package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
- package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
- package/dist/src/core/validators/ac-presence-validator.js +149 -0
- package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.js +267 -0
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
- package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
- package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
- package/dist/src/integrations/jira/filter-processor.js +207 -0
- package/dist/src/integrations/jira/filter-processor.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +13 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +33 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/utils/ac-embedder.d.ts +63 -0
- package/dist/src/utils/ac-embedder.d.ts.map +1 -0
- package/dist/src/utils/ac-embedder.js +217 -0
- package/dist/src/utils/ac-embedder.js.map +1 -0
- package/dist/src/utils/env-manager.d.ts +86 -0
- package/dist/src/utils/env-manager.d.ts.map +1 -0
- package/dist/src/utils/env-manager.js +188 -0
- package/dist/src/utils/env-manager.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
- package/plugins/specweave/commands/specweave-do.md +37 -0
- package/plugins/specweave/commands/specweave-done.md +159 -0
- package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
- package/plugins/specweave/commands/specweave-next.md +148 -3
- package/plugins/specweave/commands/specweave-qa.md +2 -2
- package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
- package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
- package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
- package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
- package/plugins/specweave-backend/commands/crud-generate.md +109 -0
- package/plugins/specweave-backend/commands/migration-generate.md +139 -0
- package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
- package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
- package/plugins/specweave-confluent/commands/schema-register.md +123 -0
- package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-core/commands/architecture-review.md +288 -0
- package/plugins/specweave-core/commands/code-review.md +213 -0
- package/plugins/specweave-core/commands/refactor-plan.md +249 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
- package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
- package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
- package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
- package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
- package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-docs/commands/docs-generate.md +441 -0
- package/plugins/specweave-docs/commands/docs-init.md +334 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
- package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
- package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-figma/commands/figma-import.md +690 -0
- package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
- package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
- package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
- package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
- package/plugins/specweave-frontend/commands/component-generate.md +510 -0
- package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
- package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
- package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
- package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
- package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/commands/import-projects.js +183 -0
- package/plugins/specweave-jira/commands/import-projects.md +97 -0
- package/plugins/specweave-jira/commands/import-projects.ts +288 -0
- package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
- package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
- package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
- package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
- package/plugins/specweave-mobile/commands/build-config.md +256 -0
- package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
- package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
- package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
- package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
- package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
- package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
- package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
- package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
- package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
- package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
- package/plugins/specweave-testing/commands/test-coverage.md +979 -0
- package/plugins/specweave-testing/commands/test-generate.md +1156 -0
- package/plugins/specweave-testing/commands/test-init.md +409 -0
- package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
- package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
- package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-ui/commands/ui-automate.md +199 -0
- package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
- package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
- package/plugins/specweave/commands/check-hooks.md +0 -257
- package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
- package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
- /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: specweave-jira:import-projects
|
|
3
|
+
description: Import additional JIRA projects post-init with filtering, resume support, and dry-run preview
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Import JIRA Projects Command
|
|
7
|
+
|
|
8
|
+
You are a JIRA project import expert. Help users add additional JIRA projects to their SpecWeave workspace after initial setup.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
This command allows users to import additional JIRA projects **after** initial SpecWeave setup (`specweave init`), with advanced filtering, resume capability, and dry-run preview.
|
|
13
|
+
|
|
14
|
+
**Use Cases**:
|
|
15
|
+
- Adding new JIRA projects to existing workspace
|
|
16
|
+
- Importing archived/paused projects later
|
|
17
|
+
- Selective import with filters (active only, specific types, custom JQL)
|
|
18
|
+
|
|
19
|
+
## Command Syntax
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Basic import (interactive)
|
|
23
|
+
/specweave-jira:import-projects
|
|
24
|
+
|
|
25
|
+
# With filters
|
|
26
|
+
/specweave-jira:import-projects --filter active
|
|
27
|
+
/specweave-jira:import-projects --type agile --lead "john.doe@company.com"
|
|
28
|
+
/specweave-jira:import-projects --jql "project IN (BACKEND, FRONTEND) AND status != Archived"
|
|
29
|
+
|
|
30
|
+
# Dry-run (preview)
|
|
31
|
+
/specweave-jira:import-projects --dry-run
|
|
32
|
+
|
|
33
|
+
# Resume interrupted import
|
|
34
|
+
/specweave-jira:import-projects --resume
|
|
35
|
+
|
|
36
|
+
# Combined
|
|
37
|
+
/specweave-jira:import-projects --filter active --dry-run
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Your Task
|
|
41
|
+
|
|
42
|
+
When the user runs this command:
|
|
43
|
+
|
|
44
|
+
### Step 1: Validate Prerequisites
|
|
45
|
+
```typescript
|
|
46
|
+
import { readEnvFile, parseEnvFile } from '../../../src/utils/env-file.js';
|
|
47
|
+
|
|
48
|
+
// 1. Check if Jira credentials exist
|
|
49
|
+
const envContent = readEnvFile(process.cwd());
|
|
50
|
+
const parsed = parseEnvFile(envContent);
|
|
51
|
+
|
|
52
|
+
if (!parsed.JIRA_API_TOKEN || !parsed.JIRA_EMAIL || !parsed.JIRA_DOMAIN) {
|
|
53
|
+
console.log('❌ Missing Jira credentials. Run `specweave init` first.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Get existing projects
|
|
58
|
+
const existingProjects = parsed.JIRA_PROJECTS?.split(',') || [];
|
|
59
|
+
console.log(`\n📋 Current projects: ${existingProjects.join(', ') || 'None'}\n`);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 2: Fetch Available Projects
|
|
63
|
+
```typescript
|
|
64
|
+
import { getProjectCount } from '../../../src/cli/helpers/project-count-fetcher.js';
|
|
65
|
+
import { AsyncProjectLoader } from '../../../src/cli/helpers/async-project-loader.js';
|
|
66
|
+
|
|
67
|
+
// Count check (< 1 second)
|
|
68
|
+
const countResult = await getProjectCount({
|
|
69
|
+
provider: 'jira',
|
|
70
|
+
credentials: {
|
|
71
|
+
domain: parsed.JIRA_DOMAIN,
|
|
72
|
+
email: parsed.JIRA_EMAIL,
|
|
73
|
+
token: parsed.JIRA_API_TOKEN,
|
|
74
|
+
instanceType: 'cloud'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`✓ Found ${countResult.accessible} accessible projects`);
|
|
79
|
+
|
|
80
|
+
// Fetch all projects (with smart pagination)
|
|
81
|
+
const loader = new AsyncProjectLoader(
|
|
82
|
+
{
|
|
83
|
+
domain: parsed.JIRA_DOMAIN,
|
|
84
|
+
email: parsed.JIRA_EMAIL,
|
|
85
|
+
token: parsed.JIRA_API_TOKEN,
|
|
86
|
+
instanceType: 'cloud'
|
|
87
|
+
},
|
|
88
|
+
'jira',
|
|
89
|
+
{
|
|
90
|
+
batchSize: 50,
|
|
91
|
+
updateFrequency: 5,
|
|
92
|
+
showEta: true
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const result = await loader.fetchAllProjects(countResult.accessible);
|
|
97
|
+
let allProjects = result.projects;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Step 3: Apply Filters (if specified)
|
|
101
|
+
```typescript
|
|
102
|
+
import { FilterProcessor } from '../../../src/integrations/jira/filter-processor.js';
|
|
103
|
+
|
|
104
|
+
const options = {
|
|
105
|
+
filter: args.filter, // 'active' | 'all'
|
|
106
|
+
type: args.type, // 'agile' | 'software' | 'business'
|
|
107
|
+
lead: args.lead, // Email address
|
|
108
|
+
jql: args.jql // Custom JQL
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const filterProcessor = new FilterProcessor({ domain: parsed.JIRA_DOMAIN, token: parsed.JIRA_API_TOKEN });
|
|
112
|
+
const filteredProjects = await filterProcessor.applyFilters(allProjects, options);
|
|
113
|
+
|
|
114
|
+
console.log(`\n🔍 Filters applied:`);
|
|
115
|
+
if (options.filter === 'active') console.log(` • Active projects only`);
|
|
116
|
+
if (options.type) console.log(` • Type: ${options.type}`);
|
|
117
|
+
if (options.lead) console.log(` • Lead: ${options.lead}`);
|
|
118
|
+
if (options.jql) console.log(` • JQL: ${options.jql}`);
|
|
119
|
+
console.log(`\n📊 Results: ${filteredProjects.length} projects (down from ${allProjects.length})\n`);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Step 4: Exclude Existing Projects
|
|
123
|
+
```typescript
|
|
124
|
+
const newProjects = filteredProjects.filter(p => !existingProjects.includes(p.key));
|
|
125
|
+
|
|
126
|
+
if (newProjects.length === 0) {
|
|
127
|
+
console.log('✅ No new projects to import. All filtered projects are already configured.');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`📥 ${newProjects.length} new project(s) available for import:\n`);
|
|
132
|
+
newProjects.forEach(p => {
|
|
133
|
+
console.log(` ✨ ${p.key} - ${p.name} (${p.projectTypeKey}, lead: ${p.lead?.displayName || 'N/A'})`);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 5: Dry-Run or Execute
|
|
138
|
+
```typescript
|
|
139
|
+
if (args.dryRun) {
|
|
140
|
+
console.log('\n🔎 DRY RUN: No changes will be made.\n');
|
|
141
|
+
console.log('The following projects would be imported:');
|
|
142
|
+
newProjects.forEach(p => {
|
|
143
|
+
const status = p.archived ? '⏭️ (archived - skipped)' : '✨';
|
|
144
|
+
console.log(` ${status} ${p.key} - ${p.name}`);
|
|
145
|
+
});
|
|
146
|
+
console.log(`\nTotal: ${newProjects.filter(p => !p.archived).length} projects would be imported\n`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Confirm import
|
|
151
|
+
const { confirmed } = await inquirer.prompt([{
|
|
152
|
+
type: 'confirm',
|
|
153
|
+
name: 'confirmed',
|
|
154
|
+
message: `Import ${newProjects.length} project(s)?`,
|
|
155
|
+
default: true
|
|
156
|
+
}]);
|
|
157
|
+
|
|
158
|
+
if (!confirmed) {
|
|
159
|
+
console.log('⏭️ Import cancelled.');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Step 6: Merge with Existing
|
|
165
|
+
```typescript
|
|
166
|
+
import { updateEnvFile, mergeProjectList } from '../../../src/utils/env-manager.js';
|
|
167
|
+
|
|
168
|
+
const newKeys = newProjects.map(p => p.key);
|
|
169
|
+
const mergedProjects = mergeProjectList(existingProjects, newKeys);
|
|
170
|
+
|
|
171
|
+
// Update .env file (atomic write)
|
|
172
|
+
await updateEnvFile('JIRA_PROJECTS', mergedProjects.join(','));
|
|
173
|
+
|
|
174
|
+
console.log('\n✅ Projects imported successfully!\n');
|
|
175
|
+
console.log(`Updated: ${existingProjects.length} → ${mergedProjects.length} projects`);
|
|
176
|
+
console.log(`\nCurrent projects:\n ${mergedProjects.join(', ')}\n`);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 7: Resume Support
|
|
180
|
+
```typescript
|
|
181
|
+
if (args.resume) {
|
|
182
|
+
const { CacheManager } = await import('../../../src/core/cache/cache-manager.js');
|
|
183
|
+
const cacheManager = new CacheManager(process.cwd());
|
|
184
|
+
|
|
185
|
+
const importState = await cacheManager.get('jira-import-state');
|
|
186
|
+
|
|
187
|
+
if (!importState) {
|
|
188
|
+
console.log('⚠️ No import state found. Use without --resume to start fresh.');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log(`\n📂 Resuming from: ${importState.lastProject} (${importState.completed}/${importState.total})`);
|
|
193
|
+
|
|
194
|
+
// Skip already-imported projects
|
|
195
|
+
const remainingProjects = allProjects.filter(p => !importState.succeeded.includes(p.key));
|
|
196
|
+
|
|
197
|
+
// Continue import with remaining projects
|
|
198
|
+
// (use same logic as Step 6)
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Examples
|
|
203
|
+
|
|
204
|
+
### Example 1: Basic Import
|
|
205
|
+
**User**: `/specweave-jira:import-projects`
|
|
206
|
+
|
|
207
|
+
**Output**:
|
|
208
|
+
```
|
|
209
|
+
📋 Current projects: BACKEND, FRONTEND
|
|
210
|
+
|
|
211
|
+
✓ Found 127 accessible projects
|
|
212
|
+
📥 5 new project(s) available for import:
|
|
213
|
+
|
|
214
|
+
✨ MOBILE - Mobile App (agile, lead: John Doe)
|
|
215
|
+
✨ INFRA - Infrastructure (software, lead: Jane Smith)
|
|
216
|
+
✨ QA - Quality Assurance (business, lead: Bob Wilson)
|
|
217
|
+
✨ DOCS - Documentation (business, lead: Alice Cooper)
|
|
218
|
+
✨ LEGACY - Legacy System (archived - skipped)
|
|
219
|
+
|
|
220
|
+
Import 5 project(s)? (Y/n)
|
|
221
|
+
|
|
222
|
+
✅ Projects imported successfully!
|
|
223
|
+
|
|
224
|
+
Updated: 2 → 6 projects
|
|
225
|
+
|
|
226
|
+
Current projects:
|
|
227
|
+
BACKEND, FRONTEND, MOBILE, INFRA, QA, DOCS
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Example 2: Filter Active Only
|
|
231
|
+
**User**: `/specweave-jira:import-projects --filter active`
|
|
232
|
+
|
|
233
|
+
**Output**:
|
|
234
|
+
```
|
|
235
|
+
🔍 Filters applied:
|
|
236
|
+
• Active projects only
|
|
237
|
+
|
|
238
|
+
📊 Results: 120 projects (down from 127)
|
|
239
|
+
|
|
240
|
+
📥 4 new project(s) available for import:
|
|
241
|
+
(LEGACY project excluded because it's archived)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Example 3: Dry-Run
|
|
245
|
+
**User**: `/specweave-jira:import-projects --dry-run`
|
|
246
|
+
|
|
247
|
+
**Output**:
|
|
248
|
+
```
|
|
249
|
+
🔎 DRY RUN: No changes will be made.
|
|
250
|
+
|
|
251
|
+
The following projects would be imported:
|
|
252
|
+
✨ MOBILE - Mobile App
|
|
253
|
+
✨ INFRA - Infrastructure
|
|
254
|
+
✨ QA - Quality Assurance
|
|
255
|
+
⏭️ LEGACY - Legacy System (archived - skipped)
|
|
256
|
+
|
|
257
|
+
Total: 3 projects would be imported
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Example 4: Custom JQL Filter
|
|
261
|
+
**User**: `/specweave-jira:import-projects --jql "project IN (MOBILE, INFRA) AND status != Archived"`
|
|
262
|
+
|
|
263
|
+
**Output**:
|
|
264
|
+
```
|
|
265
|
+
🔍 Filters applied:
|
|
266
|
+
• JQL: project IN (MOBILE, INFRA) AND status != Archived
|
|
267
|
+
|
|
268
|
+
📊 Results: 2 projects (down from 127)
|
|
269
|
+
|
|
270
|
+
📥 2 new project(s) available for import:
|
|
271
|
+
✨ MOBILE - Mobile App
|
|
272
|
+
✨ INFRA - Infrastructure
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Important Notes
|
|
276
|
+
|
|
277
|
+
- **Merge Logic**: No duplicates. Existing projects are preserved.
|
|
278
|
+
- **Atomic Updates**: Uses temp file + rename to prevent corruption.
|
|
279
|
+
- **Progress Tracking**: Shows progress bar for large imports (> 50 projects).
|
|
280
|
+
- **Resume Support**: Interrupted imports can be resumed with `--resume`.
|
|
281
|
+
- **Backup**: Creates `.env.backup` before modifying `.env`.
|
|
282
|
+
|
|
283
|
+
## Related Commands
|
|
284
|
+
|
|
285
|
+
- `/specweave:init` - Initial SpecWeave setup
|
|
286
|
+
- `/specweave-jira:sync` - Sync increments with Jira epics
|
|
287
|
+
- `/specweave-jira:refresh-cache` - Clear cached project data
|
|
288
|
+
|
|
289
|
+
## Error Handling
|
|
290
|
+
|
|
291
|
+
- **Missing Credentials**: Prompt user to run `specweave init` first
|
|
292
|
+
- **API Errors**: Show clear error message with suggestion
|
|
293
|
+
- **No New Projects**: Inform user all projects already imported
|
|
294
|
+
- **Permission Errors**: Check `.env` file permissions
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
**Post-Init Flexibility**: This command provides flexibility to add projects after initial setup, supporting evolving team structures and project lifecycles.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave-kafka",
|
|
3
3
|
"description": "Apache Kafka event streaming integration with MCP servers, CLI tools (kcat), Terraform modules, and comprehensive observability stack",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.24.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SpecWeave Team",
|
|
7
7
|
"url": "https://spec-weave.com"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave-kafka-streams",
|
|
3
3
|
"description": "Kafka Streams library integration for SpecWeave - Stream processing with Java/Kotlin, topology patterns, state stores, windowing, joins, and testing frameworks",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.24.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SpecWeave Team",
|
|
7
7
|
"url": "https://spec-weave.com"
|