feat: add undo functionality to revert the last turn and restore file changes
- Implemented `/undo` command to revert the most recent completed turn, restoring all modified files and rewinding message history. - Introduced `FileMutation` type to capture file changes for undo operations. - Enhanced session management to track and manage undo stack with a maximum of 20 turns. - Updated command parsing to include user-defined commands from `.shiro/commands.md`, allowing for custom command definitions and substitutions. - Added documentation for the new `/undo` command and user commands feature. - Implemented tests for undo functionality and user command parsing to ensure reliability.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { expect, test } from 'bun:test';
|
||||
import { expandCommand, parseUserCommands } from '../src/usercommands';
|
||||
|
||||
test('parses name, summary, and body from markdown headings', () => {
|
||||
const commands = parseUserCommands(`# scratch
|
||||
|
||||
## review
|
||||
> summarise this change
|
||||
|
||||
Summarise the diff. Be concise.
|
||||
|
||||
## scaffold
|
||||
> make a new module
|
||||
|
||||
Create a module called $1.
|
||||
`);
|
||||
expect(commands).toHaveLength(2);
|
||||
expect(commands[0]!.name).toBe('review');
|
||||
expect(commands[0]!.summary).toBe('summarise this change');
|
||||
expect(commands[0]!.body).toContain('Summarise the diff. Be concise.');
|
||||
expect(commands[1]!.name).toBe('scaffold');
|
||||
});
|
||||
|
||||
test('lowercases command names so /Review matches review', () => {
|
||||
const commands = parseUserCommands('## Template\nbody');
|
||||
expect(commands[0]!.name).toBe('template');
|
||||
});
|
||||
|
||||
test('substitutes $1 and $ARGUMENTS', async () => {
|
||||
const [cmd] = parseUserCommands('## x\nUse $1 heavily and all of $ARGUMENTS.');
|
||||
const out = await expandCommand(cmd!, 'foo bar baz', process.cwd());
|
||||
expect(out).toBe('Use foo heavily and all of foo bar baz.');
|
||||
});
|
||||
|
||||
test('reads @path files relative to the workspace root', async () => {
|
||||
const [cmd] = parseUserCommands('## r\nRead here:\n@package.json\nThen continue.');
|
||||
const out = await expandCommand(cmd!, '', process.cwd());
|
||||
const content = (await Bun.file('package.json').text()).trimEnd();
|
||||
expect(out).toContain('Read here:\n'.concat(content).concat('\nThen continue.'));
|
||||
});
|
||||
|
||||
test('expands !shell output inline', async () => {
|
||||
const [cmd] = parseUserCommands('## s\nBranch is !`git branch --show-current`.');
|
||||
const out = await expandCommand(cmd!, '', process.cwd());
|
||||
expect(out).toMatch(/Branch is (main|[a-z-]+)\./);
|
||||
});
|
||||
Reference in New Issue
Block a user