Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 67 additions & 14 deletions apps/server/src/services/auto-mode-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4597,21 +4597,54 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set.
planVersion,
});

// Build revision prompt
let revisionPrompt = `The user has requested revisions to the plan/specification.

## Previous Plan (v${planVersion - 1})
${hasEdits ? approvalResult.editedPlan : currentPlanContent}

## User Feedback
${approvalResult.feedback || 'Please revise the plan based on the edits above.'}
// Build revision prompt using customizable template
const revisionPrompts = await getPromptCustomization(
this.settingsService,
'[AutoMode]'
);

## Instructions
Please regenerate the specification incorporating the user's feedback.
Keep the same format with the \`\`\`tasks block for task definitions.
After generating the revised spec, output:
"[SPEC_GENERATED] Please review the revised specification above."
`;
// Get task format example based on planning mode
const taskFormatExample =
planningMode === 'full'
? `\`\`\`tasks
## Phase 1: Foundation
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]

## Phase 2: Core Implementation
- [ ] T003: [Description] | File: [path/to/file]
- [ ] T004: [Description] | File: [path/to/file]
\`\`\``
: `\`\`\`tasks
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
- [ ] T003: [Description] | File: [path/to/file]
\`\`\``;

let revisionPrompt = revisionPrompts.taskExecution.planRevisionTemplate;
revisionPrompt = revisionPrompt.replace(
/\{\{planVersion\}\}/g,
String(planVersion - 1)
);
revisionPrompt = revisionPrompt.replace(
/\{\{previousPlan\}\}/g,
hasEdits
? approvalResult.editedPlan || currentPlanContent
: currentPlanContent
);
revisionPrompt = revisionPrompt.replace(
/\{\{userFeedback\}\}/g,
approvalResult.feedback ||
'Please revise the plan based on the edits above.'
);
revisionPrompt = revisionPrompt.replace(
/\{\{planningMode\}\}/g,
planningMode
);
revisionPrompt = revisionPrompt.replace(
/\{\{taskFormatExample\}\}/g,
taskFormatExample
);
Comment on lines +4625 to +4647
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The repetitive use of .replace() for each placeholder can make the code less readable and harder to maintain as the number of placeholders increases. Consider encapsulating this logic within a helper function or using a more declarative approach for string templating to make the code cleaner and more scalable.

Comment on lines +4600 to +4647
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prevent $ token expansion in revision prompt substitutions.

Using regex .replace() with raw plan/feedback can mangle content containing $1, $&, etc. (common in shell/regex examples). Use a replacer function to keep literal text.

🔧 Suggested safe replacement
-                        revisionPrompt = revisionPrompt.replace(
-                          /\{\{planVersion\}\}/g,
-                          String(planVersion - 1)
-                        );
+                        revisionPrompt = revisionPrompt.replace(
+                          /\{\{planVersion\}\}/g,
+                          () => String(planVersion - 1)
+                        );
                         revisionPrompt = revisionPrompt.replace(
                           /\{\{previousPlan\}\}/g,
-                          hasEdits
-                            ? approvalResult.editedPlan || currentPlanContent
-                            : currentPlanContent
+                          () =>
+                            hasEdits
+                              ? approvalResult.editedPlan || currentPlanContent
+                              : currentPlanContent
                         );
                         revisionPrompt = revisionPrompt.replace(
                           /\{\{userFeedback\}\}/g,
-                          approvalResult.feedback ||
-                            'Please revise the plan based on the edits above.'
+                          () =>
+                            approvalResult.feedback ||
+                            'Please revise the plan based on the edits above.'
                         );
                         revisionPrompt = revisionPrompt.replace(
                           /\{\{planningMode\}\}/g,
-                          planningMode
+                          () => planningMode
                         );
                         revisionPrompt = revisionPrompt.replace(
                           /\{\{taskFormatExample\}\}/g,
-                          taskFormatExample
+                          () => taskFormatExample
                         );
🤖 Prompt for AI Agents
In `@apps/server/src/services/auto-mode-service.ts` around lines 4600 - 4647, The
current sequence of revisionPrompt = revisionPrompt.replace(...,
replacementString) risks special "$" token expansion when replacementString
contains patterns like "$1" or "$&"; update the replacements that use
user/content variables (specifically the replacements for {{previousPlan}} and
{{userFeedback}} which use approvalResult.editedPlan, currentPlanContent, and
approvalResult.feedback) to pass a replacer function instead of a raw string
(e.g., .replace(regex, () => String(...))) so the replacement is treated as
literal text; keep the other replacements (planVersion, planningMode,
taskFormatExample) consistent but you can also switch them to replacer functions
for uniformity; locate and modify uses around revisionPrompt and
revisionPrompts.taskExecution.planRevisionTemplate.


// Update status to regenerating
await this.updateFeaturePlanSpec(projectPath, featureId, {
Expand Down Expand Up @@ -4663,6 +4696,26 @@ After generating the revised spec, output:
const revisedTasks = parseTasksFromSpec(currentPlanContent);
logger.info(`Revised plan has ${revisedTasks.length} tasks`);

// Warn if no tasks found in spec/full mode - this may cause fallback to single-agent
if (
revisedTasks.length === 0 &&
(planningMode === 'spec' || planningMode === 'full')
) {
logger.warn(
`WARNING: Revised plan in ${planningMode} mode has no tasks! ` +
`This will cause fallback to single-agent execution. ` +
`The AI may have omitted the required \`\`\`tasks block.`
Comment on lines +4704 to +4707
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The warning message uses string concatenation, which can be less readable for multi-line strings or when interpolating variables. Using a template literal (``) would improve clarity and maintainability.

                            `WARNING: Revised plan in ${planningMode} mode has no tasks!\nThis will cause fallback to single-agent execution.\nThe AI may have omitted the required \`\`\`tasks block.`

);
this.emitAutoModeEvent('plan_revision_warning', {
featureId,
projectPath,
branchName,
planningMode,
warning:
'Revised plan missing tasks block - will use single-agent execution',
});
}

// Update planSpec with revised content
await this.updateFeaturePlanSpec(projectPath, featureId, {
status: 'generated',
Expand Down
16 changes: 14 additions & 2 deletions libs/prompts/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -965,8 +965,20 @@ export const DEFAULT_PLAN_REVISION_TEMPLATE = `The user has requested revisions

## Instructions
Please regenerate the specification incorporating the user's feedback.
Keep the same format with the \`\`\`tasks block for task definitions.
After generating the revised spec, output:
**Current planning mode: {{planningMode}}**

**CRITICAL REQUIREMENT**: Your revised specification MUST include a \`\`\`tasks code block containing task definitions in the EXACT format shown below. This is MANDATORY - without the tasks block, the system cannot track or execute tasks properly.

### Required Task Format
{{taskFormatExample}}

**IMPORTANT**:
1. The \`\`\`tasks block must appear in your response
2. Each task MUST start with "- [ ] T###:" where ### is a sequential number (T001, T002, T003, etc.)
3. Each task MUST include "| File:" followed by the primary file path
4. Tasks should be ordered by dependencies (foundational tasks first)

After generating the revised spec with the tasks block, output:
"[SPEC_GENERATED] Please review the revised specification above."`;

export const DEFAULT_CONTINUATION_AFTER_APPROVAL_TEMPLATE = `The plan/specification has been approved. Now implement it.
Expand Down
Loading