feat(plan): support annotating plans with feedback for iteration (#20876)

This commit is contained in:
Adib234
2026-03-02 18:03:59 -05:00
committed by GitHub
parent 06ddfa5c4c
commit 01927a36d1
3 changed files with 11 additions and 22 deletions

View File

@@ -109,8 +109,9 @@ structure, and consultation level are proportional to the task's complexity:
- **Iterate:** Provide feedback to refine the plan.
- **Refine manually:** Press **Ctrl + X** to open the plan file in your
[preferred external editor]. This allows you to manually refine the plan
steps before approval. The CLI will automatically refresh and show the
updated plan after you save and close the editor.
steps before approval. If you make any changes and save the file, the CLI
will automatically send the updated plan back to the agent for review and
iteration.
For more complex or specialized planning tasks, you can
[customize the planning workflow with skills](#customizing-planning-with-skills).

View File

@@ -11,7 +11,6 @@ import { waitFor } from '../../test-utils/async.js';
import { ExitPlanModeDialog } from './ExitPlanModeDialog.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { openFileInEditor } from '../utils/editorUtils.js';
import {
ApprovalMode,
validatePlanContent,
@@ -41,10 +40,6 @@ vi.mock('node:fs', async (importOriginal) => {
...actual,
existsSync: vi.fn(),
realpathSync: vi.fn((p) => p),
promises: {
...actual.promises,
readFile: vi.fn(),
},
};
});
@@ -546,7 +541,7 @@ Implement a comprehensive authentication system with multiple providers.
expect(onFeedback).not.toHaveBeenCalled();
});
it('opens plan in external editor when Ctrl+X is pressed', async () => {
it('automatically submits feedback when Ctrl+X is used to edit the plan', async () => {
const { stdin, lastFrame } = renderDialog({ useAlternateBuffer });
await act(async () => {
@@ -557,27 +552,16 @@ Implement a comprehensive authentication system with multiple providers.
expect(lastFrame()).toContain('Add user authentication');
});
// Reset the mock to track the second call during refresh
vi.mocked(processSingleFileContent).mockClear();
// Press Ctrl+X
await act(async () => {
writeKey(stdin, '\x18'); // Ctrl+X
});
await waitFor(() => {
expect(openFileInEditor).toHaveBeenCalledWith(
mockPlanFullPath,
expect.anything(),
expect.anything(),
undefined,
expect(onFeedback).toHaveBeenCalledWith(
'I have edited the plan or annotated it with feedback. Review the edited plan, update if necessary, and present it again for approval.',
);
});
// Verify that content is refreshed (processSingleFileContent called again)
await waitFor(() => {
expect(processSingleFileContent).toHaveBeenCalled();
});
});
},
);

View File

@@ -156,11 +156,15 @@ export const ExitPlanModeDialog: React.FC<ExitPlanModeDialogProps> = ({
const handleOpenEditor = useCallback(async () => {
try {
await openFileInEditor(planPath, stdin, setRawMode, getPreferredEditor());
onFeedback(
'I have edited the plan or annotated it with feedback. Review the edited plan, update if necessary, and present it again for approval.',
);
refresh();
} catch (err) {
debugLogger.error('Failed to open plan in editor:', err);
}
}, [planPath, stdin, setRawMode, getPreferredEditor, refresh]);
}, [planPath, stdin, setRawMode, getPreferredEditor, refresh, onFeedback]);
useKeypress(
(key) => {