specweave 0.8.17 ā 0.8.19
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.md +119 -27
- package/README.md +11 -3
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +74 -20
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrate-to-profiles.js +2 -2
- package/dist/cli/commands/migrate-to-profiles.js.map +1 -1
- package/dist/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/cli/helpers/issue-tracker/index.js +8 -3
- package/dist/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/cli/helpers/issue-tracker/utils.d.ts +5 -2
- package/dist/cli/helpers/issue-tracker/utils.d.ts.map +1 -1
- package/dist/cli/helpers/issue-tracker/utils.js +13 -6
- package/dist/cli/helpers/issue-tracker/utils.js.map +1 -1
- package/dist/core/sync/bidirectional-engine.d.ts +110 -0
- package/dist/core/sync/bidirectional-engine.d.ts.map +1 -0
- package/dist/core/sync/bidirectional-engine.js +356 -0
- package/dist/core/sync/bidirectional-engine.js.map +1 -0
- package/dist/utils/agents-md-compiler.js +2 -2
- package/dist/utils/env-multi-project-parser.d.ts +210 -0
- package/dist/utils/env-multi-project-parser.d.ts.map +1 -0
- package/dist/utils/env-multi-project-parser.js +406 -0
- package/dist/utils/env-multi-project-parser.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/post-first-increment.sh +79 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +50 -16
- package/plugins/specweave/skills/plugin-expert/SKILL.md +344 -0
- package/plugins/specweave/skills/specweave-framework/SKILL.md +2 -2
- package/plugins/specweave/skills/translator/SKILL.md +29 -0
- package/plugins/specweave/skills/translator/SKILL.md.bak +172 -0
- package/plugins/specweave-jira/lib/project-selector.ts +323 -0
- package/plugins/specweave-jira/lib/reorganization-detector.ts +359 -0
- package/plugins/specweave-jira/lib/setup-wizard.ts +256 -0
- package/src/templates/.gitignore.template +1 -0
- package/plugins/specweave-jira/lib/jira-client-v2.ts +0 -529
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Jira Setup Wizard
|
|
3
|
+
*
|
|
4
|
+
* Intelligent credential detection and setup flow:
|
|
5
|
+
* 1. Check .env for credentials (uses credentialsManager)
|
|
6
|
+
* 2. Interactive prompt only if missing
|
|
7
|
+
* 3. Never ask twice for same credentials
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import inquirer from 'inquirer';
|
|
11
|
+
import { credentialsManager, JiraCredentials } from '../../../src/core/credentials-manager.js';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Types
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export { JiraCredentials } from '../../../src/core/credentials-manager.js';
|
|
18
|
+
|
|
19
|
+
export interface CredentialDetectionResult {
|
|
20
|
+
found: boolean;
|
|
21
|
+
credentials?: JiraCredentials;
|
|
22
|
+
source?: 'env' | 'interactive';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Credential Detection
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Smart credential detection - uses existing credentialsManager
|
|
31
|
+
*/
|
|
32
|
+
export async function detectJiraCredentials(): Promise<CredentialDetectionResult> {
|
|
33
|
+
if (credentialsManager.hasJiraCredentials()) {
|
|
34
|
+
try {
|
|
35
|
+
const credentials = credentialsManager.getJiraCredentials();
|
|
36
|
+
console.log('ā
Found Jira credentials in .env');
|
|
37
|
+
return {
|
|
38
|
+
found: true,
|
|
39
|
+
credentials,
|
|
40
|
+
source: 'env',
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// Credentials exist but invalid format
|
|
44
|
+
return { found: false };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { found: false };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Interactive Setup
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Interactive Jira credential setup
|
|
57
|
+
* Only runs if credentials not found
|
|
58
|
+
*/
|
|
59
|
+
export async function setupJiraCredentials(): Promise<JiraCredentials> {
|
|
60
|
+
console.log('\nš§ Jira Setup Wizard\n');
|
|
61
|
+
|
|
62
|
+
// Check for existing credentials first
|
|
63
|
+
const detected = await detectJiraCredentials();
|
|
64
|
+
|
|
65
|
+
if (detected.found) {
|
|
66
|
+
// Ask user if they want to use existing or re-enter
|
|
67
|
+
const { useExisting } = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: 'confirm',
|
|
70
|
+
name: 'useExisting',
|
|
71
|
+
message: `Found credentials in ${detected.source}. Use these credentials?`,
|
|
72
|
+
default: true,
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
if (useExisting) {
|
|
77
|
+
return detected.credentials!;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log('\nš Enter new credentials:\n');
|
|
81
|
+
} else {
|
|
82
|
+
console.log('ā ļø No Jira credentials found\n');
|
|
83
|
+
console.log('š Let\'s set up your Jira connection:\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Interactive credential entry
|
|
87
|
+
const answers = await inquirer.prompt([
|
|
88
|
+
{
|
|
89
|
+
type: 'list',
|
|
90
|
+
name: 'setupType',
|
|
91
|
+
message: 'How would you like to connect to Jira?',
|
|
92
|
+
choices: [
|
|
93
|
+
{
|
|
94
|
+
name: 'Cloud (*.atlassian.net)',
|
|
95
|
+
value: 'cloud',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Server/Data Center (self-hosted)',
|
|
99
|
+
value: 'server',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: 'input',
|
|
105
|
+
name: 'domain',
|
|
106
|
+
message: (answers: any) =>
|
|
107
|
+
answers.setupType === 'cloud'
|
|
108
|
+
? 'Jira domain (e.g., mycompany.atlassian.net):'
|
|
109
|
+
: 'Jira server URL (e.g., jira.mycompany.com):',
|
|
110
|
+
validate: (value: string) => {
|
|
111
|
+
if (!value) return 'Domain is required';
|
|
112
|
+
if (answers.setupType === 'cloud' && !value.includes('.atlassian.net')) {
|
|
113
|
+
return 'Cloud domain must end with .atlassian.net';
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
type: 'input',
|
|
120
|
+
name: 'email',
|
|
121
|
+
message: 'Email address:',
|
|
122
|
+
validate: (value: string) => {
|
|
123
|
+
if (!value) return 'Email is required';
|
|
124
|
+
if (!value.includes('@')) return 'Must be a valid email';
|
|
125
|
+
return true;
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
type: 'password',
|
|
130
|
+
name: 'apiToken',
|
|
131
|
+
message: 'API token:',
|
|
132
|
+
mask: '*',
|
|
133
|
+
validate: (value: string) => {
|
|
134
|
+
if (!value) return 'API token is required';
|
|
135
|
+
if (value.length < 10) return 'API token seems too short';
|
|
136
|
+
return true;
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
|
|
141
|
+
const credentials: JiraCredentials = {
|
|
142
|
+
domain: answers.domain,
|
|
143
|
+
email: answers.email,
|
|
144
|
+
apiToken: answers.apiToken,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Test connection
|
|
148
|
+
console.log('\nš Testing connection...');
|
|
149
|
+
const isValid = await testJiraConnection(credentials);
|
|
150
|
+
|
|
151
|
+
if (!isValid) {
|
|
152
|
+
console.log('ā Failed to connect to Jira');
|
|
153
|
+
console.log('š” Please check your credentials and try again\n');
|
|
154
|
+
|
|
155
|
+
const { retry } = await inquirer.prompt([
|
|
156
|
+
{
|
|
157
|
+
type: 'confirm',
|
|
158
|
+
name: 'retry',
|
|
159
|
+
message: 'Would you like to try again?',
|
|
160
|
+
default: true,
|
|
161
|
+
},
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
if (retry) {
|
|
165
|
+
return setupJiraCredentials();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
throw new Error('Jira connection failed');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log('ā
Connection successful!\n');
|
|
172
|
+
|
|
173
|
+
// Save to .env using credentialsManager
|
|
174
|
+
await saveCredentialsToEnv(credentials);
|
|
175
|
+
|
|
176
|
+
return credentials;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Test Jira connection with credentials
|
|
181
|
+
*/
|
|
182
|
+
async function testJiraConnection(credentials: JiraCredentials): Promise<boolean> {
|
|
183
|
+
try {
|
|
184
|
+
const https = await import('https');
|
|
185
|
+
|
|
186
|
+
const auth = Buffer.from(`${credentials.email}:${credentials.apiToken}`).toString('base64');
|
|
187
|
+
|
|
188
|
+
return new Promise((resolve) => {
|
|
189
|
+
const req = https.request(
|
|
190
|
+
{
|
|
191
|
+
hostname: credentials.domain,
|
|
192
|
+
path: '/rest/api/3/myself',
|
|
193
|
+
method: 'GET',
|
|
194
|
+
headers: {
|
|
195
|
+
Authorization: `Basic ${auth}`,
|
|
196
|
+
'Content-Type': 'application/json',
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
(res) => {
|
|
200
|
+
resolve(res.statusCode === 200);
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
req.on('error', () => resolve(false));
|
|
205
|
+
req.setTimeout(5000, () => {
|
|
206
|
+
req.destroy();
|
|
207
|
+
resolve(false);
|
|
208
|
+
});
|
|
209
|
+
req.end();
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Save credentials to .env using credentialsManager
|
|
218
|
+
*/
|
|
219
|
+
async function saveCredentialsToEnv(credentials: JiraCredentials): Promise<void> {
|
|
220
|
+
console.log('š” Save credentials to .env for future use\n');
|
|
221
|
+
|
|
222
|
+
const { saveToEnv } = await inquirer.prompt([
|
|
223
|
+
{
|
|
224
|
+
type: 'confirm',
|
|
225
|
+
name: 'saveToEnv',
|
|
226
|
+
message: 'Save credentials to .env file?',
|
|
227
|
+
default: true,
|
|
228
|
+
},
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
if (saveToEnv) {
|
|
232
|
+
credentialsManager.saveToEnvFile({ jira: credentials });
|
|
233
|
+
console.log('ā
Credentials saved to .env');
|
|
234
|
+
console.log('ā
.env added to .gitignore');
|
|
235
|
+
} else {
|
|
236
|
+
console.log('ā ļø Credentials not saved. You\'ll need to enter them again next time.');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// Export Helpers
|
|
242
|
+
// ============================================================================
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get Jira credentials - smart detection with fallback to interactive setup
|
|
246
|
+
*/
|
|
247
|
+
export async function getJiraCredentials(): Promise<JiraCredentials> {
|
|
248
|
+
const detected = await detectJiraCredentials();
|
|
249
|
+
|
|
250
|
+
if (detected.found) {
|
|
251
|
+
return detected.credentials!;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Not found - run interactive setup
|
|
255
|
+
return setupJiraCredentials();
|
|
256
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
.specweave/cache/
|
|
3
3
|
.specweave/increments/*/logs/ # Gitignored, but structure preserved
|
|
4
4
|
.specweave/increments/*/test-results/
|
|
5
|
+
.specweave/docs-site-internal/ # Generated Docusaurus site for internal docs
|
|
5
6
|
.claude-plugin/ # Plugin marketplace (framework files)
|
|
6
7
|
plugins/ # Plugin implementations (framework files)
|
|
7
8
|
|