Skip to content

feat: add Plan mode - #232

Merged
qiankunli merged 2 commits into
mainfrom
feat/plan-mode
Jul 31, 2026
Merged

feat: add Plan mode#232
qiankunli merged 2 commits into
mainfrom
feat/plan-mode

Conversation

@qiankunli

@qiankunli qiankunli commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Normalize Default and Plan modes across Codex collaboration mode and Claude
permission mode. Add the /plan command, Shift+Tab cycling, session persistence,
status projection, and busy-turn protection.

Update chat-tui to the merged mode-cycling protocol and cover both harness
adapters plus the TUI and store behavior.

Clarify mode-switching errors, restore Claude SDK defaults, validate Codex
mode values before catalog requests, and retain an active mode during
transient collaboration-mode catalog failures.

Add regression coverage and update chat-tui to the merged PR #71 revision.

Normalize Default and Plan modes across Codex collaboration mode and Claude
permission mode. Add the /plan command, Shift+Tab cycling, session persistence,
status projection, and busy-turn protection.

Update chat-tui to the merged mode-cycling protocol and cover both harness
adapters plus the TUI and store behavior.
Comment thread src/tui/protocol/index.ts Outdated
(candidate.id === "mode" || candidate.category === "mode"),
);
if (!option || option.type !== "select" || option.options.length === 0) {
throw new Error(`${target} does not support Plan mode`);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · seed-2.1-pro

The error message in modeOption says "does not support Plan mode", but this method is also called from cycleMode() (Shift+Tab), where the user isn't necessarily trying to switch to Plan mode — they might just be cycling through modes. The error message is misleading in that context. Consider using a more generic message like "does not support mode switching" or "has no configurable modes" to accurately reflect both callers.

ccr:fp=e2a7dbc144a2

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=minor — finding 成立;modeOption 同时服务 /plan 与 Shift+Tab,错误文案已改为不支持 mode switching,避免把通用模式切换误写成仅 Plan。

Comment thread src/harness/claude/adapter.ts Outdated
throw new Error("Cannot switch Claude mode while a turn is running");
}
if (rt.activeQuery) await rt.activeQuery.setPermissionMode(value);
rt.permissionMode = value;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · deepseek-v4-pro

When setConfig is called with value === "default", rt.permissionMode is set to "default" (a truthy string). This means permissionMode: "default" will be passed to the SDK options at line 870, unlike setModel and setEffort which treat "default" as undefined (omitting the option entirely). This inconsistency means a session that explicitly selects "default" mode may behave differently from a freshly opened session where permissionMode is undefined. Consider setting rt.permissionMode to undefined when value is "default", consistent with the pattern used in setModel and setEffort.

ccr:fp=21f3a0fc5d4f

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=minor — finding 成立;切回 default 后仍对活动 Query 显式调用 setPermissionMode(default),但运行时清除覆盖,使后续 Query 恢复 SDK 缺省值。

rt.peer.request("model/list", { limit: 200 }),
]);
const selected = codexModes(modeResult).find((candidate) => candidate.id === value);
if (!selected) throw new Error(`Unknown Codex mode: ${value}`);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · seed-2.1-pro

In setConfig for mode, the value validation happens after two parallel API calls. If the value is invalid (e.g., not "default" or "plan"), both API calls are wasted. The Claude adapter (src/harness/claude/adapter.ts:741-742) validates the value first before any API calls. Consider moving the validation before the Promise.all to avoid unnecessary network requests.

ccr:fp=961f4085c4aa

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=minor — finding 成立;default/plan 的输入校验已前移到任何 catalog 请求之前,并新增零请求回归测试。

Comment thread src/harness/codex/adapter.ts Outdated
const modes = await rt.peer
.request("collaborationMode/list", {})
.then(codexModes)
.catch(() => []);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · seed-2.1-pro

In getConfig, the collaborationMode/list API call uses .catch(() => []) which silently swallows errors. If rt.mode is already set (e.g., from a previous successful setConfig call) but the API temporarily fails, the mode option won't appear in the UI even though the mode is still active on the runtime. This creates an inconsistency where the mode is silently active but invisible to the user. Consider preserving the existing mode option from rt.mode when the API call fails, or at least logging the error.

ccr:fp=de64be33af36

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=minor — finding 成立;旧 app-server 首次探测失败仍隐藏不支持的能力,但已选择模式后遇到临时 catalog 失败会保留 Default/Plan 本地快照和当前值。

@qiankunli

Copy link
Copy Markdown
Collaborator Author

🤖 devloop code-review · origin/main..HEAD · f1dfd3334 · models: deepseek-v4-pro×43, seed-2.1-pro×43, seed-2.1-turbo×39 · cost: 390s · ccr v1.9.0

4 finding(s)(4 条已作为独立 review thread 发布)

Clarify mode-switching errors, restore Claude SDK defaults, validate Codex
mode values before catalog requests, and retain an active mode during
transient collaboration-mode catalog failures.

Add regression coverage and update chat-tui to the merged PR #71 revision.
@qiankunli
qiankunli merged commit 89a6b54 into main Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants