codemini-cli 0.3.4 → 0.3.5

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.
package/src/core/tools.js CHANGED
@@ -1843,7 +1843,7 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
1843
1843
  function: {
1844
1844
  name: 'read',
1845
1845
  description:
1846
- 'Inspect a file and return content directly by default. Demo-style aliases like file_path, offset, and limit are accepted. Use metadata_only=true only when you want file metadata without content. Do not use run with cat, head, or tail for file reads.',
1846
+ 'Inspect code or text files. Use read(path) for normal file or line-window reads, read(ast_target=...) for a node-scoped AST read, and read(path, query=..., capture_name=...) to run an inline Tree-sitter query before returning the first matched node. Prefer the AST forms when targeting a function, class, or method and you want tighter context. Demo-style aliases like file_path, offset, and limit are accepted. Use metadata_only=true only when you want file metadata without content. Do not use run with cat, head, or tail for file reads.',
1847
1847
  parameters: {
1848
1848
  type: 'object',
1849
1849
  properties: {
@@ -1856,7 +1856,11 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
1856
1856
  max_chars: { type: 'number', description: 'Max chars to return' },
1857
1857
  include_content: { type: 'boolean', description: 'Legacy compatibility flag. Content is returned by default.' },
1858
1858
  read_token: { type: 'string', description: 'Legacy compatibility token. No longer required for content reads.' },
1859
- metadata_only: { type: 'boolean', description: 'Set true to return metadata without content.' }
1859
+ metadata_only: { type: 'boolean', description: 'Set true to return metadata without content.' },
1860
+ ast_target: { type: 'object', description: 'AST target from ast_query or a prior AST selection. When provided, read returns that node instead of a line window.' },
1861
+ query: { type: 'string', description: 'Optional Tree-sitter query to run inline before reading the first matched AST node. Use with path for one-shot function/class/method reads.' },
1862
+ capture_name: { type: 'string', description: 'Optional capture name to select when query is provided.' },
1863
+ language: { type: 'string', description: 'Optional Tree-sitter language override for AST reads or inline queries.' }
1860
1864
  },
1861
1865
  required: []
1862
1866
  }
@@ -1923,7 +1927,7 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
1923
1927
  function: {
1924
1928
  name: 'edit',
1925
1929
  description:
1926
- 'Edit existing files. Prefer one of these shapes: 1) {file, old_text, new_text} for exact text replacement, 2) {file, symbol, edit:{kind:"replace_block", new_content:"..."}} for block replacement, 3) {file, anchor_text, position:"before"|"after", content:"..."} for inserts. Demo-style aliases {file_path, old_string, new_string} are also accepted. Read first unless the exact target is already known. Prefer this over write for existing code changes.',
1930
+ 'Edit existing files. Prefer one of these shapes: 1) {file, old_text, new_text} for exact text replacement, 2) {file, symbol, edit:{kind:"replace_block", new_content:"..."}} for block replacement, 3) {file, anchor_text, position:"before"|"after", content:"..."} for inserts. Demo-style aliases {file_path, old_string, new_string} are also accepted. Read first unless the exact target is already known, and prefer read(ast_target=...) or read(path, query=...) before symbol- or block-level edits when you want tighter context. Prefer this over write for existing code changes.',
1927
1931
  parameters: {
1928
1932
  type: 'object',
1929
1933
  properties: {
@@ -2053,7 +2057,7 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
2053
2057
  function: {
2054
2058
  name: 'ast_query',
2055
2059
  description:
2056
- 'Run a Tree-sitter query on a code file and return ast_target objects. Use this when you need node-scoped reads or edits for functions, classes, or methods.',
2060
+ 'Run a Tree-sitter query on a code file and return ast_target objects. Use this for advanced AST workflows such as multi-match selection, explicit node caching, or when you plan to reuse ast_target across follow-up reads or edits. For a common one-shot function, class, or method read, prefer read(path, query=...) or read(ast_target=...).',
2057
2061
  parameters: {
2058
2062
  type: 'object',
2059
2063
  properties: {
@@ -2092,7 +2096,7 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
2092
2096
  function: {
2093
2097
  name: 'read_ast_node',
2094
2098
  description:
2095
- 'Read a previously selected AST node with compact structural context. Use this after ast_query before a scoped structural edit.',
2099
+ 'Read a previously selected AST node with compact structural context. Use this after ast_query when you want an explicit follow-up read of a cached node before a scoped structural edit. For common one-shot AST reads, prefer read(ast_target=...) or read(path, query=...).',
2096
2100
  parameters: {
2097
2101
  type: 'object',
2098
2102
  properties: {
@@ -2274,19 +2278,63 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
2274
2278
  const definitions = [...primaryDefinitions];
2275
2279
 
2276
2280
  const handlers = {
2277
- read: (args) =>
2278
- readFile(workspaceRoot, {
2281
+ read: async (args) => {
2282
+ const inlineQuery = String(args?.query || '').trim();
2283
+ const directAstTarget = args?.ast_target;
2284
+
2285
+ if (directAstTarget) {
2286
+ const result = await readAstNode(workspaceRoot, {
2287
+ ...args,
2288
+ path: args?.path || directAstTarget?.path,
2289
+ ast_target: directAstTarget
2290
+ });
2291
+ if (directAstTarget?.path) rememberAstSelection(directAstTarget.path, directAstTarget);
2292
+ const readPath = String(result?.path || directAstTarget?.path || '').trim();
2293
+ if (readPath) lastReadPath = readPath;
2294
+ return result;
2295
+ }
2296
+
2297
+ if (inlineQuery) {
2298
+ const queryResult = await queryAst(workspaceRoot, args);
2299
+ const firstTarget = queryResult?.matches?.[0]?.ast_target;
2300
+ if (!firstTarget) {
2301
+ return {
2302
+ path: String(args?.path || '').trim(),
2303
+ language: queryResult?.language,
2304
+ query: inlineQuery,
2305
+ capture_name: String(args?.capture_name || '').trim() || undefined,
2306
+ matches: 0,
2307
+ content: ''
2308
+ };
2309
+ }
2310
+ rememberAstSelection(firstTarget.path, firstTarget);
2311
+ const result = await readAstNode(workspaceRoot, {
2312
+ ...args,
2313
+ path: firstTarget.path,
2314
+ ast_target: firstTarget
2315
+ });
2316
+ const readPath = String(result?.path || firstTarget?.path || '').trim();
2317
+ if (readPath) lastReadPath = readPath;
2318
+ return {
2319
+ ...result,
2320
+ query: inlineQuery,
2321
+ capture_name: String(args?.capture_name || '').trim() || undefined,
2322
+ matches: queryResult.matches.length
2323
+ };
2324
+ }
2325
+
2326
+ const result = await readFile(workspaceRoot, {
2279
2327
  ...args,
2280
2328
  default_lines: config.context?.read_file_default_lines ?? 220,
2281
2329
  max_chars:
2282
2330
  typeof args?.max_chars === 'number'
2283
2331
  ? args.max_chars
2284
2332
  : config.context?.read_file_max_chars ?? 24000
2285
- }).then((result) => {
2286
- const readPath = String(result?.path || args?.path || '').trim();
2287
- if (readPath) lastReadPath = readPath;
2288
- return result;
2289
- }),
2333
+ });
2334
+ const readPath = String(result?.path || args?.path || '').trim();
2335
+ if (readPath) lastReadPath = readPath;
2336
+ return result;
2337
+ },
2290
2338
  query_project_index: async (args) => {
2291
2339
  await ensureProjectIndex();
2292
2340
  return queryProjectIndex(workspaceRoot, args);
@@ -2427,6 +2475,10 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
2427
2475
  read(result) {
2428
2476
  if (typeof result === 'string') return result;
2429
2477
  if (!result || typeof result !== 'object') return String(result);
2478
+ if (result.node && typeof result.content === 'string') {
2479
+ const header = `[AST: ${result.path || '?'} ${result.node.node_type || 'node'} ${result.node.start_line || '?'}-${result.node.end_line || '?'}${result.matches ? `, matches ${result.matches}` : ''}]`;
2480
+ return `${header}\n${result.content}`;
2481
+ }
2430
2482
  // Phase 1 metadata: small, return as-is
2431
2483
  if (result.phase === 'metadata') {
2432
2484
  return JSON.stringify(result);
@@ -961,6 +961,17 @@ function normalizeRuntimeStatus(status, copy) {
961
961
  };
962
962
  }
963
963
 
964
+ export function shouldRefreshRuntimeStateForEvent(event) {
965
+ const type = String(event?.type || '');
966
+ return (
967
+ type === 'assistant:start' ||
968
+ type === 'assistant:delta' ||
969
+ type === 'assistant:response' ||
970
+ type === 'tool:result' ||
971
+ type === 'compact:auto'
972
+ );
973
+ }
974
+
964
975
  function stageDescriptor(inputStage, busy, runtimeStatus, copy) {
965
976
  const normalized = normalizeRuntimeStatus(runtimeStatus, copy);
966
977
  const tag =
@@ -1829,6 +1840,10 @@ export function normalizeActivitySpacingRows(inputRows) {
1829
1840
  }
1830
1841
  }
1831
1842
 
1843
+ if (isTodoRow(row) && !isTodoRow(next) && next && !isBlankTextRow(next) && next.kind !== 'status') {
1844
+ normalized.push({ kind: 'todo-gap' });
1845
+ }
1846
+
1832
1847
  if (isBlankTextRow(row) && isBlankTextRow(prev)) {
1833
1848
  normalized.pop();
1834
1849
  }
@@ -2146,6 +2161,9 @@ export function renderMessageRow(msg, row, idx, loaderTick) {
2146
2161
  h(Text, { color, dimColor }, row.text || row.activeForm || ' ')
2147
2162
  );
2148
2163
  }
2164
+ if (row.kind === 'todo-gap') {
2165
+ return h(Box, { key: `row-todo-gap-${msg.id}-${idx}`, marginTop: 1 }, h(Text, { color: 'gray' }, ' '));
2166
+ }
2149
2167
  if (row.kind === 'table') {
2150
2168
  return h(
2151
2169
  Box,
@@ -2751,6 +2769,15 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
2751
2769
  setRuntimeStatus(makeIdleStatus(copy, snapshot, variant));
2752
2770
  };
2753
2771
 
2772
+ const refreshRuntimeSnapshot = () => {
2773
+ const snapshot = runtime.getRuntimeState?.();
2774
+ if (!snapshot) return;
2775
+ setDisplaySessionId(snapshot.sessionId || sessionId);
2776
+ setDisplayModel(snapshot.model || model);
2777
+ setDisplaySdkProvider(snapshot.sdkProvider || sdkProvider);
2778
+ setRuntimeState(snapshot);
2779
+ };
2780
+
2754
2781
  useEffect(() => {
2755
2782
  syncRuntimeVisualState('ready');
2756
2783
  }, []);
@@ -3126,6 +3153,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
3126
3153
 
3127
3154
  runtime
3128
3155
  .submit(line, (event) => {
3156
+ if (shouldRefreshRuntimeStateForEvent(event)) {
3157
+ refreshRuntimeSnapshot();
3158
+ }
3129
3159
  if (event?.type === 'assistant:start') {
3130
3160
  streamedAssistantHandledRef.current = true;
3131
3161
  setRuntimeStatus(makeStatus(copy.runtime.modelThinking, copy.runtime.requestDelivered, 'cyanBright'));