claude-cli-advanced-starter-pack 1.0.11 → 1.0.13
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/README.md +527 -345
- package/package.json +1 -1
- package/src/commands/init.js +270 -38
- package/src/commands/test-setup.js +7 -6
- package/src/data/releases.json +84 -2
- package/src/testing/config.js +213 -84
- package/src/utils/smart-merge.js +457 -0
- package/src/utils/version-check.js +213 -0
- package/templates/commands/create-task-list.template.md +332 -17
- package/templates/commands/project-impl.template.md +13 -0
- package/templates/commands/update-smart.template.md +111 -0
- package/templates/hooks/ccasp-update-check.template.js +75 -0
- package/templates/hooks/usage-tracking.template.js +222 -0
package/src/testing/config.js
CHANGED
|
@@ -1,16 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Testing Configuration Module
|
|
3
3
|
*
|
|
4
|
-
* Manages testing modes, credentials, and environment configuration
|
|
4
|
+
* Manages testing modes, credentials, and environment configuration.
|
|
5
|
+
* Testing config is stored in tech-stack.json under the `testing` section
|
|
6
|
+
* for unified configuration management.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
|
|
10
|
-
// Default paths
|
|
11
|
-
const
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
|
|
12
|
+
// Default paths - now uses tech-stack.json
|
|
13
|
+
const TECH_STACK_PATHS = [
|
|
14
|
+
join(process.cwd(), '.claude', 'tech-stack.json'),
|
|
15
|
+
join(process.cwd(), 'tech-stack.json'),
|
|
16
|
+
];
|
|
17
|
+
const RULES_DIR = join(process.cwd(), '.claude', 'task-lists');
|
|
12
18
|
const TESTING_RULES_FILE = 'TESTING_RULES.md';
|
|
13
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Get path to tech-stack.json (prefers .claude/tech-stack.json)
|
|
22
|
+
*/
|
|
23
|
+
function getTechStackPath() {
|
|
24
|
+
for (const path of TECH_STACK_PATHS) {
|
|
25
|
+
if (existsSync(path)) {
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return TECH_STACK_PATHS[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load tech-stack.json
|
|
34
|
+
*/
|
|
35
|
+
function loadTechStackJson() {
|
|
36
|
+
const techStackPath = getTechStackPath();
|
|
37
|
+
if (existsSync(techStackPath)) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(readFileSync(techStackPath, 'utf8'));
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Save tech-stack.json
|
|
49
|
+
*/
|
|
50
|
+
function saveTechStackJson(techStack) {
|
|
51
|
+
const techStackPath = getTechStackPath();
|
|
52
|
+
const configDir = dirname(techStackPath);
|
|
53
|
+
|
|
54
|
+
if (!existsSync(configDir)) {
|
|
55
|
+
mkdirSync(configDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
techStack._lastModified = new Date().toISOString();
|
|
59
|
+
writeFileSync(techStackPath, JSON.stringify(techStack, null, 2), 'utf8');
|
|
60
|
+
return techStackPath;
|
|
61
|
+
}
|
|
62
|
+
|
|
14
63
|
/**
|
|
15
64
|
* Testing mode definitions
|
|
16
65
|
*/
|
|
@@ -99,107 +148,155 @@ export const CREDENTIAL_SOURCES = {
|
|
|
99
148
|
};
|
|
100
149
|
|
|
101
150
|
/**
|
|
102
|
-
* Testing configuration structure
|
|
151
|
+
* Testing configuration structure (compatible with tech-stack.json `testing` section)
|
|
152
|
+
*
|
|
153
|
+
* This structure maps to tech-stack.json testing fields:
|
|
154
|
+
* - testing.e2e.framework -> e2e framework
|
|
155
|
+
* - testing.e2e.configFile -> playwright config
|
|
156
|
+
* - testing.selectors.* -> login selectors
|
|
157
|
+
* - testing.credentials.* -> env var names
|
|
103
158
|
*/
|
|
104
159
|
export function createTestingConfig(options = {}) {
|
|
105
160
|
return {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
161
|
+
// E2E testing configuration
|
|
162
|
+
e2e: {
|
|
163
|
+
framework: options.playwrightEnabled ? 'playwright' : 'none',
|
|
164
|
+
configFile: options.playwrightConfig || 'playwright.config.ts',
|
|
165
|
+
testCommand: 'npx playwright test',
|
|
166
|
+
browser: options.browser || 'chromium',
|
|
167
|
+
headless: options.headless ?? true,
|
|
168
|
+
timeout: options.timeout || 30000,
|
|
169
|
+
},
|
|
109
170
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
autoRetry: true,
|
|
116
|
-
} : null,
|
|
171
|
+
// Unit testing (preserved from existing config)
|
|
172
|
+
unit: {
|
|
173
|
+
framework: 'none',
|
|
174
|
+
testCommand: 'npm test',
|
|
175
|
+
},
|
|
117
176
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
177
|
+
// Test selectors for login flow
|
|
178
|
+
selectors: {
|
|
179
|
+
strategy: 'data-testid',
|
|
180
|
+
username: options.usernameSelector || '[data-testid="username-input"]',
|
|
181
|
+
password: options.passwordSelector || '[data-testid="password-input"]',
|
|
182
|
+
loginButton: options.loginButtonSelector || '[data-testid="login-submit"]',
|
|
183
|
+
loginSuccess: options.loginSuccessSelector || '[data-testid="dashboard"]',
|
|
124
184
|
},
|
|
125
185
|
|
|
126
|
-
// Credentials
|
|
186
|
+
// Credentials (env var names, never actual credentials)
|
|
127
187
|
credentials: {
|
|
188
|
+
usernameEnvVar: options.envVars?.username || 'TEST_USER_USERNAME',
|
|
189
|
+
passwordEnvVar: options.envVars?.password || 'TEST_USER_PASSWORD',
|
|
128
190
|
source: options.credentialSource || 'env',
|
|
129
|
-
envVars: options.envVars || {
|
|
130
|
-
username: 'TEST_USER_USERNAME',
|
|
131
|
-
password: 'TEST_USER_PASSWORD',
|
|
132
|
-
},
|
|
133
|
-
// Only stored if source is 'config'
|
|
134
|
-
username: options.credentialSource === 'config' ? options.username : undefined,
|
|
135
|
-
password: options.credentialSource === 'config' ? options.password : undefined,
|
|
136
191
|
},
|
|
137
192
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
timeout: options.timeout || 30000,
|
|
193
|
+
// Environment configuration
|
|
194
|
+
environment: {
|
|
195
|
+
type: options.envType || 'localhost',
|
|
196
|
+
baseUrl: options.baseUrl || 'http://localhost:5173',
|
|
197
|
+
port: options.port || 5173,
|
|
198
|
+
requiresSetup: options.requiresSetup || [],
|
|
145
199
|
},
|
|
146
200
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
},
|
|
201
|
+
// Testing mode (ralph loop, manual, minimal)
|
|
202
|
+
mode: options.mode || 'manual',
|
|
203
|
+
ralphConfig: options.mode === 'ralph' ? {
|
|
204
|
+
maxIterations: options.maxIterations || 10,
|
|
205
|
+
completionPromise: options.completionPromise || 'all tasks complete and tests passing',
|
|
206
|
+
autoRetry: true,
|
|
207
|
+
} : null,
|
|
154
208
|
|
|
155
209
|
// Persistent rules file path
|
|
156
210
|
rulesFile: options.rulesFile || null,
|
|
211
|
+
|
|
212
|
+
// Configuration metadata
|
|
213
|
+
_configuredAt: new Date().toISOString(),
|
|
214
|
+
_configuredBy: 'ccasp-test-setup',
|
|
157
215
|
};
|
|
158
216
|
}
|
|
159
217
|
|
|
160
218
|
/**
|
|
161
|
-
* Save testing configuration
|
|
219
|
+
* Save testing configuration to tech-stack.json
|
|
162
220
|
*/
|
|
163
221
|
export function saveTestingConfig(config) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
222
|
+
// Load existing tech-stack or create default
|
|
223
|
+
let techStack = loadTechStackJson() || {
|
|
224
|
+
version: '2.0.0',
|
|
225
|
+
project: { name: 'unnamed', description: '' },
|
|
226
|
+
frontend: {},
|
|
227
|
+
backend: {},
|
|
228
|
+
testing: {},
|
|
229
|
+
};
|
|
170
230
|
|
|
171
|
-
|
|
231
|
+
// Merge testing config into tech-stack
|
|
232
|
+
techStack.testing = {
|
|
233
|
+
...techStack.testing,
|
|
234
|
+
...config,
|
|
235
|
+
};
|
|
172
236
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
237
|
+
const configPath = saveTechStackJson(techStack);
|
|
238
|
+
|
|
239
|
+
// Also save to legacy location for backwards compatibility
|
|
240
|
+
// (will be removed in future version)
|
|
241
|
+
const legacyDir = join(process.cwd(), '.gtask');
|
|
242
|
+
if (existsSync(legacyDir)) {
|
|
243
|
+
try {
|
|
244
|
+
writeFileSync(
|
|
245
|
+
join(legacyDir, 'testing.json'),
|
|
246
|
+
JSON.stringify(config, null, 2),
|
|
247
|
+
'utf8'
|
|
248
|
+
);
|
|
249
|
+
} catch {
|
|
250
|
+
// Ignore legacy save errors
|
|
251
|
+
}
|
|
176
252
|
}
|
|
177
253
|
|
|
178
254
|
return configPath;
|
|
179
255
|
}
|
|
180
256
|
|
|
181
257
|
/**
|
|
182
|
-
* Load testing configuration
|
|
258
|
+
* Load testing configuration from tech-stack.json
|
|
183
259
|
*/
|
|
184
260
|
export function loadTestingConfig() {
|
|
185
|
-
const
|
|
261
|
+
const techStack = loadTechStackJson();
|
|
186
262
|
|
|
187
|
-
if (
|
|
188
|
-
return
|
|
263
|
+
if (techStack?.testing && Object.keys(techStack.testing).length > 0) {
|
|
264
|
+
return techStack.testing;
|
|
189
265
|
}
|
|
190
266
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
267
|
+
// Fallback to legacy .gtask/testing.json for backwards compatibility
|
|
268
|
+
const legacyPath = join(process.cwd(), '.gtask', 'testing.json');
|
|
269
|
+
if (existsSync(legacyPath)) {
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(readFileSync(legacyPath, 'utf8'));
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
195
275
|
}
|
|
276
|
+
|
|
277
|
+
return null;
|
|
196
278
|
}
|
|
197
279
|
|
|
198
280
|
/**
|
|
199
|
-
* Check if testing is configured
|
|
281
|
+
* Check if testing is configured (checks tech-stack.json and legacy location)
|
|
200
282
|
*/
|
|
201
283
|
export function hasTestingConfig() {
|
|
202
|
-
|
|
284
|
+
const techStack = loadTechStackJson();
|
|
285
|
+
|
|
286
|
+
// Check if tech-stack.json has meaningful testing config
|
|
287
|
+
if (techStack?.testing) {
|
|
288
|
+
const testing = techStack.testing;
|
|
289
|
+
// Consider configured if has e2e framework or selectors
|
|
290
|
+
if (testing.e2e?.framework && testing.e2e.framework !== 'none') {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (testing.selectors?.username) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Fallback to legacy location
|
|
299
|
+
return existsSync(join(process.cwd(), '.gtask', 'testing.json'));
|
|
203
300
|
}
|
|
204
301
|
|
|
205
302
|
/**
|
|
@@ -320,14 +417,14 @@ npx playwright test --ui
|
|
|
320
417
|
}
|
|
321
418
|
|
|
322
419
|
/**
|
|
323
|
-
* Save testing rules markdown file
|
|
420
|
+
* Save testing rules markdown file to .claude/task-lists/
|
|
324
421
|
*/
|
|
325
422
|
export function saveTestingRules(config, filename = TESTING_RULES_FILE) {
|
|
326
|
-
if (!existsSync(
|
|
327
|
-
mkdirSync(
|
|
423
|
+
if (!existsSync(RULES_DIR)) {
|
|
424
|
+
mkdirSync(RULES_DIR, { recursive: true });
|
|
328
425
|
}
|
|
329
426
|
|
|
330
|
-
const rulesPath = join(
|
|
427
|
+
const rulesPath = join(RULES_DIR, filename);
|
|
331
428
|
const content = generateTestingRules(config);
|
|
332
429
|
|
|
333
430
|
writeFileSync(rulesPath, content, 'utf8');
|
|
@@ -356,24 +453,19 @@ function ensureGitignore(pattern) {
|
|
|
356
453
|
}
|
|
357
454
|
|
|
358
455
|
/**
|
|
359
|
-
* Get credentials based on config
|
|
456
|
+
* Get credentials based on config (uses new tech-stack.json structure)
|
|
360
457
|
*/
|
|
361
458
|
export function getCredentials(config) {
|
|
362
|
-
if (!config || config.credentials
|
|
459
|
+
if (!config || config.credentials?.source === 'none') {
|
|
363
460
|
return null;
|
|
364
461
|
}
|
|
365
462
|
|
|
366
|
-
if (config.credentials
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
password: process.env[config.credentials.envVars.password] || null,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (config.credentials.source === 'config') {
|
|
463
|
+
if (config.credentials?.source === 'env') {
|
|
464
|
+
const usernameVar = config.credentials.usernameEnvVar || config.credentials.envVars?.username;
|
|
465
|
+
const passwordVar = config.credentials.passwordEnvVar || config.credentials.envVars?.password;
|
|
374
466
|
return {
|
|
375
|
-
username:
|
|
376
|
-
password:
|
|
467
|
+
username: process.env[usernameVar] || null,
|
|
468
|
+
password: process.env[passwordVar] || null,
|
|
377
469
|
};
|
|
378
470
|
}
|
|
379
471
|
|
|
@@ -382,10 +474,11 @@ export function getCredentials(config) {
|
|
|
382
474
|
}
|
|
383
475
|
|
|
384
476
|
/**
|
|
385
|
-
* Validate testing configuration
|
|
477
|
+
* Validate testing configuration (works with tech-stack.json structure)
|
|
386
478
|
*/
|
|
387
479
|
export function validateConfig(config) {
|
|
388
480
|
const errors = [];
|
|
481
|
+
const warnings = [];
|
|
389
482
|
|
|
390
483
|
if (!config.environment?.baseUrl) {
|
|
391
484
|
errors.push('Base URL is required');
|
|
@@ -393,9 +486,12 @@ export function validateConfig(config) {
|
|
|
393
486
|
|
|
394
487
|
if (config.credentials?.source === 'env') {
|
|
395
488
|
const creds = getCredentials(config);
|
|
489
|
+
const usernameVar = config.credentials.usernameEnvVar || config.credentials.envVars?.username;
|
|
490
|
+
const passwordVar = config.credentials.passwordEnvVar || config.credentials.envVars?.password;
|
|
491
|
+
|
|
396
492
|
if (!creds?.username || !creds?.password) {
|
|
397
|
-
|
|
398
|
-
`Environment variables not set: ${
|
|
493
|
+
warnings.push(
|
|
494
|
+
`Environment variables not set: ${usernameVar}, ${passwordVar}`
|
|
399
495
|
);
|
|
400
496
|
}
|
|
401
497
|
}
|
|
@@ -404,8 +500,41 @@ export function validateConfig(config) {
|
|
|
404
500
|
errors.push('Ralph configuration missing');
|
|
405
501
|
}
|
|
406
502
|
|
|
503
|
+
if (config.e2e?.framework === 'none') {
|
|
504
|
+
warnings.push('No E2E testing framework configured');
|
|
505
|
+
}
|
|
506
|
+
|
|
407
507
|
return {
|
|
408
508
|
valid: errors.length === 0,
|
|
409
509
|
errors,
|
|
510
|
+
warnings,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Get a summary of what testing features are configured
|
|
516
|
+
*/
|
|
517
|
+
export function getTestingConfigSummary() {
|
|
518
|
+
const config = loadTestingConfig();
|
|
519
|
+
|
|
520
|
+
if (!config) {
|
|
521
|
+
return {
|
|
522
|
+
configured: false,
|
|
523
|
+
e2eFramework: null,
|
|
524
|
+
hasSelectors: false,
|
|
525
|
+
hasCredentials: false,
|
|
526
|
+
environment: null,
|
|
527
|
+
mode: null,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
configured: true,
|
|
533
|
+
e2eFramework: config.e2e?.framework || config.playwright?.enabled ? 'playwright' : null,
|
|
534
|
+
hasSelectors: !!(config.selectors?.username || config.selectors?.usernameInput),
|
|
535
|
+
hasCredentials: !!(config.credentials?.usernameEnvVar || config.credentials?.envVars?.username),
|
|
536
|
+
environment: config.environment?.type || 'localhost',
|
|
537
|
+
baseUrl: config.environment?.baseUrl,
|
|
538
|
+
mode: config.mode || 'manual',
|
|
410
539
|
};
|
|
411
540
|
}
|