@fermindi/pwn-cli 0.9.3 → 0.9.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fermindi/pwn-cli",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Professional AI Workspace - Inject structured memory and automation into any project for AI-powered development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,10 +9,13 @@
9
9
  * Completed files are cleaned up at the end; failed are kept for review.
10
10
  */
11
11
 
12
- import { spawn } from 'child_process';
12
+ import { spawn, exec } from 'child_process';
13
13
  import { existsSync, readFileSync, writeFileSync, mkdirSync, createWriteStream, appendFileSync, unlinkSync, readdirSync } from 'fs';
14
14
  import { join } from 'path';
15
+ import { promisify } from 'util';
15
16
  import ora from 'ora';
17
+
18
+ const execAsync = promisify(exec);
16
19
  import chalk from 'chalk';
17
20
  import {
18
21
  parsePrdTasks,
@@ -31,7 +34,7 @@ import {
31
34
  } from './batch-service.js';
32
35
 
33
36
  // --- Constants ---
34
- const RUNNER_VERSION = '2.2';
37
+ const RUNNER_VERSION = '2.3';
35
38
  const DEFAULT_TIMEOUT_MS = 900_000; // 15 minutes fallback
36
39
  const MIN_TIMEOUT_MS = 300_000; // 5 minutes minimum (claude init ~30-40s + real work)
37
40
 
@@ -405,13 +408,78 @@ export async function runBatch(options = {}, cwd = process.cwd()) {
405
408
  console.log(chalk.yellow(' Continuing on current branch...'));
406
409
  }
407
410
 
411
+ // --- Pre-validation: check if existing branch already passes gates ---
412
+ let storyDone = false;
413
+ if (taskBranch) {
414
+ // Check if this branch has commits beyond the batch branch
415
+ try {
416
+ const { stdout: diffStat } = await execAsync('git diff --stat HEAD~1 2>/dev/null || echo ""', { cwd });
417
+ const { stdout: logCount } = await execAsync(`git rev-list --count ${batchBranch}..HEAD`, { cwd });
418
+ const commits = parseInt(logCount.trim(), 10);
419
+
420
+ if (commits > 0) {
421
+ console.log(chalk.blue(` Pre-validation: found ${commits} commit(s) on ${taskBranch}, running quality gates...`));
422
+ const preGates = await runGatesWithStatus(cwd);
423
+
424
+ if (preGates.success) {
425
+ console.log(chalk.green(` Pre-validation PASSED — skipping execution`));
426
+ markStoryDone(task.id, cwd);
427
+ appendProgress(progressPath, task.id, 'Pre-validation: existing code passed quality gates');
428
+
429
+ storyDone = true;
430
+ storiesCompleted++;
431
+ noProgressCount = 0;
432
+
433
+ if (taskFile) {
434
+ taskFile.status = 'completed';
435
+ taskFile.completed_at = new Date().toISOString();
436
+ saveTaskFile(taskFile, cwd);
437
+ }
438
+
439
+ updateBatchState({
440
+ completed: [task.id],
441
+ current_task: null,
442
+ last_completed_at: new Date().toISOString()
443
+ }, cwd);
444
+
445
+ // Merge into batch branch
446
+ try {
447
+ await checkoutBranch(batchBranch, cwd, { stash: true });
448
+ await mergeBranch(taskBranch, cwd);
449
+ mergedCount++;
450
+ mergedBranches.push(taskBranch);
451
+ console.log(chalk.green(` Merged ${taskBranch} → ${batchBranch}`));
452
+ } catch (err) {
453
+ console.log(chalk.red(` Merge failed: ${err.message}`));
454
+ console.log(chalk.yellow(` Branch ${taskBranch} available for manual merge`));
455
+ unmergedBranches.push(taskBranch);
456
+ }
457
+ } else {
458
+ console.log(chalk.yellow(` Pre-validation FAILED — will re-execute`));
459
+ }
460
+ }
461
+ } catch {
462
+ // No commits or comparison failed — proceed normally
463
+ }
464
+ }
465
+
466
+ if (storyDone) {
467
+ // Return to batch branch
468
+ if (taskBranch) {
469
+ try {
470
+ await checkoutBranch(batchBranch, cwd, { force: true, stash: true });
471
+ console.log(chalk.dim(` Returned to branch: ${batchBranch}`));
472
+ } catch {}
473
+ }
474
+ continue;
475
+ }
476
+
408
477
  // --- Phase 2: Execution ---
409
478
  const phaseLabel = noPlan ? '' : 'Phase 2';
410
479
  console.log(chalk.blue(` ${noPlan ? 'Executing' : 'Phase 2: Executing'} ${task.id}...`));
411
480
 
412
481
  let retry = 0;
413
482
  let rateLimitAttempts = 0;
414
- let storyDone = false;
415
483
  let errorContext = '';
416
484
 
417
485
  while (retry <= MAX_RETRIES && !storyDone) {