chore: include pre-built dist for github install

This commit is contained in:
Robin Mordasiewicz
2026-03-14 04:56:50 +00:00
parent a7f0a4cf46
commit bce8ff3a75
1011 changed files with 150081 additions and 0 deletions
@@ -0,0 +1,10 @@
import type { BackgroundTask } from "./types";
export type BackgroundTaskNotificationStatus = "COMPLETED" | "CANCELLED" | "INTERRUPTED";
export declare function buildBackgroundTaskNotificationText(input: {
task: BackgroundTask;
duration: string;
statusText: BackgroundTaskNotificationStatus;
allComplete: boolean;
remainingCount: number;
completedTasks: BackgroundTask[];
}): string;
@@ -0,0 +1,18 @@
import type { StoredMessage } from "../hook-message-injector";
type SessionMessage = {
info?: {
agent?: string;
model?: {
providerID?: string;
modelID?: string;
variant?: string;
};
providerID?: string;
modelID?: string;
tools?: StoredMessage["tools"];
};
};
export declare function isCompactionAgent(agent: string | undefined): boolean;
export declare function resolvePromptContextFromSessionMessages(messages: SessionMessage[], sessionID?: string): StoredMessage | null;
export declare function findNearestMessageExcludingCompaction(messageDir: string, sessionID?: string): StoredMessage | null;
export {};
+27
View File
@@ -0,0 +1,27 @@
import type { BackgroundTaskConfig } from "../../config/schema";
export declare class ConcurrencyManager {
private config?;
private counts;
private queues;
constructor(config?: BackgroundTaskConfig);
getConcurrencyLimit(model: string): number;
acquire(model: string): Promise<void>;
release(model: string): void;
/**
* Cancel all waiting acquires for a model. Used during cleanup.
*/
cancelWaiters(model: string): void;
/**
* Clear all state. Used during manager cleanup/shutdown.
* Cancels all pending waiters.
*/
clear(): void;
/**
* Get current count for a model (for testing/debugging)
*/
getCount(model: string): number;
/**
* Get queue length for a model (for testing/debugging)
*/
getQueueLength(model: string): number;
}
+45
View File
@@ -0,0 +1,45 @@
import type { PluginInput } from "@opencode-ai/plugin";
import type { BackgroundTask, LaunchInput } from "./types";
export declare const TASK_TTL_MS: number;
export declare const MIN_STABILITY_TIME_MS: number;
export declare const DEFAULT_STALE_TIMEOUT_MS = 180000;
export declare const DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
export declare const MIN_RUNTIME_BEFORE_STALE_MS = 30000;
export declare const MIN_IDLE_TIME_MS = 5000;
export declare const POLLING_INTERVAL_MS = 3000;
export declare const TASK_CLEANUP_DELAY_MS: number;
export declare const TMUX_CALLBACK_DELAY_MS = 200;
export type ProcessCleanupEvent = NodeJS.Signals | "beforeExit" | "exit";
export type OpencodeClient = PluginInput["client"];
export interface MessagePartInfo {
sessionID?: string;
type?: string;
tool?: string;
}
export interface EventProperties {
sessionID?: string;
info?: {
id?: string;
};
[key: string]: unknown;
}
export interface BackgroundEvent {
type: string;
properties?: EventProperties;
}
export interface Todo {
content: string;
status: string;
priority: string;
id?: string;
}
export interface QueueItem {
task: BackgroundTask;
input: LaunchInput;
}
export interface SubagentSessionCreatedEvent {
sessionID: string;
parentID: string;
title: string;
}
export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => Promise<void>;
@@ -0,0 +1 @@
export declare function formatDuration(start: Date, end?: Date): string;
+10
View File
@@ -0,0 +1,10 @@
export declare function isRecord(value: unknown): value is Record<string, unknown>;
export declare function isAbortedSessionError(error: unknown): boolean;
export declare function getErrorText(error: unknown): string;
export declare function extractErrorName(error: unknown): string | undefined;
export declare function extractErrorMessage(error: unknown): string | undefined;
interface EventPropertiesLike {
[key: string]: unknown;
}
export declare function getSessionErrorMessage(properties: EventPropertiesLike): string | undefined;
export {};
@@ -0,0 +1,16 @@
import type { BackgroundTask } from "./types";
import type { ConcurrencyManager } from "./concurrency";
import type { OpencodeClient, QueueItem } from "./constants";
export declare function tryFallbackRetry(args: {
task: BackgroundTask;
errorInfo: {
name?: string;
message?: string;
};
source: string;
concurrencyManager: ConcurrencyManager;
client: OpencodeClient;
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
queuesByKey: Map<string, QueueItem[]>;
processKey: (key: string) => void;
}): boolean;
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export { BackgroundManager, type SubagentSessionCreatedEvent, type OnSubagentSessionCreated } from "./manager";
+156
View File
@@ -0,0 +1,156 @@
import type { PluginInput } from "@opencode-ai/plugin";
import type { BackgroundTask, LaunchInput, ResumeInput } from "./types";
import { TaskHistory } from "./task-history";
import type { BackgroundTaskConfig, TmuxConfig } from "../../config/schema";
import { type SubagentSpawnContext } from "./subagent-spawn-limits";
interface EventProperties {
sessionID?: string;
info?: {
id?: string;
};
[key: string]: unknown;
}
interface Event {
type: string;
properties?: EventProperties;
}
export interface SubagentSessionCreatedEvent {
sessionID: string;
parentID: string;
title: string;
}
export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => Promise<void>;
export declare class BackgroundManager {
private tasks;
private notifications;
private pendingNotifications;
private pendingByParent;
private client;
private directory;
private pollingInterval?;
private pollingInFlight;
private concurrencyManager;
private shutdownTriggered;
private config?;
private tmuxEnabled;
private onSubagentSessionCreated?;
private onShutdown?;
private queuesByKey;
private processingKeys;
private completionTimers;
private completedTaskSummaries;
private idleDeferralTimers;
private notificationQueueByParent;
private rootDescendantCounts;
private preStartDescendantReservations;
private enableParentSessionNotifications;
readonly taskHistory: TaskHistory;
constructor(ctx: PluginInput, config?: BackgroundTaskConfig, options?: {
tmuxConfig?: TmuxConfig;
onSubagentSessionCreated?: OnSubagentSessionCreated;
onShutdown?: () => void | Promise<void>;
enableParentSessionNotifications?: boolean;
});
assertCanSpawn(parentSessionID: string): Promise<SubagentSpawnContext>;
reserveSubagentSpawn(parentSessionID: string): Promise<{
spawnContext: SubagentSpawnContext;
descendantCount: number;
commit: () => number;
rollback: () => void;
}>;
private registerRootDescendant;
private unregisterRootDescendant;
private markPreStartDescendantReservation;
private settlePreStartDescendantReservation;
private rollbackPreStartDescendantReservation;
launch(input: LaunchInput): Promise<BackgroundTask>;
private processKey;
private startTask;
getTask(id: string): BackgroundTask | undefined;
getTasksByParentSession(sessionID: string): BackgroundTask[];
getAllDescendantTasks(sessionID: string): BackgroundTask[];
findBySession(sessionID: string): BackgroundTask | undefined;
private getConcurrencyKeyFromInput;
/**
* Track a task created elsewhere (e.g., from task) for notification tracking.
* This allows tasks created by other tools to receive the same toast/prompt notifications.
*/
trackTask(input: {
taskId: string;
sessionID: string;
parentSessionID: string;
description: string;
agent?: string;
parentAgent?: string;
concurrencyKey?: string;
}): Promise<BackgroundTask>;
resume(input: ResumeInput): Promise<BackgroundTask>;
private checkSessionTodos;
handleEvent(event: Event): void;
private tryFallbackRetry;
markForNotification(task: BackgroundTask): void;
getPendingNotifications(sessionID: string): BackgroundTask[];
clearNotifications(sessionID: string): void;
queuePendingNotification(sessionID: string | undefined, notification: string): void;
injectPendingNotificationsIntoChatMessage(output: {
parts: Array<{
type: string;
text?: string;
[key: string]: unknown;
}>;
}, sessionID: string): void;
/**
* Validates that a session has actual assistant/tool output before marking complete.
* Prevents premature completion when session.idle fires before agent responds.
*/
private validateSessionHasOutput;
private clearNotificationsForTask;
/**
* Remove task from pending tracking for its parent session.
* Cleans up the parent entry if no pending tasks remain.
*/
private cleanupPendingByParent;
private clearTaskHistoryWhenParentTasksGone;
private scheduleTaskRemoval;
cancelTask(taskId: string, options?: {
source?: string;
reason?: string;
abortSession?: boolean;
skipNotification?: boolean;
}): Promise<boolean>;
/**
* Cancels a pending task by removing it from queue and marking as cancelled.
* Does NOT abort session (no session exists yet) or release concurrency slot (wasn't acquired).
*/
cancelPendingTask(taskId: string): boolean;
private startPolling;
private stopPolling;
private registerProcessCleanup;
private unregisterProcessCleanup;
/**
* Get all running tasks (for compaction hook)
*/
getRunningTasks(): BackgroundTask[];
/**
* Get all non-running tasks still in memory (for compaction hook)
*/
getNonRunningTasks(): BackgroundTask[];
/**
* Safely complete a task with race condition protection.
* Returns true if task was successfully completed, false if already completed by another path.
*/
private tryCompleteTask;
private notifyParentSession;
private hasRunningTasks;
private pruneStaleTasksAndNotifications;
private checkAndInterruptStaleTasks;
private pollRunningTasks;
/**
* Shutdown the manager gracefully.
* Cancels all pending concurrency waiters and clears timers.
* Should be called when the plugin is unloaded.
*/
shutdown(): Promise<void>;
private enqueueNotificationForParent;
}
export {};
+2
View File
@@ -0,0 +1,2 @@
import type { PluginInput } from "@opencode-ai/plugin";
export type OpencodeClient = PluginInput["client"];
+8
View File
@@ -0,0 +1,8 @@
interface CleanupTarget {
shutdown(): void | Promise<void>;
}
export declare function registerManagerForCleanup(manager: CleanupTarget): void;
export declare function unregisterManagerForCleanup(manager: CleanupTarget): void;
/** @internal — test-only reset for module-level singleton state */
export declare function _resetForTesting(): void;
export {};
@@ -0,0 +1 @@
export declare function removeTaskToastTracking(taskId: string): void;
@@ -0,0 +1,10 @@
import type { BackgroundTask } from "./types";
export declare function handleSessionIdleBackgroundEvent(args: {
properties: Record<string, unknown>;
findBySession: (sessionID: string) => BackgroundTask | undefined;
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
validateSessionHasOutput: (sessionID: string) => Promise<boolean>;
checkSessionTodos: (sessionID: string) => Promise<boolean>;
tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean>;
emitIdleEvent: (sessionID: string) => void;
}): void;
+14
View File
@@ -0,0 +1,14 @@
import type { BackgroundTask, LaunchInput, ResumeInput } from "./types";
import type { OpencodeClient, OnSubagentSessionCreated, QueueItem } from "./constants";
import type { ConcurrencyManager } from "./concurrency";
export interface SpawnerContext {
client: OpencodeClient;
directory: string;
concurrencyManager: ConcurrencyManager;
tmuxEnabled: boolean;
onSubagentSessionCreated?: OnSubagentSessionCreated;
onTaskError: (task: BackgroundTask, error: Error) => void;
}
export declare function createTask(input: LaunchInput): BackgroundTask;
export declare function startTask(item: QueueItem, ctx: SpawnerContext): Promise<void>;
export declare function resumeTask(task: BackgroundTask, input: ResumeInput, ctx: Pick<SpawnerContext, "client" | "concurrencyManager" | "onTaskError">): Promise<void>;
@@ -0,0 +1,6 @@
import type { OpencodeClient } from "../constants";
export declare function resolveParentDirectory(options: {
client: OpencodeClient;
parentSessionID: string;
defaultDirectory: string;
}): Promise<string>;
+35
View File
@@ -0,0 +1,35 @@
import type { BackgroundTask, LaunchInput } from "./types";
import type { QueueItem } from "./constants";
export declare class TaskStateManager {
readonly tasks: Map<string, BackgroundTask>;
readonly notifications: Map<string, BackgroundTask[]>;
readonly pendingByParent: Map<string, Set<string>>;
readonly queuesByKey: Map<string, QueueItem[]>;
readonly processingKeys: Set<string>;
readonly completionTimers: Map<string, ReturnType<typeof setTimeout>>;
getTask(id: string): BackgroundTask | undefined;
findBySession(sessionID: string): BackgroundTask | undefined;
getTasksByParentSession(sessionID: string): BackgroundTask[];
getAllDescendantTasks(sessionID: string): BackgroundTask[];
getRunningTasks(): BackgroundTask[];
getNonRunningTasks(): BackgroundTask[];
hasRunningTasks(): boolean;
getConcurrencyKeyFromInput(input: LaunchInput): string;
getConcurrencyKeyFromTask(task: BackgroundTask): string;
addTask(task: BackgroundTask): void;
removeTask(taskId: string): void;
trackPendingTask(parentSessionID: string, taskId: string): void;
cleanupPendingByParent(task: BackgroundTask): void;
markForNotification(task: BackgroundTask): void;
getPendingNotifications(sessionID: string): BackgroundTask[];
clearNotifications(sessionID: string): void;
clearNotificationsForTask(taskId: string): void;
addToQueue(key: string, item: QueueItem): void;
getQueue(key: string): QueueItem[] | undefined;
removeFromQueue(key: string, taskId: string): boolean;
setCompletionTimer(taskId: string, timer: ReturnType<typeof setTimeout>): void;
clearCompletionTimer(taskId: string): void;
clearAllCompletionTimers(): void;
clear(): void;
cancelPendingTask(taskId: string): boolean;
}
@@ -0,0 +1,23 @@
import type { BackgroundTaskConfig } from "../../config/schema";
import type { OpencodeClient } from "./constants";
export declare const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
export declare const DEFAULT_MAX_ROOT_SESSION_SPAWN_BUDGET = 50;
export interface SubagentSpawnContext {
rootSessionID: string;
parentDepth: number;
childDepth: number;
}
export declare function getMaxSubagentDepth(config?: BackgroundTaskConfig): number;
export declare function getMaxRootSessionSpawnBudget(config?: BackgroundTaskConfig): number;
export declare function resolveSubagentSpawnContext(client: OpencodeClient, parentSessionID: string): Promise<SubagentSpawnContext>;
export declare function createSubagentDepthLimitError(input: {
childDepth: number;
maxDepth: number;
parentSessionID: string;
rootSessionID: string;
}): Error;
export declare function createSubagentDescendantLimitError(input: {
rootSessionID: string;
descendantCount: number;
maxDescendants: number;
}): Error;
+19
View File
@@ -0,0 +1,19 @@
import type { BackgroundTaskStatus } from "./types";
export interface TaskHistoryEntry {
id: string;
sessionID?: string;
agent: string;
description: string;
status: BackgroundTaskStatus;
category?: string;
startedAt?: Date;
completedAt?: Date;
}
export declare class TaskHistory {
private entries;
record(parentSessionID: string | undefined, entry: TaskHistoryEntry): void;
getByParentSession(parentSessionID: string): TaskHistoryEntry[];
clearSession(parentSessionID: string): void;
clearAll(): void;
formatForCompaction(parentSessionID: string): string | null;
}
+21
View File
@@ -0,0 +1,21 @@
import type { BackgroundTaskConfig } from "../../config/schema";
import type { BackgroundTask } from "./types";
import type { ConcurrencyManager } from "./concurrency";
import type { OpencodeClient } from "./opencode-client";
export declare function pruneStaleTasksAndNotifications(args: {
tasks: Map<string, BackgroundTask>;
notifications: Map<string, BackgroundTask[]>;
onTaskPruned: (taskId: string, task: BackgroundTask, errorMessage: string) => void;
}): void;
export type SessionStatusMap = Record<string, {
type: string;
}>;
export declare function checkAndInterruptStaleTasks(args: {
tasks: Iterable<BackgroundTask>;
client: OpencodeClient;
config: BackgroundTaskConfig | undefined;
concurrencyManager: ConcurrencyManager;
notifyParentSession: (task: BackgroundTask) => Promise<void>;
sessionStatuses?: SessionStatusMap;
onTaskInterrupted?: (task: BackgroundTask) => void;
}): Promise<void>;
+94
View File
@@ -0,0 +1,94 @@
import type { FallbackEntry } from "../../shared/model-requirements";
import type { SessionPermissionRule } from "../../shared/question-denied-session-permission";
export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled" | "interrupt";
export interface TaskProgress {
toolCalls: number;
lastTool?: string;
lastUpdate: Date;
lastMessage?: string;
lastMessageAt?: Date;
}
export interface BackgroundTask {
id: string;
sessionID?: string;
rootSessionID?: string;
parentSessionID: string;
parentMessageID: string;
description: string;
prompt: string;
agent: string;
spawnDepth?: number;
status: BackgroundTaskStatus;
queuedAt?: Date;
startedAt?: Date;
completedAt?: Date;
result?: string;
error?: string;
progress?: TaskProgress;
parentModel?: {
providerID: string;
modelID: string;
};
model?: {
providerID: string;
modelID: string;
variant?: string;
};
/** Fallback chain for runtime retry on model errors */
fallbackChain?: FallbackEntry[];
/** Number of fallback retry attempts made */
attemptCount?: number;
/** Active concurrency slot key */
concurrencyKey?: string;
/** Persistent key for re-acquiring concurrency on resume */
concurrencyGroup?: string;
/** Parent session's agent name for notification */
parentAgent?: string;
/** Parent session's tool restrictions for notification prompts */
parentTools?: Record<string, boolean>;
/** Marks if the task was launched from an unstable agent/category */
isUnstableAgent?: boolean;
/** Category used for this task (e.g., 'quick', 'visual-engineering') */
category?: string;
/** Last message count for stability detection */
lastMsgCount?: number;
/** Number of consecutive polls with stable message count */
stablePolls?: number;
}
export interface LaunchInput {
description: string;
prompt: string;
agent: string;
parentSessionID: string;
parentMessageID: string;
parentModel?: {
providerID: string;
modelID: string;
};
parentAgent?: string;
parentTools?: Record<string, boolean>;
model?: {
providerID: string;
modelID: string;
variant?: string;
};
/** Fallback chain for runtime retry on model errors */
fallbackChain?: FallbackEntry[];
isUnstableAgent?: boolean;
skills?: string[];
skillContent?: string;
category?: string;
sessionPermission?: SessionPermissionRule[];
}
export interface ResumeInput {
sessionId: string;
prompt: string;
parentSessionID: string;
parentMessageID: string;
parentModel?: {
providerID: string;
modelID: string;
};
parentAgent?: string;
parentTools?: Record<string, boolean>;
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Boulder State Constants
*/
export declare const BOULDER_DIR = ".sisyphus";
export declare const BOULDER_FILE = "boulder.json";
export declare const BOULDER_STATE_PATH = ".sisyphus/boulder.json";
export declare const NOTEPAD_DIR = "notepads";
export declare const NOTEPAD_BASE_PATH = ".sisyphus/notepads";
/** Prometheus plan directory pattern */
export declare const PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
+3
View File
@@ -0,0 +1,3 @@
export * from "./types";
export * from "./constants";
export * from "./storage";
+28
View File
@@ -0,0 +1,28 @@
/**
* Boulder State Storage
*
* Handles reading/writing boulder.json for active plan tracking.
*/
import type { BoulderState, PlanProgress } from "./types";
export declare function getBoulderFilePath(directory: string): string;
export declare function readBoulderState(directory: string): BoulderState | null;
export declare function writeBoulderState(directory: string, state: BoulderState): boolean;
export declare function appendSessionId(directory: string, sessionId: string): BoulderState | null;
export declare function clearBoulderState(directory: string): boolean;
/**
* Find Prometheus plan files for this project.
* Prometheus stores plans at: {project}/.sisyphus/plans/{name}.md
*/
export declare function findPrometheusPlans(directory: string): string[];
/**
* Parse a plan file and count checkbox progress.
*/
export declare function getPlanProgress(planPath: string): PlanProgress;
/**
* Extract plan name from file path.
*/
export declare function getPlanName(planPath: string): string;
/**
* Create a new boulder state for a plan.
*/
export declare function createBoulderState(planPath: string, sessionId: string, agent?: string, worktreePath?: string): BoulderState;
+28
View File
@@ -0,0 +1,28 @@
/**
* Boulder State Types
*
* Manages the active work plan state for Sisyphus orchestrator.
* Named after Sisyphus's boulder - the eternal task that must be rolled.
*/
export interface BoulderState {
/** Absolute path to the active plan file */
active_plan: string;
/** ISO timestamp when work started */
started_at: string;
/** Session IDs that have worked on this plan */
session_ids: string[];
/** Plan name derived from filename */
plan_name: string;
/** Agent type to use when resuming (e.g., 'atlas') */
agent?: string;
/** Absolute path to the git worktree root where work happens */
worktree_path?: string;
}
export interface PlanProgress {
/** Total number of checkboxes */
total: number;
/** Number of completed checkboxes */
completed: number;
/** Whether all tasks are done */
isComplete: boolean;
}
+2
View File
@@ -0,0 +1,2 @@
import type { BuiltinCommandName, BuiltinCommands } from "./types";
export declare function loadBuiltinCommands(disabledCommands?: BuiltinCommandName[]): BuiltinCommands;
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export * from "./commands";
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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 ULW_LOOP_TEMPLATE = "You are starting an ULTRAWORK Loop - a self-referential development loop that runs until verified completion.\n\n## How ULTRAWORK Loop Works\n\n1. You will work on the task continuously\n2. When you believe the work is complete, output: `<promise>{{COMPLETION_PROMISE}}</promise>`\n3. That does NOT finish the loop yet. The system will require Oracle verification\n4. The loop only ends after the system confirms Oracle verified the result\n5. There is no iteration limit\n\n## Rules\n\n- Focus on finishing the task completely\n- After you emit the completion promise, run Oracle verification when instructed\n- Do not treat DONE as final completion until Oracle verifies it\n\n## Exit Conditions\n\n1. **Verified Completion**: Oracle verifies the result and the system confirms it\n2. **Cancel**: User runs `/cancel-ralph`\n\n## Your Task\n\nParse the arguments below and begin working on the task. The format is:\n`\"task description\" [--completion-promise=TEXT] [--strategy=reset|continue]`\n\nDefault completion promise is \"DONE\".";
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.";
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export declare const START_WORK_TEMPLATE = "You are starting a Sisyphus work session.\n\n## ARGUMENTS\n\n- `/start-work [plan-name] [--worktree <path>]`\n - `plan-name` (optional): name or partial match of the plan to start\n - `--worktree <path>` (optional): absolute path to an existing git worktree to work in\n - If specified and valid: hook pre-sets worktree_path in boulder.json\n - If specified but invalid: you must run `git worktree add <path> <branch>` first\n - If omitted: you MUST choose or create a worktree (see Worktree Setup below)\n\n## WHAT TO DO\n\n1. **Find available plans**: Search for Prometheus-generated plan files at `.sisyphus/plans/`\n\n2. **Check for active boulder state**: Read `.sisyphus/boulder.json` if it exists\n\n3. **Decision logic**:\n - If `.sisyphus/boulder.json` exists AND plan is NOT complete (has unchecked boxes):\n - **APPEND** current session to session_ids\n - Continue work on existing plan\n - If no active plan OR plan is complete:\n - List available plan files\n - If ONE plan: auto-select it\n - If MULTIPLE plans: show list with timestamps, ask user to select\n\n4. **Worktree Setup** (when `worktree_path` not already set in boulder.json):\n 1. `git worktree list --porcelain` \u2014 see available worktrees\n 2. Create: `git worktree add <absolute-path> <branch-or-HEAD>`\n 3. Update boulder.json to add `\"worktree_path\": \"<absolute-path>\"`\n 4. All work happens inside that worktree directory\n\n5. **Create/Update boulder.json**:\n ```json\n {\n \"active_plan\": \"/absolute/path/to/plan.md\",\n \"started_at\": \"ISO_TIMESTAMP\",\n \"session_ids\": [\"session_id_1\", \"session_id_2\"],\n \"plan_name\": \"plan-name\",\n \"worktree_path\": \"/absolute/path/to/git/worktree\"\n }\n ```\n\n6. **Read the plan file** and start executing tasks according to atlas workflow\n\n## OUTPUT FORMAT\n\nWhen listing plans for selection:\n```\nAvailable Work Plans\n\nCurrent Time: {ISO timestamp}\nSession ID: {current session id}\n\n1. [plan-name-1.md] - Modified: {date} - Progress: 3/10 tasks\n2. [plan-name-2.md] - Modified: {date} - Progress: 0/5 tasks\n\nWhich plan would you like to work on? (Enter number or plan name)\n```\n\nWhen resuming existing work:\n```\nResuming Work Session\n\nActive Plan: {plan-name}\nProgress: {completed}/{total} tasks\nSessions: {count} (appending current session)\nWorktree: {worktree_path}\n\nReading plan and continuing from last incomplete task...\n```\n\nWhen auto-selecting single plan:\n```\nStarting Work Session\n\nPlan: {plan-name}\nSession ID: {session_id}\nStarted: {timestamp}\nWorktree: {worktree_path}\n\nReading plan and beginning execution...\n```\n\n## CRITICAL\n\n- The session_id is injected by the hook - use it directly\n- Always update boulder.json BEFORE starting work\n- Always set worktree_path in boulder.json before executing any tasks\n- Read the FULL plan file before delegating any tasks\n- Follow atlas delegation protocols (7-section format)";
@@ -0,0 +1 @@
export declare const STOP_CONTINUATION_TEMPLATE = "Stop all continuation mechanisms for the current session.\n\nThis command will:\n1. Stop the todo-continuation-enforcer from automatically continuing incomplete tasks\n2. Cancel any active Ralph Loop\n3. Clear the boulder state for the current project\n\nAfter running this command:\n- The session will not auto-continue when idle\n- You can manually continue work when ready\n- The stop state is per-session and clears when the session ends\n\nUse this when you need to pause automated continuation and take manual control.";
+6
View File
@@ -0,0 +1,6 @@
import type { CommandDefinition } from "../claude-code-command-loader";
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation" | "handoff";
export interface BuiltinCommandConfig {
disabled_commands?: BuiltinCommandName[];
}
export type BuiltinCommands = Record<string, CommandDefinition>;
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export { createBuiltinSkills, type CreateBuiltinSkillsOptions } from "./skills";
+7
View File
@@ -0,0 +1,7 @@
import type { BuiltinSkill } from "./types";
import type { BrowserAutomationProvider } from "../../config/schema";
export interface CreateBuiltinSkillsOptions {
browserProvider?: BrowserAutomationProvider;
disabledSkills?: Set<string>;
}
export declare function createBuiltinSkills(options?: CreateBuiltinSkillsOptions): BuiltinSkill[];
+2
View File
@@ -0,0 +1,2 @@
import type { BuiltinSkill } from "../types";
export declare const devBrowserSkill: BuiltinSkill;
@@ -0,0 +1,2 @@
import type { BuiltinSkill } from "../types";
export declare const frontendUiUxSkill: BuiltinSkill;
@@ -0,0 +1,2 @@
export declare const GIT_MASTER_SKILL_NAME = "git-master";
export declare const GIT_MASTER_SKILL_DESCRIPTION = "MUST USE for ANY git operations. Atomic commits, rebase/squash, history search (blame, bisect, log -S). STRONGLY RECOMMENDED: Use with task(category='quick', load_skills=['git-master'], ...) to save context. Triggers: 'commit', 'rebase', 'squash', 'who wrote', 'when was X added', 'find the commit that'.";
+2
View File
@@ -0,0 +1,2 @@
import type { BuiltinSkill } from "../types";
export declare const gitMasterSkill: BuiltinSkill;
+5
View File
@@ -0,0 +1,5 @@
export { playwrightSkill, agentBrowserSkill } from "./playwright";
export { playwrightCliSkill } from "./playwright-cli";
export { frontendUiUxSkill } from "./frontend-ui-ux";
export { gitMasterSkill } from "./git-master";
export { devBrowserSkill } from "./dev-browser";
+10
View File
@@ -0,0 +1,10 @@
import type { BuiltinSkill } from "../types";
/**
* Playwright CLI skill — token-efficient CLI alternative to the MCP-based playwright skill.
*
* Uses name "playwright" (not "playwright-cli") because agents hardcode "playwright" as the
* canonical browser skill name. The browserProvider config swaps the implementation behind
* the same name: "playwright" gives MCP, "playwright-cli" gives this CLI variant.
* The binary is still called `playwright-cli` (see allowedTools).
*/
export declare const playwrightCliSkill: BuiltinSkill;
+3
View File
@@ -0,0 +1,3 @@
import type { BuiltinSkill } from "../types";
export declare const playwrightSkill: BuiltinSkill;
export declare const agentBrowserSkill: BuiltinSkill;
+15
View File
@@ -0,0 +1,15 @@
import type { SkillMcpConfig } from "../skill-mcp-manager/types";
export interface BuiltinSkill {
name: string;
description: string;
template: string;
license?: string;
compatibility?: string;
metadata?: Record<string, unknown>;
allowedTools?: string[];
agent?: string;
model?: string;
subtask?: boolean;
argumentHint?: string;
mcpConfig?: SkillMcpConfig;
}
@@ -0,0 +1,4 @@
export declare function mapClaudeModelToOpenCode(model: string | undefined): {
providerID: string;
modelID: string;
} | undefined;
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export * from "./loader";
+3
View File
@@ -0,0 +1,3 @@
import type { ClaudeCodeAgentConfig } from "./types";
export declare function loadUserAgents(): Record<string, ClaudeCodeAgentConfig>;
export declare function loadProjectAgents(directory?: string): Record<string, ClaudeCodeAgentConfig>;
+21
View File
@@ -0,0 +1,21 @@
import type { AgentConfig } from "@opencode-ai/sdk";
export type AgentScope = "user" | "project";
export type ClaudeCodeAgentConfig = Omit<AgentConfig, "model"> & {
model?: string | {
providerID: string;
modelID: string;
};
};
export interface AgentFrontmatter {
name?: string;
description?: string;
model?: string;
tools?: string;
mode?: "subagent" | "primary" | "all";
}
export interface LoadedAgent {
name: string;
path: string;
config: ClaudeCodeAgentConfig;
scope: AgentScope;
}
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export * from "./loader";
+6
View File
@@ -0,0 +1,6 @@
import type { CommandDefinition } from "./types";
export declare function loadUserCommands(): Promise<Record<string, CommandDefinition>>;
export declare function loadProjectCommands(directory?: string): Promise<Record<string, CommandDefinition>>;
export declare function loadOpencodeGlobalCommands(): Promise<Record<string, CommandDefinition>>;
export declare function loadOpencodeProjectCommands(directory?: string): Promise<Record<string, CommandDefinition>>;
export declare function loadAllCommands(directory?: string): Promise<Record<string, CommandDefinition>>;
+42
View File
@@ -0,0 +1,42 @@
export type CommandScope = "user" | "project" | "opencode" | "opencode-project";
/**
* Handoff definition for command workflows.
* Based on speckit's handoff pattern for multi-agent orchestration.
* @see https://github.com/github/spec-kit
*/
export interface HandoffDefinition {
/** Human-readable label for the handoff action */
label: string;
/** Target agent/command identifier (e.g., "speckit.tasks") */
agent: string;
/** Pre-filled prompt text for the handoff */
prompt: string;
/** If true, automatically executes after command completion; if false, shows as suggestion */
send?: boolean;
}
export interface CommandDefinition {
name: string;
description?: string;
template: string;
agent?: string;
model?: string;
subtask?: boolean;
argumentHint?: string;
/** Handoff definitions for workflow transitions */
handoffs?: HandoffDefinition[];
}
export interface CommandFrontmatter {
description?: string;
"argument-hint"?: string;
agent?: string;
model?: string;
subtask?: boolean;
/** Handoff definitions for workflow transitions */
handoffs?: HandoffDefinition[];
}
export interface LoadedCommand {
name: string;
path: string;
definition: CommandDefinition;
scope: CommandScope;
}
@@ -0,0 +1,2 @@
export declare function expandEnvVars(value: string): string;
export declare function expandEnvVarsInObject<T>(obj: T): T;
+10
View File
@@ -0,0 +1,10 @@
/**
* MCP Configuration Loader
*
* Loads Claude Code .mcp.json format configurations from multiple scopes
* and transforms them to OpenCode SDK format
*/
export * from "./types";
export * from "./loader";
export * from "./transformer";
export * from "./env-expander";
+4
View File
@@ -0,0 +1,4 @@
import type { LoadedMcpServer, McpLoadResult } from "./types";
export declare function getSystemMcpServerNames(): Set<string>;
export declare function loadMcpConfigs(disabledMcps?: string[]): Promise<McpLoadResult>;
export declare function formatLoadedServersForToast(loadedServers: LoadedMcpServer[]): string;
+2
View File
@@ -0,0 +1,2 @@
import type { ClaudeCodeMcpServer, McpServerConfig } from "./types";
export declare function transformMcpServer(name: string, server: ClaudeCodeMcpServer): McpServerConfig;
+39
View File
@@ -0,0 +1,39 @@
export type McpScope = "user" | "project" | "local";
export interface ClaudeCodeMcpServer {
type?: "http" | "sse" | "stdio";
url?: string;
command?: string;
args?: string[];
env?: Record<string, string>;
headers?: Record<string, string>;
oauth?: {
clientId?: string;
scopes?: string[];
};
disabled?: boolean;
}
export interface ClaudeCodeMcpConfig {
mcpServers?: Record<string, ClaudeCodeMcpServer>;
}
export interface McpLocalConfig {
type: "local";
command: string[];
environment?: Record<string, string>;
enabled?: boolean;
}
export interface McpRemoteConfig {
type: "remote";
url: string;
headers?: Record<string, string>;
enabled?: boolean;
}
export type McpServerConfig = McpLocalConfig | McpRemoteConfig;
export interface LoadedMcpServer {
name: string;
scope: McpScope;
config: McpServerConfig;
}
export interface McpLoadResult {
servers: Record<string, McpServerConfig>;
loadedServers: LoadedMcpServer[];
}
@@ -0,0 +1,3 @@
import type { ClaudeCodeAgentConfig } from "../claude-code-agent-loader/types";
import type { LoadedPlugin } from "./types";
export declare function loadPluginAgents(plugins: LoadedPlugin[]): Record<string, ClaudeCodeAgentConfig>;
@@ -0,0 +1,3 @@
import type { CommandDefinition } from "../claude-code-command-loader/types";
import type { LoadedPlugin } from "./types";
export declare function loadPluginCommands(plugins: LoadedPlugin[]): Record<string, CommandDefinition>;
@@ -0,0 +1,2 @@
import type { PluginLoadResult, PluginLoaderOptions } from "./types";
export declare function discoverInstalledPlugins(options?: PluginLoaderOptions): PluginLoadResult;
@@ -0,0 +1,2 @@
import type { HooksConfig, LoadedPlugin } from "./types";
export declare function loadPluginHooksConfigs(plugins: LoadedPlugin[]): HooksConfig[];
+10
View File
@@ -0,0 +1,10 @@
export * from "./types";
export * from "./loader";
export * from "./discovery";
export * from "./plugin-path-resolver";
export * from "./command-loader";
export * from "./skill-loader";
export * from "./agent-loader";
export * from "./mcp-server-loader";
export * from "./hook-loader";
export type { PluginLoaderOptions, ClaudeSettings } from "./types";
+20
View File
@@ -0,0 +1,20 @@
import type { CommandDefinition } from "../claude-code-command-loader/types";
import type { McpServerConfig } from "../claude-code-mcp-loader/types";
import type { ClaudeCodeAgentConfig } from "../claude-code-agent-loader/types";
import type { HooksConfig, LoadedPlugin, PluginLoadError, PluginLoaderOptions } from "./types";
export { discoverInstalledPlugins } from "./discovery";
export { loadPluginCommands } from "./command-loader";
export { loadPluginSkillsAsCommands } from "./skill-loader";
export { loadPluginAgents } from "./agent-loader";
export { loadPluginMcpServers } from "./mcp-server-loader";
export { loadPluginHooksConfigs } from "./hook-loader";
export interface PluginComponentsResult {
commands: Record<string, CommandDefinition>;
skills: Record<string, CommandDefinition>;
agents: Record<string, ClaudeCodeAgentConfig>;
mcpServers: Record<string, McpServerConfig>;
hooksConfigs: HooksConfig[];
plugins: LoadedPlugin[];
errors: PluginLoadError[];
}
export declare function loadAllPluginComponents(options?: PluginLoaderOptions): Promise<PluginComponentsResult>;
@@ -0,0 +1,3 @@
import type { McpServerConfig } from "../claude-code-mcp-loader/types";
import type { LoadedPlugin } from "./types";
export declare function loadPluginMcpServers(plugins: LoadedPlugin[]): Promise<Record<string, McpServerConfig>>;
@@ -0,0 +1,2 @@
export declare function resolvePluginPath(path: string, pluginRoot: string): string;
export declare function resolvePluginPaths<T>(obj: T, pluginRoot: string): T;
@@ -0,0 +1,3 @@
import type { CommandDefinition } from "../claude-code-command-loader/types";
import type { LoadedPlugin } from "./types";
export declare function loadPluginSkillsAsCommands(plugins: LoadedPlugin[]): Record<string, CommandDefinition>;
+215
View File
@@ -0,0 +1,215 @@
/**
* Claude Code Plugin Types
*
* Type definitions for Claude Code plugin system compatibility.
* Based on https://code.claude.com/docs/en/plugins-reference
*/
export type PluginScope = "user" | "project" | "local" | "managed";
/**
* Plugin installation entry in installed_plugins.json
*/
export interface PluginInstallation {
scope: PluginScope;
installPath: string;
version: string;
installedAt: string;
lastUpdated: string;
gitCommitSha?: string;
isLocal?: boolean;
}
/**
* Installed plugins database v1 (legacy)
* plugins stored as direct objects
*/
export interface InstalledPluginsDatabaseV1 {
version: 1;
plugins: Record<string, PluginInstallation>;
}
/**
* Installed plugins database v2
* plugins stored as arrays keyed by plugin identifier
*/
export interface InstalledPluginsDatabaseV2 {
version: 2;
plugins: Record<string, PluginInstallation[]>;
}
/**
* Installed plugins database v3 entry (current Claude Code format)
* A flat array of plugin entries, each containing name and marketplace fields
* used to construct the plugin key as "name@marketplace".
*/
export interface InstalledPluginEntryV3 {
name: string;
marketplace: string;
scope: PluginScope;
version: string;
installPath: string;
lastUpdated: string;
gitCommitSha?: string;
}
/**
* Installed plugins database structure
* Located at ~/.claude/plugins/installed_plugins.json
*
* Supports three formats:
* - v1: { version: 1, plugins: Record<string, PluginInstallation> }
* - v2: { version: 2, plugins: Record<string, PluginInstallation[]> }
* - v3: InstalledPluginEntryV3[] (flat array, current Claude Code format)
*/
export type InstalledPluginsDatabase = InstalledPluginsDatabaseV1 | InstalledPluginsDatabaseV2 | InstalledPluginEntryV3[];
/**
* Plugin author information
*/
export interface PluginAuthor {
name?: string;
email?: string;
url?: string;
}
/**
* Plugin manifest (plugin.json)
* Located at <plugin_root>/.claude-plugin/plugin.json
*/
export interface PluginManifest {
name: string;
version?: string;
description?: string;
author?: PluginAuthor;
homepage?: string;
repository?: string;
license?: string;
keywords?: string[];
commands?: string | string[];
agents?: string | string[];
skills?: string | string[];
hooks?: string | HooksConfig;
mcpServers?: string | McpServersConfig;
lspServers?: string | LspServersConfig;
outputStyles?: string | string[];
}
/**
* Hooks configuration
*/
export type HookEntry = {
type: "command";
command?: string;
} | {
type: "prompt";
prompt?: string;
} | {
type: "agent";
agent?: string;
} | {
type: "http";
url: string;
headers?: Record<string, string>;
allowedEnvVars?: string[];
timeout?: number;
};
export interface HookMatcher {
matcher?: string;
hooks: HookEntry[];
}
export interface HooksConfig {
hooks?: {
PreToolUse?: HookMatcher[];
PostToolUse?: HookMatcher[];
PostToolUseFailure?: HookMatcher[];
PermissionRequest?: HookMatcher[];
UserPromptSubmit?: HookMatcher[];
Notification?: HookMatcher[];
Stop?: HookMatcher[];
SubagentStart?: HookMatcher[];
SubagentStop?: HookMatcher[];
SessionStart?: HookMatcher[];
SessionEnd?: HookMatcher[];
PreCompact?: HookMatcher[];
};
}
/**
* MCP servers configuration in plugin
*/
export interface PluginMcpServer {
command?: string;
args?: string[];
env?: Record<string, string>;
cwd?: string;
url?: string;
type?: "stdio" | "http" | "sse";
disabled?: boolean;
}
export interface McpServersConfig {
mcpServers?: Record<string, PluginMcpServer>;
}
/**
* LSP server configuration
*/
export interface LspServerConfig {
command: string;
args?: string[];
extensionToLanguage: Record<string, string>;
transport?: "stdio" | "socket";
env?: Record<string, string>;
initializationOptions?: Record<string, unknown>;
settings?: Record<string, unknown>;
workspaceFolder?: string;
startupTimeout?: number;
shutdownTimeout?: number;
restartOnCrash?: boolean;
maxRestarts?: number;
loggingConfig?: {
args?: string[];
env?: Record<string, string>;
};
}
export interface LspServersConfig {
[language: string]: LspServerConfig;
}
/**
* Loaded plugin with all resolved components
*/
export interface LoadedPlugin {
name: string;
version: string;
scope: PluginScope;
installPath: string;
manifest?: PluginManifest;
pluginKey: string;
commandsDir?: string;
agentsDir?: string;
skillsDir?: string;
hooksPath?: string;
mcpPath?: string;
lspPath?: string;
}
/**
* Plugin load result with all components
*/
export interface PluginLoadResult {
plugins: LoadedPlugin[];
errors: PluginLoadError[];
}
export interface PluginLoadError {
pluginKey: string;
installPath: string;
error: string;
}
/**
* Claude settings from ~/.claude/settings.json
*/
export interface ClaudeSettings {
enabledPlugins?: Record<string, boolean>;
[key: string]: unknown;
}
/**
* Plugin loader options
*/
export interface PluginLoaderOptions {
/**
* Override enabled plugins from oh-my-opencode config.
* Key format: "pluginName@marketplace" (e.g., "shell-scripting@claude-code-workflows")
* Value: true = enabled, false = disabled
*
* This takes precedence over ~/.claude/settings.json enabledPlugins
*/
enabledPluginsOverride?: Record<string, boolean>;
}
+1
View File
@@ -0,0 +1 @@
export * from "./state";
+10
View File
@@ -0,0 +1,10 @@
export declare const subagentSessions: Set<string>;
export declare const syncSubagentSessions: Set<string>;
export declare function setMainSession(id: string | undefined): void;
export declare function getMainSessionID(): string | undefined;
/** @internal For testing only */
export declare function _resetForTesting(): void;
export declare function setSessionAgent(sessionID: string, agent: string): void;
export declare function updateSessionAgent(sessionID: string, agent: string): void;
export declare function getSessionAgent(sessionID: string): string | undefined;
export declare function clearSessionAgent(sessionID: string): void;
+9
View File
@@ -0,0 +1,9 @@
import type { OhMyOpenCodeConfig } from "../../config/schema";
export declare function getSessionTaskDir(config: Partial<OhMyOpenCodeConfig>, sessionID: string): string;
export declare function listSessionTaskFiles(config: Partial<OhMyOpenCodeConfig>, sessionID: string): string[];
export declare function listAllSessionDirs(config: Partial<OhMyOpenCodeConfig>): string[];
export interface TaskLocation {
path: string;
sessionID: string;
}
export declare function findTaskAcrossSessions(config: Partial<OhMyOpenCodeConfig>, taskId: string): TaskLocation | null;
+14
View File
@@ -0,0 +1,14 @@
import type { z } from "zod";
import type { OhMyOpenCodeConfig } from "../../config/schema";
export declare function getTaskDir(config?: Partial<OhMyOpenCodeConfig>): string;
export declare function sanitizePathSegment(value: string): string;
export declare function resolveTaskListId(config?: Partial<OhMyOpenCodeConfig>): string;
export declare function ensureDir(dirPath: string): void;
export declare function readJsonSafe<T>(filePath: string, schema: z.ZodType<T>): T | null;
export declare function writeJsonAtomic(filePath: string, data: unknown): void;
export declare function generateTaskId(): string;
export declare function listTaskFiles(config?: Partial<OhMyOpenCodeConfig>): string[];
export declare function acquireLock(dirPath: string): {
acquired: boolean;
release: () => void;
};
+25
View File
@@ -0,0 +1,25 @@
import { z } from "zod";
export declare const TaskStatusSchema: z.ZodEnum<{
pending: "pending";
in_progress: "in_progress";
completed: "completed";
deleted: "deleted";
}>;
export type TaskStatus = z.infer<typeof TaskStatusSchema>;
export declare const TaskSchema: z.ZodObject<{
id: z.ZodString;
subject: z.ZodString;
description: z.ZodString;
status: z.ZodEnum<{
pending: "pending";
in_progress: "in_progress";
completed: "completed";
deleted: "deleted";
}>;
activeForm: z.ZodOptional<z.ZodString>;
blocks: z.ZodArray<z.ZodString>;
blockedBy: z.ZodArray<z.ZodString>;
owner: z.ZodOptional<z.ZodString>;
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
}, z.core.$strict>;
export type Task = z.infer<typeof TaskSchema>;
+11
View File
@@ -0,0 +1,11 @@
import type { PendingContext, RegisterContextOptions } from "./types";
export declare class ContextCollector {
private sessions;
register(sessionID: string, options: RegisterContextOptions): void;
getPending(sessionID: string): PendingContext;
consume(sessionID: string): PendingContext;
clear(sessionID: string): void;
hasPending(sessionID: string): boolean;
private sortEntries;
}
export declare const contextCollector: ContextCollector;
+3
View File
@@ -0,0 +1,3 @@
export { ContextCollector, contextCollector } from "./collector";
export { createContextInjectorMessagesTransformHook, } from "./injector";
export type { ContextSourceType, ContextPriority, ContextEntry, RegisterContextOptions, PendingContext, MessageContext, OutputParts, InjectionStrategy, } from "./types";
+39
View File
@@ -0,0 +1,39 @@
import type { ContextCollector } from "./collector";
import type { Message, Part } from "@opencode-ai/sdk";
interface OutputPart {
type: string;
text?: string;
[key: string]: unknown;
}
interface InjectionResult {
injected: boolean;
contextLength: number;
}
export declare function injectPendingContext(collector: ContextCollector, sessionID: string, parts: OutputPart[]): InjectionResult;
interface ChatMessageInput {
sessionID: string;
agent?: string;
model?: {
providerID: string;
modelID: string;
};
messageID?: string;
}
interface ChatMessageOutput {
message: Record<string, unknown>;
parts: OutputPart[];
}
export declare function createContextInjectorHook(collector: ContextCollector): {
"chat.message": (input: ChatMessageInput, output: ChatMessageOutput) => Promise<void>;
};
interface MessageWithParts {
info: Message;
parts: Part[];
}
type MessagesTransformHook = {
"experimental.chat.messages.transform"?: (input: Record<string, never>, output: {
messages: MessageWithParts[];
}) => Promise<void>;
};
export declare function createContextInjectorMessagesTransformHook(collector: ContextCollector): MessagesTransformHook;
export {};
+83
View File
@@ -0,0 +1,83 @@
/**
* Source identifier for context injection
* Each source registers context that will be merged and injected together
*/
export type ContextSourceType = "keyword-detector" | "rules-injector" | "directory-agents" | "directory-readme" | "custom";
/**
* Priority levels for context ordering
* Higher priority contexts appear first in the merged output
*/
export type ContextPriority = "critical" | "high" | "normal" | "low";
/**
* A single context entry registered by a source
*/
export interface ContextEntry {
/** Unique identifier for this entry within the source */
id: string;
/** The source that registered this context */
source: ContextSourceType;
/** The actual context content to inject */
content: string;
/** Priority for ordering (default: normal) */
priority: ContextPriority;
/** Monotonic order when registered */
registrationOrder: number;
/** Optional metadata for debugging/logging */
metadata?: Record<string, unknown>;
}
/**
* Options for registering context
*/
export interface RegisterContextOptions {
/** Unique ID for this context entry (used for deduplication) */
id: string;
/** Source identifier */
source: ContextSourceType;
/** The content to inject */
content: string;
/** Priority for ordering (default: normal) */
priority?: ContextPriority;
/** Optional metadata */
metadata?: Record<string, unknown>;
}
/**
* Result of getting pending context for a session
*/
export interface PendingContext {
/** Merged context string, ready for injection */
merged: string;
/** Individual entries that were merged */
entries: ContextEntry[];
/** Whether there's any content to inject */
hasContent: boolean;
}
/**
* Message context from the original user message
* Used when injecting to match the message format
*/
export interface MessageContext {
agent?: string;
model?: {
providerID?: string;
modelID?: string;
};
path?: {
cwd?: string;
root?: string;
};
tools?: Record<string, boolean>;
}
/**
* Output parts from chat.message hook
*/
export interface OutputParts {
parts: Array<{
type: string;
text?: string;
[key: string]: unknown;
}>;
}
/**
* Injection strategy
*/
export type InjectionStrategy = "prepend-parts" | "storage" | "auto";
+1
View File
@@ -0,0 +1 @@
export { OPENCODE_STORAGE, MESSAGE_STORAGE, PART_STORAGE } from "../../shared";
+4
View File
@@ -0,0 +1,4 @@
export { injectHookMessage, findNearestMessageWithFields, findFirstMessageWithAgent, findNearestMessageWithFieldsFromSDK, findFirstMessageWithAgentFromSDK, resolveMessageContext, } from "./injector";
export type { StoredMessage } from "./injector";
export type { MessageMeta, OriginalMessageContext, TextPart, ToolPermission } from "./types";
export { MESSAGE_STORAGE } from "./constants";
+69
View File
@@ -0,0 +1,69 @@
import type { PluginInput } from "@opencode-ai/plugin";
import type { OriginalMessageContext, ToolPermission } from "./types";
export interface StoredMessage {
agent?: string;
model?: {
providerID?: string;
modelID?: string;
variant?: string;
};
tools?: Record<string, ToolPermission>;
}
type OpencodeClient = PluginInput["client"];
/**
* Finds the nearest message with required fields using SDK (for beta/SQLite backend).
* Uses client.session.messages() to fetch message data from SQLite.
*/
export declare function findNearestMessageWithFieldsFromSDK(client: OpencodeClient, sessionID: string): Promise<StoredMessage | null>;
/**
* Finds the FIRST (oldest) message with agent field using SDK (for beta/SQLite backend).
*/
export declare function findFirstMessageWithAgentFromSDK(client: OpencodeClient, sessionID: string): Promise<string | null>;
/**
* Finds the nearest message with required fields (agent, model.providerID, model.modelID).
* Reads from JSON files - for stable (JSON) backend.
*
* **Version-gated behavior:**
* - On beta (SQLite backend): Returns null immediately (no JSON storage)
* - On stable (JSON backend): Reads from JSON files in messageDir
*
* @deprecated Use findNearestMessageWithFieldsFromSDK for beta/SQLite backend
*/
export declare function findNearestMessageWithFields(messageDir: string): StoredMessage | null;
/**
* Finds the FIRST (oldest) message in the session with agent field.
* Reads from JSON files - for stable (JSON) backend.
*
* **Version-gated behavior:**
* - On beta (SQLite backend): Returns null immediately (no JSON storage)
* - On stable (JSON backend): Reads from JSON files in messageDir
*
* @deprecated Use findFirstMessageWithAgentFromSDK for beta/SQLite backend
*/
export declare function findFirstMessageWithAgent(messageDir: string): string | null;
export declare function generateMessageId(): string;
export declare function generatePartId(): string;
/**
* Injects a hook message into the session storage.
*
* **Version-gated behavior:**
* - On beta (SQLite backend): Logs warning and skips injection (writes are invisible to SQLite)
* - On stable (JSON backend): Writes message and part JSON files
*
* Features degraded on beta:
* - Hook message injection (e.g., continuation prompts, context injection) won't persist
* - Atlas hook's injected messages won't be visible in SQLite backend
* - Todo continuation enforcer's injected prompts won't persist
* - Ralph loop's continuation prompts won't persist
*
* @param sessionID - Target session ID
* @param hookContent - Content to inject
* @param originalMessage - Context from the original message
* @returns true if injection succeeded, false otherwise
*/
export declare function injectHookMessage(sessionID: string, hookContent: string, originalMessage: OriginalMessageContext): boolean;
export declare function resolveMessageContext(sessionID: string, client: OpencodeClient, messageDir: string | null): Promise<{
prevMessage: StoredMessage | null;
firstMessageAgent: string | null;
}>;
export {};
+46
View File
@@ -0,0 +1,46 @@
export type ToolPermission = boolean | "allow" | "deny" | "ask";
export interface MessageMeta {
id: string;
sessionID: string;
role: "user" | "assistant";
time: {
created: number;
completed?: number;
};
agent?: string;
model?: {
providerID: string;
modelID: string;
variant?: string;
};
path?: {
cwd: string;
root: string;
};
tools?: Record<string, ToolPermission>;
}
export interface OriginalMessageContext {
agent?: string;
model?: {
providerID?: string;
modelID?: string;
variant?: string;
};
path?: {
cwd?: string;
root?: string;
};
tools?: Record<string, ToolPermission>;
}
export interface TextPart {
id: string;
type: "text";
text: string;
synthetic: boolean;
time: {
start: number;
end: number;
};
messageID: string;
sessionID: string;
}
+11
View File
@@ -0,0 +1,11 @@
export type OAuthCallbackResult = {
code: string;
state: string;
};
export type CallbackServer = {
port: number;
waitForCallback: () => Promise<OAuthCallbackResult>;
close: () => void;
};
export declare function findAvailablePort(startPort?: number): Promise<number>;
export declare function startCallbackServer(startPort?: number): Promise<CallbackServer>;
+34
View File
@@ -0,0 +1,34 @@
export type ClientRegistrationRequest = {
redirect_uris: string[];
client_name: string;
grant_types: ["authorization_code", "refresh_token"];
response_types: ["code"];
token_endpoint_auth_method: "none" | "client_secret_post";
};
export type ClientCredentials = {
clientId: string;
clientSecret?: string;
};
export type ClientRegistrationStorage = {
getClientRegistration: (serverIdentifier: string) => ClientCredentials | null;
setClientRegistration: (serverIdentifier: string, credentials: ClientCredentials) => void;
};
export type DynamicClientRegistrationOptions = {
registrationEndpoint?: string | null;
serverIdentifier?: string;
clientName: string;
redirectUris: string[];
tokenEndpointAuthMethod: "none" | "client_secret_post";
clientId?: string | null;
storage: ClientRegistrationStorage;
fetch?: DcrFetch;
};
export type DcrFetch = (input: string, init?: {
method?: string;
headers?: Record<string, string>;
body?: string;
}) => Promise<{
ok: boolean;
json: () => Promise<unknown>;
}>;
export declare function getOrRegisterClient(options: DynamicClientRegistrationOptions): Promise<ClientCredentials | null>;
+8
View File
@@ -0,0 +1,8 @@
export interface OAuthServerMetadata {
authorizationEndpoint: string;
tokenEndpoint: string;
registrationEndpoint?: string;
resource: string;
}
export declare function discoverOAuthServerMetadata(resource: string): Promise<OAuthServerMetadata>;
export declare function resetDiscoveryCache(): void;
+26
View File
@@ -0,0 +1,26 @@
export type OAuthCallbackResult = {
code: string;
state: string;
};
export declare function generateCodeVerifier(): string;
export declare function generateCodeChallenge(verifier: string): string;
export declare function buildAuthorizationUrl(authorizationEndpoint: string, options: {
clientId: string;
redirectUri: string;
codeChallenge: string;
state: string;
scopes?: string[];
resource?: string;
}): string;
export declare function startCallbackServer(port: number): Promise<OAuthCallbackResult>;
export declare function runAuthorizationCodeRedirect(options: {
authorizationEndpoint: string;
callbackPort: number;
clientId: string;
redirectUri: string;
scopes?: string[];
resource?: string;
}): Promise<{
code: string;
verifier: string;
}>;
+29
View File
@@ -0,0 +1,29 @@
import type { OAuthTokenData } from "./storage";
import type { OAuthServerMetadata } from "./discovery";
import type { ClientCredentials } from "./dcr";
import { buildAuthorizationUrl, generateCodeChallenge, generateCodeVerifier, startCallbackServer } from "./oauth-authorization-flow";
export type McpOAuthProviderOptions = {
serverUrl: string;
clientId?: string;
scopes?: string[];
};
export declare class McpOAuthProvider {
private readonly serverUrl;
private readonly configClientId;
private readonly scopes;
private storedCodeVerifier;
private storedClientInfo;
private callbackPort;
constructor(options: McpOAuthProviderOptions);
tokens(): OAuthTokenData | null;
saveTokens(tokenData: OAuthTokenData): boolean;
clientInformation(): ClientCredentials | null;
redirectUrl(): string;
saveCodeVerifier(verifier: string): void;
codeVerifier(): string | null;
redirectToAuthorization(metadata: OAuthServerMetadata): Promise<{
code: string;
}>;
login(): Promise<OAuthTokenData>;
}
export { generateCodeVerifier, generateCodeChallenge, buildAuthorizationUrl, startCallbackServer };
+2
View File
@@ -0,0 +1,2 @@
export declare function getResourceIndicator(url: string): string;
export declare function addResourceToParams(params: URLSearchParams, resource: string): void;
+6
View File
@@ -0,0 +1,6 @@
import { z } from "zod";
export declare const McpOauthSchema: z.ZodObject<{
clientId: z.ZodOptional<z.ZodString>;
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strip>;
export type McpOauth = z.infer<typeof McpOauthSchema>;
+8
View File
@@ -0,0 +1,8 @@
export interface StepUpInfo {
requiredScopes: string[];
error?: string;
errorDescription?: string;
}
export declare function parseWwwAuthenticate(header: string): StepUpInfo | null;
export declare function mergeScopes(existing: string[], required: string[]): string[];
export declare function isStepUpRequired(statusCode: number, headers: Record<string, string>): StepUpInfo | null;
+17
View File
@@ -0,0 +1,17 @@
export interface OAuthTokenData {
accessToken: string;
refreshToken?: string;
expiresAt?: number;
clientInfo?: {
clientId: string;
clientSecret?: string;
};
}
type TokenStore = Record<string, OAuthTokenData>;
export declare function getMcpOauthStoragePath(): string;
export declare function loadToken(serverHost: string, resource: string): OAuthTokenData | null;
export declare function saveToken(serverHost: string, resource: string, token: OAuthTokenData): boolean;
export declare function deleteToken(serverHost: string, resource: string): boolean;
export declare function listTokensByHost(serverHost: string): TokenStore;
export declare function listAllTokens(): TokenStore;
export {};
@@ -0,0 +1 @@
export declare function parseAllowedTools(allowedTools: string | string[] | undefined): string[] | undefined;
+6
View File
@@ -0,0 +1,6 @@
import type { SkillScope, LoadedSkill } from "./types";
import type { SkillMcpConfig } from "../skill-mcp-manager/types";
export declare function mapWithConcurrency<T, R>(items: T[], mapper: (item: T) => Promise<R>, concurrency: number): Promise<R[]>;
export declare function loadMcpJsonFromDirAsync(skillDir: string): Promise<SkillMcpConfig | undefined>;
export declare function loadSkillFromPathAsync(skillPath: string, resolvedPath: string, defaultName: string, scope: SkillScope): Promise<LoadedSkill | null>;
export declare function discoverSkillsInDirAsync(skillsDir: string): Promise<LoadedSkill[]>;
+2
View File
@@ -0,0 +1,2 @@
import type { LoadedSkill, SkillScope } from "./types";
export declare function discoverAllSkillsBlocking(dirs: string[], scopes: SkillScope[]): LoadedSkill[];
@@ -0,0 +1,7 @@
import type { SkillsConfig } from "../../config/schema";
import type { LoadedSkill } from "./types";
export declare function normalizePathForGlob(path: string): string;
export declare function discoverConfigSourceSkills(options: {
config: SkillsConfig | undefined;
configDir: string;
}): Promise<LoadedSkill[]>;
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,2 @@
import { type GitMasterConfig } from "../../config/schema";
export declare function injectGitMasterConfig(template: string, config?: GitMasterConfig): string;
+15
View File
@@ -0,0 +1,15 @@
export * from "./types";
export * from "./loader";
export * from "./merger";
export * from "./skill-content";
export * from "./skill-directory-loader";
export * from "./loaded-skill-from-path";
export * from "./skill-mcp-config";
export * from "./skill-deduplication";
export * from "./skill-definition-record";
export * from "./git-master-template-injection";
export * from "./skill-discovery";
export * from "./skill-resolution-options";
export * from "./loaded-skill-template-extractor";
export * from "./skill-template-resolver";
export * from "./config-source-discovery";
@@ -0,0 +1,9 @@
import type { SkillScope, LoadedSkill } from "./types";
export declare function loadSkillFromPath(options: {
skillPath: string;
resolvedPath: string;
defaultName: string;
scope: SkillScope;
namePrefix?: string;
}): Promise<LoadedSkill | null>;
export declare function inferSkillNameFromFileName(filePath: string): string;
@@ -0,0 +1,2 @@
import type { LoadedSkill } from "./types";
export declare function extractSkillTemplate(skill: LoadedSkill): string;
+19
View File
@@ -0,0 +1,19 @@
import type { CommandDefinition } from "../claude-code-command-loader/types";
import type { LoadedSkill } from "./types";
export declare function loadUserSkills(): Promise<Record<string, CommandDefinition>>;
export declare function loadProjectSkills(directory?: string): Promise<Record<string, CommandDefinition>>;
export declare function loadOpencodeGlobalSkills(): Promise<Record<string, CommandDefinition>>;
export declare function loadOpencodeProjectSkills(directory?: string): Promise<Record<string, CommandDefinition>>;
export interface DiscoverSkillsOptions {
includeClaudeCodePaths?: boolean;
directory?: string;
}
export declare function discoverAllSkills(directory?: string): Promise<LoadedSkill[]>;
export declare function discoverSkills(options?: DiscoverSkillsOptions): Promise<LoadedSkill[]>;
export declare function getSkillByName(name: string, options?: DiscoverSkillsOptions): Promise<LoadedSkill | undefined>;
export declare function discoverUserClaudeSkills(): Promise<LoadedSkill[]>;
export declare function discoverProjectClaudeSkills(directory?: string): Promise<LoadedSkill[]>;
export declare function discoverOpencodeGlobalSkills(): Promise<LoadedSkill[]>;
export declare function discoverOpencodeProjectSkills(directory?: string): Promise<LoadedSkill[]>;
export declare function discoverProjectAgentsSkills(directory?: string): Promise<LoadedSkill[]>;
export declare function discoverGlobalAgentsSkills(): Promise<LoadedSkill[]>;
+7
View File
@@ -0,0 +1,7 @@
import type { LoadedSkill } from "./types";
import type { SkillsConfig } from "../../config/schema";
import type { BuiltinSkill } from "../builtin-skills/types";
export interface MergeSkillsOptions {
configDir?: string;
}
export declare function mergeSkills(builtinSkills: BuiltinSkill[], config: SkillsConfig | undefined, configSourceSkills: LoadedSkill[], userClaudeSkills: LoadedSkill[], userOpencodeSkills: LoadedSkill[], projectClaudeSkills: LoadedSkill[], projectOpencodeSkills: LoadedSkill[], options?: MergeSkillsOptions): LoadedSkill[];
@@ -0,0 +1,3 @@
import type { BuiltinSkill } from "../../builtin-skills/types";
import type { LoadedSkill } from "../types";
export declare function builtinToLoadedSkill(builtin: BuiltinSkill): LoadedSkill;
@@ -0,0 +1,3 @@
import type { LoadedSkill } from "../types";
import type { SkillDefinition } from "../../../config/schema";
export declare function configEntryToLoadedSkill(name: string, entry: SkillDefinition, configDir?: string): LoadedSkill | null;

Some files were not shown because too many files have changed in this diff Show More