@fermindi/pwn-cli 0.9.8 → 0.9.10
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
|
@@ -534,7 +534,7 @@ export async function runBatch(options = {}, cwd = process.cwd()) {
|
|
|
534
534
|
console.log(chalk.yellow(` Retry ${retry}/${MAX_RETRIES}`));
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
-
const prompt = buildPrompt(task.id, batchWorktreePath, mainCwd, errorContext);
|
|
537
|
+
const prompt = buildPrompt(task.id, batchWorktreePath, mainCwd, errorContext, retry);
|
|
538
538
|
if (!prompt) {
|
|
539
539
|
console.log(chalk.red(` Cannot build prompt for ${task.id} — skipping`));
|
|
540
540
|
break;
|
|
@@ -626,6 +626,9 @@ export async function runBatch(options = {}, cwd = process.cwd()) {
|
|
|
626
626
|
markStoryDone(task.id, taskWorktreePath);
|
|
627
627
|
appendProgress(progressPath, task.id, 'All quality gates passed');
|
|
628
628
|
|
|
629
|
+
// Remove symlinks before commit so git add . doesn't stage them
|
|
630
|
+
unprepareWorktree(taskWorktreePath);
|
|
631
|
+
|
|
629
632
|
const config = loadConfig(mainCwd);
|
|
630
633
|
if (config.auto_commit) {
|
|
631
634
|
await commitTask(task, {}, taskWorktreePath);
|
|
@@ -926,12 +929,14 @@ async function runGatesWithStatus(taskCwd, mainCwd = taskCwd) {
|
|
|
926
929
|
|
|
927
930
|
/**
|
|
928
931
|
* Build the prompt from template, substituting placeholders.
|
|
932
|
+
* On retries, generates a focused "fix" prompt instead of the full template.
|
|
929
933
|
* @param {string} storyId - Story ID
|
|
930
934
|
* @param {string} batchWorktreePath - Batch worktree (for prd.json, tracked file)
|
|
931
935
|
* @param {string} mainCwd - Main repo (for prompt.md, gitignored file)
|
|
932
936
|
* @param {string} extraContext - Error context from previous attempt
|
|
937
|
+
* @param {number} retry - Retry number (0 = first attempt)
|
|
933
938
|
*/
|
|
934
|
-
function buildPrompt(storyId, batchWorktreePath, mainCwd, extraContext) {
|
|
939
|
+
function buildPrompt(storyId, batchWorktreePath, mainCwd, extraContext, retry = 0) {
|
|
935
940
|
const prdPath = join(batchWorktreePath, '.ai', 'tasks', 'prd.json');
|
|
936
941
|
const promptPath = join(mainCwd, '.ai', 'batch', 'prompt.md');
|
|
937
942
|
|
|
@@ -942,9 +947,36 @@ function buildPrompt(storyId, batchWorktreePath, mainCwd, extraContext) {
|
|
|
942
947
|
return '';
|
|
943
948
|
}
|
|
944
949
|
|
|
945
|
-
const doneIds = prd.stories.filter(s => s.passes).map(s => s.id);
|
|
946
950
|
const acList = (story.acceptance_criteria || []).map(ac => `- ${ac}`).join('\n') || 'None';
|
|
947
951
|
|
|
952
|
+
// --- Retry: focused fix prompt ---
|
|
953
|
+
if (retry > 0 && extraContext) {
|
|
954
|
+
return `You are fixing story ${storyId}: ${story.title}
|
|
955
|
+
|
|
956
|
+
## Context
|
|
957
|
+
A previous attempt already implemented this story. The working tree contains the implementation.
|
|
958
|
+
Read the modified files to understand what was done.
|
|
959
|
+
|
|
960
|
+
## Acceptance criteria
|
|
961
|
+
${acList}
|
|
962
|
+
|
|
963
|
+
## Quality gate failures
|
|
964
|
+
\`\`\`
|
|
965
|
+
${extraContext}
|
|
966
|
+
\`\`\`
|
|
967
|
+
|
|
968
|
+
## Instructions
|
|
969
|
+
1. Read the existing code in the working tree
|
|
970
|
+
2. Analyze the quality gate errors above
|
|
971
|
+
3. Fix ONLY what's needed to make the gates pass
|
|
972
|
+
4. If the errors indicate a deeper design problem that can't be patched, you may rewrite the implementation — but try fixing first
|
|
973
|
+
5. Make sure the fix still satisfies all acceptance criteria above
|
|
974
|
+
6. Commit your changes when done`;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// --- First attempt: full template ---
|
|
978
|
+
const doneIds = prd.stories.filter(s => s.passes).map(s => s.id);
|
|
979
|
+
|
|
948
980
|
let depsList = 'None';
|
|
949
981
|
if (story.dependencies?.length > 0) {
|
|
950
982
|
depsList = story.dependencies
|
|
@@ -963,10 +995,6 @@ function buildPrompt(storyId, batchWorktreePath, mainCwd, extraContext) {
|
|
|
963
995
|
prompt = prompt.replaceAll('{NOTES}', story.notes || '');
|
|
964
996
|
prompt = prompt.replaceAll('{DEPENDENCIES}', depsList);
|
|
965
997
|
|
|
966
|
-
if (extraContext) {
|
|
967
|
-
prompt += `\n\n## Previous Attempt Failed\nThe previous attempt failed quality gates. Here is the error output:\n\n\`\`\`\n${extraContext}\n\`\`\`\n\nFix these issues before committing.`;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
998
|
return prompt;
|
|
971
999
|
}
|
|
972
1000
|
|
|
@@ -809,7 +809,18 @@ export async function runQualityGate(gate, cwd = process.cwd(), { configCwd } =
|
|
|
809
809
|
command
|
|
810
810
|
};
|
|
811
811
|
} catch (error) {
|
|
812
|
-
//
|
|
812
|
+
// Custom commands must not be silently skipped — "not found" is a real failure
|
|
813
|
+
if (customCmd) {
|
|
814
|
+
return {
|
|
815
|
+
success: false,
|
|
816
|
+
error: error.message,
|
|
817
|
+
output: error.stdout,
|
|
818
|
+
stderr: error.stderr,
|
|
819
|
+
command
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// For default/fallback commands: if command not found, try next variant
|
|
813
824
|
if (error.message?.includes('not found') ||
|
|
814
825
|
error.message?.includes('ENOENT') ||
|
|
815
826
|
error.message?.includes('missing script')) {
|
|
@@ -827,7 +838,7 @@ export async function runQualityGate(gate, cwd = process.cwd(), { configCwd } =
|
|
|
827
838
|
}
|
|
828
839
|
}
|
|
829
840
|
|
|
830
|
-
// No command variant worked
|
|
841
|
+
// No default command variant worked (only reached for non-custom gates)
|
|
831
842
|
return {
|
|
832
843
|
success: true, // Skip if no command available
|
|
833
844
|
skipped: true,
|