mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
Default agent auto-run prompt modified tab now drops tabs inside of large text inputs
This commit is contained in:
@@ -619,6 +619,33 @@ describe('AgentPromptComposerModal', () => {
|
||||
});
|
||||
|
||||
describe('textarea interaction', () => {
|
||||
it('inserts tab character on Tab key', async () => {
|
||||
// Use "HelloWorld" without space so tab insertion is clearer
|
||||
renderWithLayerStack(
|
||||
<AgentPromptComposerModal
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
theme={theme}
|
||||
initialValue="HelloWorld"
|
||||
onSubmit={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByPlaceholderText(
|
||||
'Enter your agent prompt... (type {{ for variables)'
|
||||
) as HTMLTextAreaElement;
|
||||
|
||||
// Set cursor position after "Hello"
|
||||
textarea.selectionStart = 5;
|
||||
textarea.selectionEnd = 5;
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.keyDown(textarea, { key: 'Tab' });
|
||||
});
|
||||
|
||||
expect(textarea.value).toBe('Hello\tWorld');
|
||||
});
|
||||
|
||||
it('updates value when typing', async () => {
|
||||
renderWithLayerStack(
|
||||
<AgentPromptComposerModal
|
||||
|
||||
@@ -354,6 +354,25 @@ describe('AutoRun', () => {
|
||||
});
|
||||
|
||||
describe('Keyboard Shortcuts', () => {
|
||||
it('inserts tab character on Tab key', async () => {
|
||||
// Use "HelloWorld" without space so tab insertion is clearer
|
||||
const props = createDefaultProps({ content: 'HelloWorld' });
|
||||
render(<AutoRun {...props} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox') as HTMLTextAreaElement;
|
||||
fireEvent.focus(textarea);
|
||||
|
||||
// Set cursor position after "Hello"
|
||||
textarea.selectionStart = 5;
|
||||
textarea.selectionEnd = 5;
|
||||
|
||||
fireEvent.keyDown(textarea, { key: 'Tab' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(textarea.value).toBe('Hello\tWorld');
|
||||
});
|
||||
});
|
||||
|
||||
it('toggles mode on Cmd+E', async () => {
|
||||
const props = createDefaultProps({ mode: 'edit' });
|
||||
render(<AutoRun {...props} />);
|
||||
|
||||
@@ -495,6 +495,25 @@ describe('BatchRunnerModal', () => {
|
||||
});
|
||||
|
||||
describe('Agent Prompt', () => {
|
||||
it('inserts tab character on Tab key', async () => {
|
||||
render(<BatchRunnerModal {...createDefaultProps()} />);
|
||||
|
||||
const textarea = screen.getByPlaceholderText('Enter the prompt for the batch agent...') as HTMLTextAreaElement;
|
||||
|
||||
// Clear and set a simple value (no space - "HelloWorld")
|
||||
fireEvent.change(textarea, { target: { value: 'HelloWorld' } });
|
||||
|
||||
// Set cursor position after "Hello"
|
||||
textarea.selectionStart = 5;
|
||||
textarea.selectionEnd = 5;
|
||||
|
||||
fireEvent.keyDown(textarea, { key: 'Tab' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(textarea.value).toBe('Hello\tWorld');
|
||||
});
|
||||
});
|
||||
|
||||
it('displays default prompt in textarea', async () => {
|
||||
render(<BatchRunnerModal {...createDefaultProps()} />);
|
||||
|
||||
|
||||
@@ -104,6 +104,21 @@ export function AgentPromptComposerModal({
|
||||
if (handleAutocompleteKeyDown(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert actual tab character instead of moving focus
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const textarea = e.currentTarget;
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const newValue = value.substring(0, start) + '\t' + value.substring(end);
|
||||
setValue(newValue);
|
||||
// Restore cursor position after the tab
|
||||
requestAnimationFrame(() => {
|
||||
textarea.selectionStart = start + 1;
|
||||
textarea.selectionEnd = start + 1;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const tokenCount = estimateTokenCount(value);
|
||||
|
||||
@@ -1248,6 +1248,28 @@ const AutoRunInner = forwardRef<AutoRunHandle, AutoRunProps>(function AutoRunInn
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert actual tab character instead of moving focus
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const textarea = e.currentTarget;
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
|
||||
// Push undo state before modifying content
|
||||
pushUndoState();
|
||||
|
||||
const newContent = localContent.substring(0, start) + '\t' + localContent.substring(end);
|
||||
setLocalContent(newContent);
|
||||
lastUndoSnapshotRef.current = newContent;
|
||||
|
||||
// Restore cursor position after the tab
|
||||
requestAnimationFrame(() => {
|
||||
textarea.selectionStart = start + 1;
|
||||
textarea.selectionEnd = start + 1;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Cmd+Z to undo, Cmd+Shift+Z to redo
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'z') {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -17,6 +17,9 @@ Your name is **{{AGENT_NAME}}**, a Maestro-managed AI agent.
|
||||
- **Git Branch:** {{GIT_BRANCH}}
|
||||
- **Auto Run Folder:** {{AUTORUN_FOLDER}}
|
||||
- **Loop Iteration:** {{LOOP_NUMBER}}
|
||||
- **Working Folder for Temporary Files:** {{AUTORUN_FOLDER}}/Working
|
||||
|
||||
If you need to create the working folder, do so.
|
||||
|
||||
---
|
||||
|
||||
@@ -26,20 +29,23 @@ Your name is **{{AGENT_NAME}}**, a Maestro-managed AI agent.
|
||||
Begin by reviewing CLAUDE.md (when available) in this folder to understand the project's structure, conventions, and workflow expectations.
|
||||
|
||||
2. Task Selection
|
||||
Process the FIRST unchecked task (- [ ]) from top to bottom. Note that there may be relevant images associated with the task, analyze them, and include in your final synopsis back how many images you analyzed in preparation for solving the task.
|
||||
Process the FIRST unchecked task (- [ ]) from top to bottom. Note that there may be relevant images associated with the task. If there are, analyze them, and include in your final synopsis back how many images you analyzed in preparation for solving the task.
|
||||
|
||||
IMPORTANT: You will only work on this single task. If it appears to have logical subtasks, treat them as one cohesive unit—but do not move on to the next top-level task.
|
||||
IMPORTANT: You will only work on this single task. If it appears to have logical subtasks, treat them as one cohesive unit—but do not move on to the next major item.
|
||||
|
||||
3. Task Evaluation
|
||||
- Fully understand the task and inspect the relevant code.
|
||||
- If you determine the task should not be executed, mark it as completed anyway and record a concise explanation of why it was skipped directly in the document.
|
||||
- If upon examining the code carefully you decide there is a better approach, take it, and document record a detailed explanation of why directly in the document.
|
||||
- Determine which tasks you're going to work on in this run.
|
||||
- There will be future runs to take care of other tasks.
|
||||
- Your goal is to select enough items from the top of the unfinished list that make sense to work on within the same context window.
|
||||
|
||||
4. Task Implementation
|
||||
Implement the task according to the project's established style, architecture, and coding norms.
|
||||
- Implement the task according to the project's established style, architecture, and coding norms.
|
||||
- Ensure that test cases are created, and that they pass.
|
||||
- Ensure you haven't broken any existing test cases.
|
||||
|
||||
5. Completion + Reporting
|
||||
- Mark the task as completed in the scratchpad by changing - [ ] to - [x].
|
||||
- Mark the task as completed by changing "- [ ]" to "- [x]".
|
||||
- CRITICAL: Your FIRST sentence MUST be a specific synopsis of what you accomplished (e.g., "Added pagination to the user list component" or "Refactored auth middleware to use JWT tokens"). Never start with generic phrases like "Task completed successfully" - always lead with the specific work done.
|
||||
- Follow with any relevant details about:
|
||||
- Implementation approach or key decisions made
|
||||
@@ -48,16 +54,12 @@ Your name is **{{AGENT_NAME}}**, a Maestro-managed AI agent.
|
||||
|
||||
6. Version Control
|
||||
For any code or documentation changes, if we're in a Github repo:
|
||||
- Commit using a descriptive message prefixed with MAESTRO:.
|
||||
- Commit using a descriptive message prefixed with "MAESTRO: ".
|
||||
- Push to GitHub.
|
||||
- Update CLAUDE.md, README.md, or any other top-level documentation if appropriate.
|
||||
|
||||
7. Exit Immediately
|
||||
After completing (or skipping) the single task, EXIT. Do not proceed to additional tasks—another agent instance will handle them.
|
||||
|
||||
NOTE: If you see a clear issue tag like a little moniker or some short form in front of the task, then your synopsis message should start with that exact token because we're clearly using it as a unique identifier.
|
||||
|
||||
If there are no remaining open tasks, exit immediately and state that there is nothing left to do.
|
||||
After completing (or skipping) your task, EXIT. Do not proceed to additional tasks—another agent instance will handle them. If there are no remaining open tasks, exit immediately and state that there is nothing left to do.
|
||||
|
||||
---
|
||||
|
||||
@@ -67,7 +69,7 @@ Process tasks from this document:
|
||||
|
||||
{{DOCUMENT_PATH}}
|
||||
|
||||
Save changes directly in that file.`;
|
||||
Check of tasks and add any relevant notes around the completion directly within that document.`;
|
||||
|
||||
interface BatchRunnerModalProps {
|
||||
theme: Theme;
|
||||
@@ -1854,6 +1856,22 @@ export function BatchRunnerModal(props: BatchRunnerModalProps) {
|
||||
ref={textareaRef}
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
// Insert actual tab character instead of moving focus
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const textarea = e.currentTarget;
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const newValue = prompt.substring(0, start) + '\t' + prompt.substring(end);
|
||||
setPrompt(newValue);
|
||||
// Restore cursor position after the tab
|
||||
requestAnimationFrame(() => {
|
||||
textarea.selectionStart = start + 1;
|
||||
textarea.selectionEnd = start + 1;
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="w-full p-4 pr-10 rounded border bg-transparent outline-none resize-none font-mono text-sm"
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
|
||||
@@ -468,17 +468,22 @@ ${docList}
|
||||
}));
|
||||
|
||||
// AUTORUN LOG: Start
|
||||
console.log('[AUTORUN] Logging start event');
|
||||
window.maestro.logger.autorun(
|
||||
`Auto Run started`,
|
||||
session.name,
|
||||
{
|
||||
documents: documents.map(d => d.filename),
|
||||
totalTasks: initialTotalTasks,
|
||||
loopEnabled,
|
||||
maxLoops: maxLoops ?? 'unlimited'
|
||||
}
|
||||
);
|
||||
try {
|
||||
console.log('[AUTORUN] Logging start event - calling window.maestro.logger.autorun');
|
||||
window.maestro.logger.autorun(
|
||||
`Auto Run started`,
|
||||
session.name,
|
||||
{
|
||||
documents: documents.map(d => d.filename),
|
||||
totalTasks: initialTotalTasks,
|
||||
loopEnabled,
|
||||
maxLoops: maxLoops ?? 'unlimited'
|
||||
}
|
||||
);
|
||||
console.log('[AUTORUN] Start event logged successfully');
|
||||
} catch (err) {
|
||||
console.error('[AUTORUN] Error logging start event:', err);
|
||||
}
|
||||
|
||||
// Store custom prompt for persistence
|
||||
setCustomPrompts(prev => ({ ...prev, [sessionId]: prompt }));
|
||||
|
||||
Reference in New Issue
Block a user