✨ feat(vscode): codex 命令统一组装,模型/思考级别可在设置面板配置
新增 cc/codexCommand.ts 作为 codex 调用的唯一出口(buildCodexArgs / buildCodexCommandString),把 exec-review / resume / interactive 三种形态 的 flag 拼装收敛到一处;模型与思考级别作为自由文本项写入全局设置并贯穿 webview 消息通道,空值则不传 -c,沿用 codex config.toml 默认。
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Single source of truth for assembling `codex` invocations.
|
||||||
|
*
|
||||||
|
* The review flow shells codex three different ways — headless
|
||||||
|
* `codex exec review` (spawned as an arg array), `codex resume <id>` and the
|
||||||
|
* interactive `codex '<prompt>'` TUI (both sent to a terminal). Building the
|
||||||
|
* flags in one place keeps `--dangerously-bypass-approvals-and-sandbox` and the
|
||||||
|
* optional model / reasoning-effort `-c` overrides from drifting apart.
|
||||||
|
*
|
||||||
|
* Model and reasoning effort are free-form: an empty value omits the override
|
||||||
|
* so codex falls back to its own `config.toml` default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface CodexCommandOpts {
|
||||||
|
/** 'exec-review' = `codex exec review ...`;'resume' = `codex resume <id>`;'interactive' = `codex '<prompt>'` */
|
||||||
|
mode: 'exec-review' | 'resume' | 'interactive'
|
||||||
|
/** resume 模式必填:codex thread id */
|
||||||
|
sessionId?: string
|
||||||
|
/** interactive / exec-review 模式的 prompt(positional)。调用方保证 interactive 模式下不含单引号。 */
|
||||||
|
prompt?: string
|
||||||
|
/** 空 = 不传 `-c model=`,用 codex 默认 */
|
||||||
|
model?: string
|
||||||
|
/** 空 = 不传 `-c model_reasoning_effort=`,用 codex 默认。合法值 minimal/low/medium/high/xhigh */
|
||||||
|
reasoningEffort?: string
|
||||||
|
/** exec-review 模式:追加 --json */
|
||||||
|
json?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const BYPASS_FLAG = '--dangerously-bypass-approvals-and-sandbox'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `-c key=value` overrides for model / reasoning effort. Blank values are
|
||||||
|
* dropped so codex keeps its config.toml default.
|
||||||
|
*/
|
||||||
|
function configOverrides(opts: CodexCommandOpts): string[] {
|
||||||
|
const parts: string[] = []
|
||||||
|
const model = opts.model?.trim()
|
||||||
|
if (model)
|
||||||
|
parts.push('-c', `model=${model}`)
|
||||||
|
const effort = opts.reasoningEffort?.trim()
|
||||||
|
if (effort)
|
||||||
|
parts.push('-c', `model_reasoning_effort=${effort}`)
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared assembly for both output shapes. `quotePrompt` wraps the positional
|
||||||
|
* prompt in single quotes for the shell-string form; the spawn-array form
|
||||||
|
* passes it raw (no shell, so no quoting).
|
||||||
|
*/
|
||||||
|
function buildParts(opts: CodexCommandOpts, quotePrompt: boolean): string[] {
|
||||||
|
const overrides = configOverrides(opts)
|
||||||
|
const prompt = opts.prompt
|
||||||
|
? (quotePrompt ? `'${opts.prompt}'` : opts.prompt)
|
||||||
|
: undefined
|
||||||
|
switch (opts.mode) {
|
||||||
|
case 'exec-review':
|
||||||
|
return [
|
||||||
|
'exec',
|
||||||
|
'review',
|
||||||
|
...overrides,
|
||||||
|
BYPASS_FLAG,
|
||||||
|
...(opts.json ? ['--json'] : []),
|
||||||
|
...(prompt !== undefined ? [prompt] : []),
|
||||||
|
]
|
||||||
|
case 'resume':
|
||||||
|
return ['resume', ...overrides, BYPASS_FLAG, ...(opts.sessionId ? [opts.sessionId] : [])]
|
||||||
|
case 'interactive':
|
||||||
|
return [...overrides, BYPASS_FLAG, ...(prompt !== undefined ? [prompt] : [])]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Argument array (without the leading `codex`) for `spawn('codex', args)`. */
|
||||||
|
export function buildCodexArgs(opts: CodexCommandOpts): string[] {
|
||||||
|
return buildParts(opts, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Full shell command string (with `codex` prefix) for `terminal.sendText`. */
|
||||||
|
export function buildCodexCommandString(opts: CodexCommandOpts): string {
|
||||||
|
return ['codex', ...buildParts(opts, true)].join(' ')
|
||||||
|
}
|
||||||
@@ -11,11 +11,16 @@
|
|||||||
|
|
||||||
import { spawn } from 'node:child_process'
|
import { spawn } from 'node:child_process'
|
||||||
import { logger } from '../logging/logger'
|
import { logger } from '../logging/logger'
|
||||||
|
import { buildCodexArgs } from './codexCommand'
|
||||||
|
|
||||||
export interface RunReviewOpts {
|
export interface RunReviewOpts {
|
||||||
workspaceRoot: string
|
workspaceRoot: string
|
||||||
/** Already-substituted prompt — placeholders must be resolved by the caller. */
|
/** Already-substituted prompt — placeholders must be resolved by the caller. */
|
||||||
prompt: string
|
prompt: string
|
||||||
|
/** 空 = 不传 `-c model=`,用 codex config.toml 默认。 */
|
||||||
|
model?: string
|
||||||
|
/** 空 = 不传 `-c model_reasoning_effort=`,用 codex 默认。 */
|
||||||
|
reasoningEffort?: string
|
||||||
/** Spawn timeout in milliseconds. Defaults to 5 minutes. */
|
/** Spawn timeout in milliseconds. Defaults to 5 minutes. */
|
||||||
timeoutMs?: number
|
timeoutMs?: number
|
||||||
/**
|
/**
|
||||||
@@ -34,15 +39,13 @@ const DEFAULT_TIMEOUT_MS = 300_000
|
|||||||
*/
|
*/
|
||||||
export async function runReview(opts: RunReviewOpts): Promise<void> {
|
export async function runReview(opts: RunReviewOpts): Promise<void> {
|
||||||
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
||||||
const args: string[] = [
|
const args = buildCodexArgs({
|
||||||
'exec',
|
mode: 'exec-review',
|
||||||
'-c',
|
prompt: opts.prompt,
|
||||||
'model_reasoning_effort=xhigh',
|
model: opts.model,
|
||||||
'review',
|
reasoningEffort: opts.reasoningEffort,
|
||||||
'--dangerously-bypass-approvals-and-sandbox',
|
json: true,
|
||||||
'--json',
|
})
|
||||||
opts.prompt,
|
|
||||||
]
|
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
let child: ReturnType<typeof spawn>
|
let child: ReturnType<typeof spawn>
|
||||||
|
|||||||
@@ -855,6 +855,8 @@ export class KanbanWebviewPanel {
|
|||||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||||
implTabPostCloseScript: s.implTabPostCloseScript,
|
implTabPostCloseScript: s.implTabPostCloseScript,
|
||||||
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
||||||
|
codexModel: s.codexModel,
|
||||||
|
codexReasoningEffort: s.codexReasoningEffort,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -918,6 +920,8 @@ export class KanbanWebviewPanel {
|
|||||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||||
implTabPostCloseScript: s.implTabPostCloseScript,
|
implTabPostCloseScript: s.implTabPostCloseScript,
|
||||||
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
||||||
|
codexModel: s.codexModel,
|
||||||
|
codexReasoningEffort: s.codexReasoningEffort,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as path from 'node:path'
|
|||||||
import { window, workspace } from 'vscode'
|
import { window, workspace } from 'vscode'
|
||||||
import { getToken } from '../../auth/secrets'
|
import { getToken } from '../../auth/secrets'
|
||||||
import { buildCcCommand } from '../../cc/ccCommand'
|
import { buildCcCommand } from '../../cc/ccCommand'
|
||||||
|
import { buildCodexCommandString } from '../../cc/codexCommand'
|
||||||
import { watchForNewCodexSession } from '../../cc/codexSessionWatcher'
|
import { watchForNewCodexSession } from '../../cc/codexSessionWatcher'
|
||||||
import { getBrainstormContinuePrompt, getImplementPlanPrompt } from '../../cc/prompts'
|
import { getBrainstormContinuePrompt, getImplementPlanPrompt } from '../../cc/prompts'
|
||||||
import { projectsDirFor, watchForNewSession } from '../../cc/sessionWatcher'
|
import { projectsDirFor, watchForNewSession } from '../../cc/sessionWatcher'
|
||||||
@@ -287,7 +288,13 @@ export async function handleResumeReviewSession(panel: KanbanWebviewPanel, sessi
|
|||||||
panel.reviewTerminals.set(sessionId, terminal)
|
panel.reviewTerminals.set(sessionId, terminal)
|
||||||
panel.trackSessionTerminal(terminal, issueNumber, 'review')
|
panel.trackSessionTerminal(terminal, issueNumber, 'review')
|
||||||
terminal.show(false)
|
terminal.show(false)
|
||||||
terminal.sendText(`codex resume -c model_reasoning_effort=xhigh --dangerously-bypass-approvals-and-sandbox ${sessionId}`)
|
const settings = getSettings(panel.context)
|
||||||
|
terminal.sendText(buildCodexCommandString({
|
||||||
|
mode: 'resume',
|
||||||
|
sessionId,
|
||||||
|
model: settings.codexModel,
|
||||||
|
reasoningEffort: settings.codexReasoningEffort,
|
||||||
|
}))
|
||||||
logger.add({
|
logger.add({
|
||||||
level: 'info',
|
level: 'info',
|
||||||
source: 'terminal',
|
source: 'terminal',
|
||||||
@@ -418,7 +425,13 @@ export async function triggerAutoReviewTab(panel: KanbanWebviewPanel, opts: {
|
|||||||
panel.reviewTerminals.set(existingSessionId, terminal)
|
panel.reviewTerminals.set(existingSessionId, terminal)
|
||||||
panel.trackSessionTerminal(terminal, opts.issueNumber, 'review')
|
panel.trackSessionTerminal(terminal, opts.issueNumber, 'review')
|
||||||
terminal.show(false)
|
terminal.show(false)
|
||||||
terminal.sendText(`codex resume -c model_reasoning_effort=xhigh --dangerously-bypass-approvals-and-sandbox ${existingSessionId}`)
|
const settings = getSettings(panel.context)
|
||||||
|
terminal.sendText(buildCodexCommandString({
|
||||||
|
mode: 'resume',
|
||||||
|
sessionId: existingSessionId,
|
||||||
|
model: settings.codexModel,
|
||||||
|
reasoningEffort: settings.codexReasoningEffort,
|
||||||
|
}))
|
||||||
// codex resume rebuilds the conversation from the jsonl rollout
|
// codex resume rebuilds the conversation from the jsonl rollout
|
||||||
// before the TUI accepts input — empirically much slower than the
|
// before the TUI accepts input — empirically much slower than the
|
||||||
// 250ms gap the reuse path uses. 8s is a conservative guess that
|
// 250ms gap the reuse path uses. 8s is a conservative guess that
|
||||||
@@ -494,7 +507,13 @@ export async function triggerAutoReviewTab(panel: KanbanWebviewPanel, opts: {
|
|||||||
// the terminal alive after the run so users can follow up with codex,
|
// the terminal alive after the run so users can follow up with codex,
|
||||||
// mirroring how claude implementation/brainstorm sessions stay open.
|
// mirroring how claude implementation/brainstorm sessions stay open.
|
||||||
// 提示词模板自带 /review 前缀,不在这里再拼。
|
// 提示词模板自带 /review 前缀,不在这里再拼。
|
||||||
const cmd = `codex -c model_reasoning_effort=xhigh --dangerously-bypass-approvals-and-sandbox '${opts.prompt}'`
|
const settings = getSettings(panel.context)
|
||||||
|
const cmd = buildCodexCommandString({
|
||||||
|
mode: 'interactive',
|
||||||
|
prompt: opts.prompt,
|
||||||
|
model: settings.codexModel,
|
||||||
|
reasoningEffort: settings.codexReasoningEffort,
|
||||||
|
})
|
||||||
terminal.sendText(cmd)
|
terminal.sendText(cmd)
|
||||||
logger.add({
|
logger.add({
|
||||||
level: 'info',
|
level: 'info',
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
@@ -61,6 +63,9 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
|||||||
const trimmedImplPost = payload.implTabPostCloseScript.trim()
|
const trimmedImplPost = payload.implTabPostCloseScript.trim()
|
||||||
// conflict-resolution profile: '' is meaningful (= DEFAULT_PROFILE_PATH).
|
// conflict-resolution profile: '' is meaningful (= DEFAULT_PROFILE_PATH).
|
||||||
const trimmedConflictProfile = payload.conflictResolutionProfilePath.trim()
|
const trimmedConflictProfile = payload.conflictResolutionProfilePath.trim()
|
||||||
|
// codex 模型 / 思考级别:空串有意义(= 用 codex 默认),只 trim。
|
||||||
|
const trimmedCodexModel = payload.codexModel.trim()
|
||||||
|
const trimmedCodexReasoningEffort = payload.codexReasoningEffort.trim()
|
||||||
const prev = getSettings(panel.context)
|
const prev = getSettings(panel.context)
|
||||||
// Capture the previous token *for this host* before overwriting it, so
|
// Capture the previous token *for this host* before overwriting it, so
|
||||||
// we can decide below whether the kanban needs a re-fetch. (Only host
|
// we can decide below whether the kanban needs a re-fetch. (Only host
|
||||||
@@ -91,6 +96,8 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
|||||||
implTabPreCreateScript: trimmedImplPre,
|
implTabPreCreateScript: trimmedImplPre,
|
||||||
implTabPostCloseScript: trimmedImplPost,
|
implTabPostCloseScript: trimmedImplPost,
|
||||||
conflictResolutionProfilePath: trimmedConflictProfile,
|
conflictResolutionProfilePath: trimmedConflictProfile,
|
||||||
|
codexModel: trimmedCodexModel,
|
||||||
|
codexReasoningEffort: trimmedCodexReasoningEffort,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -113,6 +120,8 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
|||||||
implTabPreCreateScript: trimmedImplPre,
|
implTabPreCreateScript: trimmedImplPre,
|
||||||
implTabPostCloseScript: trimmedImplPost,
|
implTabPostCloseScript: trimmedImplPost,
|
||||||
conflictResolutionProfilePath: trimmedConflictProfile,
|
conflictResolutionProfilePath: trimmedConflictProfile,
|
||||||
|
codexModel: trimmedCodexModel,
|
||||||
|
codexReasoningEffort: trimmedCodexReasoningEffort,
|
||||||
youtrackBaseUrl: trimmedYtBase,
|
youtrackBaseUrl: trimmedYtBase,
|
||||||
youtrackProjectShortName: trimmedYtProject,
|
youtrackProjectShortName: trimmedYtProject,
|
||||||
youtrackCloseCommand: trimmedYtClose,
|
youtrackCloseCommand: trimmedYtClose,
|
||||||
@@ -196,6 +205,8 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom
|
|||||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||||
implTabPostCloseScript: s.implTabPostCloseScript,
|
implTabPostCloseScript: s.implTabPostCloseScript,
|
||||||
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
||||||
|
codexModel: s.codexModel,
|
||||||
|
codexReasoningEffort: s.codexReasoningEffort,
|
||||||
youtrackBaseUrl: s.youtrackBaseUrl,
|
youtrackBaseUrl: s.youtrackBaseUrl,
|
||||||
youtrackProjectShortName: s.youtrackProjectShortName,
|
youtrackProjectShortName: s.youtrackProjectShortName,
|
||||||
youtrackCloseCommand: s.youtrackCloseCommand,
|
youtrackCloseCommand: s.youtrackCloseCommand,
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ export type ExtensionToWebview
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl?: string
|
youtrackBaseUrl?: string
|
||||||
youtrackProjectShortName?: string
|
youtrackProjectShortName?: string
|
||||||
youtrackCloseCommand?: string
|
youtrackCloseCommand?: string
|
||||||
@@ -129,6 +131,8 @@ export type WebviewToExtension
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
|
|||||||
@@ -169,6 +169,16 @@ export interface Settings {
|
|||||||
* (drag to 完成 when PR merge conflicts). Empty = DEFAULT_PROFILE_PATH.
|
* (drag to 完成 when PR merge conflicts). Empty = DEFAULT_PROFILE_PATH.
|
||||||
*/
|
*/
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
/**
|
||||||
|
* codex 审查会话使用的模型(`-c model=`)。空串 = 不传,用 codex
|
||||||
|
* `config.toml` 默认。
|
||||||
|
*/
|
||||||
|
codexModel: string
|
||||||
|
/**
|
||||||
|
* codex 审查会话的思考级别(`-c model_reasoning_effort=`)。空串 = 不传,
|
||||||
|
* 用 codex 默认。合法值 minimal/low/medium/high/xhigh。
|
||||||
|
*/
|
||||||
|
codexReasoningEffort: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SETTINGS_KEY = 'superpowers.settings'
|
export const SETTINGS_KEY = 'superpowers.settings'
|
||||||
@@ -194,6 +204,8 @@ function defaults(ctx: ExtensionContext): Settings {
|
|||||||
youtrackProjectShortName: '',
|
youtrackProjectShortName: '',
|
||||||
youtrackCloseCommand: '',
|
youtrackCloseCommand: '',
|
||||||
conflictResolutionProfilePath: '',
|
conflictResolutionProfilePath: '',
|
||||||
|
codexModel: '',
|
||||||
|
codexReasoningEffort: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +290,13 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
|||||||
const conflictResolutionProfilePath = typeof stored.conflictResolutionProfilePath === 'string'
|
const conflictResolutionProfilePath = typeof stored.conflictResolutionProfilePath === 'string'
|
||||||
? stored.conflictResolutionProfilePath
|
? stored.conflictResolutionProfilePath
|
||||||
: base.conflictResolutionProfilePath
|
: base.conflictResolutionProfilePath
|
||||||
|
// codex 模型 / 思考级别:'' 有意义(= 用 codex 默认),不强制回退。
|
||||||
|
const codexModel = typeof stored.codexModel === 'string'
|
||||||
|
? stored.codexModel
|
||||||
|
: base.codexModel
|
||||||
|
const codexReasoningEffort = typeof stored.codexReasoningEffort === 'string'
|
||||||
|
? stored.codexReasoningEffort
|
||||||
|
: base.codexReasoningEffort
|
||||||
|
|
||||||
return {
|
return {
|
||||||
webhookPort,
|
webhookPort,
|
||||||
@@ -299,6 +318,8 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
|||||||
youtrackProjectShortName,
|
youtrackProjectShortName,
|
||||||
youtrackCloseCommand,
|
youtrackCloseCommand,
|
||||||
conflictResolutionProfilePath,
|
conflictResolutionProfilePath,
|
||||||
|
codexModel,
|
||||||
|
codexReasoningEffort,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -453,6 +453,8 @@ export function App() {
|
|||||||
initialImplTabPreCreateScript={settings?.implTabPreCreateScript ?? ''}
|
initialImplTabPreCreateScript={settings?.implTabPreCreateScript ?? ''}
|
||||||
initialImplTabPostCloseScript={settings?.implTabPostCloseScript ?? ''}
|
initialImplTabPostCloseScript={settings?.implTabPostCloseScript ?? ''}
|
||||||
initialConflictResolutionProfilePath={settings?.conflictResolutionProfilePath ?? ''}
|
initialConflictResolutionProfilePath={settings?.conflictResolutionProfilePath ?? ''}
|
||||||
|
initialCodexModel={settings?.codexModel ?? ''}
|
||||||
|
initialCodexReasoningEffort={settings?.codexReasoningEffort ?? ''}
|
||||||
initialYoutrackBaseUrl={settings?.youtrackBaseUrl ?? ''}
|
initialYoutrackBaseUrl={settings?.youtrackBaseUrl ?? ''}
|
||||||
initialYoutrackProjectShortName={settings?.youtrackProjectShortName ?? ''}
|
initialYoutrackProjectShortName={settings?.youtrackProjectShortName ?? ''}
|
||||||
initialYoutrackCloseCommand={settings?.youtrackCloseCommand ?? ''}
|
initialYoutrackCloseCommand={settings?.youtrackCloseCommand ?? ''}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ interface SubmitValues {
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
@@ -72,6 +74,8 @@ export interface SettingsModalProps {
|
|||||||
initialImplTabPreCreateScript: string
|
initialImplTabPreCreateScript: string
|
||||||
initialImplTabPostCloseScript: string
|
initialImplTabPostCloseScript: string
|
||||||
initialConflictResolutionProfilePath: string
|
initialConflictResolutionProfilePath: string
|
||||||
|
initialCodexModel: string
|
||||||
|
initialCodexReasoningEffort: string
|
||||||
initialYoutrackBaseUrl: string
|
initialYoutrackBaseUrl: string
|
||||||
initialYoutrackProjectShortName: string
|
initialYoutrackProjectShortName: string
|
||||||
initialYoutrackCloseCommand: string
|
initialYoutrackCloseCommand: string
|
||||||
@@ -146,6 +150,8 @@ export function SettingsModal({
|
|||||||
initialImplTabPreCreateScript,
|
initialImplTabPreCreateScript,
|
||||||
initialImplTabPostCloseScript,
|
initialImplTabPostCloseScript,
|
||||||
initialConflictResolutionProfilePath,
|
initialConflictResolutionProfilePath,
|
||||||
|
initialCodexModel,
|
||||||
|
initialCodexReasoningEffort,
|
||||||
initialYoutrackBaseUrl,
|
initialYoutrackBaseUrl,
|
||||||
initialYoutrackProjectShortName,
|
initialYoutrackProjectShortName,
|
||||||
initialYoutrackCloseCommand,
|
initialYoutrackCloseCommand,
|
||||||
@@ -174,6 +180,8 @@ export function SettingsModal({
|
|||||||
const [implTabPreCreateScript, setImplTabPreCreateScript] = useState(initialImplTabPreCreateScript)
|
const [implTabPreCreateScript, setImplTabPreCreateScript] = useState(initialImplTabPreCreateScript)
|
||||||
const [implTabPostCloseScript, setImplTabPostCloseScript] = useState(initialImplTabPostCloseScript)
|
const [implTabPostCloseScript, setImplTabPostCloseScript] = useState(initialImplTabPostCloseScript)
|
||||||
const [conflictResolutionProfilePath, setConflictResolutionProfilePath] = useState(initialConflictResolutionProfilePath)
|
const [conflictResolutionProfilePath, setConflictResolutionProfilePath] = useState(initialConflictResolutionProfilePath)
|
||||||
|
const [codexModel, setCodexModel] = useState(initialCodexModel)
|
||||||
|
const [codexReasoningEffort, setCodexReasoningEffort] = useState(initialCodexReasoningEffort)
|
||||||
const [youtrackBaseUrl, setYoutrackBaseUrl] = useState(initialYoutrackBaseUrl)
|
const [youtrackBaseUrl, setYoutrackBaseUrl] = useState(initialYoutrackBaseUrl)
|
||||||
const [youtrackProjectShortName, setYoutrackProjectShortName] = useState(initialYoutrackProjectShortName)
|
const [youtrackProjectShortName, setYoutrackProjectShortName] = useState(initialYoutrackProjectShortName)
|
||||||
const [youtrackCloseCommand, setYoutrackCloseCommand] = useState(initialYoutrackCloseCommand)
|
const [youtrackCloseCommand, setYoutrackCloseCommand] = useState(initialYoutrackCloseCommand)
|
||||||
@@ -204,6 +212,8 @@ export function SettingsModal({
|
|||||||
setImplTabPreCreateScript(initialImplTabPreCreateScript)
|
setImplTabPreCreateScript(initialImplTabPreCreateScript)
|
||||||
setImplTabPostCloseScript(initialImplTabPostCloseScript)
|
setImplTabPostCloseScript(initialImplTabPostCloseScript)
|
||||||
setConflictResolutionProfilePath(initialConflictResolutionProfilePath)
|
setConflictResolutionProfilePath(initialConflictResolutionProfilePath)
|
||||||
|
setCodexModel(initialCodexModel)
|
||||||
|
setCodexReasoningEffort(initialCodexReasoningEffort)
|
||||||
setYoutrackBaseUrl(initialYoutrackBaseUrl)
|
setYoutrackBaseUrl(initialYoutrackBaseUrl)
|
||||||
setYoutrackProjectShortName(initialYoutrackProjectShortName)
|
setYoutrackProjectShortName(initialYoutrackProjectShortName)
|
||||||
setYoutrackCloseCommand(initialYoutrackCloseCommand)
|
setYoutrackCloseCommand(initialYoutrackCloseCommand)
|
||||||
@@ -287,6 +297,9 @@ export function SettingsModal({
|
|||||||
implTabPostCloseScript: implTabPostCloseScript.trim(),
|
implTabPostCloseScript: implTabPostCloseScript.trim(),
|
||||||
// 冲突解决 profile:空串 = 用 DEFAULT_PROFILE_PATH
|
// 冲突解决 profile:空串 = 用 DEFAULT_PROFILE_PATH
|
||||||
conflictResolutionProfilePath: conflictResolutionProfilePath.trim(),
|
conflictResolutionProfilePath: conflictResolutionProfilePath.trim(),
|
||||||
|
// codex 模型 / 思考级别:trim 后留空 = 用 codex config.toml 默认
|
||||||
|
codexModel: codexModel.trim(),
|
||||||
|
codexReasoningEffort: codexReasoningEffort.trim(),
|
||||||
youtrackBaseUrl: youtrackBaseUrl.trim(),
|
youtrackBaseUrl: youtrackBaseUrl.trim(),
|
||||||
youtrackProjectShortName: youtrackProjectShortName.trim(),
|
youtrackProjectShortName: youtrackProjectShortName.trim(),
|
||||||
youtrackCloseCommand: youtrackCloseCommand.trim(),
|
youtrackCloseCommand: youtrackCloseCommand.trim(),
|
||||||
@@ -463,6 +476,49 @@ export function SettingsModal({
|
|||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label="codex 模型"
|
||||||
|
hint={(
|
||||||
|
<>
|
||||||
|
审查会话使用的 codex 模型(
|
||||||
|
<code>-c model=</code>
|
||||||
|
)。留空则用 codex
|
||||||
|
{' '}
|
||||||
|
<code>config.toml</code>
|
||||||
|
{' '}
|
||||||
|
默认。
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={codexModel}
|
||||||
|
onChange={e => setCodexModel(e.target.value)}
|
||||||
|
placeholder="留空用 codex 默认(如 gpt-5.6-sol)"
|
||||||
|
className={inputClass}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label="codex 思考级别"
|
||||||
|
hint={(
|
||||||
|
<>
|
||||||
|
审查会话的
|
||||||
|
{' '}
|
||||||
|
<code>model_reasoning_effort</code>
|
||||||
|
;留空则用 codex 默认。
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={codexReasoningEffort}
|
||||||
|
onChange={e => setCodexReasoningEffort(e.target.value)}
|
||||||
|
placeholder="留空用 codex 默认(minimal/low/medium/high/xhigh)"
|
||||||
|
className={inputClass}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export interface SettingsValues {
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
@@ -67,6 +69,8 @@ export interface SettingsOverlayState {
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
@@ -281,6 +285,8 @@ export function useIssues(): UseIssuesResult {
|
|||||||
implTabPreCreateScript: values.implTabPreCreateScript,
|
implTabPreCreateScript: values.implTabPreCreateScript,
|
||||||
implTabPostCloseScript: values.implTabPostCloseScript,
|
implTabPostCloseScript: values.implTabPostCloseScript,
|
||||||
conflictResolutionProfilePath: values.conflictResolutionProfilePath,
|
conflictResolutionProfilePath: values.conflictResolutionProfilePath,
|
||||||
|
codexModel: values.codexModel,
|
||||||
|
codexReasoningEffort: values.codexReasoningEffort,
|
||||||
youtrackBaseUrl: values.youtrackBaseUrl,
|
youtrackBaseUrl: values.youtrackBaseUrl,
|
||||||
youtrackProjectShortName: values.youtrackProjectShortName,
|
youtrackProjectShortName: values.youtrackProjectShortName,
|
||||||
youtrackCloseCommand: values.youtrackCloseCommand,
|
youtrackCloseCommand: values.youtrackCloseCommand,
|
||||||
@@ -610,6 +616,8 @@ export function useIssues(): UseIssuesResult {
|
|||||||
implTabPreCreateScript: msg.implTabPreCreateScript,
|
implTabPreCreateScript: msg.implTabPreCreateScript,
|
||||||
implTabPostCloseScript: msg.implTabPostCloseScript,
|
implTabPostCloseScript: msg.implTabPostCloseScript,
|
||||||
conflictResolutionProfilePath: msg.conflictResolutionProfilePath,
|
conflictResolutionProfilePath: msg.conflictResolutionProfilePath,
|
||||||
|
codexModel: msg.codexModel,
|
||||||
|
codexReasoningEffort: msg.codexReasoningEffort,
|
||||||
youtrackBaseUrl: msg.youtrackBaseUrl ?? '',
|
youtrackBaseUrl: msg.youtrackBaseUrl ?? '',
|
||||||
youtrackProjectShortName: msg.youtrackProjectShortName ?? '',
|
youtrackProjectShortName: msg.youtrackProjectShortName ?? '',
|
||||||
youtrackCloseCommand: msg.youtrackCloseCommand ?? '',
|
youtrackCloseCommand: msg.youtrackCloseCommand ?? '',
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ export type ExtensionToWebview
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl?: string
|
youtrackBaseUrl?: string
|
||||||
youtrackProjectShortName?: string
|
youtrackProjectShortName?: string
|
||||||
youtrackCloseCommand?: string
|
youtrackCloseCommand?: string
|
||||||
@@ -144,6 +146,8 @@ export type WebviewToExtension
|
|||||||
implTabPreCreateScript: string
|
implTabPreCreateScript: string
|
||||||
implTabPostCloseScript: string
|
implTabPostCloseScript: string
|
||||||
conflictResolutionProfilePath: string
|
conflictResolutionProfilePath: string
|
||||||
|
codexModel: string
|
||||||
|
codexReasoningEffort: string
|
||||||
youtrackBaseUrl: string
|
youtrackBaseUrl: string
|
||||||
youtrackProjectShortName: string
|
youtrackProjectShortName: string
|
||||||
youtrackCloseCommand: string
|
youtrackCloseCommand: string
|
||||||
|
|||||||
Reference in New Issue
Block a user