@w32191/just-loop 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ export type CommandDefinition = {
2
+ name: string;
3
+ description: string;
4
+ template: string;
5
+ };
6
+ export declare function getBuiltinCommands(): Record<string, CommandDefinition>;
@@ -0,0 +1,59 @@
1
+ const RALPH_LOOP_TEMPLATE = `You are starting a Ralph Loop - a self-referential development loop that runs until task completion.
2
+
3
+ ## How Ralph Loop Works
4
+
5
+ 1. You will work on the task continuously
6
+ 2. When you believe the task is FULLY complete, output: \`<promise>{{COMPLETION_PROMISE}}</promise>\`
7
+ 3. If you don't output the promise, the loop will automatically inject another prompt to continue
8
+ 4. Maximum iterations: Configurable (default 100)
9
+
10
+ ## Rules
11
+
12
+ - Focus on completing the task fully, not partially
13
+ - Don't output the completion promise until the task is truly done
14
+ - Each iteration should make meaningful progress toward the goal
15
+ - If stuck, try different approaches
16
+ - Use todos to track your progress
17
+
18
+ ## Exit Conditions
19
+
20
+ 1. **Completion**: Output your completion promise tag when fully complete
21
+ 2. **Max Iterations**: Loop stops automatically at limit
22
+ 3. **Cancel**: User runs \`/cancel-ralph\` command
23
+
24
+ ## Your Task
25
+
26
+ Parse the arguments below and begin working on the task. The format is:
27
+ \`"task description" [--completion-promise=TEXT] [--max-iterations=N] [--strategy=reset|continue]\`
28
+
29
+ Default completion promise is "DONE" and default max iterations is 100.`;
30
+ const CANCEL_RALPH_TEMPLATE = `Cancel the currently active Ralph Loop.
31
+
32
+ This will:
33
+ 1. Stop the loop from continuing
34
+ 2. Clear the loop state file
35
+ 3. Allow the session to end normally
36
+
37
+ Check if a loop is active and cancel it. Inform the user of the result.`;
38
+ export function getBuiltinCommands() {
39
+ return {
40
+ "ralph-loop": {
41
+ name: "ralph-loop",
42
+ description: "(builtin) Start self-referential development loop until completion",
43
+ template: `<command-instruction>
44
+ ${RALPH_LOOP_TEMPLATE}
45
+ </command-instruction>
46
+
47
+ <user-task>
48
+ $ARGUMENTS
49
+ </user-task>`,
50
+ },
51
+ "cancel-ralph": {
52
+ name: "cancel-ralph",
53
+ description: "(builtin) Cancel active Ralph Loop",
54
+ template: `<command-instruction>
55
+ ${CANCEL_RALPH_TEMPLATE}
56
+ </command-instruction>`,
57
+ },
58
+ };
59
+ }
@@ -0,0 +1 @@
1
+ export declare function handleConfig(input: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,10 @@
1
+ import { getBuiltinCommands } from "./command-definitions.js";
2
+ export async function handleConfig(input) {
3
+ const existingCommands = input.command && typeof input.command === "object"
4
+ ? input.command
5
+ : {};
6
+ input.command = {
7
+ ...getBuiltinCommands(),
8
+ ...existingCommands,
9
+ };
10
+ }
@@ -25,9 +25,21 @@ type ChatMessageOutput = {
25
25
  type EventInput = {
26
26
  event?: unknown;
27
27
  };
28
+ type ToolExecuteBeforeInput = {
29
+ tool?: unknown;
30
+ sessionID?: unknown;
31
+ callID?: unknown;
32
+ };
33
+ type ToolExecuteBeforeOutput = {
34
+ args?: {
35
+ name?: unknown;
36
+ };
37
+ };
28
38
  export type PluginHooks = {
29
39
  "chat.message": (input: ChatMessageInput, output: ChatMessageOutput) => Promise<void>;
30
40
  event: (input: EventInput) => Promise<void>;
41
+ config: (input: Record<string, unknown>) => Promise<void>;
42
+ "tool.execute.before": (input: ToolExecuteBeforeInput, output: ToolExecuteBeforeOutput) => Promise<void>;
31
43
  };
32
44
  export declare function createPlugin(ctx?: PluginInput, deps?: CreatePluginDeps): Promise<PluginHooks>;
33
45
  export {};
@@ -1,7 +1,8 @@
1
1
  import { createOpenCodeHostAdapter } from "../host-adapter/opencode-host-adapter.js";
2
2
  import { createLoopCore } from "../ralph-loop/loop-core.js";
3
- import { handleChatMessage } from "./chat-message-handler.js";
3
+ import { handleConfig } from "./config-handler.js";
4
4
  import { handleEvent } from "./event-handler.js";
5
+ import { handleToolExecuteBefore } from "./tool-execute-before-handler.js";
5
6
  function extractSessionID(input) {
6
7
  if (typeof input.sessionID === "string")
7
8
  return input.sessionID;
@@ -37,16 +38,22 @@ export async function createPlugin(ctx, deps = {}) {
37
38
  });
38
39
  const core = createCore({ rootDir: ctx.directory, adapter });
39
40
  return {
41
+ config: async (input) => {
42
+ await handleConfig(input);
43
+ },
40
44
  "chat.message": async (input, output) => {
41
45
  const sessionID = extractSessionID(input);
42
46
  if (!sessionID)
43
47
  return;
44
- await handleChatMessage(extractChatText(output.parts), core, sessionID);
48
+ extractChatText(output.parts);
45
49
  },
46
50
  event: async (input) => {
47
51
  if (!input.event)
48
52
  return;
49
53
  await handleEvent(input.event, core);
50
54
  },
55
+ "tool.execute.before": async (input, output) => {
56
+ await handleToolExecuteBefore(input, output, core);
57
+ },
51
58
  };
52
59
  }
@@ -0,0 +1,18 @@
1
+ type LoopCore = {
2
+ startLoop: (sessionID: string, prompt: string, options: {
3
+ maxIterations?: number;
4
+ completionPromise?: string;
5
+ }) => Promise<unknown>;
6
+ cancelLoop: (sessionID: string) => Promise<unknown>;
7
+ };
8
+ type ToolExecuteBeforeInput = {
9
+ tool?: unknown;
10
+ sessionID?: unknown;
11
+ };
12
+ type ToolExecuteBeforeOutput = {
13
+ args?: {
14
+ name?: unknown;
15
+ };
16
+ };
17
+ export declare function handleToolExecuteBefore(input: ToolExecuteBeforeInput, output: ToolExecuteBeforeOutput, core: LoopCore): Promise<void>;
18
+ export {};
@@ -0,0 +1,23 @@
1
+ import { parseRalphLoopCommand } from "../commands/parse-ralph-loop-command.js";
2
+ function normalizeCommandName(name) {
3
+ return name.startsWith("/") ? name : `/${name}`;
4
+ }
5
+ export async function handleToolExecuteBefore(input, output, core) {
6
+ if (input.tool !== "skill")
7
+ return;
8
+ if (typeof input.sessionID !== "string")
9
+ return;
10
+ if (typeof output.args?.name !== "string")
11
+ return;
12
+ const command = parseRalphLoopCommand(normalizeCommandName(output.args.name));
13
+ if (!command)
14
+ return;
15
+ if (command.kind === "cancel") {
16
+ await core.cancelLoop(input.sessionID);
17
+ return;
18
+ }
19
+ await core.startLoop(input.sessionID, command.prompt, {
20
+ maxIterations: command.maxIterations,
21
+ completionPromise: command.completionPromise,
22
+ });
23
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@w32191/just-loop",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "description": "OpenCode plugin package for just-loop.",
6
6
  "license": "MIT",
7
7
  "main": "./dist/src/index.js",
@@ -12,7 +12,11 @@
12
12
  "import": "./dist/src/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist/", "README.md", "LICENSE"],
15
+ "files": [
16
+ "dist/",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
16
20
  "publishConfig": {
17
21
  "access": "public"
18
22
  },