@exreve/exk 1.0.47 → 1.0.49

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.
@@ -960,25 +960,62 @@ export class AgentSessionManager {
960
960
  }
961
961
  else if (message.type === 'user') {
962
962
  const msg = message;
963
- // SDK 0.2.x: tool results can appear in two places:
964
- // 1. msg.tool_use_result (top-level field, present in 0.1.x and some 0.2.x messages)
965
- // 2. msg.message.content array with type='tool_result' blocks (common in 0.2.x subagent calls)
963
+ // SDK sends tool results with TWO data sources:
964
+ // message.content[0] — {type:'tool_result', tool_use_id:'...', content: <raw text>}
965
+ // msg.tool_use_result — structured object with {stdout,stderr} for Bash,
966
+ // {type,file} for Read, or [{type:'text',text:'...'}] for MCP tools
967
+ //
968
+ // The tool_use_id lives ONLY in message.content[].tool_use_id.
969
+ // The structured result lives in tool_use_result for built-in tools.
970
+ // For MCP tools, tool_use_result is a content-block array we need to parse.
966
971
  let toolResult = null;
967
972
  let toolUseId = msg.parent_tool_use_id;
968
- // Check top-level tool_use_result first
973
+ // STEP 1: Always extract tool_use_id from message.content (authoritative source)
974
+ if (Array.isArray(msg.message?.content)) {
975
+ const contentBlocks = msg.message.content;
976
+ const toolResultBlock = contentBlocks.find((c) => c.type === 'tool_result');
977
+ if (toolResultBlock?.tool_use_id) {
978
+ toolUseId = toolResultBlock.tool_use_id;
979
+ }
980
+ }
981
+ // STEP 2: Use tool_use_result as the primary data source.
982
+ // For built-in tools (Bash, Read, Edit, Write, Glob, Grep) it's a structured
983
+ // object like {stdout, stderr} or {type:'text', file:{...}} — use directly.
984
+ // For MCP tools it's a content-block array [{type:'text', text:'...'}] — parse text.
969
985
  if (msg.tool_use_result) {
970
- toolResult = msg.tool_use_result;
986
+ const raw = msg.tool_use_result;
987
+ if (Array.isArray(raw)) {
988
+ // MCP tool result: [{type:'text', text:'...'}] — extract and parse
989
+ const textParts = raw
990
+ .filter((c) => c.type === 'text')
991
+ .map((c) => c.text);
992
+ const rawContent = textParts.join('\n');
993
+ try {
994
+ toolResult = JSON.parse(rawContent);
995
+ }
996
+ catch {
997
+ toolResult = { content: rawContent, type: 'text' };
998
+ }
999
+ }
1000
+ else if (typeof raw === 'object' && raw !== null) {
1001
+ // Built-in tool result: {stdout, stderr, ...} or {type, file, ...} — use directly
1002
+ toolResult = raw;
1003
+ }
1004
+ else if (typeof raw === 'string') {
1005
+ try {
1006
+ toolResult = JSON.parse(raw);
1007
+ }
1008
+ catch {
1009
+ toolResult = { content: raw, type: 'text' };
1010
+ }
1011
+ }
971
1012
  }
972
- // Check message.content for tool_result blocks (SDK 0.2.x nested calls)
1013
+ // STEP 3: Fallback to parsing message.content if tool_use_result wasn't available
1014
+ // (e.g. subagent calls where tool_use_result may be absent)
973
1015
  if (!toolResult && Array.isArray(msg.message?.content)) {
974
1016
  const contentBlocks = msg.message.content;
975
1017
  const toolResultBlock = contentBlocks.find((c) => c.type === 'tool_result');
976
1018
  if (toolResultBlock) {
977
- // Extract tool use ID from the content block
978
- if (toolResultBlock.tool_use_id) {
979
- toolUseId = toolResultBlock.tool_use_id;
980
- }
981
- // The result content can be a string or array of content blocks
982
1019
  if (typeof toolResultBlock.content === 'string') {
983
1020
  try {
984
1021
  toolResult = JSON.parse(toolResultBlock.content);
@@ -988,7 +1025,6 @@ export class AgentSessionManager {
988
1025
  }
989
1026
  }
990
1027
  else if (Array.isArray(toolResultBlock.content)) {
991
- // Extract text from content blocks
992
1028
  const textParts = toolResultBlock.content
993
1029
  .filter((c) => c.type === 'text')
994
1030
  .map((c) => c.text);
@@ -132,6 +132,9 @@ async function handleStatus(projectPath) {
132
132
  // empty
133
133
  }
134
134
  const changedFiles = parsePorcelainStatus(porcelainOutput);
135
+ const MAX_CHANGED_FILES = 1000;
136
+ const totalChangedFiles = changedFiles.length;
137
+ const truncated = totalChangedFiles > MAX_CHANGED_FILES ? changedFiles.slice(0, MAX_CHANGED_FILES) : changedFiles;
135
138
  return {
136
139
  success: true,
137
140
  status: {
@@ -140,9 +143,10 @@ async function handleStatus(projectPath) {
140
143
  remote: remote || undefined,
141
144
  ahead,
142
145
  behind,
143
- changedFiles,
146
+ changedFiles: truncated,
144
147
  stashCount,
145
148
  ghAvailable: ghOk,
149
+ ...(totalChangedFiles > MAX_CHANGED_FILES && { totalChangedFiles }),
146
150
  },
147
151
  };
148
152
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {
@@ -61,4 +61,4 @@
61
61
  "engines": {
62
62
  "node": ">=20.0.0"
63
63
  }
64
- }
64
+ }