index.test.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { describe, expect, test } from 'bun:test';
  2. import { createLoopCommandHook } from './index';
  3. describe('loop command hook', () => {
  4. test('registers /loop command when absent', () => {
  5. const hook = createLoopCommandHook();
  6. const config: Record<string, unknown> = {};
  7. hook.registerCommand(config);
  8. const command = (config.command as Record<string, unknown>).loop as {
  9. template: string;
  10. description: string;
  11. };
  12. expect(command).toBeDefined();
  13. expect(command.template).toContain('loop');
  14. expect(command.description).toBeDefined();
  15. });
  16. test('does not overwrite existing /loop command', () => {
  17. const hook = createLoopCommandHook();
  18. const existing = { template: 'custom', description: 'custom loop' };
  19. const config: Record<string, unknown> = { command: { loop: existing } };
  20. hook.registerCommand(config);
  21. expect((config.command as Record<string, unknown>).loop).toBe(existing);
  22. });
  23. test('shows help when no arguments provided', async () => {
  24. const hook = createLoopCommandHook();
  25. const output = { parts: [] as Array<{ type: string; text?: string }> };
  26. await hook.handleCommandExecuteBefore(
  27. { command: 'loop', sessionID: 's1', arguments: ' ' },
  28. output,
  29. );
  30. expect(output.parts.length).toBe(1);
  31. expect(output.parts[0].text).toContain('Usage');
  32. });
  33. test('generates activation prompt with user text', async () => {
  34. const hook = createLoopCommandHook();
  35. const output = { parts: [] as Array<{ type: string; text?: string }> };
  36. await hook.handleCommandExecuteBefore(
  37. {
  38. command: 'loop',
  39. sessionID: 's1',
  40. arguments: 'fix typescript errors until typecheck passes, max 3 tries',
  41. },
  42. output,
  43. );
  44. const text = output.parts[0].text;
  45. expect(output.parts.length).toBe(1);
  46. expect(text).toContain('The user ran `/loop`');
  47. expect(text).toContain(
  48. 'fix typescript errors until typecheck passes, max 3 tries',
  49. );
  50. expect(text).toContain('goal, successCriteria, maxAttempts');
  51. expect(text).toContain('missing or unclear');
  52. expect(text).toContain('.opencode/loop-history/');
  53. expect(text).toContain('history-{NNN}.md');
  54. expect(text).not.toContain('attempt-{N}.md');
  55. expect(text).toContain('Dispatch @fixer');
  56. });
  57. test('ignores other commands', async () => {
  58. const hook = createLoopCommandHook();
  59. const output = { parts: [{ type: 'text' as const, text: 'original' }] };
  60. await hook.handleCommandExecuteBefore(
  61. { command: 'deepwork', sessionID: 's1', arguments: 'x' },
  62. output,
  63. );
  64. expect(output.parts.length).toBe(1);
  65. expect(output.parts[0].text).toBe('original');
  66. });
  67. });