Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24f10b0d09 | |||
| 3bf7c71677 | |||
| 55f28777f6 | |||
| e94fdb01b8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
dist/
|
|
||||||
node_modules/
|
node_modules/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
5
build.sh
Executable file
5
build.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
bun build src/index.ts --outdir dist --target bun --format esm
|
||||||
|
tsc --emitDeclarationOnly
|
||||||
4
clean.sh
Executable file
4
clean.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
rm -rf dist
|
||||||
3
dist/index.d.ts
vendored
Normal file
3
dist/index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
declare const RalphLoopPlugin: Plugin;
|
||||||
|
export default RalphLoopPlugin;
|
||||||
1036
dist/index.js
vendored
Normal file
1036
dist/index.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/ralph-loop/claude-config-dir.d.ts
vendored
Normal file
1
dist/ralph-loop/claude-config-dir.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare function getClaudeConfigDir(): string;
|
||||||
8
dist/ralph-loop/command-arguments.d.ts
vendored
Normal file
8
dist/ralph-loop/command-arguments.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type RalphLoopStrategy = "reset" | "continue";
|
||||||
|
export type ParsedRalphLoopArguments = {
|
||||||
|
prompt: string;
|
||||||
|
maxIterations?: number;
|
||||||
|
completionPromise?: string;
|
||||||
|
strategy?: RalphLoopStrategy;
|
||||||
|
};
|
||||||
|
export declare function parseRalphLoopArguments(rawArguments: string): ParsedRalphLoopArguments;
|
||||||
3
dist/ralph-loop/commands.d.ts
vendored
Normal file
3
dist/ralph-loop/commands.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare const RALPH_LOOP_TEMPLATE = "You are starting a Ralph Loop - a self-referential development loop that runs until task completion.\n\n## How Ralph Loop Works\n\n1. You will work on the task continuously\n2. When you believe the task is FULLY complete, output: `<promise>{{COMPLETION_PROMISE}}</promise>`\n3. If you don't output the promise, the loop will automatically inject another prompt to continue\n4. Maximum iterations: Configurable (default 100)\n\n## Rules\n\n- Focus on completing the task fully, not partially\n- Don't output the completion promise until the task is truly done\n- Each iteration should make meaningful progress toward the goal\n- If stuck, try different approaches\n- Use todos to track your progress\n\n## Exit Conditions\n\n1. **Completion**: Output your completion promise tag when fully complete\n2. **Max Iterations**: Loop stops automatically at limit\n3. **Cancel**: User runs `/cancel-ralph` command\n\n## Your Task\n\nParse the arguments below and begin working on the task. The format is:\n`\"task description\" [--completion-promise=TEXT] [--max-iterations=N] [--strategy=reset|continue]`\n\nDefault completion promise is \"DONE\" and default max iterations is 100.";
|
||||||
|
export declare const CANCEL_RALPH_TEMPLATE = "Cancel the currently active Ralph Loop.\n\nThis will:\n1. Stop the loop from continuing\n2. Clear the loop state file\n3. Allow the session to end normally\n\nCheck if a loop is active and cancel it. Inform the user of the result.";
|
||||||
|
export declare const RALPH_LOOP_COMMANDS: Record<string, unknown>;
|
||||||
8
dist/ralph-loop/completion-promise-detector.d.ts
vendored
Normal file
8
dist/ralph-loop/completion-promise-detector.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
export declare function detectCompletionInTranscript(transcriptPath: string | undefined, promise: string): boolean;
|
||||||
|
export declare function detectCompletionInSessionMessages(ctx: PluginInput, options: {
|
||||||
|
sessionID: string;
|
||||||
|
promise: string;
|
||||||
|
apiTimeoutMs: number;
|
||||||
|
directory: string;
|
||||||
|
}): Promise<boolean>;
|
||||||
8
dist/ralph-loop/config.d.ts
vendored
Normal file
8
dist/ralph-loop/config.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type RalphLoopStrategy = "reset" | "continue";
|
||||||
|
export type RalphLoopConfig = {
|
||||||
|
enabled?: boolean;
|
||||||
|
default_max_iterations?: number;
|
||||||
|
state_dir?: string;
|
||||||
|
default_strategy?: RalphLoopStrategy;
|
||||||
|
};
|
||||||
|
export declare function loadRalphLoopConfig(projectDir: string): RalphLoopConfig;
|
||||||
4
dist/ralph-loop/constants.d.ts
vendored
Normal file
4
dist/ralph-loop/constants.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export declare const HOOK_NAME = "ralph-loop";
|
||||||
|
export declare const DEFAULT_STATE_FILE = ".sisyphus/ralph-loop.local.md";
|
||||||
|
export declare const DEFAULT_MAX_ITERATIONS = 100;
|
||||||
|
export declare const DEFAULT_COMPLETION_PROMISE = "DONE";
|
||||||
2
dist/ralph-loop/continuation-prompt-builder.d.ts
vendored
Normal file
2
dist/ralph-loop/continuation-prompt-builder.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import type { RalphLoopState } from "./types";
|
||||||
|
export declare function buildContinuationPrompt(state: RalphLoopState): string;
|
||||||
8
dist/ralph-loop/continuation-prompt-injector.d.ts
vendored
Normal file
8
dist/ralph-loop/continuation-prompt-injector.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
export declare function injectContinuationPrompt(ctx: PluginInput, options: {
|
||||||
|
sessionID: string;
|
||||||
|
prompt: string;
|
||||||
|
directory: string;
|
||||||
|
apiTimeoutMs: number;
|
||||||
|
inheritFromSessionID?: string;
|
||||||
|
}): Promise<void>;
|
||||||
5
dist/ralph-loop/internal-initiator-marker.d.ts
vendored
Normal file
5
dist/ralph-loop/internal-initiator-marker.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export declare const OMO_INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->";
|
||||||
|
export declare function createInternalAgentTextPart(text: string): {
|
||||||
|
type: "text";
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
12
dist/ralph-loop/iteration-continuation.d.ts
vendored
Normal file
12
dist/ralph-loop/iteration-continuation.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
import type { RalphLoopState } from "./types";
|
||||||
|
type ContinuationOptions = {
|
||||||
|
directory: string;
|
||||||
|
apiTimeoutMs: number;
|
||||||
|
previousSessionID: string;
|
||||||
|
loopState: {
|
||||||
|
setSessionID: (sessionID: string) => RalphLoopState | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export declare function continueIteration(ctx: PluginInput, state: RalphLoopState, options: ContinuationOptions): Promise<void>;
|
||||||
|
export {};
|
||||||
2
dist/ralph-loop/logger.d.ts
vendored
Normal file
2
dist/ralph-loop/logger.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export declare function log(message: string, meta?: unknown): void;
|
||||||
|
export declare function getLogFilePath(): string;
|
||||||
7
dist/ralph-loop/loop-session-recovery.d.ts
vendored
Normal file
7
dist/ralph-loop/loop-session-recovery.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export declare function createLoopSessionRecovery(options?: {
|
||||||
|
recoveryWindowMs?: number;
|
||||||
|
}): {
|
||||||
|
isRecovering(sessionID: string): boolean;
|
||||||
|
markRecovering(sessionID: string): void;
|
||||||
|
clear(sessionID: string): void;
|
||||||
|
};
|
||||||
18
dist/ralph-loop/loop-state-controller.d.ts
vendored
Normal file
18
dist/ralph-loop/loop-state-controller.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { RalphLoopOptions, RalphLoopState } from "./types";
|
||||||
|
export declare function createLoopStateController(options: {
|
||||||
|
directory: string;
|
||||||
|
stateDir: string | undefined;
|
||||||
|
config: RalphLoopOptions["config"] | undefined;
|
||||||
|
}): {
|
||||||
|
startLoop(sessionID: string, prompt: string, loopOptions?: {
|
||||||
|
maxIterations?: number;
|
||||||
|
completionPromise?: string;
|
||||||
|
ultrawork?: boolean;
|
||||||
|
strategy?: "reset" | "continue";
|
||||||
|
}): boolean;
|
||||||
|
cancelLoop(sessionID: string): boolean;
|
||||||
|
getState(): RalphLoopState | null;
|
||||||
|
clear(): boolean;
|
||||||
|
incrementIteration(): RalphLoopState | null;
|
||||||
|
setSessionID(sessionID: string): RalphLoopState | null;
|
||||||
|
};
|
||||||
3
dist/ralph-loop/normalize-sdk-response.d.ts
vendored
Normal file
3
dist/ralph-loop/normalize-sdk-response.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare function normalizeSDKResponse<T>(response: unknown, fallback: T, options?: {
|
||||||
|
preferResponseOnMissingData?: boolean;
|
||||||
|
}): T;
|
||||||
2
dist/ralph-loop/prompt-tools.d.ts
vendored
Normal file
2
dist/ralph-loop/prompt-tools.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export type PromptToolPermission = boolean | "allow" | "deny" | "ask";
|
||||||
|
export declare function normalizePromptTools(tools: Record<string, PromptToolPermission> | undefined): Record<string, boolean> | undefined;
|
||||||
28
dist/ralph-loop/ralph-loop-event-handler.d.ts
vendored
Normal file
28
dist/ralph-loop/ralph-loop-event-handler.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
import type { RalphLoopOptions, RalphLoopState } from "./types";
|
||||||
|
type SessionRecovery = {
|
||||||
|
isRecovering: (sessionID: string) => boolean;
|
||||||
|
markRecovering: (sessionID: string) => void;
|
||||||
|
clear: (sessionID: string) => void;
|
||||||
|
};
|
||||||
|
type LoopStateController = {
|
||||||
|
getState: () => RalphLoopState | null;
|
||||||
|
clear: () => boolean;
|
||||||
|
incrementIteration: () => RalphLoopState | null;
|
||||||
|
setSessionID: (sessionID: string) => RalphLoopState | null;
|
||||||
|
};
|
||||||
|
type RalphLoopEventHandlerOptions = {
|
||||||
|
directory: string;
|
||||||
|
apiTimeoutMs: number;
|
||||||
|
getTranscriptPath: (sessionID: string) => string | undefined;
|
||||||
|
checkSessionExists?: RalphLoopOptions["checkSessionExists"];
|
||||||
|
sessionRecovery: SessionRecovery;
|
||||||
|
loopState: LoopStateController;
|
||||||
|
};
|
||||||
|
export declare function createRalphLoopEventHandler(ctx: PluginInput, options: RalphLoopEventHandlerOptions): ({ event }: {
|
||||||
|
event: {
|
||||||
|
type: string;
|
||||||
|
properties?: unknown;
|
||||||
|
};
|
||||||
|
}) => Promise<void>;
|
||||||
|
export {};
|
||||||
19
dist/ralph-loop/ralph-loop-hook.d.ts
vendored
Normal file
19
dist/ralph-loop/ralph-loop-hook.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
import type { RalphLoopOptions, RalphLoopState } from "./types";
|
||||||
|
export interface RalphLoopHook {
|
||||||
|
event: (input: {
|
||||||
|
event: {
|
||||||
|
type: string;
|
||||||
|
properties?: unknown;
|
||||||
|
};
|
||||||
|
}) => Promise<void>;
|
||||||
|
startLoop: (sessionID: string, prompt: string, options?: {
|
||||||
|
maxIterations?: number;
|
||||||
|
completionPromise?: string;
|
||||||
|
ultrawork?: boolean;
|
||||||
|
strategy?: "reset" | "continue";
|
||||||
|
}) => boolean;
|
||||||
|
cancelLoop: (sessionID: string) => boolean;
|
||||||
|
getState: () => RalphLoopState | null;
|
||||||
|
}
|
||||||
|
export declare function createRalphLoopHook(ctx: PluginInput, options?: RalphLoopOptions): RalphLoopHook;
|
||||||
3
dist/ralph-loop/session-reset-strategy.d.ts
vendored
Normal file
3
dist/ralph-loop/session-reset-strategy.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin";
|
||||||
|
export declare function createIterationSession(ctx: PluginInput, parentSessionID: string, directory: string): Promise<string | null>;
|
||||||
|
export declare function selectSessionInTui(client: PluginInput["client"], sessionID: string): Promise<boolean>;
|
||||||
4
dist/ralph-loop/simple-frontmatter.d.ts
vendored
Normal file
4
dist/ralph-loop/simple-frontmatter.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export declare function parseFrontmatter(content: string): {
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
body: string;
|
||||||
|
};
|
||||||
6
dist/ralph-loop/storage.d.ts
vendored
Normal file
6
dist/ralph-loop/storage.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { RalphLoopState } from "./types";
|
||||||
|
export declare function getStateFilePath(directory: string, customPath?: string): string;
|
||||||
|
export declare function readState(directory: string, customPath?: string): RalphLoopState | null;
|
||||||
|
export declare function writeState(directory: string, state: RalphLoopState, customPath?: string): boolean;
|
||||||
|
export declare function clearState(directory: string, customPath?: string): boolean;
|
||||||
|
export declare function incrementIteration(directory: string, customPath?: string): RalphLoopState | null;
|
||||||
1
dist/ralph-loop/system-directive.d.ts
vendored
Normal file
1
dist/ralph-loop/system-directive.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare const SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: OH-MY-OPENCODE";
|
||||||
1
dist/ralph-loop/transcript.d.ts
vendored
Normal file
1
dist/ralph-loop/transcript.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare function getTranscriptPath(sessionId: string): string;
|
||||||
18
dist/ralph-loop/types.d.ts
vendored
Normal file
18
dist/ralph-loop/types.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { RalphLoopConfig } from "./config";
|
||||||
|
export interface RalphLoopState {
|
||||||
|
active: boolean;
|
||||||
|
iteration: number;
|
||||||
|
max_iterations: number;
|
||||||
|
completion_promise: string;
|
||||||
|
started_at: string;
|
||||||
|
prompt: string;
|
||||||
|
session_id?: string;
|
||||||
|
ultrawork?: boolean;
|
||||||
|
strategy?: "reset" | "continue";
|
||||||
|
}
|
||||||
|
export interface RalphLoopOptions {
|
||||||
|
config?: RalphLoopConfig;
|
||||||
|
getTranscriptPath?: (sessionId: string) => string | undefined;
|
||||||
|
apiTimeout?: number;
|
||||||
|
checkSessionExists?: (sessionId: string) => Promise<boolean>;
|
||||||
|
}
|
||||||
1
dist/ralph-loop/with-timeout.d.ts
vendored
Normal file
1
dist/ralph-loop/with-timeout.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare function withTimeout<TData>(promise: Promise<TData>, timeoutMs: number): Promise<TData>;
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun build src/index.ts --outdir dist --target bun --format esm && tsc --emitDeclarationOnly",
|
"bundle": "./build.sh",
|
||||||
"clean": "rm -rf dist",
|
"clean": "./clean.sh",
|
||||||
"prepublishOnly": "bun run clean && bun run build",
|
"prepublishOnly": "./prepublish.sh",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "./typecheck.sh"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"opencode",
|
"opencode",
|
||||||
|
|||||||
5
prepublish.sh
Executable file
5
prepublish.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
./clean.sh
|
||||||
|
./build.sh
|
||||||
40
release.sh
Executable file
40
release.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#! /usr/bin/env nix
|
||||||
|
#! nix shell nixpkgs#bash nixpkgs#bun nixpkgs#git nixpkgs#nodejs --command bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 1 ]]; then
|
||||||
|
printf 'Usage: %s <version>\n' "$0" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version="$1"
|
||||||
|
branch="$(git branch --show-current)"
|
||||||
|
|
||||||
|
if [[ "$branch" != "main" ]]; then
|
||||||
|
printf 'Release must run from main, current branch: %s\n' "$branch" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$(git status --porcelain)" ]]; then
|
||||||
|
printf 'Working tree must be clean before releasing.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git fetch origin main --tags
|
||||||
|
|
||||||
|
if ! git merge-base --is-ancestor origin/main HEAD; then
|
||||||
|
printf 'Local main is behind origin/main. Pull or rebase before releasing.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bun run typecheck
|
||||||
|
bun run clean
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
npm version "$version"
|
||||||
|
|
||||||
|
git push origin main
|
||||||
|
git push origin "v$version"
|
||||||
|
|
||||||
|
# vim: set ft=bash :
|
||||||
4
typecheck.sh
Executable file
4
typecheck.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
tsc --noEmit
|
||||||
Reference in New Issue
Block a user