codemini-cli 0.4.0 → 0.4.2

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.
@@ -41,22 +41,92 @@ async function ensureParent(filePath) {
41
41
  function buildFilePath(scope, workspaceRoot = process.cwd(), projectAlias = '') {
42
42
  if (scope === 'user') return path.join(getMemoryDir(), 'user.json');
43
43
  if (scope === 'global') return path.join(getMemoryDir(), 'global.json');
44
- return path.join(getProjectMemoryDir(workspaceRoot), `${getProjectMemoryKey(workspaceRoot, projectAlias)}.json`);
44
+ return path.join(getProjectMemoryDir(workspaceRoot), 'project.json');
45
+ }
46
+
47
+ async function listProjectMemoryFiles(workspaceRoot = process.cwd()) {
48
+ const dir = getProjectMemoryDir(workspaceRoot);
49
+ try {
50
+ const entries = await fs.readdir(dir, { withFileTypes: true });
51
+ return entries
52
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
53
+ .map((entry) => path.join(dir, entry.name))
54
+ .sort();
55
+ } catch {
56
+ return [];
57
+ }
45
58
  }
46
59
 
47
60
  async function readMemoryBucket(filePath) {
61
+ const doc = await readMemoryBucketDocument(filePath);
62
+ return doc.items;
63
+ }
64
+
65
+ async function readMemoryBucketDocument(filePath) {
48
66
  try {
49
67
  const raw = await fs.readFile(filePath, 'utf8');
50
68
  const parsed = JSON.parse(raw);
51
- return Array.isArray(parsed?.items) ? parsed.items : [];
69
+ return {
70
+ items: Array.isArray(parsed?.items) ? parsed.items : [],
71
+ maintenance: parsed?.maintenance && typeof parsed.maintenance === 'object' ? parsed.maintenance : null
72
+ };
52
73
  } catch {
53
- return [];
74
+ return { items: [], maintenance: null };
54
75
  }
55
76
  }
56
77
 
57
- async function writeMemoryBucket(filePath, items) {
78
+ function memoryBucketHash(items = []) {
79
+ const stable = (Array.isArray(items) ? items : [])
80
+ .map((item) => ({
81
+ id: String(item?.id || ''),
82
+ kind: String(item?.kind || ''),
83
+ content: normalizeMemoryText(item?.content || ''),
84
+ summary: normalizeMemoryText(item?.summary || ''),
85
+ lifecycle: String(item?.lifecycle || ''),
86
+ pinned: item?.pinned === true
87
+ }))
88
+ .sort((left, right) => left.id.localeCompare(right.id));
89
+ return sha256(JSON.stringify(stable));
90
+ }
91
+
92
+ async function writeMemoryBucket(filePath, items, { maintenance = null } = {}) {
58
93
  await ensureParent(filePath);
59
- await fs.writeFile(filePath, `${JSON.stringify({ items }, null, 2)}\n`, 'utf8');
94
+ const doc = { items };
95
+ if (maintenance) doc.maintenance = maintenance;
96
+ await fs.writeFile(filePath, `${JSON.stringify(doc, null, 2)}\n`, 'utf8');
97
+ }
98
+
99
+ function dedupeMemoryItems(items = []) {
100
+ const deduped = [];
101
+ const seen = new Set();
102
+ for (const item of items) {
103
+ const key = item.id ? `id:${item.id}` : `${item.kind}:${normalizeMemoryText(item.content)}`;
104
+ if (seen.has(key)) continue;
105
+ seen.add(key);
106
+ deduped.push(item);
107
+ }
108
+ return deduped;
109
+ }
110
+
111
+ async function readProjectMemoryItems(workspaceRoot = process.cwd(), projectAlias = '') {
112
+ const projectKey = getProjectMemoryKey(workspaceRoot, projectAlias);
113
+ const files = await listProjectMemoryFiles(workspaceRoot);
114
+ const items = [];
115
+ for (const file of files) {
116
+ const bucket = await readMemoryBucket(file);
117
+ items.push(...bucket.map((item) => normalizeMemoryItem(item, 'project', projectKey)));
118
+ }
119
+ return dedupeMemoryItems(items)
120
+ .sort((left, right) => String(right.updatedAt).localeCompare(String(left.updatedAt)));
121
+ }
122
+
123
+ async function readScopeMemoryItems(scope, workspaceRoot = process.cwd(), projectAlias = '') {
124
+ const normalizedScope = ensureScope(scope);
125
+ if (normalizedScope === 'project') return readProjectMemoryItems(workspaceRoot, projectAlias);
126
+ const filePath = buildFilePath(normalizedScope, workspaceRoot, projectAlias);
127
+ return (await readMemoryBucket(filePath))
128
+ .map((item) => normalizeMemoryItem(item, normalizedScope, ''))
129
+ .sort((left, right) => String(right.updatedAt).localeCompare(String(left.updatedAt)));
60
130
  }
61
131
 
62
132
  function normalizeMemoryItem(item, scope, projectKey = '') {
@@ -74,7 +144,8 @@ function normalizeMemoryItem(item, scope, projectKey = '') {
74
144
  createdAt: String(item?.createdAt || now),
75
145
  updatedAt: String(item?.updatedAt || now),
76
146
  hits: Number.isFinite(Number(item?.hits)) ? Number(item.hits) : 0,
77
- pinned: item?.pinned === true
147
+ pinned: item?.pinned === true,
148
+ ...(item?.lifecycle ? { lifecycle: String(item.lifecycle) } : {})
78
149
  };
79
150
  }
80
151
 
@@ -96,13 +167,69 @@ function budgetForScope(scope, config = {}) {
96
167
  }
97
168
 
98
169
  export async function listMemories({ scope, workspaceRoot = process.cwd(), projectAlias = '' }) {
170
+ const normalizedScope = ensureScope(scope);
171
+ return readScopeMemoryItems(normalizedScope, workspaceRoot, projectAlias);
172
+ }
173
+
174
+ export async function getMemoryBucketMaintenance({ scope, workspaceRoot = process.cwd(), projectAlias = '' }) {
175
+ const normalizedScope = ensureScope(scope);
176
+ const filePath = buildFilePath(normalizedScope, workspaceRoot, projectAlias);
177
+ const doc = await readMemoryBucketDocument(filePath);
178
+ const items = normalizedScope === 'project'
179
+ ? await readProjectMemoryItems(workspaceRoot, projectAlias)
180
+ : doc.items.map((item) => normalizeMemoryItem(item, normalizedScope, ''));
181
+ const currentHash = memoryBucketHash(items);
182
+ const storedHash = String(doc.maintenance?.contentHash || '');
183
+ const maintainedAt = String(doc.maintenance?.maintainedAt || '');
184
+ return {
185
+ scope: normalizedScope,
186
+ itemCount: items.length,
187
+ contentHash: currentHash,
188
+ storedHash,
189
+ maintainedAt,
190
+ fresh: Boolean(maintainedAt && storedHash && storedHash === currentHash)
191
+ };
192
+ }
193
+
194
+ export async function markMemoryBucketMaintained({ scope, workspaceRoot = process.cwd(), projectAlias = '' }) {
195
+ const normalizedScope = ensureScope(scope);
196
+ const filePath = buildFilePath(normalizedScope, workspaceRoot, projectAlias);
197
+ const items = await readScopeMemoryItems(normalizedScope, workspaceRoot, projectAlias);
198
+ const maintenance = {
199
+ maintainedAt: nowIso(),
200
+ contentHash: memoryBucketHash(items),
201
+ itemCount: items.length
202
+ };
203
+ await writeMemoryBucket(filePath, items, { maintenance });
204
+ return { scope: normalizedScope, ...maintenance };
205
+ }
206
+
207
+ export async function replaceMemoryBucket({
208
+ scope,
209
+ items = [],
210
+ workspaceRoot = process.cwd(),
211
+ projectAlias = '',
212
+ markMaintained = false
213
+ } = {}) {
99
214
  const normalizedScope = ensureScope(scope);
100
215
  const filePath = buildFilePath(normalizedScope, workspaceRoot, projectAlias);
101
216
  const projectKey = normalizedScope === 'project' ? getProjectMemoryKey(workspaceRoot, projectAlias) : '';
102
- const items = await readMemoryBucket(filePath);
103
- return items
217
+ const normalizedItems = (Array.isArray(items) ? items : [])
104
218
  .map((item) => normalizeMemoryItem(item, normalizedScope, projectKey))
105
- .sort((left, right) => String(right.updatedAt).localeCompare(String(left.updatedAt)));
219
+ .filter((item) => item.content);
220
+ const maintenance = markMaintained
221
+ ? {
222
+ maintainedAt: nowIso(),
223
+ contentHash: memoryBucketHash(normalizedItems),
224
+ itemCount: normalizedItems.length
225
+ }
226
+ : null;
227
+ await writeMemoryBucket(filePath, normalizedItems, { maintenance });
228
+ return {
229
+ scope: normalizedScope,
230
+ items: normalizedItems,
231
+ maintenance
232
+ };
106
233
  }
107
234
 
108
235
  export async function rememberMemory({
@@ -125,7 +252,7 @@ export async function rememberMemory({
125
252
 
126
253
  const filePath = buildFilePath(normalizedScope, workspaceRoot, projectAlias);
127
254
  const projectKey = normalizedScope === 'project' ? getProjectMemoryKey(workspaceRoot, projectAlias) : '';
128
- const existing = (await readMemoryBucket(filePath)).map((item) => normalizeMemoryItem(item, normalizedScope, projectKey));
255
+ const existing = await readScopeMemoryItems(normalizedScope, workspaceRoot, projectAlias);
129
256
  const probe = normalizeMemoryItem({ content: normalizedContent, kind, summary, source, confidence, pinned }, normalizedScope, projectKey);
130
257
 
131
258
  const replaceIndex = replaceSimilar ? existing.findIndex((item) => sameMemory(item, probe)) : -1;
@@ -170,6 +297,14 @@ export async function forgetMemory({ scope, id, workspaceRoot = process.cwd(), p
170
297
  const existing = await listMemories({ scope: normalizedScope, workspaceRoot, projectAlias });
171
298
  const kept = existing.filter((item) => item.id !== id);
172
299
  await writeMemoryBucket(filePath, kept);
300
+ if (normalizedScope === 'project') {
301
+ const files = (await listProjectMemoryFiles(workspaceRoot)).filter((file) => file !== filePath);
302
+ await Promise.all(files.map(async (file) => {
303
+ const bucket = await readMemoryBucket(file);
304
+ const next = bucket.filter((item) => String(item?.id || '') !== id);
305
+ if (next.length !== bucket.length) await writeMemoryBucket(file, next);
306
+ }));
307
+ }
173
308
  return { removed: existing.length - kept.length };
174
309
  }
175
310
 
@@ -54,6 +54,36 @@ async function parseJsonResponse(response) {
54
54
  return response.json();
55
55
  }
56
56
 
57
+ function isRetryableStatus(status) {
58
+ return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
59
+ }
60
+
61
+ function isRetryableError(error) {
62
+ const name = String(error?.name || '');
63
+ if (name === 'AbortError' || name === 'TimeoutError') return false;
64
+ const message = String(error?.message || error || '');
65
+ return /fetch failed|network|socket|ECONNRESET|ETIMEDOUT|EAI_AGAIN/i.test(message);
66
+ }
67
+
68
+ async function fetchWithRetry(url, init, { maxRetries = 0 } = {}) {
69
+ const attempts = Math.max(0, Number(maxRetries) || 0) + 1;
70
+ let lastError;
71
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
72
+ try {
73
+ const response = await fetch(url, init);
74
+ if (response.ok || !isRetryableStatus(response.status) || attempt === attempts - 1) {
75
+ return response;
76
+ }
77
+ await response.arrayBuffer().catch(() => null);
78
+ } catch (error) {
79
+ lastError = error;
80
+ if (!isRetryableError(error) || attempt === attempts - 1) throw error;
81
+ }
82
+ await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
83
+ }
84
+ throw lastError || new Error('Gateway request failed');
85
+ }
86
+
57
87
  async function* iterateSseEvents(stream) {
58
88
  const decoder = new TextDecoder();
59
89
  let buffer = '';
@@ -318,12 +348,12 @@ export async function createChatCompletion({
318
348
  maxRetries = 2
319
349
  }) {
320
350
  const payload = buildPayload({ model, temperature, messages, tools });
321
- const response = await fetch(buildChatCompletionsUrl(baseUrl), {
351
+ const response = await fetchWithRetry(buildChatCompletionsUrl(baseUrl), {
322
352
  method: 'POST',
323
353
  headers: createHeaders(apiKey),
324
354
  body: JSON.stringify(payload),
325
355
  signal: AbortSignal.timeout(timeoutMs)
326
- });
356
+ }, { maxRetries });
327
357
  const data = await parseJsonResponse(response);
328
358
  const message = data?.choices?.[0]?.message || {};
329
359
  const text = sanitizeMiniMaxText(model, extractTextContent(message.content));
@@ -386,12 +416,12 @@ export async function createChatCompletionStream({
386
416
  }
387
417
  }
388
418
  const payload = buildPayload({ model, temperature, messages, tools, stream: true });
389
- const response = await fetch(buildChatCompletionsUrl(baseUrl), {
419
+ const response = await fetchWithRetry(buildChatCompletionsUrl(baseUrl), {
390
420
  method: 'POST',
391
421
  headers: createHeaders(apiKey),
392
422
  body: JSON.stringify(payload),
393
423
  signal: controller.signal
394
- });
424
+ }, { maxRetries });
395
425
  if (!response.ok || !response.body) {
396
426
  const text = await response.text().catch(() => '');
397
427
  throw new Error(`Gateway error ${response.status}: ${text || response.statusText}`);
@@ -402,7 +432,8 @@ export async function createChatCompletionStream({
402
432
  let usage = null;
403
433
  let miniMaxStreamState = { rawContent: '', visibleText: '' };
404
434
 
405
- for await (const chunk of iterateSseEvents(response.body)) {
435
+ try {
436
+ for await (const chunk of iterateSseEvents(response.body)) {
406
437
  usage = chunk?.usage || usage;
407
438
  const choice0 = chunk?.choices?.[0] || {};
408
439
  const delta = choice0?.delta || {};
@@ -452,6 +483,10 @@ export async function createChatCompletionStream({
452
483
  if (choice0?.finish_reason) {
453
484
  break;
454
485
  }
486
+ }
487
+ } finally {
488
+ timeoutSignal.removeEventListener('abort', onAbort);
489
+ if (externalSignal) externalSignal.removeEventListener('abort', onAbort);
455
490
  }
456
491
 
457
492
  const result = buildFinalStreamResult(text, toolCallsByIndex, usage, messages);
@@ -0,0 +1,178 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getProjectSkillsDir, getSkillsDir } from './paths.js';
4
+ import { createChatCompletion } from './provider/index.js';
5
+
6
+ const REFLECT_TIMEOUT_MS = 45000;
7
+
8
+ function slugifySkillName(value) {
9
+ const slug = String(value || '')
10
+ .trim()
11
+ .toLowerCase()
12
+ .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
13
+ .replace(/^-+|-+$/g, '');
14
+ return slug || 'reflected-success-workflow';
15
+ }
16
+
17
+ function escapeFrontmatter(value) {
18
+ return String(value || '').replace(/\r?\n/g, ' ').replace(/"/g, '\\"').trim();
19
+ }
20
+
21
+ function hasFrontmatter(content) {
22
+ return /^---\r?\n[\s\S]*?\r?\n---\r?\n/.test(String(content || '').trimStart());
23
+ }
24
+
25
+ function renderSkillContent({ name, description, content }) {
26
+ const body = String(content || '').trim() || [
27
+ '## Workflow',
28
+ '',
29
+ '1. Recreate the successful chain from the recent task.',
30
+ '2. Preserve the key decision that made it work.',
31
+ '3. Verify with the narrowest relevant check.',
32
+ '',
33
+ '## Boundaries',
34
+ '',
35
+ 'Use this only when the current task matches the preserved workflow.'
36
+ ].join('\n');
37
+ if (hasFrontmatter(body)) return `${body.trim()}\n`;
38
+ return [
39
+ '---',
40
+ `name: ${name}`,
41
+ `description: ${escapeFrontmatter(description) || `Use when this reflected workflow applies.`}`,
42
+ '---',
43
+ '',
44
+ body
45
+ ].join('\n').trimEnd() + '\n';
46
+ }
47
+
48
+ export function normalizeReflectDraft(raw = {}) {
49
+ const name = slugifySkillName(raw.name || raw.skillName || raw.title);
50
+ const description = String(raw.description || raw.summary || `Use when the ${name} workflow applies.`).trim();
51
+ const confidence = Math.min(1, Math.max(0, Number(raw.confidence ?? 0.75)));
52
+ return {
53
+ id: Number(raw.id || 1),
54
+ name,
55
+ description,
56
+ confidence,
57
+ content: renderSkillContent({ name, description, content: raw.content || raw.markdown || raw.body })
58
+ };
59
+ }
60
+
61
+ export function buildReflectTargetPath({ scope = 'project', name, workspaceRoot = process.cwd() } = {}) {
62
+ const safeName = slugifySkillName(name);
63
+ const baseDir = String(scope || '').toLowerCase() === 'global'
64
+ ? getSkillsDir()
65
+ : getProjectSkillsDir(workspaceRoot);
66
+ return path.join(baseDir, safeName, 'SKILL.md');
67
+ }
68
+
69
+ export function parseReflectScope(args = []) {
70
+ let scope = 'project';
71
+ const requestParts = [];
72
+ for (let index = 0; index < args.length; index += 1) {
73
+ const arg = String(args[index] || '');
74
+ if (arg === '--scope') {
75
+ const next = String(args[index + 1] || '').toLowerCase();
76
+ if (next === 'global' || next === 'project') {
77
+ scope = next;
78
+ index += 1;
79
+ }
80
+ continue;
81
+ }
82
+ if (arg.startsWith('--scope=')) {
83
+ const value = arg.slice('--scope='.length).toLowerCase();
84
+ if (value === 'global' || value === 'project') scope = value;
85
+ continue;
86
+ }
87
+ requestParts.push(arg);
88
+ }
89
+ return { scope, request: requestParts.join(' ').trim() };
90
+ }
91
+
92
+ function parseModelDrafts(text) {
93
+ const raw = String(text || '').trim();
94
+ if (!raw) return [];
95
+ const unfenced = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim();
96
+ try {
97
+ const parsed = JSON.parse(unfenced);
98
+ if (Array.isArray(parsed?.candidates)) return parsed.candidates.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
99
+ if (Array.isArray(parsed)) return parsed.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
100
+ if (parsed && typeof parsed === 'object') return [normalizeReflectDraft(parsed)];
101
+ } catch {
102
+ // Fall back to wrapping plain markdown below.
103
+ }
104
+ return [normalizeReflectDraft({
105
+ name: 'reflected-success-workflow',
106
+ description: 'Use when the reflected successful workflow applies.',
107
+ content: raw
108
+ })];
109
+ }
110
+
111
+ function recentContext(session, limit = 10) {
112
+ const messages = Array.isArray(session?.messages) ? session.messages : [];
113
+ return messages
114
+ .slice(-limit)
115
+ .map((message) => `${message.role}: ${String(message.content || '').slice(0, 1200)}`)
116
+ .join('\n\n');
117
+ }
118
+
119
+ export async function buildReflectSkillDraft({
120
+ request = '',
121
+ scope = 'project',
122
+ session,
123
+ config = {},
124
+ model,
125
+ systemPrompt = '',
126
+ previousDraft = null,
127
+ feedback = ''
128
+ } = {}) {
129
+ const mode = String(request || '').trim() ? 'directed' : 'exploratory';
130
+ const prompt = [
131
+ 'Create a reusable Codex/CodeMini SKILL.md draft from a successful workflow.',
132
+ `Mode: ${mode}`,
133
+ `Target scope: ${scope}`,
134
+ request ? `User reflection request:\n${request}` : 'No explicit request was supplied. Be conservative and return no candidates if the recent context does not show a reusable success pattern.',
135
+ previousDraft ? `Existing draft to revise:\n${previousDraft.content || ''}` : '',
136
+ feedback ? `User edit feedback:\n${feedback}` : '',
137
+ 'Recent session context:',
138
+ recentContext(session),
139
+ 'Return valid JSON only, no markdown fences.',
140
+ 'Shape: {"candidates":[{"name":"kebab-case-name","description":"when to use this skill","confidence":0.0,"content":"full SKILL.md body or markdown body"}]}',
141
+ 'The content must include trigger conditions, workflow/toolchain, key decisions, pitfalls, verification, and boundaries.',
142
+ 'Do not write memory or inbox content. This is only a skill draft.'
143
+ ].filter(Boolean).join('\n\n');
144
+
145
+ const result = await createChatCompletion({
146
+ sdkProvider: config?.sdk?.provider,
147
+ baseUrl: config?.gateway?.base_url,
148
+ apiKey: config?.gateway?.api_key,
149
+ model: model || config?.model?.name,
150
+ messages: [
151
+ { role: 'system', content: systemPrompt || 'You draft concise, reusable coding workflow skills.' },
152
+ { role: 'user', content: prompt }
153
+ ],
154
+ temperature: 0,
155
+ timeoutMs: REFLECT_TIMEOUT_MS
156
+ });
157
+
158
+ return parseModelDrafts(result?.text || '');
159
+ }
160
+
161
+ export function attachReflectTargets({ candidates = [], scope = 'project', workspaceRoot = process.cwd() } = {}) {
162
+ return candidates.map((candidate, index) => {
163
+ const draft = normalizeReflectDraft({ id: index + 1, ...candidate });
164
+ return {
165
+ ...draft,
166
+ targetPath: buildReflectTargetPath({ scope, name: draft.name, workspaceRoot })
167
+ };
168
+ });
169
+ }
170
+
171
+ export async function writeReflectSkillDraft({ draft, scope = 'project', workspaceRoot = process.cwd() } = {}) {
172
+ const normalized = normalizeReflectDraft(draft);
173
+ const filePath = buildReflectTargetPath({ scope, name: normalized.name, workspaceRoot });
174
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
175
+ await fs.writeFile(filePath, normalized.content, 'utf8');
176
+ return { filePath, draft: normalized };
177
+ }
178
+
@@ -146,11 +146,11 @@ export function getShellSystemPrompt(value) {
146
146
  ALWAYS prefer dedicated tools over raw shell commands:
147
147
  - The visible default tool list is intentionally small. If a needed capability is not currently listed, do not assume it is unavailable — call tool_search to load additional tools first
148
148
  - Use query_project_index first for broad repository understanding. It combines project-map metadata with indexed file symbols so you can narrow candidates before reading source files
149
- - Use read to inspect files — NEVER use cat, head, or tail via run. read returns content directly by default; demo-style shapes like {file_path:"src/app.ts"}, {path:"src/app.ts:10-40"}, or {file_path:"src/app.ts", offset:10, limit:30} are accepted
149
+ - Use read to inspect files — NEVER use cat, head, or tail via run. Use canonical shapes like {path:"src/app.ts"}, {path:"src/app.ts:10-40"}, or {path:"src/app.ts", start_line:10, end_line:40}
150
150
  - Use grep to search file contents — NEVER use grep or rg via run
151
151
  - Use list for directory-by-directory filesystem discovery. If you specifically need pattern-based file lookup like src/**/*.ts, load glob with tool_search instead of falling back to run
152
- - Use edit to modify existing files — this is the DEFAULT path for code changes. Demo-style aliases like {file_path:"src/app.ts", old_string:"foo", new_string:"bar"} are accepted
153
- - Use write only for creating new files or complete rewrites (set full_file_rewrite=true for existing code files). Aliases like {file:"notes.txt", text:"..."} are accepted
152
+ - Use edit to modify existing files — this is the DEFAULT path for code changes. Prefer {path:"src/app.ts", old_text:"foo", new_text:"bar"}
153
+ - Use write only for creating new files or complete rewrites (set full_file_rewrite=true for existing code files). Prefer {path:"notes.txt", content:"..."}
154
154
  - Use update_todos to manage the session todo checklist for complex work. Provide the full current list each time and usually keep exactly one item in_progress
155
155
  - Use read_plan and update_plan to recover or sync structured plan state when plan progress was interrupted (for example by transient gateway/model errors)
156
156
  - Use run for shell commands. For long-running processes (dev servers, watchers), set run_in_background=true when you know you do not need the final result immediately. Long-running commands may also be backgrounded automatically
@@ -181,15 +181,15 @@ For background commands: use run to launch. If you need management tools that ar
181
181
  Common tool call patterns:
182
182
  - Query the project index first: {query:"login auth flow", path:"src", max_results:5}
183
183
  - Load a deferred tool when needed: {query:"glob"} or {query:"all"}
184
- - Read a file: {path:"src/app.ts"} or {file_path:"src/app.ts", offset:20, limit:40}
184
+ - Read a file: {path:"src/app.ts"} or {path:"src/app.ts", start_line:20, end_line:60}
185
185
  - Read a specific range inline: {path:"src/app.ts:20-60"}
186
186
  - Search text: {pattern:"loginUser", path:"src"} or {query:"loginUser", directory:"src"}
187
187
  - List a directory first: {path:"src"}
188
188
  - After loading glob, find files by pattern: {pattern:"src/**/*.ts"} or {query:"src/**/*.ts"}
189
- - Edit exact text: {file_path:"src/app.ts", old_string:"foo", new_string:"bar"}
189
+ - Edit exact text: {path:"src/app.ts", old_text:"foo", new_text:"bar"}
190
190
  - Edit with shorthand: {path:"src/app.ts", old_text:"foo", content:"bar"}
191
- - Write a new file: {file:"notes.txt", text:"..."} or {path:"src/page.tsx", content:"..."}
192
- - When the environment provides a Working directory, prefer absolute file_path values rooted there instead of guessing prefixes
191
+ - Write a new file: {path:"notes.txt", content:"..."} or {path:"src/page.tsx", content:"..."}
192
+ - When the environment provides a Working directory, prefer absolute path values rooted there instead of guessing prefixes
193
193
  - If the user gives a relative path like src/app.ts, resolve it from the current Working directory rather than inventing ../ or sibling folders
194
194
 
195
195
  # Doing tasks
@@ -218,7 +218,7 @@ Common tool call patterns:
218
218
  - Keep answers compact and easy to scan
219
219
  - Lead with the answer or next action, not scene-setting
220
220
  - Do not restate the user's request unless a brief restatement prevents ambiguity
221
- - When referencing code, use file_path:line_number format
221
+ - When referencing code, use path:line_number format
222
222
  - Keep technical wording, commands, paths, and error details exact
223
223
  - Only use emojis if the user explicitly requests it`;
224
224
  }