claude-cli-advanced-starter-pack 1.0.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/LICENSE +21 -0
- package/OVERVIEW.md +597 -0
- package/README.md +439 -0
- package/bin/gtask.js +282 -0
- package/bin/postinstall.js +53 -0
- package/package.json +69 -0
- package/src/agents/phase-dev-templates.js +1011 -0
- package/src/agents/templates.js +668 -0
- package/src/analysis/checklist-parser.js +414 -0
- package/src/analysis/codebase.js +481 -0
- package/src/cli/menu.js +958 -0
- package/src/commands/claude-audit.js +1482 -0
- package/src/commands/claude-settings.js +2243 -0
- package/src/commands/create-agent.js +681 -0
- package/src/commands/create-command.js +337 -0
- package/src/commands/create-hook.js +262 -0
- package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
- package/src/commands/create-phase-dev/documentation-generator.js +352 -0
- package/src/commands/create-phase-dev/post-completion.js +404 -0
- package/src/commands/create-phase-dev/scale-calculator.js +344 -0
- package/src/commands/create-phase-dev/wizard.js +492 -0
- package/src/commands/create-phase-dev.js +481 -0
- package/src/commands/create-skill.js +313 -0
- package/src/commands/create.js +446 -0
- package/src/commands/decompose.js +392 -0
- package/src/commands/detect-tech-stack.js +768 -0
- package/src/commands/explore-mcp/claude-md-updater.js +252 -0
- package/src/commands/explore-mcp/mcp-installer.js +346 -0
- package/src/commands/explore-mcp/mcp-registry.js +438 -0
- package/src/commands/explore-mcp.js +638 -0
- package/src/commands/gtask-init.js +641 -0
- package/src/commands/help.js +128 -0
- package/src/commands/init.js +1890 -0
- package/src/commands/install.js +250 -0
- package/src/commands/list.js +116 -0
- package/src/commands/roadmap.js +750 -0
- package/src/commands/setup-wizard.js +482 -0
- package/src/commands/setup.js +351 -0
- package/src/commands/sync.js +534 -0
- package/src/commands/test-run.js +456 -0
- package/src/commands/test-setup.js +456 -0
- package/src/commands/validate.js +67 -0
- package/src/config/tech-stack.defaults.json +182 -0
- package/src/config/tech-stack.schema.json +502 -0
- package/src/github/client.js +359 -0
- package/src/index.js +84 -0
- package/src/templates/claude-command.js +244 -0
- package/src/templates/issue-body.js +284 -0
- package/src/testing/config.js +411 -0
- package/src/utils/template-engine.js +398 -0
- package/src/utils/validate-templates.js +223 -0
- package/src/utils.js +396 -0
- package/templates/commands/ccasp-setup.template.md +113 -0
- package/templates/commands/context-audit.template.md +97 -0
- package/templates/commands/create-task-list.template.md +382 -0
- package/templates/commands/deploy-full.template.md +261 -0
- package/templates/commands/github-task-start.template.md +99 -0
- package/templates/commands/github-update.template.md +69 -0
- package/templates/commands/happy-start.template.md +117 -0
- package/templates/commands/phase-track.template.md +142 -0
- package/templates/commands/tunnel-start.template.md +127 -0
- package/templates/commands/tunnel-stop.template.md +106 -0
- package/templates/hooks/context-guardian.template.js +173 -0
- package/templates/hooks/deployment-orchestrator.template.js +219 -0
- package/templates/hooks/github-progress-hook.template.js +197 -0
- package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
- package/templates/hooks/phase-dev-enforcer.template.js +183 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing Setup Wizard Command
|
|
3
|
+
*
|
|
4
|
+
* Interactive setup for testing configuration including:
|
|
5
|
+
* - Testing mode (Ralph Loop, Manual, Minimal)
|
|
6
|
+
* - Environment (localhost, ngrok, custom)
|
|
7
|
+
* - Credentials (env vars, config file, prompt)
|
|
8
|
+
* - Playwright configuration
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import inquirer from 'inquirer';
|
|
13
|
+
import ora from 'ora';
|
|
14
|
+
import { existsSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
|
|
17
|
+
import {
|
|
18
|
+
TESTING_MODES,
|
|
19
|
+
ENVIRONMENTS,
|
|
20
|
+
CREDENTIAL_SOURCES,
|
|
21
|
+
createTestingConfig,
|
|
22
|
+
saveTestingConfig,
|
|
23
|
+
saveTestingRules,
|
|
24
|
+
loadTestingConfig,
|
|
25
|
+
hasTestingConfig,
|
|
26
|
+
} from '../testing/config.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run the testing setup wizard
|
|
30
|
+
*/
|
|
31
|
+
export async function runTestSetup(options) {
|
|
32
|
+
showHeader('Testing Setup Wizard');
|
|
33
|
+
|
|
34
|
+
// Check for existing config
|
|
35
|
+
if (hasTestingConfig() && !options.force) {
|
|
36
|
+
const existing = loadTestingConfig();
|
|
37
|
+
console.log(chalk.dim('Existing testing configuration found:'));
|
|
38
|
+
console.log(chalk.dim(` Mode: ${existing.mode}`));
|
|
39
|
+
console.log(chalk.dim(` Environment: ${existing.environment.type}`));
|
|
40
|
+
console.log(chalk.dim(` URL: ${existing.environment.baseUrl}`));
|
|
41
|
+
console.log('');
|
|
42
|
+
|
|
43
|
+
const { overwrite } = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'confirm',
|
|
46
|
+
name: 'overwrite',
|
|
47
|
+
message: 'Overwrite existing configuration?',
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!overwrite) {
|
|
53
|
+
console.log(chalk.yellow('Setup cancelled. Use --force to overwrite.'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const config = {};
|
|
59
|
+
|
|
60
|
+
// Step 1: Testing Mode
|
|
61
|
+
console.log(chalk.cyan.bold('\n📋 Step 1: Testing Mode\n'));
|
|
62
|
+
|
|
63
|
+
const modeChoices = Object.entries(TESTING_MODES).map(([key, mode]) => ({
|
|
64
|
+
name: `${mode.name}${mode.recommended ? chalk.green(' (Recommended)') : ''}\n ${chalk.dim(mode.description)}`,
|
|
65
|
+
value: key,
|
|
66
|
+
short: mode.name,
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
const { testingMode } = await inquirer.prompt([
|
|
70
|
+
{
|
|
71
|
+
type: 'list',
|
|
72
|
+
name: 'testingMode',
|
|
73
|
+
message: 'How should tests be run?',
|
|
74
|
+
choices: modeChoices,
|
|
75
|
+
default: 'ralph',
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
config.mode = testingMode;
|
|
80
|
+
|
|
81
|
+
// Ralph-specific configuration
|
|
82
|
+
if (testingMode === 'ralph') {
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log(chalk.dim('Ralph Loop continuously runs tests and retries fixes until all pass.'));
|
|
85
|
+
|
|
86
|
+
const { maxIterations, completionPromise } = await inquirer.prompt([
|
|
87
|
+
{
|
|
88
|
+
type: 'number',
|
|
89
|
+
name: 'maxIterations',
|
|
90
|
+
message: 'Maximum retry iterations:',
|
|
91
|
+
default: 10,
|
|
92
|
+
validate: (n) => (n > 0 && n <= 50 ? true : 'Enter 1-50'),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'input',
|
|
96
|
+
name: 'completionPromise',
|
|
97
|
+
message: 'Completion phrase (what Claude should say when done):',
|
|
98
|
+
default: 'all tasks complete and tests passing',
|
|
99
|
+
},
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
config.maxIterations = maxIterations;
|
|
103
|
+
config.completionPromise = completionPromise;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 2: Environment
|
|
107
|
+
console.log(chalk.cyan.bold('\n🌐 Step 2: Testing Environment\n'));
|
|
108
|
+
|
|
109
|
+
const envChoices = Object.entries(ENVIRONMENTS).map(([key, env]) => ({
|
|
110
|
+
name: `${env.name}${env.recommended ? chalk.green(' (Recommended)') : ''}\n ${chalk.dim(env.description)}`,
|
|
111
|
+
value: key,
|
|
112
|
+
short: env.name,
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
const { envType } = await inquirer.prompt([
|
|
116
|
+
{
|
|
117
|
+
type: 'list',
|
|
118
|
+
name: 'envType',
|
|
119
|
+
message: 'Where should tests run?',
|
|
120
|
+
choices: envChoices,
|
|
121
|
+
default: 'localhost',
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
config.envType = envType;
|
|
126
|
+
|
|
127
|
+
// Environment-specific configuration
|
|
128
|
+
if (envType === 'localhost') {
|
|
129
|
+
const { port } = await inquirer.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: 'number',
|
|
132
|
+
name: 'port',
|
|
133
|
+
message: 'Development server port:',
|
|
134
|
+
default: 5173,
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
config.port = port;
|
|
138
|
+
config.baseUrl = `http://localhost:${port}`;
|
|
139
|
+
config.requiresSetup = ['npm run dev'];
|
|
140
|
+
} else if (envType === 'ngrok') {
|
|
141
|
+
const { subdomain, port } = await inquirer.prompt([
|
|
142
|
+
{
|
|
143
|
+
type: 'input',
|
|
144
|
+
name: 'subdomain',
|
|
145
|
+
message: 'ngrok subdomain (e.g., my.app.benefits360):',
|
|
146
|
+
default: 'my.app',
|
|
147
|
+
validate: (s) => (s.length > 0 ? true : 'Subdomain required'),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
type: 'number',
|
|
151
|
+
name: 'port',
|
|
152
|
+
message: 'Local port to tunnel:',
|
|
153
|
+
default: 5173,
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
config.port = port;
|
|
157
|
+
config.baseUrl = `https://${subdomain}.ngrok.dev`;
|
|
158
|
+
config.requiresSetup = ['npm run dev', `ngrok http ${port}`];
|
|
159
|
+
} else if (envType === 'custom') {
|
|
160
|
+
const { customUrl } = await inquirer.prompt([
|
|
161
|
+
{
|
|
162
|
+
type: 'input',
|
|
163
|
+
name: 'customUrl',
|
|
164
|
+
message: 'Custom testing URL:',
|
|
165
|
+
default: 'https://example.com',
|
|
166
|
+
validate: (url) => {
|
|
167
|
+
try {
|
|
168
|
+
new URL(url);
|
|
169
|
+
return true;
|
|
170
|
+
} catch {
|
|
171
|
+
return 'Enter a valid URL';
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
]);
|
|
176
|
+
config.baseUrl = customUrl;
|
|
177
|
+
config.requiresSetup = [];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Step 3: Credentials
|
|
181
|
+
console.log(chalk.cyan.bold('\n🔐 Step 3: Test Credentials\n'));
|
|
182
|
+
|
|
183
|
+
const { needsAuth } = await inquirer.prompt([
|
|
184
|
+
{
|
|
185
|
+
type: 'confirm',
|
|
186
|
+
name: 'needsAuth',
|
|
187
|
+
message: 'Do your tests require login/authentication?',
|
|
188
|
+
default: true,
|
|
189
|
+
},
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
if (needsAuth) {
|
|
193
|
+
const credChoices = Object.entries(CREDENTIAL_SOURCES)
|
|
194
|
+
.filter(([key]) => key !== 'none')
|
|
195
|
+
.map(([key, source]) => ({
|
|
196
|
+
name: `${source.name}${source.recommended ? chalk.green(' (Recommended)') : ''}${source.secure ? chalk.blue(' 🔒') : ''}\n ${chalk.dim(source.description)}`,
|
|
197
|
+
value: key,
|
|
198
|
+
short: source.name,
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
const { credentialSource } = await inquirer.prompt([
|
|
202
|
+
{
|
|
203
|
+
type: 'list',
|
|
204
|
+
name: 'credentialSource',
|
|
205
|
+
message: 'How should credentials be managed?',
|
|
206
|
+
choices: credChoices,
|
|
207
|
+
default: 'env',
|
|
208
|
+
},
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
config.credentialSource = credentialSource;
|
|
212
|
+
|
|
213
|
+
if (credentialSource === 'env') {
|
|
214
|
+
const { usernameVar, passwordVar } = await inquirer.prompt([
|
|
215
|
+
{
|
|
216
|
+
type: 'input',
|
|
217
|
+
name: 'usernameVar',
|
|
218
|
+
message: 'Username environment variable name:',
|
|
219
|
+
default: 'TEST_USER_USERNAME',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
type: 'input',
|
|
223
|
+
name: 'passwordVar',
|
|
224
|
+
message: 'Password environment variable name:',
|
|
225
|
+
default: 'TEST_USER_PASSWORD',
|
|
226
|
+
},
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
config.envVars = {
|
|
230
|
+
username: usernameVar,
|
|
231
|
+
password: passwordVar,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Check if env vars are set
|
|
235
|
+
const hasUsername = !!process.env[usernameVar];
|
|
236
|
+
const hasPassword = !!process.env[passwordVar];
|
|
237
|
+
|
|
238
|
+
if (!hasUsername || !hasPassword) {
|
|
239
|
+
console.log('');
|
|
240
|
+
showWarning('Environment variables not currently set!');
|
|
241
|
+
console.log(chalk.dim(`Add to your .env file or shell:`));
|
|
242
|
+
console.log(chalk.dim(` export ${usernameVar}="your_username"`));
|
|
243
|
+
console.log(chalk.dim(` export ${passwordVar}="your_password"`));
|
|
244
|
+
}
|
|
245
|
+
} else if (credentialSource === 'config') {
|
|
246
|
+
showWarning('Credentials will be stored in .gtask/testing.json');
|
|
247
|
+
console.log(chalk.dim('This file will be added to .gitignore'));
|
|
248
|
+
console.log('');
|
|
249
|
+
|
|
250
|
+
const { username, password } = await inquirer.prompt([
|
|
251
|
+
{
|
|
252
|
+
type: 'input',
|
|
253
|
+
name: 'username',
|
|
254
|
+
message: 'Test username:',
|
|
255
|
+
validate: (u) => (u.length > 0 ? true : 'Username required'),
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
type: 'password',
|
|
259
|
+
name: 'password',
|
|
260
|
+
message: 'Test password:',
|
|
261
|
+
mask: '*',
|
|
262
|
+
validate: (p) => (p.length > 0 ? true : 'Password required'),
|
|
263
|
+
},
|
|
264
|
+
]);
|
|
265
|
+
|
|
266
|
+
config.username = username;
|
|
267
|
+
config.password = password;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Login selectors
|
|
271
|
+
console.log('');
|
|
272
|
+
console.log(chalk.dim('Configure login form selectors (used by Playwright):'));
|
|
273
|
+
|
|
274
|
+
const { customSelectors } = await inquirer.prompt([
|
|
275
|
+
{
|
|
276
|
+
type: 'confirm',
|
|
277
|
+
name: 'customSelectors',
|
|
278
|
+
message: 'Use custom selectors? (No = use defaults)',
|
|
279
|
+
default: false,
|
|
280
|
+
},
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
if (customSelectors) {
|
|
284
|
+
const selectors = await inquirer.prompt([
|
|
285
|
+
{
|
|
286
|
+
type: 'input',
|
|
287
|
+
name: 'usernameSelector',
|
|
288
|
+
message: 'Username input selector:',
|
|
289
|
+
default: '[data-testid="username-input"]',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: 'input',
|
|
293
|
+
name: 'passwordSelector',
|
|
294
|
+
message: 'Password input selector:',
|
|
295
|
+
default: '[data-testid="password-input"]',
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
type: 'input',
|
|
299
|
+
name: 'loginButtonSelector',
|
|
300
|
+
message: 'Login button selector:',
|
|
301
|
+
default: '[data-testid="login-submit"]',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
type: 'input',
|
|
305
|
+
name: 'loginSuccessSelector',
|
|
306
|
+
message: 'Login success indicator selector:',
|
|
307
|
+
default: '[data-testid="dashboard"]',
|
|
308
|
+
},
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
Object.assign(config, selectors);
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
config.credentialSource = 'none';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Step 4: Playwright Configuration
|
|
318
|
+
console.log(chalk.cyan.bold('\n🎭 Step 4: Playwright Configuration\n'));
|
|
319
|
+
|
|
320
|
+
const { usePlaywright } = await inquirer.prompt([
|
|
321
|
+
{
|
|
322
|
+
type: 'confirm',
|
|
323
|
+
name: 'usePlaywright',
|
|
324
|
+
message: 'Will you use Playwright for E2E testing?',
|
|
325
|
+
default: true,
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
config.playwrightEnabled = usePlaywright;
|
|
330
|
+
|
|
331
|
+
if (usePlaywright) {
|
|
332
|
+
// Check if playwright is installed
|
|
333
|
+
const playwrightConfigExists = existsSync(join(process.cwd(), 'playwright.config.ts')) ||
|
|
334
|
+
existsSync(join(process.cwd(), 'playwright.config.js'));
|
|
335
|
+
|
|
336
|
+
if (!playwrightConfigExists) {
|
|
337
|
+
showInfo('No playwright.config found in current directory.');
|
|
338
|
+
|
|
339
|
+
const { initPlaywright } = await inquirer.prompt([
|
|
340
|
+
{
|
|
341
|
+
type: 'list',
|
|
342
|
+
name: 'initPlaywright',
|
|
343
|
+
message: 'Would you like to initialize Playwright?',
|
|
344
|
+
choices: [
|
|
345
|
+
{ name: 'Yes - Run npx playwright install', value: 'install' },
|
|
346
|
+
{ name: 'Skip - I\'ll set it up later', value: 'skip' },
|
|
347
|
+
{ name: 'Show me the commands', value: 'show' },
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
]);
|
|
351
|
+
|
|
352
|
+
if (initPlaywright === 'install') {
|
|
353
|
+
console.log('');
|
|
354
|
+
console.log(chalk.dim('Run these commands after setup:'));
|
|
355
|
+
console.log(chalk.cyan(' npm init playwright@latest'));
|
|
356
|
+
console.log(chalk.cyan(' npx playwright install'));
|
|
357
|
+
} else if (initPlaywright === 'show') {
|
|
358
|
+
console.log('');
|
|
359
|
+
console.log(chalk.dim('Playwright setup commands:'));
|
|
360
|
+
console.log(chalk.cyan(' npm init playwright@latest # Initialize Playwright'));
|
|
361
|
+
console.log(chalk.cyan(' npx playwright install # Install browsers'));
|
|
362
|
+
console.log(chalk.cyan(' npx playwright test # Run tests'));
|
|
363
|
+
console.log(chalk.cyan(' npx playwright test --ui # Interactive UI mode'));
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
console.log(chalk.green('✓ Playwright config found'));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const { browser, headless } = await inquirer.prompt([
|
|
370
|
+
{
|
|
371
|
+
type: 'list',
|
|
372
|
+
name: 'browser',
|
|
373
|
+
message: 'Default browser for tests:',
|
|
374
|
+
choices: [
|
|
375
|
+
{ name: 'Chromium (Recommended)', value: 'chromium' },
|
|
376
|
+
{ name: 'Firefox', value: 'firefox' },
|
|
377
|
+
{ name: 'WebKit (Safari)', value: 'webkit' },
|
|
378
|
+
{ name: 'All browsers', value: 'all' },
|
|
379
|
+
],
|
|
380
|
+
default: 'chromium',
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
type: 'confirm',
|
|
384
|
+
name: 'headless',
|
|
385
|
+
message: 'Run tests in headless mode?',
|
|
386
|
+
default: true,
|
|
387
|
+
},
|
|
388
|
+
]);
|
|
389
|
+
|
|
390
|
+
config.browser = browser;
|
|
391
|
+
config.headless = headless;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Step 5: Generate files
|
|
395
|
+
console.log(chalk.cyan.bold('\n📄 Step 5: Generate Configuration\n'));
|
|
396
|
+
|
|
397
|
+
const spinner = ora('Creating configuration files...').start();
|
|
398
|
+
|
|
399
|
+
const testingConfig = createTestingConfig(config);
|
|
400
|
+
|
|
401
|
+
// Save main config
|
|
402
|
+
const configPath = saveTestingConfig(testingConfig);
|
|
403
|
+
spinner.text = 'Saved testing.json';
|
|
404
|
+
|
|
405
|
+
// Save testing rules markdown
|
|
406
|
+
const { generateRules } = await inquirer.prompt([
|
|
407
|
+
{
|
|
408
|
+
type: 'confirm',
|
|
409
|
+
name: 'generateRules',
|
|
410
|
+
message: 'Generate TESTING_RULES.md documentation?',
|
|
411
|
+
default: true,
|
|
412
|
+
},
|
|
413
|
+
]);
|
|
414
|
+
|
|
415
|
+
let rulesPath = null;
|
|
416
|
+
if (generateRules) {
|
|
417
|
+
rulesPath = saveTestingRules(testingConfig);
|
|
418
|
+
spinner.text = 'Saved TESTING_RULES.md';
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
spinner.succeed('Configuration files created');
|
|
422
|
+
|
|
423
|
+
// Summary
|
|
424
|
+
const details = [
|
|
425
|
+
`Mode: ${TESTING_MODES[config.mode].name}`,
|
|
426
|
+
`Environment: ${config.baseUrl}`,
|
|
427
|
+
`Credentials: ${CREDENTIAL_SOURCES[config.credentialSource].name}`,
|
|
428
|
+
`Playwright: ${config.playwrightEnabled ? 'Enabled' : 'Disabled'}`,
|
|
429
|
+
'',
|
|
430
|
+
`Config: ${configPath}`,
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
if (rulesPath) {
|
|
434
|
+
details.push(`Rules: ${rulesPath}`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (config.requiresSetup?.length > 0) {
|
|
438
|
+
details.push('');
|
|
439
|
+
details.push('Before running tests:');
|
|
440
|
+
for (const cmd of config.requiresSetup) {
|
|
441
|
+
details.push(` ${cmd}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
showSuccess('Testing Setup Complete!', details);
|
|
446
|
+
|
|
447
|
+
// Show next steps
|
|
448
|
+
console.log(chalk.dim('\nNext steps:'));
|
|
449
|
+
console.log(chalk.dim(' gtask test - Run tests'));
|
|
450
|
+
console.log(chalk.dim(' gtask test --watch - Watch mode'));
|
|
451
|
+
if (config.mode === 'ralph') {
|
|
452
|
+
console.log(chalk.dim(' gtask sync watch - Start Ralph loop with task tracking'));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return testingConfig;
|
|
456
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Command
|
|
3
|
+
*
|
|
4
|
+
* Validates templates for hardcoded values and ensures platform agnosticism.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import { showHeader, showSuccess, showError } from '../cli/menu.js';
|
|
11
|
+
import { scanDirectory, formatViolations } from '../utils/validate-templates.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Run template validation
|
|
15
|
+
*/
|
|
16
|
+
export async function runValidate(options = {}) {
|
|
17
|
+
showHeader('Template Validation');
|
|
18
|
+
|
|
19
|
+
const { path: targetPath = 'templates', fix = false, verbose = false } = options;
|
|
20
|
+
|
|
21
|
+
const fullPath = join(process.cwd(), targetPath);
|
|
22
|
+
|
|
23
|
+
if (!existsSync(fullPath)) {
|
|
24
|
+
showError('Directory not found', `Could not find: ${fullPath}`);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(chalk.dim(` Scanning: ${fullPath}`));
|
|
29
|
+
console.log(chalk.dim(` Checking for hardcoded values...\n`));
|
|
30
|
+
|
|
31
|
+
const violations = scanDirectory(fullPath, {
|
|
32
|
+
extensions: ['.md', '.js', '.ts', '.json'],
|
|
33
|
+
exclude: ['node_modules', '.git', 'dist', 'build', 'cache'],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log(formatViolations(violations));
|
|
37
|
+
|
|
38
|
+
if (violations.length === 0) {
|
|
39
|
+
showSuccess('Validation Passed', [
|
|
40
|
+
'',
|
|
41
|
+
'All templates use parameterized values.',
|
|
42
|
+
'No hardcoded project-specific values detected.',
|
|
43
|
+
]);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(chalk.yellow('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
48
|
+
console.log(chalk.yellow.bold('\nHow to Fix:\n'));
|
|
49
|
+
console.log(chalk.dim(' 1. Replace hardcoded values with {{placeholder}} syntax'));
|
|
50
|
+
console.log(chalk.dim(' 2. Add the placeholder path to tech-stack.schema.json'));
|
|
51
|
+
console.log(chalk.dim(' 3. Set default values in tech-stack.defaults.json'));
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(chalk.dim(' Example transformations:'));
|
|
54
|
+
console.log(chalk.dim(' --project-name=my-project'));
|
|
55
|
+
console.log(chalk.green(' --project-name={{deployment.frontend.projectName}}'));
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(chalk.dim(' ngrok http 5174'));
|
|
58
|
+
console.log(chalk.green(' {{devEnvironment.tunnel.startCommand}}'));
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(chalk.dim(' projectId: "abc123-..."'));
|
|
61
|
+
console.log(chalk.green(' projectId: "{{deployment.backend.projectId}}"'));
|
|
62
|
+
console.log('');
|
|
63
|
+
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default runValidate;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"project": {
|
|
4
|
+
"name": "{{PROJECT_NAME}}",
|
|
5
|
+
"description": "{{PROJECT_DESCRIPTION}}",
|
|
6
|
+
"rootPath": "."
|
|
7
|
+
},
|
|
8
|
+
"frontend": {
|
|
9
|
+
"framework": "{{FRONTEND_FRAMEWORK}}",
|
|
10
|
+
"buildTool": "{{FRONTEND_BUILD_TOOL}}",
|
|
11
|
+
"stateManager": "{{FRONTEND_STATE_MANAGER}}",
|
|
12
|
+
"styling": "{{FRONTEND_STYLING}}",
|
|
13
|
+
"port": "{{FRONTEND_PORT}}",
|
|
14
|
+
"devCommand": "{{FRONTEND_DEV_COMMAND}}",
|
|
15
|
+
"buildCommand": "{{FRONTEND_BUILD_COMMAND}}",
|
|
16
|
+
"distDir": "{{FRONTEND_DIST_DIR}}"
|
|
17
|
+
},
|
|
18
|
+
"backend": {
|
|
19
|
+
"language": "{{BACKEND_LANGUAGE}}",
|
|
20
|
+
"framework": "{{BACKEND_FRAMEWORK}}",
|
|
21
|
+
"apiStyle": "{{BACKEND_API_STYLE}}",
|
|
22
|
+
"port": "{{BACKEND_PORT}}",
|
|
23
|
+
"devCommand": "{{BACKEND_DEV_COMMAND}}",
|
|
24
|
+
"healthEndpoint": "{{BACKEND_HEALTH_ENDPOINT}}"
|
|
25
|
+
},
|
|
26
|
+
"database": {
|
|
27
|
+
"primary": "{{DATABASE_TYPE}}",
|
|
28
|
+
"orm": "{{DATABASE_ORM}}",
|
|
29
|
+
"host": "{{DATABASE_HOST}}",
|
|
30
|
+
"port": "{{DATABASE_PORT}}"
|
|
31
|
+
},
|
|
32
|
+
"deployment": {
|
|
33
|
+
"frontend": {
|
|
34
|
+
"platform": "{{DEPLOY_FRONTEND_PLATFORM}}",
|
|
35
|
+
"projectName": "{{DEPLOY_FRONTEND_PROJECT}}",
|
|
36
|
+
"productionUrl": "{{DEPLOY_FRONTEND_URL}}",
|
|
37
|
+
"deployCommand": "{{DEPLOY_FRONTEND_COMMAND}}"
|
|
38
|
+
},
|
|
39
|
+
"backend": {
|
|
40
|
+
"platform": "{{DEPLOY_BACKEND_PLATFORM}}",
|
|
41
|
+
"projectId": "{{DEPLOY_BACKEND_PROJECT_ID}}",
|
|
42
|
+
"serviceId": "{{DEPLOY_BACKEND_SERVICE_ID}}",
|
|
43
|
+
"environmentId": "{{DEPLOY_BACKEND_ENV_ID}}",
|
|
44
|
+
"productionUrl": "{{DEPLOY_BACKEND_URL}}",
|
|
45
|
+
"selfHostedConfig": {
|
|
46
|
+
"sshUser": "{{SELF_HOSTED_SSH_USER}}",
|
|
47
|
+
"sshHost": "{{SELF_HOSTED_SSH_HOST}}",
|
|
48
|
+
"sshPort": 22,
|
|
49
|
+
"deployScript": "{{SELF_HOSTED_DEPLOY_SCRIPT}}",
|
|
50
|
+
"appPath": "{{SELF_HOSTED_APP_PATH}}"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"cicd": "{{CICD_PLATFORM}}"
|
|
54
|
+
},
|
|
55
|
+
"devEnvironment": {
|
|
56
|
+
"tunnel": {
|
|
57
|
+
"service": "{{TUNNEL_SERVICE}}",
|
|
58
|
+
"url": "{{TUNNEL_URL}}",
|
|
59
|
+
"subdomain": "{{TUNNEL_SUBDOMAIN}}",
|
|
60
|
+
"startCommand": "{{TUNNEL_START_COMMAND}}",
|
|
61
|
+
"adminPort": "{{TUNNEL_ADMIN_PORT}}"
|
|
62
|
+
},
|
|
63
|
+
"container": "{{CONTAINER_RUNTIME}}",
|
|
64
|
+
"packageManager": "{{PACKAGE_MANAGER}}"
|
|
65
|
+
},
|
|
66
|
+
"testing": {
|
|
67
|
+
"e2e": {
|
|
68
|
+
"framework": "{{E2E_FRAMEWORK}}",
|
|
69
|
+
"configFile": "{{E2E_CONFIG_FILE}}",
|
|
70
|
+
"testCommand": "{{E2E_TEST_COMMAND}}",
|
|
71
|
+
"browsers": ["{{E2E_BROWSERS}}"],
|
|
72
|
+
"headless": true,
|
|
73
|
+
"timeout": 30000
|
|
74
|
+
},
|
|
75
|
+
"unit": {
|
|
76
|
+
"framework": "{{UNIT_TEST_FRAMEWORK}}",
|
|
77
|
+
"testCommand": "{{UNIT_TEST_COMMAND}}",
|
|
78
|
+
"coverageTarget": 80
|
|
79
|
+
},
|
|
80
|
+
"selectors": {
|
|
81
|
+
"strategy": "{{SELECTOR_STRATEGY}}",
|
|
82
|
+
"username": "{{SELECTOR_USERNAME}}",
|
|
83
|
+
"password": "{{SELECTOR_PASSWORD}}",
|
|
84
|
+
"loginButton": "{{SELECTOR_LOGIN_BUTTON}}",
|
|
85
|
+
"loginSuccess": "{{SELECTOR_LOGIN_SUCCESS}}"
|
|
86
|
+
},
|
|
87
|
+
"credentials": {
|
|
88
|
+
"usernameEnvVar": "{{TEST_USERNAME_ENV_VAR}}",
|
|
89
|
+
"passwordEnvVar": "{{TEST_PASSWORD_ENV_VAR}}"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"versionControl": {
|
|
93
|
+
"provider": "{{VCS_PROVIDER}}",
|
|
94
|
+
"owner": "{{VCS_OWNER}}",
|
|
95
|
+
"repo": "{{VCS_REPO}}",
|
|
96
|
+
"defaultBranch": "{{VCS_DEFAULT_BRANCH}}",
|
|
97
|
+
"projectBoard": {
|
|
98
|
+
"type": "{{PROJECT_BOARD_TYPE}}",
|
|
99
|
+
"number": "{{PROJECT_BOARD_NUMBER}}",
|
|
100
|
+
"id": "{{PROJECT_BOARD_ID}}",
|
|
101
|
+
"url": "{{PROJECT_BOARD_URL}}"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"urls": {
|
|
105
|
+
"local": {
|
|
106
|
+
"frontend": "http://localhost:{{FRONTEND_PORT}}",
|
|
107
|
+
"backend": "http://localhost:{{BACKEND_PORT}}",
|
|
108
|
+
"api": "http://localhost:{{BACKEND_PORT}}/api"
|
|
109
|
+
},
|
|
110
|
+
"tunnel": {
|
|
111
|
+
"frontend": "{{TUNNEL_URL}}",
|
|
112
|
+
"backend": "{{TUNNEL_URL}}"
|
|
113
|
+
},
|
|
114
|
+
"production": {
|
|
115
|
+
"frontend": "{{DEPLOY_FRONTEND_URL}}",
|
|
116
|
+
"backend": "{{DEPLOY_BACKEND_URL}}"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"tokenManagement": {
|
|
120
|
+
"enabled": false,
|
|
121
|
+
"dailyBudget": 200000,
|
|
122
|
+
"thresholds": {
|
|
123
|
+
"compact": 0.75,
|
|
124
|
+
"archive": 0.85,
|
|
125
|
+
"respawn": 0.90
|
|
126
|
+
},
|
|
127
|
+
"trackingFile": ".claude/hooks/cache/token-usage.json"
|
|
128
|
+
},
|
|
129
|
+
"happyMode": {
|
|
130
|
+
"enabled": false,
|
|
131
|
+
"dashboardUrl": null,
|
|
132
|
+
"checkpointInterval": 10,
|
|
133
|
+
"verbosity": "condensed",
|
|
134
|
+
"notifications": {
|
|
135
|
+
"onTaskComplete": true,
|
|
136
|
+
"onError": true,
|
|
137
|
+
"onCheckpoint": false
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"agents": {
|
|
141
|
+
"enabled": true,
|
|
142
|
+
"l1": {
|
|
143
|
+
"model": "sonnet",
|
|
144
|
+
"tools": ["Task", "Read", "Grep", "Glob", "WebSearch"],
|
|
145
|
+
"maxTokens": 16000
|
|
146
|
+
},
|
|
147
|
+
"l2": {
|
|
148
|
+
"model": "sonnet",
|
|
149
|
+
"tools": ["Read", "Edit", "Write", "Bash", "Grep", "Glob"],
|
|
150
|
+
"maxTokens": 8000
|
|
151
|
+
},
|
|
152
|
+
"l3": {
|
|
153
|
+
"model": "haiku",
|
|
154
|
+
"tools": ["Read", "Grep"],
|
|
155
|
+
"maxTokens": 500
|
|
156
|
+
},
|
|
157
|
+
"maxConcurrent": 4
|
|
158
|
+
},
|
|
159
|
+
"phasedDevelopment": {
|
|
160
|
+
"enabled": true,
|
|
161
|
+
"defaultScale": "M",
|
|
162
|
+
"successTarget": 0.95,
|
|
163
|
+
"progressFile": ".claude/docs/{project-slug}/PROGRESS.json",
|
|
164
|
+
"generateArtifacts": {
|
|
165
|
+
"executiveSummary": true,
|
|
166
|
+
"deploymentRunbook": true,
|
|
167
|
+
"rollbackPlan": true,
|
|
168
|
+
"testPlan": true
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"hooks": {
|
|
172
|
+
"enabled": true,
|
|
173
|
+
"priorities": {
|
|
174
|
+
"lifecycle": 100,
|
|
175
|
+
"tools": 1000,
|
|
176
|
+
"automation": 2000
|
|
177
|
+
},
|
|
178
|
+
"errorBehavior": "approve",
|
|
179
|
+
"cacheDir": ".claude/hooks/cache",
|
|
180
|
+
"configDir": ".claude/hooks/config"
|
|
181
|
+
}
|
|
182
|
+
}
|