diff --git a/README.ja.md b/README.ja.md index a810d76..c403f70 100644 --- a/README.ja.md +++ b/README.ja.md @@ -717,8 +717,8 @@ Oh My OpenCode は以下の場所からフックを読み込んで実行しま 有効時(デフォルト)、Sisyphus はオプションの特殊エージェントを備えた強力なオーケストレーターを提供します: - **Sisyphus**: プライマリオーケストレーターエージェント (Claude Opus 4.5) -- **Builder-Sisyphus**: OhMyOpenCode 強化版のビルドエージェント(デフォルトで無効) -- **Planner-Sisyphus**: OhMyOpenCode 強化版のプランエージェント(デフォルトで有効) +- **Builder-Sisyphus**: OpenCode のデフォルトビルドエージェント(SDK 制限により名前変更、デフォルトで無効) +- **Planner-Sisyphus**: OpenCode のデフォルトプランエージェント(SDK 制限により名前変更、デフォルトで有効) **設定オプション:** @@ -779,8 +779,8 @@ Oh My OpenCode は以下の場所からフックを読み込んで実行しま | オプション | デフォルト | 説明 | | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `disabled` | `false` | `true` の場合、すべての Sisyphus オーケストレーションを無効化し、元の build/plan をプライマリとして復元します。 | -| `builder_enabled` | `false` | `true` の場合、Builder-Sisyphus エージェント(OhMyOpenCode 強化版ビルドモード)を有効化します。デフォルトの OpenCode ビルド体験を維持するため、デフォルトでは無効です。 | -| `planner_enabled` | `true` | `true` の場合、Planner-Sisyphus エージェント(OhMyOpenCode 強化版プランモード)を有効化します。デフォルトで有効です。 | +| `builder_enabled` | `false` | `true` の場合、Builder-Sisyphus エージェントを有効化します(OpenCode build と同じ、SDK 制限により名前変更)。デフォルトでは無効です。 | +| `planner_enabled` | `true` | `true` の場合、Planner-Sisyphus エージェントを有効化します(OpenCode plan と同じ、SDK 制限により名前変更)。デフォルトで有効です。 | | `replace_build` | `true` | `true` の場合、デフォルトのビルドエージェントをサブエージェントモードに降格させます。`false` に設定すると、Builder-Sisyphus とデフォルトのビルドの両方を利用できます。 | | `replace_plan` | `true` | `true` の場合、デフォルトのプランエージェントをサブエージェントモードに降格させます。`false` に設定すると、Planner-Sisyphus とデフォルトのプランの両方を利用できます。 | diff --git a/README.ko.md b/README.ko.md index d485cc7..d91f95f 100644 --- a/README.ko.md +++ b/README.ko.md @@ -711,8 +711,8 @@ Schema 자동 완성이 지원됩니다: 활성화 시 (기본값), Sisyphus는 옵션으로 선택 가능한 특화 에이전트들과 함께 강력한 오케스트레이터를 제공합니다: - **Sisyphus**: Primary 오케스트레이터 에이전트 (Claude Opus 4.5) -- **Builder-Sisyphus**: OhMyOpenCode 강화 버전 빌드 에이전트 (기본적으로 비활성화) -- **Planner-Sisyphus**: OhMyOpenCode 강화 버전 플랜 에이전트 (기본적으로 활성화) +- **Builder-Sisyphus**: OpenCode 기본 빌드 에이전트 (SDK 제한으로 이름만 변경, 기본적으로 비활성화) +- **Planner-Sisyphus**: OpenCode 기본 플랜 에이전트 (SDK 제한으로 이름만 변경, 기본적으로 활성화) **설정 옵션:** @@ -773,8 +773,8 @@ Schema 자동 완성이 지원됩니다: | 옵션 | 기본값 | 설명 | | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `disabled` | `false` | `true`면 모든 Sisyphus 오케스트레이션을 비활성화하고 원래 build/plan을 primary로 복원합니다. | -| `builder_enabled` | `false` | `true`면 Builder-Sisyphus 에이전트 (OhMyOpenCode 강화 빌드 모드)를 활성화합니다. 기본 OpenCode 빌드 경험을 보존하기 위해 기본적으로 비활성화되어 있습니다. | -| `planner_enabled` | `true` | `true`면 Planner-Sisyphus 에이전트 (OhMyOpenCode 강화 플랜 모드)를 활성화합니다. 기본적으로 활성화되어 있습니다. | +| `builder_enabled` | `false` | `true`면 Builder-Sisyphus 에이전트를 활성화합니다 (OpenCode build와 동일, SDK 제한으로 이름만 변경). 기본적으로 비활성화되어 있습니다. | +| `planner_enabled` | `true` | `true`면 Planner-Sisyphus 에이전트를 활성화합니다 (OpenCode plan과 동일, SDK 제한으로 이름만 변경). 기본적으로 활성화되어 있습니다. | | `replace_build` | `true` | `true`면 기본 빌드 에이전트를 subagent 모드로 강등시킵니다. `false`로 설정하면 Builder-Sisyphus와 기본 빌드를 모두 사용할 수 있습니다. | | `replace_plan` | `true` | `true`면 기본 플랜 에이전트를 subagent 모드로 강등시킵니다. `false`로 설정하면 Planner-Sisyphus와 기본 플랜을 모두 사용할 수 있습니다. | diff --git a/README.md b/README.md index 8a9c056..0564e46 100644 --- a/README.md +++ b/README.md @@ -783,8 +783,8 @@ Available agents: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, ` When enabled (default), Sisyphus provides a powerful orchestrator with optional specialized agents: - **Sisyphus**: Primary orchestrator agent (Claude Opus 4.5) -- **Builder-Sisyphus**: Optional build agent with OhMyOpenCode enhancements (disabled by default) -- **Planner-Sisyphus**: Plan agent with OhMyOpenCode enhancements (enabled by default) +- **Builder-Sisyphus**: OpenCode's default build agent, renamed due to SDK limitations (disabled by default) +- **Planner-Sisyphus**: OpenCode's default plan agent, renamed due to SDK limitations (enabled by default) **Configuration Options:** @@ -845,8 +845,8 @@ You can also customize Sisyphus agents like other agents: | Option | Default | Description | | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `disabled` | `false` | When `true`, disables all Sisyphus orchestration and restores original build/plan as primary. | -| `builder_enabled` | `false` | When `true`, enables Builder-Sisyphus agent (OhMyOpenCode enhanced build mode). Disabled by default to preserve default OpenCode build experience. | -| `planner_enabled` | `true` | When `true`, enables Planner-Sisyphus agent (OhMyOpenCode enhanced plan mode). Enabled by default. | +| `builder_enabled` | `false` | When `true`, enables Builder-Sisyphus agent (same as OpenCode build, renamed due to SDK limitations). Disabled by default. | +| `planner_enabled` | `true` | When `true`, enables Planner-Sisyphus agent (same as OpenCode plan, renamed due to SDK limitations). Enabled by default. | | `replace_build` | `true` | When `true`, demotes default build agent to subagent mode. Set to `false` to keep both Builder-Sisyphus and default build available. | | `replace_plan` | `true` | When `true`, demotes default plan agent to subagent mode. Set to `false` to keep both Planner-Sisyphus and default plan available. | diff --git a/README.zh-cn.md b/README.zh-cn.md index 4ad6da9..f97535b 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -717,8 +717,8 @@ Agent 爽了,你自然也爽。但我还想直接让你爽。 默认开启。Sisyphus 提供一个强力的编排器,带可选的专门 Agent: - **Sisyphus**:主编排 Agent(Claude Opus 4.5) -- **Builder-Sisyphus**:OhMyOpenCode 增强版构建 Agent(默认禁用) -- **Planner-Sisyphus**:OhMyOpenCode 增强版计划 Agent(默认启用) +- **Builder-Sisyphus**:OpenCode 默认构建 Agent(因 SDK 限制仅改名,默认禁用) +- **Planner-Sisyphus**:OpenCode 默认计划 Agent(因 SDK 限制仅改名,默认启用) **配置选项:** @@ -779,8 +779,8 @@ Sisyphus Agent 也能自定义: | 选项 | 默认值 | 说明 | | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `disabled` | `false` | 设为 `true` 就禁用所有 Sisyphus 编排,恢复原来的 build/plan。 | -| `builder_enabled` | `false` | 设为 `true` 就启用 Builder-Sisyphus Agent(OhMyOpenCode 增强构建模式)。为了保留默认 OpenCode 构建体验,默认禁用。 | -| `planner_enabled` | `true` | 设为 `true` 就启用 Planner-Sisyphus Agent(OhMyOpenCode 增强计划模式)。默认启用。 | +| `builder_enabled` | `false` | 设为 `true` 就启用 Builder-Sisyphus Agent(与 OpenCode build 相同,因 SDK 限制仅改名)。默认禁用。 | +| `planner_enabled` | `true` | 设为 `true` 就启用 Planner-Sisyphus Agent(与 OpenCode plan 相同,因 SDK 限制仅改名)。默认启用。 | | `replace_build` | `true` | 设为 `true` 就把默认构建 Agent 降级为子 Agent 模式。设为 `false` 可以同时保留 Builder-Sisyphus 和默认构建。 | | `replace_plan` | `true` | 设为 `true` 就把默认计划 Agent 降级为子 Agent 模式。设为 `false` 可以同时保留 Planner-Sisyphus 和默认计划。 | diff --git a/src/cli/run/events.test.ts b/src/cli/run/events.test.ts index 91421a9..f84d032 100644 --- a/src/cli/run/events.test.ts +++ b/src/cli/run/events.test.ts @@ -16,13 +16,15 @@ async function* toAsyncIterable(items: T[]): AsyncIterable { } describe("createEventState", () => { - it("creates initial state with mainSessionIdle false and empty lastOutput", () => { + it("creates initial state with correct defaults", () => { // #given / #when const state = createEventState() // #then expect(state.mainSessionIdle).toBe(false) expect(state.lastOutput).toBe("") + expect(state.lastPartText).toBe("") + expect(state.currentTool).toBe(null) }) }) @@ -73,6 +75,8 @@ describe("event handling", () => { const state: EventState = { mainSessionIdle: true, lastOutput: "", + lastPartText: "", + currentTool: null, } const payload: EventPayload = { diff --git a/src/cli/run/events.ts b/src/cli/run/events.ts index 2cd4a94..9f2ed8f 100644 --- a/src/cli/run/events.ts +++ b/src/cli/run/events.ts @@ -1,20 +1,28 @@ +import pc from "picocolors" import type { RunContext, EventPayload, SessionIdleProps, SessionStatusProps, MessageUpdatedProps, + MessagePartUpdatedProps, + ToolExecuteProps, + ToolResultProps, } from "./types" export interface EventState { mainSessionIdle: boolean lastOutput: string + lastPartText: string + currentTool: string | null } export function createEventState(): EventState { return { mainSessionIdle: false, lastOutput: "", + lastPartText: "", + currentTool: null, } } @@ -32,7 +40,10 @@ export async function processEvents( handleSessionIdle(ctx, payload, state) handleSessionStatus(ctx, payload, state) + handleMessagePartUpdated(ctx, payload, state) handleMessageUpdated(ctx, payload, state) + handleToolExecute(ctx, payload, state) + handleToolResult(ctx, payload, state) } catch {} } } @@ -63,6 +74,29 @@ function handleSessionStatus( } } +function handleMessagePartUpdated( + ctx: RunContext, + payload: EventPayload, + state: EventState +): void { + if (payload.type !== "message.part.updated") return + + const props = payload.properties as MessagePartUpdatedProps | undefined + if (props?.info?.sessionID !== ctx.sessionID) return + if (props?.info?.role !== "assistant") return + + const part = props.part + if (!part) return + + if (part.type === "text" && part.text) { + const newText = part.text.slice(state.lastPartText.length) + if (newText) { + process.stdout.write(newText) + } + state.lastPartText = part.text + } +} + function handleMessageUpdated( ctx: RunContext, payload: EventPayload, @@ -77,9 +111,66 @@ function handleMessageUpdated( const content = props.content if (!content || content === state.lastOutput) return - const newContent = content.slice(state.lastOutput.length) - if (newContent) { - process.stdout.write(newContent) + if (state.lastPartText.length === 0) { + const newContent = content.slice(state.lastOutput.length) + if (newContent) { + process.stdout.write(newContent) + } } state.lastOutput = content } + +function handleToolExecute( + ctx: RunContext, + payload: EventPayload, + state: EventState +): void { + if (payload.type !== "tool.execute") return + + const props = payload.properties as ToolExecuteProps | undefined + if (props?.sessionID !== ctx.sessionID) return + + const toolName = props?.name || "unknown" + state.currentTool = toolName + + let inputPreview = "" + if (props?.input) { + const input = props.input + if (input.command) { + inputPreview = ` ${pc.dim(String(input.command).slice(0, 60))}` + } else if (input.pattern) { + inputPreview = ` ${pc.dim(String(input.pattern).slice(0, 40))}` + } else if (input.filePath) { + inputPreview = ` ${pc.dim(String(input.filePath))}` + } else if (input.query) { + inputPreview = ` ${pc.dim(String(input.query).slice(0, 40))}` + } + } + + process.stdout.write(`\n${pc.cyan("⚡")} ${pc.bold(toolName)}${inputPreview}\n`) +} + +function handleToolResult( + ctx: RunContext, + payload: EventPayload, + state: EventState +): void { + if (payload.type !== "tool.result") return + + const props = payload.properties as ToolResultProps | undefined + if (props?.sessionID !== ctx.sessionID) return + + const output = props?.output || "" + const maxLen = 200 + const preview = output.length > maxLen + ? output.slice(0, maxLen) + "..." + : output + + if (preview.trim()) { + const lines = preview.split("\n").slice(0, 3) + process.stdout.write(pc.dim(` └─ ${lines.join("\n ")}\n`)) + } + + state.currentTool = null + state.lastPartText = "" +} diff --git a/src/cli/run/types.ts b/src/cli/run/types.ts index cac95a2..c1218f9 100644 --- a/src/cli/run/types.ts +++ b/src/cli/run/types.ts @@ -47,3 +47,25 @@ export interface MessageUpdatedProps { info?: { sessionID?: string; role?: string } content?: string } + +export interface MessagePartUpdatedProps { + info?: { sessionID?: string; role?: string } + part?: { + type?: string + text?: string + name?: string + input?: unknown + } +} + +export interface ToolExecuteProps { + sessionID?: string + name?: string + input?: Record +} + +export interface ToolResultProps { + sessionID?: string + name?: string + output?: string +}