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
|
+
* Test Run Command
|
|
3
|
+
*
|
|
4
|
+
* Run tests with support for different modes:
|
|
5
|
+
* - Ralph Loop: Auto-retry until tests pass
|
|
6
|
+
* - Manual: Run once
|
|
7
|
+
* - Watch: Interactive test watching
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import inquirer from 'inquirer';
|
|
12
|
+
import ora from 'ora';
|
|
13
|
+
import { spawn } from 'child_process';
|
|
14
|
+
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
|
|
17
|
+
import {
|
|
18
|
+
loadTestingConfig,
|
|
19
|
+
hasTestingConfig,
|
|
20
|
+
getCredentials,
|
|
21
|
+
validateConfig,
|
|
22
|
+
} from '../testing/config.js';
|
|
23
|
+
import { loadTaskState, updateTaskStatus } from './sync.js';
|
|
24
|
+
|
|
25
|
+
// Ralph loop state file
|
|
26
|
+
const RALPH_STATE_FILE = '.gtask/ralph-loop.json';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run tests command
|
|
30
|
+
*/
|
|
31
|
+
export async function runTest(options) {
|
|
32
|
+
// Load config
|
|
33
|
+
const config = loadTestingConfig();
|
|
34
|
+
|
|
35
|
+
if (!config) {
|
|
36
|
+
showError('Testing not configured', 'Run "gtask test-setup" first.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate config
|
|
41
|
+
const validation = validateConfig(config);
|
|
42
|
+
if (!validation.valid) {
|
|
43
|
+
showError('Invalid testing configuration');
|
|
44
|
+
for (const err of validation.errors) {
|
|
45
|
+
console.log(chalk.red(` - ${err}`));
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Determine mode
|
|
51
|
+
const mode = options.mode || config.mode || 'manual';
|
|
52
|
+
|
|
53
|
+
// Get credentials if needed
|
|
54
|
+
let credentials = null;
|
|
55
|
+
if (config.credentials.source !== 'none') {
|
|
56
|
+
credentials = getCredentials(config);
|
|
57
|
+
|
|
58
|
+
if (!credentials && config.credentials.source === 'prompt') {
|
|
59
|
+
credentials = await promptCredentials();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!credentials?.username || !credentials?.password) {
|
|
63
|
+
if (config.credentials.source === 'env') {
|
|
64
|
+
showWarning('Credentials not found in environment variables.');
|
|
65
|
+
console.log(chalk.dim(`Set ${config.credentials.envVars.username} and ${config.credentials.envVars.password}`));
|
|
66
|
+
|
|
67
|
+
const { continueWithout } = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: 'confirm',
|
|
70
|
+
name: 'continueWithout',
|
|
71
|
+
message: 'Continue without credentials?',
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
if (!continueWithout) return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Run based on mode
|
|
82
|
+
switch (mode) {
|
|
83
|
+
case 'ralph':
|
|
84
|
+
await runRalphLoop(config, credentials, options);
|
|
85
|
+
break;
|
|
86
|
+
case 'watch':
|
|
87
|
+
await runWatchMode(config, credentials, options);
|
|
88
|
+
break;
|
|
89
|
+
case 'manual':
|
|
90
|
+
default:
|
|
91
|
+
await runOnce(config, credentials, options);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Run tests once
|
|
97
|
+
*/
|
|
98
|
+
async function runOnce(config, credentials, options) {
|
|
99
|
+
showHeader('Running Tests');
|
|
100
|
+
|
|
101
|
+
console.log(chalk.dim(`Environment: ${config.environment.baseUrl}`));
|
|
102
|
+
console.log(chalk.dim(`Browser: ${config.playwright.browser}`));
|
|
103
|
+
console.log('');
|
|
104
|
+
|
|
105
|
+
// Build environment variables
|
|
106
|
+
const env = buildTestEnv(config, credentials);
|
|
107
|
+
|
|
108
|
+
// Determine test command
|
|
109
|
+
const testCommand = options.command || 'npx playwright test';
|
|
110
|
+
const testArgs = [];
|
|
111
|
+
|
|
112
|
+
if (options.file) {
|
|
113
|
+
testArgs.push(options.file);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!config.playwright.headless || options.headed) {
|
|
117
|
+
testArgs.push('--headed');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (options.ui) {
|
|
121
|
+
testArgs.push('--ui');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(chalk.dim(`Running: ${testCommand} ${testArgs.join(' ')}`));
|
|
125
|
+
console.log('');
|
|
126
|
+
|
|
127
|
+
const result = await executeTests(testCommand, testArgs, env);
|
|
128
|
+
|
|
129
|
+
if (result.success) {
|
|
130
|
+
showSuccess('All Tests Passed!', [
|
|
131
|
+
`Duration: ${result.duration}s`,
|
|
132
|
+
`Tests: ${result.passed} passed`,
|
|
133
|
+
]);
|
|
134
|
+
} else {
|
|
135
|
+
showError('Tests Failed', `${result.failed} test(s) failed`);
|
|
136
|
+
if (result.output) {
|
|
137
|
+
console.log(chalk.dim('\nOutput:'));
|
|
138
|
+
console.log(result.output.slice(-2000));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Run Ralph Loop - continuous test-fix cycle
|
|
147
|
+
*/
|
|
148
|
+
async function runRalphLoop(config, credentials, options) {
|
|
149
|
+
showHeader('Ralph Loop - Test-Fix Cycle');
|
|
150
|
+
|
|
151
|
+
const maxIterations = options.max || config.ralphConfig?.maxIterations || 10;
|
|
152
|
+
const completionPromise = config.ralphConfig?.completionPromise || 'all tasks complete';
|
|
153
|
+
|
|
154
|
+
console.log(chalk.cyan(`Max iterations: ${maxIterations}`));
|
|
155
|
+
console.log(chalk.cyan(`Completion phrase: "${completionPromise}"`));
|
|
156
|
+
console.log('');
|
|
157
|
+
|
|
158
|
+
// Initialize state
|
|
159
|
+
const state = {
|
|
160
|
+
iteration: 0,
|
|
161
|
+
maxIterations,
|
|
162
|
+
completionPromise,
|
|
163
|
+
startedAt: new Date().toISOString(),
|
|
164
|
+
history: [],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
saveRalphState(state);
|
|
168
|
+
|
|
169
|
+
// Build environment
|
|
170
|
+
const env = buildTestEnv(config, credentials);
|
|
171
|
+
|
|
172
|
+
let iteration = 0;
|
|
173
|
+
let allPassed = false;
|
|
174
|
+
|
|
175
|
+
while (iteration < maxIterations && !allPassed) {
|
|
176
|
+
iteration++;
|
|
177
|
+
state.iteration = iteration;
|
|
178
|
+
|
|
179
|
+
console.log(chalk.cyan.bold(`\n═══ Iteration ${iteration}/${maxIterations} ═══\n`));
|
|
180
|
+
|
|
181
|
+
// Run tests
|
|
182
|
+
const result = await executeTests('npx playwright test', [], env);
|
|
183
|
+
|
|
184
|
+
state.history.push({
|
|
185
|
+
iteration,
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
passed: result.success,
|
|
188
|
+
passedCount: result.passed,
|
|
189
|
+
failedCount: result.failed,
|
|
190
|
+
duration: result.duration,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
saveRalphState(state);
|
|
194
|
+
|
|
195
|
+
if (result.success) {
|
|
196
|
+
allPassed = true;
|
|
197
|
+
console.log(chalk.green.bold('\n✓ All tests passed!\n'));
|
|
198
|
+
} else {
|
|
199
|
+
console.log(chalk.yellow(`\n✗ ${result.failed} test(s) failed\n`));
|
|
200
|
+
|
|
201
|
+
if (iteration < maxIterations) {
|
|
202
|
+
// Show failures
|
|
203
|
+
if (result.failedTests?.length > 0) {
|
|
204
|
+
console.log(chalk.dim('Failed tests:'));
|
|
205
|
+
for (const test of result.failedTests.slice(0, 5)) {
|
|
206
|
+
console.log(chalk.dim(` - ${test}`));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Prompt for fix or continue
|
|
211
|
+
const { action } = await inquirer.prompt([
|
|
212
|
+
{
|
|
213
|
+
type: 'list',
|
|
214
|
+
name: 'action',
|
|
215
|
+
message: 'What would you like to do?',
|
|
216
|
+
choices: [
|
|
217
|
+
{ name: 'Continue - I\'ve made fixes, retry tests', value: 'continue' },
|
|
218
|
+
{ name: 'Show errors - View detailed error output', value: 'show' },
|
|
219
|
+
{ name: 'Stop - Exit Ralph loop', value: 'stop' },
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
]);
|
|
223
|
+
|
|
224
|
+
if (action === 'stop') {
|
|
225
|
+
console.log(chalk.yellow('Ralph loop stopped by user.'));
|
|
226
|
+
break;
|
|
227
|
+
} else if (action === 'show') {
|
|
228
|
+
console.log('\n' + chalk.dim('─'.repeat(60)));
|
|
229
|
+
console.log(result.output?.slice(-3000) || 'No output captured');
|
|
230
|
+
console.log(chalk.dim('─'.repeat(60)) + '\n');
|
|
231
|
+
|
|
232
|
+
const { continueAfterShow } = await inquirer.prompt([
|
|
233
|
+
{
|
|
234
|
+
type: 'confirm',
|
|
235
|
+
name: 'continueAfterShow',
|
|
236
|
+
message: 'Continue with next iteration?',
|
|
237
|
+
default: true,
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
|
|
241
|
+
if (!continueAfterShow) {
|
|
242
|
+
console.log(chalk.yellow('Ralph loop stopped.'));
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log(chalk.dim('\nWaiting for fixes... Press Enter when ready.\n'));
|
|
248
|
+
await inquirer.prompt([
|
|
249
|
+
{
|
|
250
|
+
type: 'input',
|
|
251
|
+
name: 'ready',
|
|
252
|
+
message: 'Press Enter to continue...',
|
|
253
|
+
},
|
|
254
|
+
]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Clean up
|
|
260
|
+
cleanupRalphState();
|
|
261
|
+
|
|
262
|
+
// Summary
|
|
263
|
+
const totalDuration = state.history.reduce((sum, h) => sum + (h.duration || 0), 0);
|
|
264
|
+
|
|
265
|
+
if (allPassed) {
|
|
266
|
+
showSuccess('Ralph Loop Complete!', [
|
|
267
|
+
`Iterations: ${iteration}`,
|
|
268
|
+
`Total duration: ${totalDuration}s`,
|
|
269
|
+
`Final result: All tests passing`,
|
|
270
|
+
]);
|
|
271
|
+
} else {
|
|
272
|
+
showError('Ralph Loop Stopped', [
|
|
273
|
+
`Iterations: ${iteration}/${maxIterations}`,
|
|
274
|
+
`Total duration: ${totalDuration}s`,
|
|
275
|
+
`Final result: Tests still failing`,
|
|
276
|
+
]);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { success: allPassed, iterations: iteration, state };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Run watch mode - interactive test watching
|
|
284
|
+
*/
|
|
285
|
+
async function runWatchMode(config, credentials, options) {
|
|
286
|
+
showHeader('Test Watch Mode');
|
|
287
|
+
|
|
288
|
+
console.log(chalk.dim('Tests will re-run on file changes.'));
|
|
289
|
+
console.log(chalk.dim('Press q to quit, r to re-run manually.'));
|
|
290
|
+
console.log('');
|
|
291
|
+
|
|
292
|
+
const env = buildTestEnv(config, credentials);
|
|
293
|
+
|
|
294
|
+
// Use Playwright's built-in watch mode if available
|
|
295
|
+
const args = ['--ui'];
|
|
296
|
+
|
|
297
|
+
console.log(chalk.dim('Starting Playwright UI mode...'));
|
|
298
|
+
console.log(chalk.dim('This opens an interactive test runner.'));
|
|
299
|
+
console.log('');
|
|
300
|
+
|
|
301
|
+
await executeTests('npx playwright test', args, env, { interactive: true });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Execute tests and capture results
|
|
306
|
+
*/
|
|
307
|
+
function executeTests(command, args, env, options = {}) {
|
|
308
|
+
return new Promise((resolve) => {
|
|
309
|
+
const startTime = Date.now();
|
|
310
|
+
let output = '';
|
|
311
|
+
let passed = 0;
|
|
312
|
+
let failed = 0;
|
|
313
|
+
const failedTests = [];
|
|
314
|
+
|
|
315
|
+
const [cmd, ...cmdArgs] = command.split(' ');
|
|
316
|
+
const allArgs = [...cmdArgs, ...args];
|
|
317
|
+
|
|
318
|
+
const proc = spawn(cmd, allArgs, {
|
|
319
|
+
env: { ...process.env, ...env },
|
|
320
|
+
shell: true,
|
|
321
|
+
stdio: options.interactive ? 'inherit' : 'pipe',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (!options.interactive) {
|
|
325
|
+
proc.stdout?.on('data', (data) => {
|
|
326
|
+
const text = data.toString();
|
|
327
|
+
output += text;
|
|
328
|
+
process.stdout.write(text);
|
|
329
|
+
|
|
330
|
+
// Parse test results from output
|
|
331
|
+
const passMatch = text.match(/(\d+) passed/);
|
|
332
|
+
const failMatch = text.match(/(\d+) failed/);
|
|
333
|
+
|
|
334
|
+
if (passMatch) passed = parseInt(passMatch[1], 10);
|
|
335
|
+
if (failMatch) failed = parseInt(failMatch[1], 10);
|
|
336
|
+
|
|
337
|
+
// Capture failed test names
|
|
338
|
+
const failedMatch = text.match(/✘\s+(.+)/g);
|
|
339
|
+
if (failedMatch) {
|
|
340
|
+
failedTests.push(...failedMatch.map((m) => m.replace('✘ ', '')));
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
proc.stderr?.on('data', (data) => {
|
|
345
|
+
const text = data.toString();
|
|
346
|
+
output += text;
|
|
347
|
+
process.stderr.write(text);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
proc.on('close', (code) => {
|
|
352
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
353
|
+
|
|
354
|
+
resolve({
|
|
355
|
+
success: code === 0,
|
|
356
|
+
exitCode: code,
|
|
357
|
+
passed,
|
|
358
|
+
failed,
|
|
359
|
+
failedTests,
|
|
360
|
+
duration,
|
|
361
|
+
output,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
proc.on('error', (err) => {
|
|
366
|
+
resolve({
|
|
367
|
+
success: false,
|
|
368
|
+
error: err.message,
|
|
369
|
+
output,
|
|
370
|
+
duration: Math.round((Date.now() - startTime) / 1000),
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Build environment variables for tests
|
|
378
|
+
*/
|
|
379
|
+
function buildTestEnv(config, credentials) {
|
|
380
|
+
const env = {
|
|
381
|
+
BASE_URL: config.environment.baseUrl,
|
|
382
|
+
PLAYWRIGHT_BASE_URL: config.environment.baseUrl,
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
if (credentials) {
|
|
386
|
+
env.TEST_USER_USERNAME = credentials.username;
|
|
387
|
+
env.TEST_USER_PASSWORD = credentials.password;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (config.playwright) {
|
|
391
|
+
if (!config.playwright.headless) {
|
|
392
|
+
env.HEADED = 'true';
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return env;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Prompt for credentials
|
|
401
|
+
*/
|
|
402
|
+
async function promptCredentials() {
|
|
403
|
+
const { username, password } = await inquirer.prompt([
|
|
404
|
+
{
|
|
405
|
+
type: 'input',
|
|
406
|
+
name: 'username',
|
|
407
|
+
message: 'Test username:',
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: 'password',
|
|
411
|
+
name: 'password',
|
|
412
|
+
message: 'Test password:',
|
|
413
|
+
mask: '*',
|
|
414
|
+
},
|
|
415
|
+
]);
|
|
416
|
+
|
|
417
|
+
return { username, password };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Save Ralph loop state
|
|
422
|
+
*/
|
|
423
|
+
function saveRalphState(state) {
|
|
424
|
+
try {
|
|
425
|
+
writeFileSync(RALPH_STATE_FILE, JSON.stringify(state, null, 2), 'utf8');
|
|
426
|
+
} catch {
|
|
427
|
+
// Ignore errors
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Load Ralph loop state
|
|
433
|
+
*/
|
|
434
|
+
function loadRalphState() {
|
|
435
|
+
try {
|
|
436
|
+
if (existsSync(RALPH_STATE_FILE)) {
|
|
437
|
+
return JSON.parse(readFileSync(RALPH_STATE_FILE, 'utf8'));
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
// Ignore errors
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Clean up Ralph loop state
|
|
447
|
+
*/
|
|
448
|
+
function cleanupRalphState() {
|
|
449
|
+
try {
|
|
450
|
+
if (existsSync(RALPH_STATE_FILE)) {
|
|
451
|
+
unlinkSync(RALPH_STATE_FILE);
|
|
452
|
+
}
|
|
453
|
+
} catch {
|
|
454
|
+
// Ignore errors
|
|
455
|
+
}
|
|
456
|
+
}
|