Skip to content

Commit

Permalink
feat: Add --no-plan mode for streamlined code generatio (#77)
Browse files Browse the repository at this point in the history
* prompting: further improve prompts

* feat: add --no-plan mode
  • Loading branch information
gmickel authored Aug 5, 2024
1 parent 71f3062 commit 2fffd57
Show file tree
Hide file tree
Showing 9 changed files with 609 additions and 44 deletions.
5 changes: 4 additions & 1 deletion USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ codewhisper task [options]
| `-c, --context <paths...>` | Specify files or directories to include in the task context. Can be file paths, directory paths, or glob patterns. Multiple entries should be space-separated. |
| `--github-issue` | Use GitHub issue for task input |
| `--github-issue-filters <filters>` | Use these filters when fetching issues. Format: comma-separated key:value pairs. Example: labels:p1,assignee:abc Note: see "query parameters" at https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues--parameters for all options. |
| `-df, --diff` | Use diff format for file updates instead of full file content |
| `-df, --diff` | Use diff format for file updates instead of full file content (default: true) |
| `--no-diff` | Disable diff mode (default: false) |
| `--plan` | Use the planning mode, this generates an intermediate plan, which can be modified. Useful for complex tasks. (default: true) |
| `--no-plan` | Disable the planning mode. Useful for simple tasks (default: false) |
| `-g, --gitignore <path>` | Path to .gitignore file (default: .gitignore) |
| `-f, --filter <patterns...>` | File patterns to include (use glob patterns, e.g., "src/\*_/_.js") |
| `-e, --exclude <patterns...>` | File patterns to exclude (use glob patterns, e.g., "\*_/_.test.js") |
Expand Down
125 changes: 99 additions & 26 deletions src/ai/task-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,32 +64,49 @@ export async function runAIAssistedTask(options: AiAssistedTaskOptions) {
);
spinner.succeed('Files processed successfully');

const planPrompt = await generatePlanPrompt(
processedFiles,
templateContent,
customData,
options,
basePath,
);
if (options.plan) {
const planPrompt = await generatePlanPrompt(
processedFiles,
templateContent,
customData,
options,
basePath,
);

const generatedPlan = await generatePlan(planPrompt, modelKey, options);
const generatedPlan = await generatePlan(planPrompt, modelKey, options);

// Cache the task data
taskCache.setTaskData(basePath, {
selectedFiles,
generatedPlan,
taskDescription,
instructions,
model: modelKey,
});
// Cache the task data
taskCache.setTaskData(basePath, {
selectedFiles,
generatedPlan,
taskDescription,
instructions,
model: modelKey,
});

await continueTaskWorkflow(
options,
basePath,
taskCache,
generatedPlan,
modelKey,
);
} else {
taskCache.setTaskData(basePath, {
selectedFiles,
generatedPlan: '',
taskDescription,
instructions,
model: modelKey,
});

await continueTaskWorkflow(
options,
basePath,
taskCache,
generatedPlan,
modelKey,
);
await continueTaskWorkflowWithoutPlan(
options,
basePath,
taskCache,
modelKey,
);
}
} catch (error) {
spinner.fail('Error in AI-assisted task');
console.error(
Expand Down Expand Up @@ -336,7 +353,60 @@ export async function continueTaskWorkflow(
codegenTemplateContent,
taskCache,
basePath,
options,
reviewedPlan,
);

spinner.start('Generating Codegen prompt...');
const codeGenPrompt = await generateCodegenPrompt(
options,
basePath,
taskCache,
codegenTemplateContent,
codegenCustomData,
);
spinner.succeed('Codegen prompt generated successfully');

const generatedCode = await generateCode(codeGenPrompt, modelKey, options);
const parsedResponse = parseAICodegenResponse(
generatedCode,
options.logAiInteractions,
options.diff,
);

if (options.dryRun) {
await handleDryRun(
basePath,
parsedResponse,
taskCache.getLastTaskData(basePath)?.taskDescription || '',
);
} else {
await applyCodeModifications(options, basePath, parsedResponse);
}

spinner.succeed('AI-assisted task completed! 🎉');
}

export async function continueTaskWorkflowWithoutPlan(
options: AiAssistedTaskOptions,
basePath: string,
taskCache: TaskCache,
modelKey: string,
) {
const spinner = ora();

const codegenTemplatePath = options.diff
? getTemplatePath('codegen-diff-no-plan-prompt')
: getTemplatePath('codegen-no-plan-prompt');

const codegenTemplateContent = await fs.readFile(
codegenTemplatePath,
'utf-8',
);
const codegenCustomData = await prepareCodegenCustomData(
codegenTemplateContent,
taskCache,
basePath,
options,
);

Expand Down Expand Up @@ -374,17 +444,20 @@ async function prepareCodegenCustomData(
codegenTemplateContent: string,
taskCache: TaskCache,
basePath: string,
reviewedPlan: string,
options: AiAssistedTaskOptions,
reviewedPlan?: string,
): Promise<Record<string, string>> {
const codegenVariables = extractTemplateVariables(codegenTemplateContent);
const lastTaskData = taskCache.getLastTaskData(basePath);

const codegenDataObj = {
var_taskDescription: lastTaskData?.taskDescription || '',
var_instructions: lastTaskData?.instructions || '',
var_plan: reviewedPlan,
};
} as Record<string, string>;

if (reviewedPlan) {
codegenDataObj.var_plan = reviewedPlan;
}

return collectVariables(
JSON.stringify(codegenDataObj),
Expand Down
9 changes: 9 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ export function cli(_args: string[]) {
true,
)
.option('--no-diff', 'Use full file content for updates')
.option(
'--plan',
'Use the plan mode for AI-generated code modifications',
true,
)
.option(
'--no-plan',
'Directly provide the code modifications without the intermediate planning step',
)
.option(
'-cw, --context-window <number>',
'Specify the context window for the AI model. Only applicable for Ollama models.',
Expand Down
180 changes: 180 additions & 0 deletions src/templates/codegen-diff-no-plan-prompt.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
You are an expert developer tasked with implementing a given task. Your goal is to write all the code changes needed to complete the task, ensuring it integrates well with the existing codebase and follows best practices.

You will be given:
- A task description
- A codebase
- Instructions

Before implementing the task, carefully analyze the task description, instructions, and codebase. Plan your implementation strategy, considering best practices, coding standards, potential edge cases, and performance implications. Approach this task as a senior developer working on a critical feature for a high-profile client.

Note: Focus solely on the technical implementation. Ignore any mentions of human tasks or non-technical aspects.

Encoded in XML tags, here is what you will be given:

TASK: Context about the task to complete.
INSTRUCTIONS: Instructions on how to complete the task.
CODEBASE: Files from the codebase you have access to.
FORMAT: Instructions for how to format your response.

---
<task>
{{var_taskDescription}}
</task>
---

<instructions>
Follow these instructions:

{{var_instructions}}
</instructions>

---
<codebase>
## Code Summary
{{tableOfContents files}}
## Selected Files:
{{#each files}}
### {{relativePath this.path}}
{{#codeblock this.content this.language}}{{/codeblock}}
{{/each}}
</codebase>
---

<format>

Generate diffs for modified files, full content for new files, and only the file path for deleted files.

If you don't need to modify a file, don't include it - this simplifies Git diffs.

Format your response as follows:
<file_list>
FILE_PATH_1
FILE_PATH_2
...
</file_list>
<git_branch_name>
__GIT_BRANCH_NAME__
</git_branch_name>

<git_commit_message>
__GIT_COMMIT_MESSAGE__
</git_commit_message>

<summary>
__BRIEF_SUMMARY_OF_CHANGES__
</summary>

<potential_issues>
__LIST_OF_POTENTIAL_ISSUES_OR_TRADE_OFFS__
Include any constraints (e.g., performance, scalability, maintainability) you've considered and explain why the trade-offs you've made are appropriate for this task.
</potential_issues>

Then, for each file:
<file>
<file_path>__FILE_PATH__</file_path>
<file_status>__STATUS__</file_status>
<file_content language="__LANGUAGE__">
__FILE_CONTENT_OR_DIFF__
</file_content>
<explanation>
__EXPLANATION__
</explanation>
</file>

Please adhere to the following guidelines:

FILE_PATH: Use the full path from the project root.
Example: 'src/components/Button.tsx'

LANGUAGE: Specify the language or file type. For example:
'tsx' for .tsx files
'javascript' for .js files
'css' for .css files
'json' for .json files
etc

FILE_CONTENT_OR_DIFF:
- For new files: Provide the complete file content, including all necessary imports, function definitions, and exports.
- For modified files: Provide a unified diff format. Use '---' for removed lines and '+++' for added lines.
- For deleted files: Leave this section empty.

Ensure proper indentation and follow the project's coding standards.

STATUS: Use 'new' for newly created files, 'modified' for existing files that are being updated, and 'deleted' for files that are being deleted.

EXPLANATION: Provide a brief explanation for your implementation choices, including any significant design decisions, alternatives considered, and reasons for your final decision. Address any non-obvious implementations or optimizations.

When creating diffs for modified files:
- Use '+' at the beginning of the line to indicate added lines, including new imports.
- Use '-' at the beginning of the line to indicate removed lines.
- Use ' ' (space) at the beginning of the line for unchanged lines (context).
- Ensure that new imports are marked with '+' at the beginning of the line.
- Include at least 3 lines of unchanged context before and after changes to help with patch application.

Example of a correct diff format:
<file_content language="typescript">
--- src/types/index.ts
+++ src/types/index.ts
@@ -1,4 +1,6 @@
import type { ParsedDiff } from 'diff';
+import type { LogLevel } from '../utils/logger';
+
export interface GitHubIssue {
number: number;
title: string;
@@ -40,6 +42,7 @@
| 'noCodeblock'
> & {
dryRun: boolean;
+ logLevel?: LogLevel;
maxCostThreshold?: number;
task?: string;
description?: string;
</file_content>

Before modifying a file, carefully review its entire content. Ensure that your changes, especially new imports, are placed in the correct location and don't duplicate existing code.

When generating diffs:
1. Start with the original file content.
2. Make your changes, keeping track of line numbers.
3. Generate the diff by comparing the original and modified versions.
4. Include at least 3 lines of unchanged context before and after changes.
5. Verify that the diff accurately represents your intended changes.

After generating each diff:
- Verify that all new lines (including imports and blank lines) start with '+'.
- Ensure that the line numbers in the diff headers (@@ -old,oldlines +new,newlines @@) are correct and account for added/removed lines.
- Check that there are sufficient unchanged context lines around modifications.

Ensure that:
- You have thoroughly analyzed the task and planned your implementation strategy.
- Everything specified in the task description and instructions is implemented.
- All new files contain the full code.
- All modified files have accurate and clear diffs.
- The content includes all necessary imports, function definitions, and exports.
- The code is clean, maintainable, efficient, and considers performance implications.
- The code is properly formatted and follows the project's coding standards.
- Necessary comments for clarity are included if needed.
- Any conceptual or high-level descriptions are translated into actual, executable code.
- You've considered and handled potential edge cases.
- Your changes are consistent with the existing codebase.
- You haven't introduced any potential bugs or performance issues.
- Your code is easy to understand and maintain.
- You complete all necessary work to fully implement the task.

Note: The accuracy of the diff format is crucial for successful patch application. Even small errors in formatting can cause the entire patch to fail. Pay extra attention to the correctness of your diff output.

</format>

---
Now, implement the task described above. Take your time to think through the problem and craft an elegant, efficient, and complete solution that fully addresses the task requirements and integrates seamlessly with the existing codebase.
Loading

0 comments on commit 2fffd57

Please sign in to comment.