index.ts 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { createInternalAgentTextPart } from '../../utils';
  2. import { registerCommandHook } from '../command-hook-utils';
  3. const COMMAND_NAME = 'loop';
  4. function historyDir(): string {
  5. const shortID = Math.random().toString(36).slice(2, 8);
  6. const timestamp = Date.now().toString(36);
  7. return `.opencode/loop-history/loop-${timestamp}-${shortID}`;
  8. }
  9. function activationPrompt(text: string): string {
  10. const dir = historyDir();
  11. return [
  12. 'The user ran `/loop`. From the text below, extract: goal, successCriteria, maxAttempts.',
  13. '',
  14. 'If ANY are missing or unclear - push back and ask the user to clarify.',
  15. 'Do not assume or guess. All three must be explicit.',
  16. '',
  17. 'Once all three are clear, run the loop:',
  18. '',
  19. text,
  20. '',
  21. 'For each attempt:',
  22. `1. Read \`${dir}/\` for prior results`,
  23. '2. Dispatch @fixer with the goal',
  24. '3. Verify per the successCriteria',
  25. `4. Write result to \`${dir}/history-{NNN}.md\` (PASS/FAIL + reason)`,
  26. '5. PASS -> stop. FAIL under maxAttempts -> retry. FAIL at max -> escalate.',
  27. ].join('\n');
  28. }
  29. function helpPrompt(): string {
  30. return [
  31. 'Usage: `/loop <description>`',
  32. '',
  33. 'Describe what to accomplish, what success looks like, and how many tries.',
  34. '',
  35. 'Examples:',
  36. ' `/loop fix typescript errors until typecheck passes, max 3 tries`',
  37. ' `/loop improve api performance until response under 500ms, try 5 times`',
  38. ' `/loop refactor auth module, tests must pass, 4 attempts max`',
  39. ].join('\n');
  40. }
  41. export function createLoopCommandHook(): {
  42. registerCommand: (config: Record<string, unknown>) => void;
  43. handleCommandExecuteBefore: (
  44. input: { command: string; sessionID: string; arguments: string },
  45. output: { parts: Array<{ type: string; text?: string }> },
  46. ) => Promise<void>;
  47. } {
  48. return {
  49. registerCommand: (opencodeConfig) => {
  50. registerCommandHook(
  51. opencodeConfig,
  52. COMMAND_NAME,
  53. 'Run an automated execute-verify loop',
  54. 'Dispatch fixer, verify, iterate with file-based history on disk.',
  55. );
  56. },
  57. handleCommandExecuteBefore: async (input, output) => {
  58. if (input.command !== COMMAND_NAME) return;
  59. output.parts.length = 0;
  60. const args = input.arguments.trim();
  61. if (!args) {
  62. output.parts.push(createInternalAgentTextPart(helpPrompt()));
  63. return;
  64. }
  65. output.parts.push({ type: 'text', text: activationPrompt(args) });
  66. },
  67. };
  68. }