ai-cli-mcp 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,276 @@
1
+ name: Watch Session PRs
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '17 0 * * *'
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ issues: write
11
+
12
+ jobs:
13
+ watch-codex-fork:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Check Codex fork PR and update issue
17
+ uses: actions/github-script@v7
18
+ env:
19
+ UPSTREAM_OWNER: openai
20
+ UPSTREAM_REPO: codex
21
+ UPSTREAM_PR_NUMBER: '13537'
22
+ TARGET_ISSUE_NUMBER: '7'
23
+ COMMENT_MARKER: '<!-- codex-fork-pr-13537-status -->'
24
+ READY_LABEL: '[ready]'
25
+ STOP_LABEL: '[stop]'
26
+ with:
27
+ github-token: ${{ secrets.GITHUB_TOKEN }}
28
+ script: |
29
+ const owner = process.env.UPSTREAM_OWNER;
30
+ const repo = process.env.UPSTREAM_REPO;
31
+ const pull_number = Number(process.env.UPSTREAM_PR_NUMBER);
32
+ const issue_number = Number(process.env.TARGET_ISSUE_NUMBER);
33
+ const marker = process.env.COMMENT_MARKER;
34
+ const readyLabel = process.env.READY_LABEL;
35
+ const stopLabel = process.env.STOP_LABEL;
36
+
37
+ const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number });
38
+ const desiredLabel = pr.merged_at ? readyLabel : stopLabel;
39
+ const staleLabel = pr.merged_at ? stopLabel : readyLabel;
40
+ const { data: issue } = await github.rest.issues.get({
41
+ owner: context.repo.owner,
42
+ repo: context.repo.repo,
43
+ issue_number,
44
+ });
45
+
46
+ if (!issue.labels.some((label) => label.name === desiredLabel)) {
47
+ await github.rest.issues.addLabels({
48
+ owner: context.repo.owner,
49
+ repo: context.repo.repo,
50
+ issue_number,
51
+ labels: [desiredLabel],
52
+ });
53
+ core.info(`Added label ${desiredLabel} to issue #${issue_number}.`);
54
+ }
55
+
56
+ if (issue.labels.some((label) => label.name === staleLabel)) {
57
+ try {
58
+ await github.rest.issues.removeLabel({
59
+ owner: context.repo.owner,
60
+ repo: context.repo.repo,
61
+ issue_number,
62
+ name: staleLabel,
63
+ });
64
+ core.info(`Removed label ${staleLabel} from issue #${issue_number}.`);
65
+ } catch (error) {
66
+ core.warning(`Failed to remove label ${staleLabel}: ${error.message}`);
67
+ }
68
+ }
69
+
70
+ core.summary
71
+ .addHeading('Codex PR status')
72
+ .addTable([
73
+ [
74
+ { data: 'Field', header: true },
75
+ { data: 'Value', header: true },
76
+ ],
77
+ ['PR', `${owner}/${repo}#${pr.number}`],
78
+ ['Title', pr.title],
79
+ ['State', pr.state],
80
+ ['Merged', pr.merged_at ? 'yes' : 'no'],
81
+ ['Merged at', pr.merged_at ?? 'not merged'],
82
+ ['Updated at', pr.updated_at],
83
+ ['URL', pr.html_url],
84
+ ]);
85
+
86
+ const comments = await github.paginate(github.rest.issues.listComments, {
87
+ owner: context.repo.owner,
88
+ repo: context.repo.repo,
89
+ issue_number,
90
+ per_page: 100,
91
+ });
92
+
93
+ const existingComment = comments.find((comment) => comment.body?.includes(marker));
94
+ const body = [
95
+ marker,
96
+ 'upstream PR `openai/codex#13537` status watcher',
97
+ '',
98
+ `- PR: ${pr.html_url}`,
99
+ `- State: ${pr.state}`,
100
+ `- Merged: ${pr.merged_at ? 'yes' : 'no'}`,
101
+ `- Merged at: ${pr.merged_at ?? 'not merged'}`,
102
+ `- Updated at: ${pr.updated_at}`,
103
+ '',
104
+ pr.merged_at
105
+ ? 'This PR has been merged. `codex exec --fork <SESSION_ID> [PROMPT]` should become available once the change lands in the installed Codex version.'
106
+ : 'This PR is not merged yet. Non-interactive forking is still not available in released Codex builds.',
107
+ 'A new session ID is expected when using `--fork`; `resume` itself continues the same session.',
108
+ ].join('\n');
109
+
110
+ if (existingComment?.body === body) {
111
+ core.info(`Issue #${issue_number} status comment is already up to date.`);
112
+ await core.summary.addRaw(`No update needed: ${existingComment.html_url}\n`).write();
113
+ return;
114
+ }
115
+
116
+ if (existingComment) {
117
+ const { data: comment } = await github.rest.issues.updateComment({
118
+ owner: context.repo.owner,
119
+ repo: context.repo.repo,
120
+ comment_id: existingComment.id,
121
+ body,
122
+ });
123
+ core.info(`Updated issue #${issue_number}: ${comment.html_url}`);
124
+ await core.summary.addRaw(`Updated status comment: ${comment.html_url}\n`).write();
125
+ return;
126
+ }
127
+
128
+ const { data: comment } = await github.rest.issues.createComment({
129
+ owner: context.repo.owner,
130
+ repo: context.repo.repo,
131
+ issue_number,
132
+ body,
133
+ });
134
+ core.info(`Created issue #${issue_number} status comment: ${comment.html_url}`);
135
+ await core.summary.addRaw(`Created status comment: ${comment.html_url}\n`).write();
136
+
137
+ watch-gemini-session-work:
138
+ runs-on: ubuntu-latest
139
+ steps:
140
+ - name: Check Gemini session PRs and update issue
141
+ uses: actions/github-script@v7
142
+ env:
143
+ UPSTREAM_OWNER: google-gemini
144
+ UPSTREAM_REPO: gemini-cli
145
+ TARGET_ISSUE_NUMBER: '16'
146
+ COMMENT_MARKER: '<!-- gemini-session-pr-status -->'
147
+ WATCH_PR_NUMBERS: '17921,19629,19994'
148
+ READY_PR_NUMBERS: '17921,19629'
149
+ READY_LABEL: '[ready]'
150
+ STOP_LABEL: '[stop]'
151
+ with:
152
+ github-token: ${{ secrets.GITHUB_TOKEN }}
153
+ script: |
154
+ const owner = process.env.UPSTREAM_OWNER;
155
+ const repo = process.env.UPSTREAM_REPO;
156
+ const issue_number = Number(process.env.TARGET_ISSUE_NUMBER);
157
+ const marker = process.env.COMMENT_MARKER;
158
+ const pullNumbers = process.env.WATCH_PR_NUMBERS.split(',').map((value) => Number(value.trim()));
159
+ const readyPullNumbers = process.env.READY_PR_NUMBERS.split(',').map((value) => Number(value.trim()));
160
+ const readyLabel = process.env.READY_LABEL;
161
+ const stopLabel = process.env.STOP_LABEL;
162
+
163
+ const pulls = await Promise.all(
164
+ pullNumbers.map(async (pull_number) => {
165
+ const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number });
166
+ return pr;
167
+ }),
168
+ );
169
+ const isReady = pulls.some((pr) => readyPullNumbers.includes(pr.number) && Boolean(pr.merged_at));
170
+ const desiredLabel = isReady ? readyLabel : stopLabel;
171
+ const staleLabel = isReady ? stopLabel : readyLabel;
172
+
173
+ const { data: issue } = await github.rest.issues.get({
174
+ owner: context.repo.owner,
175
+ repo: context.repo.repo,
176
+ issue_number,
177
+ });
178
+
179
+ if (!issue.labels.some((label) => label.name === desiredLabel)) {
180
+ await github.rest.issues.addLabels({
181
+ owner: context.repo.owner,
182
+ repo: context.repo.repo,
183
+ issue_number,
184
+ labels: [desiredLabel],
185
+ });
186
+ core.info(`Added label ${desiredLabel} to issue #${issue_number}.`);
187
+ }
188
+
189
+ if (issue.labels.some((label) => label.name === staleLabel)) {
190
+ try {
191
+ await github.rest.issues.removeLabel({
192
+ owner: context.repo.owner,
193
+ repo: context.repo.repo,
194
+ issue_number,
195
+ name: staleLabel,
196
+ });
197
+ core.info(`Removed label ${staleLabel} from issue #${issue_number}.`);
198
+ } catch (error) {
199
+ core.warning(`Failed to remove label ${staleLabel}: ${error.message}`);
200
+ }
201
+ }
202
+
203
+ const lines = pulls.flatMap((pr) => [
204
+ `- \`${owner}/${repo}#${pr.number}\` ${pr.title}`,
205
+ ` - State: ${pr.state}`,
206
+ ` - Merged: ${pr.merged_at ? 'yes' : 'no'}`,
207
+ ` - Merged at: ${pr.merged_at ?? 'not merged'}`,
208
+ ` - Updated at: ${pr.updated_at}`,
209
+ ` - URL: ${pr.html_url}`,
210
+ ]);
211
+
212
+ core.summary
213
+ .addHeading('Gemini PR status')
214
+ .addTable([
215
+ [
216
+ { data: 'PR', header: true },
217
+ { data: 'State', header: true },
218
+ { data: 'Merged', header: true },
219
+ { data: 'Updated at', header: true },
220
+ ],
221
+ ...pulls.map((pr) => [
222
+ `${owner}/${repo}#${pr.number}`,
223
+ pr.state,
224
+ pr.merged_at ? 'yes' : 'no',
225
+ pr.updated_at,
226
+ ]),
227
+ ]);
228
+
229
+ const comments = await github.paginate(github.rest.issues.listComments, {
230
+ owner: context.repo.owner,
231
+ repo: context.repo.repo,
232
+ issue_number,
233
+ per_page: 100,
234
+ });
235
+
236
+ const existingComment = comments.find((comment) => comment.body?.includes(marker));
237
+ const body = [
238
+ marker,
239
+ 'upstream Gemini CLI session-related PR status watcher',
240
+ '',
241
+ 'Tracked PRs:',
242
+ ...lines,
243
+ '',
244
+ 'Interpretation:',
245
+ '- `#19994` is a fix for resumed session ID stats mismatch, not a fork/new-session feature.',
246
+ '- `#17921` is the `/new` command proposal for starting a fresh session while preserving the previous one.',
247
+ '- `#19629` is the `--session-id` proposal for explicit session UUID control.',
248
+ '- Even if these land, that still does not automatically imply Codex-style headless fork semantics from `--resume` alone.',
249
+ ].join('\n');
250
+
251
+ if (existingComment?.body === body) {
252
+ core.info(`Issue #${issue_number} Gemini status comment is already up to date.`);
253
+ await core.summary.addRaw(`No update needed: ${existingComment.html_url}\n`).write();
254
+ return;
255
+ }
256
+
257
+ if (existingComment) {
258
+ const { data: comment } = await github.rest.issues.updateComment({
259
+ owner: context.repo.owner,
260
+ repo: context.repo.repo,
261
+ comment_id: existingComment.id,
262
+ body,
263
+ });
264
+ core.info(`Updated issue #${issue_number}: ${comment.html_url}`);
265
+ await core.summary.addRaw(`Updated status comment: ${comment.html_url}\n`).write();
266
+ return;
267
+ }
268
+
269
+ const { data: comment } = await github.rest.issues.createComment({
270
+ owner: context.repo.owner,
271
+ repo: context.repo.repo,
272
+ issue_number,
273
+ body,
274
+ });
275
+ core.info(`Created issue #${issue_number} status comment: ${comment.html_url}`);
276
+ await core.summary.addRaw(`Created status comment: ${comment.html_url}\n`).write();
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [2.11.0](https://github.com/mkXultra/ai-cli-mcp/compare/v2.10.0...v2.11.0) (2026-03-08)
2
+
3
+
4
+ ### Features
5
+
6
+ * ClaudeモデルへのReasoning Effort(--effort)サポートを追加 ([ac6b8cc](https://github.com/mkXultra/ai-cli-mcp/commit/ac6b8cc47a06a2d207258e85cc21b1d4ce83e5b2))
7
+
1
8
  # [2.10.0](https://github.com/mkXultra/ai-cli-mcp/compare/v2.9.1...v2.10.0) (2026-03-07)
2
9
 
3
10
 
package/README.ja.md CHANGED
@@ -131,11 +131,11 @@ Claude CLI、Codex CLI、またはGemini CLIを使用してプロンプトを実
131
131
  - `prompt_file` (string, 任意): プロンプトを含むファイルへのパス。`prompt` または `prompt_file` のいずれかが必須です。絶対パス、または `workFolder` からの相対パスが指定可能です。
132
132
  - `workFolder` (string, 必須): CLIを実行する作業ディレクトリ。絶対パスである必要があります。
133
133
  - **モデル (Models):**
134
- - **Ultra エイリアス:** `claude-ultra`, `codex-ultra` (自動的に high-reasoning に設定), `gemini-ultra`
134
+ - **Ultra エイリアス:** `claude-ultra` (自動的に high effort に設定), `codex-ultra` (自動的に xhigh reasoning に設定), `gemini-ultra`
135
135
  - Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
136
136
  - Codex: `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
137
137
  - Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
138
- - `reasoning_effort` (string, 任意): Codex専用。`model_reasoning_effort` を設定します(許容値: "low", "medium", "high", "xhigh")。
138
+ - `reasoning_effort` (string, 任意): Claude と Codex の推論制御。Claude では `--effort` を使います(許容値: "low", "medium", "high")。Codex では `model_reasoning_effort` を使います(許容値: "low", "medium", "high", "xhigh")。
139
139
  - `session_id` (string, 任意): 以前のセッションを再開するためのセッションID。対応モデル: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview。
140
140
 
141
141
  ### `wait`
package/README.md CHANGED
@@ -130,11 +130,11 @@ Executes a prompt using Claude CLI, Codex CLI, or Gemini CLI. The appropriate CL
130
130
  - `prompt_file` (string, optional): Path to a file containing the prompt. Either `prompt` or `prompt_file` is required. Can be absolute path or relative to `workFolder`.
131
131
  - `workFolder` (string, required): The working directory for the CLI execution. Must be an absolute path.
132
132
  **Models:**
133
- - **Ultra Aliases:** `claude-ultra`, `codex-ultra` (defaults to high-reasoning), `gemini-ultra`
133
+ - **Ultra Aliases:** `claude-ultra` (defaults to high effort), `codex-ultra` (defaults to xhigh reasoning), `gemini-ultra`
134
134
  - Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
135
135
  - Codex: `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
136
136
  - Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
137
- - `reasoning_effort` (string, optional): Codex only. Sets `model_reasoning_effort` (allowed: "low", "medium", "high", "xhigh").
137
+ - `reasoning_effort` (string, optional): Reasoning control for Claude and Codex. Claude uses `--effort` (allowed: "low", "medium", "high"). Codex uses `model_reasoning_effort` (allowed: "low", "medium", "high", "xhigh").
138
138
  - `session_id` (string, optional): Optional session ID to resume a previous session. Supported for: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview.
139
139
 
140
140
  ### `wait`
@@ -58,12 +58,17 @@ describe('cli-builder', () => {
58
58
  expect(getReasoningEffort('gpt-5.2', 'medium')).toBe('medium');
59
59
  expect(getReasoningEffort('gpt-5.2', 'high')).toBe('high');
60
60
  expect(getReasoningEffort('gpt-5.2', 'xhigh')).toBe('xhigh');
61
+ expect(getReasoningEffort('sonnet', 'high')).toBe('high');
62
+ expect(getReasoningEffort('', 'low')).toBe('low');
61
63
  });
62
64
  it('should throw for invalid reasoning effort value', () => {
63
65
  expect(() => getReasoningEffort('gpt-5.2', 'ultra')).toThrow('Invalid reasoning_effort: ultra. Allowed values: low, medium, high, xhigh.');
64
66
  });
65
- it('should throw for non-codex models', () => {
66
- expect(() => getReasoningEffort('sonnet', 'high')).toThrow('reasoning_effort is only supported for Codex models (gpt-*).');
67
+ it('should reject xhigh for claude models', () => {
68
+ expect(() => getReasoningEffort('sonnet', 'xhigh')).toThrow('Claude reasoning_effort supports only low, medium, high.');
69
+ });
70
+ it('should throw for unsupported model families', () => {
71
+ expect(() => getReasoningEffort('gemini-2.5-pro', 'high')).toThrow('reasoning_effort is only supported for Claude and Codex models.');
67
72
  });
68
73
  });
69
74
  describe('buildCliCommand', () => {
@@ -173,6 +178,48 @@ describe('cli-builder', () => {
173
178
  expect(cmd.resolvedModel).toBe('opus');
174
179
  expect(cmd.args).toContain('opus');
175
180
  });
181
+ it('should resolve claude-ultra and default to high effort', () => {
182
+ const cmd = buildCliCommand({
183
+ prompt: 'test',
184
+ workFolder: '/tmp',
185
+ model: 'claude-ultra',
186
+ cliPaths: DEFAULT_CLI_PATHS,
187
+ });
188
+ expect(cmd.args).toContain('--effort');
189
+ expect(cmd.args).toContain('high');
190
+ });
191
+ it('should build claude command with reasoning_effort using --effort', () => {
192
+ const cmd = buildCliCommand({
193
+ prompt: 'test',
194
+ workFolder: '/tmp',
195
+ model: 'sonnet',
196
+ reasoning_effort: 'medium',
197
+ cliPaths: DEFAULT_CLI_PATHS,
198
+ });
199
+ expect(cmd.args).toContain('--effort');
200
+ expect(cmd.args).toContain('medium');
201
+ });
202
+ it('should reject xhigh reasoning_effort for claude', () => {
203
+ expect(() => buildCliCommand({
204
+ prompt: 'test',
205
+ workFolder: '/tmp',
206
+ model: 'sonnet',
207
+ reasoning_effort: 'xhigh',
208
+ cliPaths: DEFAULT_CLI_PATHS,
209
+ })).toThrow('Claude reasoning_effort supports only low, medium, high.');
210
+ });
211
+ it('should allow overriding reasoning_effort for claude-ultra', () => {
212
+ const cmd = buildCliCommand({
213
+ prompt: 'test',
214
+ workFolder: '/tmp',
215
+ model: 'claude-ultra',
216
+ reasoning_effort: 'low',
217
+ cliPaths: DEFAULT_CLI_PATHS,
218
+ });
219
+ expect(cmd.args).toContain('--effort');
220
+ expect(cmd.args).toContain('low');
221
+ expect(cmd.args).not.toContain('high');
222
+ });
176
223
  });
177
224
  describe('codex agent', () => {
178
225
  it('should build codex command', () => {
@@ -251,7 +251,7 @@ describe('Argument Validation Tests', () => {
251
251
  }
252
252
  })).rejects.toThrow(/reasoning_effort/i);
253
253
  });
254
- it('should reject reasoning_effort for non-codex models', async () => {
254
+ it('should reject reasoning_effort for unsupported model families', async () => {
255
255
  await setupServer();
256
256
  const handler = handlers.get('callTool');
257
257
  await expect(handler({
@@ -260,7 +260,7 @@ describe('Argument Validation Tests', () => {
260
260
  arguments: {
261
261
  prompt: 'test',
262
262
  workFolder: '/tmp',
263
- model: 'sonnet',
263
+ model: 'gemini-2.5-pro',
264
264
  reasoning_effort: 'low'
265
265
  }
266
266
  }
@@ -7,6 +7,16 @@ export const MODEL_ALIASES = {
7
7
  'gemini-ultra': 'gemini-3.1-pro-preview'
8
8
  };
9
9
  export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
10
+ const CLAUDE_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
11
+ function getAgentForModel(model) {
12
+ if (model.startsWith('gpt-')) {
13
+ return 'codex';
14
+ }
15
+ if (model.startsWith('gemini')) {
16
+ return 'gemini';
17
+ }
18
+ return 'claude';
19
+ }
10
20
  /**
11
21
  * Resolves model aliases to their full model names
12
22
  * @param model - The model name or alias to resolve
@@ -32,8 +42,12 @@ export function getReasoningEffort(model, rawValue) {
32
42
  if (!ALLOWED_REASONING_EFFORTS.has(normalized)) {
33
43
  throw new Error(`Invalid reasoning_effort: ${rawValue}. Allowed values: low, medium, high, xhigh.`);
34
44
  }
35
- if (!model.startsWith('gpt-')) {
36
- throw new Error('reasoning_effort is only supported for Codex models (gpt-*).');
45
+ const agent = getAgentForModel(model);
46
+ if (agent === 'gemini') {
47
+ throw new Error('reasoning_effort is only supported for Claude and Codex models.');
48
+ }
49
+ if (agent === 'claude' && !CLAUDE_REASONING_EFFORTS.has(normalized)) {
50
+ throw new Error('Claude reasoning_effort supports only low, medium, high.');
37
51
  }
38
52
  return normalized;
39
53
  }
@@ -83,23 +97,18 @@ export function buildCliCommand(options) {
83
97
  // Resolve model
84
98
  const rawModel = options.model || '';
85
99
  const resolvedModel = resolveModelAlias(rawModel);
86
- // Special handling for codex-ultra: default to high reasoning effort if not specified
100
+ const agent = getAgentForModel(resolvedModel);
101
+ // Special handling for ultra aliases: default to higher reasoning if not specified
87
102
  let reasoningEffortArg = options.reasoning_effort;
88
- if (rawModel === 'codex-ultra' && !reasoningEffortArg) {
89
- reasoningEffortArg = 'xhigh';
103
+ if (!reasoningEffortArg) {
104
+ if (rawModel === 'codex-ultra') {
105
+ reasoningEffortArg = 'xhigh';
106
+ }
107
+ else if (rawModel === 'claude-ultra') {
108
+ reasoningEffortArg = 'high';
109
+ }
90
110
  }
91
111
  const reasoningEffort = getReasoningEffort(resolvedModel, reasoningEffortArg);
92
- // Determine agent
93
- let agent;
94
- if (resolvedModel.startsWith('gpt-')) {
95
- agent = 'codex';
96
- }
97
- else if (resolvedModel.startsWith('gemini')) {
98
- agent = 'gemini';
99
- }
100
- else {
101
- agent = 'claude';
102
- }
103
112
  // Build CLI path and args
104
113
  let cliPath;
105
114
  let args;
@@ -136,6 +145,9 @@ export function buildCliCommand(options) {
136
145
  if (options.session_id && typeof options.session_id === 'string') {
137
146
  args.push('-r', options.session_id, '--fork-session');
138
147
  }
148
+ if (reasoningEffort) {
149
+ args.push('--effort', reasoningEffort);
150
+ }
139
151
  args.push('-p', prompt);
140
152
  if (resolvedModel) {
141
153
  args.push('--model', resolvedModel);
package/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ Options:
40
40
  --prompt Prompt string (mutually exclusive with --prompt_file)
41
41
  --prompt_file Path to a file containing the prompt
42
42
  --session_id Session ID to resume
43
- --reasoning_effort Codex only: low, medium, high, xhigh
43
+ --reasoning_effort Claude/Codex: Claude=low|medium|high, Codex=low|medium|high|xhigh
44
44
  --help Show this help message
45
45
 
46
46
  Raw CLI output goes to stdout. Use cli.run.parse to parse the output:
package/dist/server.js CHANGED
@@ -144,11 +144,11 @@ export class ClaudeCodeServer {
144
144
  },
145
145
  model: {
146
146
  type: 'string',
147
- description: 'The model to use. Aliases: "claude-ultra", "codex-ultra" (auto high-reasoning), "gemini-ultra". Standard: "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
147
+ description: 'The model to use. Aliases: "claude-ultra" (auto high effort), "codex-ultra" (auto xhigh reasoning), "gemini-ultra". Standard: "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
148
148
  },
149
149
  reasoning_effort: {
150
150
  type: 'string',
151
- description: 'Codex only. Sets model_reasoning_effort. Allowed: "low", "medium", "high", "xhigh".',
151
+ description: 'Reasoning control for Claude and Codex. Claude uses --effort with "low", "medium", "high". Codex uses model_reasoning_effort with "low", "medium", "high", "xhigh".',
152
152
  },
153
153
  session_id: {
154
154
  type: 'string',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cli-mcp",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "mcpName": "io.github.mkXultra/ai-cli-mcp",
5
5
  "description": "MCP server for AI CLI tools (Claude, Codex, and Gemini) with background process management",
6
6
  "author": "mkXultra",
@@ -76,6 +76,8 @@ describe('cli-builder', () => {
76
76
  expect(getReasoningEffort('gpt-5.2', 'medium')).toBe('medium');
77
77
  expect(getReasoningEffort('gpt-5.2', 'high')).toBe('high');
78
78
  expect(getReasoningEffort('gpt-5.2', 'xhigh')).toBe('xhigh');
79
+ expect(getReasoningEffort('sonnet', 'high')).toBe('high');
80
+ expect(getReasoningEffort('', 'low')).toBe('low');
79
81
  });
80
82
 
81
83
  it('should throw for invalid reasoning effort value', () => {
@@ -84,9 +86,15 @@ describe('cli-builder', () => {
84
86
  );
85
87
  });
86
88
 
87
- it('should throw for non-codex models', () => {
88
- expect(() => getReasoningEffort('sonnet', 'high')).toThrow(
89
- 'reasoning_effort is only supported for Codex models (gpt-*).'
89
+ it('should reject xhigh for claude models', () => {
90
+ expect(() => getReasoningEffort('sonnet', 'xhigh')).toThrow(
91
+ 'Claude reasoning_effort supports only low, medium, high.'
92
+ );
93
+ });
94
+
95
+ it('should throw for unsupported model families', () => {
96
+ expect(() => getReasoningEffort('gemini-2.5-pro', 'high')).toThrow(
97
+ 'reasoning_effort is only supported for Claude and Codex models.'
90
98
  );
91
99
  });
92
100
  });
@@ -224,6 +232,57 @@ describe('cli-builder', () => {
224
232
  expect(cmd.resolvedModel).toBe('opus');
225
233
  expect(cmd.args).toContain('opus');
226
234
  });
235
+
236
+ it('should resolve claude-ultra and default to high effort', () => {
237
+ const cmd = buildCliCommand({
238
+ prompt: 'test',
239
+ workFolder: '/tmp',
240
+ model: 'claude-ultra',
241
+ cliPaths: DEFAULT_CLI_PATHS,
242
+ });
243
+
244
+ expect(cmd.args).toContain('--effort');
245
+ expect(cmd.args).toContain('high');
246
+ });
247
+
248
+ it('should build claude command with reasoning_effort using --effort', () => {
249
+ const cmd = buildCliCommand({
250
+ prompt: 'test',
251
+ workFolder: '/tmp',
252
+ model: 'sonnet',
253
+ reasoning_effort: 'medium',
254
+ cliPaths: DEFAULT_CLI_PATHS,
255
+ });
256
+
257
+ expect(cmd.args).toContain('--effort');
258
+ expect(cmd.args).toContain('medium');
259
+ });
260
+
261
+ it('should reject xhigh reasoning_effort for claude', () => {
262
+ expect(() =>
263
+ buildCliCommand({
264
+ prompt: 'test',
265
+ workFolder: '/tmp',
266
+ model: 'sonnet',
267
+ reasoning_effort: 'xhigh',
268
+ cliPaths: DEFAULT_CLI_PATHS,
269
+ })
270
+ ).toThrow('Claude reasoning_effort supports only low, medium, high.');
271
+ });
272
+
273
+ it('should allow overriding reasoning_effort for claude-ultra', () => {
274
+ const cmd = buildCliCommand({
275
+ prompt: 'test',
276
+ workFolder: '/tmp',
277
+ model: 'claude-ultra',
278
+ reasoning_effort: 'low',
279
+ cliPaths: DEFAULT_CLI_PATHS,
280
+ });
281
+
282
+ expect(cmd.args).toContain('--effort');
283
+ expect(cmd.args).toContain('low');
284
+ expect(cmd.args).not.toContain('high');
285
+ });
227
286
  });
228
287
 
229
288
  describe('codex agent', () => {
@@ -301,7 +301,7 @@ describe('Argument Validation Tests', () => {
301
301
  ).rejects.toThrow(/reasoning_effort/i);
302
302
  });
303
303
 
304
- it('should reject reasoning_effort for non-codex models', async () => {
304
+ it('should reject reasoning_effort for unsupported model families', async () => {
305
305
  await setupServer();
306
306
  const handler = handlers.get('callTool')!;
307
307
 
@@ -312,7 +312,7 @@ describe('Argument Validation Tests', () => {
312
312
  arguments: {
313
313
  prompt: 'test',
314
314
  workFolder: '/tmp',
315
- model: 'sonnet',
315
+ model: 'gemini-2.5-pro',
316
316
  reasoning_effort: 'low'
317
317
  }
318
318
  }
@@ -9,6 +9,17 @@ export const MODEL_ALIASES: Record<string, string> = {
9
9
  };
10
10
 
11
11
  export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
12
+ const CLAUDE_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
13
+
14
+ function getAgentForModel(model: string): 'codex' | 'claude' | 'gemini' {
15
+ if (model.startsWith('gpt-')) {
16
+ return 'codex';
17
+ }
18
+ if (model.startsWith('gemini')) {
19
+ return 'gemini';
20
+ }
21
+ return 'claude';
22
+ }
12
23
 
13
24
  /**
14
25
  * Resolves model aliases to their full model names
@@ -38,9 +49,15 @@ export function getReasoningEffort(model: string, rawValue: unknown): string {
38
49
  `Invalid reasoning_effort: ${rawValue}. Allowed values: low, medium, high, xhigh.`
39
50
  );
40
51
  }
41
- if (!model.startsWith('gpt-')) {
52
+ const agent = getAgentForModel(model);
53
+ if (agent === 'gemini') {
54
+ throw new Error(
55
+ 'reasoning_effort is only supported for Claude and Codex models.'
56
+ );
57
+ }
58
+ if (agent === 'claude' && !CLAUDE_REASONING_EFFORTS.has(normalized)) {
42
59
  throw new Error(
43
- 'reasoning_effort is only supported for Codex models (gpt-*).'
60
+ 'Claude reasoning_effort supports only low, medium, high.'
44
61
  );
45
62
  }
46
63
  return normalized;
@@ -117,25 +134,20 @@ export function buildCliCommand(options: BuildCliCommandOptions): CliCommand {
117
134
  // Resolve model
118
135
  const rawModel = options.model || '';
119
136
  const resolvedModel = resolveModelAlias(rawModel);
137
+ const agent = getAgentForModel(resolvedModel);
120
138
 
121
- // Special handling for codex-ultra: default to high reasoning effort if not specified
139
+ // Special handling for ultra aliases: default to higher reasoning if not specified
122
140
  let reasoningEffortArg: string | undefined = options.reasoning_effort;
123
- if (rawModel === 'codex-ultra' && !reasoningEffortArg) {
124
- reasoningEffortArg = 'xhigh';
141
+ if (!reasoningEffortArg) {
142
+ if (rawModel === 'codex-ultra') {
143
+ reasoningEffortArg = 'xhigh';
144
+ } else if (rawModel === 'claude-ultra') {
145
+ reasoningEffortArg = 'high';
146
+ }
125
147
  }
126
148
 
127
149
  const reasoningEffort = getReasoningEffort(resolvedModel, reasoningEffortArg);
128
150
 
129
- // Determine agent
130
- let agent: 'codex' | 'claude' | 'gemini';
131
- if (resolvedModel.startsWith('gpt-')) {
132
- agent = 'codex';
133
- } else if (resolvedModel.startsWith('gemini')) {
134
- agent = 'gemini';
135
- } else {
136
- agent = 'claude';
137
- }
138
-
139
151
  // Build CLI path and args
140
152
  let cliPath: string;
141
153
  let args: string[];
@@ -180,6 +192,10 @@ export function buildCliCommand(options: BuildCliCommandOptions): CliCommand {
180
192
  args.push('-r', options.session_id, '--fork-session');
181
193
  }
182
194
 
195
+ if (reasoningEffort) {
196
+ args.push('--effort', reasoningEffort);
197
+ }
198
+
183
199
  args.push('-p', prompt);
184
200
  if (resolvedModel) {
185
201
  args.push('--model', resolvedModel);
package/src/cli.ts CHANGED
@@ -40,7 +40,7 @@ Options:
40
40
  --prompt Prompt string (mutually exclusive with --prompt_file)
41
41
  --prompt_file Path to a file containing the prompt
42
42
  --session_id Session ID to resume
43
- --reasoning_effort Codex only: low, medium, high, xhigh
43
+ --reasoning_effort Claude/Codex: Claude=low|medium|high, Codex=low|medium|high|xhigh
44
44
  --help Show this help message
45
45
 
46
46
  Raw CLI output goes to stdout. Use cli.run.parse to parse the output:
package/src/server.ts CHANGED
@@ -190,11 +190,11 @@ export class ClaudeCodeServer {
190
190
  },
191
191
  model: {
192
192
  type: 'string',
193
- description: 'The model to use. Aliases: "claude-ultra", "codex-ultra" (auto high-reasoning), "gemini-ultra". Standard: "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
193
+ description: 'The model to use. Aliases: "claude-ultra" (auto high effort), "codex-ultra" (auto xhigh reasoning), "gemini-ultra". Standard: "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
194
194
  },
195
195
  reasoning_effort: {
196
196
  type: 'string',
197
- description: 'Codex only. Sets model_reasoning_effort. Allowed: "low", "medium", "high", "xhigh".',
197
+ description: 'Reasoning control for Claude and Codex. Claude uses --effort with "low", "medium", "high". Codex uses model_reasoning_effort with "low", "medium", "high", "xhigh".',
198
198
  },
199
199
  session_id: {
200
200
  type: 'string',
@@ -1,98 +0,0 @@
1
- name: Watch Codex Fork PR
2
-
3
- on:
4
- schedule:
5
- - cron: '17 0 * * *'
6
- workflow_dispatch:
7
-
8
- permissions:
9
- contents: read
10
- issues: write
11
-
12
- jobs:
13
- watch-pr:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - name: Check upstream PR and notify issue
17
- uses: actions/github-script@v7
18
- env:
19
- UPSTREAM_OWNER: openai
20
- UPSTREAM_REPO: codex
21
- UPSTREAM_PR_NUMBER: '13537'
22
- TARGET_ISSUE_NUMBER: '7'
23
- COMMENT_MARKER: '<!-- codex-fork-pr-13537-merged -->'
24
- with:
25
- github-token: ${{ secrets.GITHUB_TOKEN }}
26
- script: |
27
- const upstreamOwner = process.env.UPSTREAM_OWNER;
28
- const upstreamRepo = process.env.UPSTREAM_REPO;
29
- const pull_number = Number(process.env.UPSTREAM_PR_NUMBER);
30
- const issue_number = Number(process.env.TARGET_ISSUE_NUMBER);
31
- const marker = process.env.COMMENT_MARKER;
32
-
33
- const { data: pr } = await github.rest.pulls.get({
34
- owner: upstreamOwner,
35
- repo: upstreamRepo,
36
- pull_number,
37
- });
38
-
39
- core.summary
40
- .addHeading('Upstream PR status')
41
- .addTable([
42
- [
43
- { data: 'Field', header: true },
44
- { data: 'Value', header: true },
45
- ],
46
- ['PR', `${upstreamOwner}/${upstreamRepo}#${pr.number}`],
47
- ['Title', pr.title],
48
- ['State', pr.state],
49
- ['Merged', String(Boolean(pr.merged_at))],
50
- ['Merged at', pr.merged_at ?? 'not merged'],
51
- ['Updated at', pr.updated_at],
52
- ['URL', pr.html_url],
53
- ]);
54
-
55
- if (!pr.merged_at) {
56
- core.info(`PR #${pr.number} is not merged yet. No issue comment posted.`);
57
- await core.summary.write();
58
- return;
59
- }
60
-
61
- const comments = await github.paginate(github.rest.issues.listComments, {
62
- owner: context.repo.owner,
63
- repo: context.repo.repo,
64
- issue_number,
65
- per_page: 100,
66
- });
67
-
68
- const existingComment = comments.find((comment) => comment.body?.includes(marker));
69
- if (existingComment) {
70
- core.info(`Issue #${issue_number} already has a merged notification comment.`);
71
- await core.summary.addRaw(`Merged notification already exists: ${existingComment.html_url}\n`).write();
72
- return;
73
- }
74
-
75
- const body = [
76
- marker,
77
- 'upstream PR `openai/codex#13537` has been merged.',
78
- '',
79
- `- PR: ${pr.html_url}`,
80
- `- Merged at: ${pr.merged_at}`,
81
- '',
82
- 'This is the PR that adds non-interactive forking via `codex exec --fork <SESSION_ID> [PROMPT]`.',
83
- 'That means a new session ID can be issued when forking an existing session headlessly.',
84
- '`resume` itself still continues the same session and does not issue a new session ID.',
85
- ].join('\n');
86
-
87
- const { data: comment } = await github.rest.issues.createComment({
88
- owner: context.repo.owner,
89
- repo: context.repo.repo,
90
- issue_number,
91
- body,
92
- });
93
-
94
- core.info(`Posted merged notification to issue #${issue_number}: ${comment.html_url}`);
95
- await core.summary
96
- .addHeading('Issue notification')
97
- .addRaw(`Posted merged notification: ${comment.html_url}\n`)
98
- .write();