ideacode 1.1.4 → 1.1.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/dist/repl.js CHANGED
@@ -91,7 +91,7 @@ function replayMessagesToLogLines(messages) {
91
91
  const msg = messages[i];
92
92
  if (msg.role === "user") {
93
93
  if (typeof msg.content === "string") {
94
- lines.push(...userPromptBox(msg.content).split("\n"), "");
94
+ lines.push("", ...userPromptBox(msg.content).split("\n"), "");
95
95
  }
96
96
  else if (Array.isArray(msg.content)) {
97
97
  const prev = messages[i - 1];
@@ -102,11 +102,12 @@ function replayMessagesToLogLines(messages) {
102
102
  for (const tr of toolResults) {
103
103
  const block = toolUses.find((b) => b.id === tr.tool_use_id);
104
104
  if (block?.name) {
105
+ const name = block.name.trim().toLowerCase();
105
106
  const firstVal = block.input && typeof block.input === "object" ? Object.values(block.input)[0] : undefined;
106
107
  const argPreview = String(firstVal ?? "").slice(0, 50) || "—";
107
108
  const content = tr.content ?? "";
108
109
  const ok = !content.startsWith("error:");
109
- lines.push(toolCallBox(block.name, argPreview, ok));
110
+ lines.push(toolCallBox(name, argPreview, ok));
110
111
  const preview = content.split("\n")[0]?.slice(0, 60) ?? "";
111
112
  lines.push(toolResultLine(preview, ok));
112
113
  const tokens = estimateTokensForString(content);
@@ -120,7 +121,8 @@ function replayMessagesToLogLines(messages) {
120
121
  const blocks = msg.content;
121
122
  for (const block of blocks) {
122
123
  if (block.type === "text" && block.text?.trim()) {
123
- lines.push("");
124
+ if (lines[lines.length - 1] !== "")
125
+ lines.push("");
124
126
  lines.push(...agentMessage(block.text).trimEnd().split("\n"));
125
127
  }
126
128
  }
@@ -157,40 +159,55 @@ export function Repl({ apiKey, cwd, onQuit }) {
157
159
  ██║██████╔╝███████╗██║ ██║╚██████╗╚██████╔╝██████╔╝███████╗
158
160
  ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
159
161
  `;
162
+ const hasRestoredLogRef = useRef(false);
163
+ const restoredCwdRef = useRef(null);
160
164
  const [logLines, setLogLines] = useState(() => {
161
165
  const model = getModel();
162
166
  const version = getVersion();
163
- return [
167
+ const banner = [
164
168
  "",
165
169
  matchaGradient(bigLogo),
166
170
  colors.accent(` ideacode v${version}`) + colors.dim(" · ") + colors.accentPale(model) + colors.dim(" · ") + colors.bold("OpenRouter") + colors.dim(` · ${cwd}`),
167
171
  colors.mutedDark(" / commands ! shell @ files · Ctrl+P palette · Ctrl+C or /q to quit"),
168
172
  "",
169
173
  ];
174
+ const loaded = loadConversation(cwd);
175
+ if (loaded.length > 0) {
176
+ hasRestoredLogRef.current = true;
177
+ restoredCwdRef.current = cwd;
178
+ return [...banner, ...replayMessagesToLogLines(loaded)];
179
+ }
180
+ return banner;
170
181
  });
171
182
  const [inputValue, setInputValue] = useState("");
172
183
  const [currentModel, setCurrentModel] = useState(getModel);
173
184
  const [messages, setMessages] = useState(() => loadConversation(cwd));
174
185
  const messagesRef = useRef(messages);
175
- const hasRestoredLogRef = useRef(false);
176
186
  useEffect(() => {
177
187
  messagesRef.current = messages;
178
188
  }, [messages]);
179
189
  useEffect(() => {
190
+ if (hasRestoredLogRef.current && restoredCwdRef.current === cwd)
191
+ return;
180
192
  const loaded = loadConversation(cwd);
181
- if (loaded.length > 0 && !hasRestoredLogRef.current) {
193
+ const model = getModel();
194
+ const version = getVersion();
195
+ const banner = [
196
+ "",
197
+ matchaGradient(bigLogo),
198
+ colors.accent(` ideacode v${version}`) + colors.dim(" · ") + colors.accent(model) + colors.dim(" · ") + colors.accentPale("OpenRouter") + colors.dim(` · ${cwd}`),
199
+ colors.mutedDark(" / commands ! shell @ files · Ctrl+P palette · Ctrl+C or /q to quit"),
200
+ "",
201
+ ];
202
+ if (loaded.length > 0) {
182
203
  hasRestoredLogRef.current = true;
183
- const model = getModel();
184
- const version = getVersion();
185
- const banner = [
186
- "",
187
- matchaGradient(bigLogo),
188
- colors.accent(` ideacode v${version}`) + colors.dim(" · ") + colors.accent(model) + colors.dim(" · ") + colors.accentPale("OpenRouter") + colors.dim(` · ${cwd}`),
189
- colors.mutedDark(" / commands ! shell @ files · Ctrl+P palette · Ctrl+C or /q to quit"),
190
- "",
191
- ];
192
204
  setLogLines([...banner, ...replayMessagesToLogLines(loaded)]);
193
205
  }
206
+ else {
207
+ hasRestoredLogRef.current = false;
208
+ setLogLines(banner);
209
+ }
210
+ restoredCwdRef.current = cwd;
194
211
  }, [cwd]);
195
212
  const saveDebounceRef = useRef(null);
196
213
  useEffect(() => {
@@ -300,11 +317,16 @@ export function Repl({ apiKey, cwd, onQuit }) {
300
317
  useEffect(() => {
301
318
  setAtSuggestionIndex(0);
302
319
  }, [atFilter]);
320
+ const lastLogLineRef = useRef("");
303
321
  const appendLog = useCallback((line) => {
304
322
  const lines = line.split("\n");
305
323
  if (lines.length > 1 && lines[0] === "")
306
324
  lines.shift();
307
- setLogLines((prev) => [...prev, ...lines]);
325
+ setLogLines((prev) => {
326
+ const next = [...prev, ...lines];
327
+ lastLogLineRef.current = next[next.length - 1] ?? "";
328
+ return next;
329
+ });
308
330
  }, []);
309
331
  useEffect(() => {
310
332
  const version = getVersion();
@@ -395,6 +417,7 @@ export function Repl({ apiKey, cwd, onQuit }) {
395
417
  setLogScrollOffset(0);
396
418
  }
397
419
  lastUserMessageRef.current = userInput;
420
+ appendLog("");
398
421
  appendLog(userPromptBox(userInput));
399
422
  appendLog("");
400
423
  let state = [...messages, { role: "user", content: userInput }];
@@ -418,11 +441,12 @@ export function Repl({ apiKey, cwd, onQuit }) {
418
441
  for (let bi = 0; bi < contentBlocks.length; bi++) {
419
442
  const block = contentBlocks[bi];
420
443
  if (block.type === "text" && block.text?.trim()) {
421
- appendLog("");
444
+ if (lastLogLineRef.current !== "")
445
+ appendLog("");
422
446
  appendLog(agentMessage(block.text).trimEnd());
423
447
  }
424
448
  if (block.type === "tool_use" && block.name && block.input) {
425
- const toolName = block.name;
449
+ const toolName = block.name.trim().toLowerCase();
426
450
  const toolArgs = block.input;
427
451
  const firstVal = Object.values(toolArgs)[0];
428
452
  const argPreview = String(firstVal ?? "").slice(0, 100) || "—";
@@ -48,14 +48,18 @@ function getTools() {
48
48
  const { web_search: _, ...rest } = TOOLS;
49
49
  return rest;
50
50
  }
51
+ function normalizeToolName(name) {
52
+ return name.trim().toLowerCase();
53
+ }
51
54
  export async function runTool(name, args) {
52
- if (name === "web_search" && !getBraveSearchApiKey()) {
55
+ const canonical = normalizeToolName(name);
56
+ if (canonical === "web_search" && !getBraveSearchApiKey()) {
53
57
  return "error: Brave Search API key not set. Use /brave or set BRAVE_API_KEY to enable web search.";
54
58
  }
55
59
  const tools = getTools();
56
- const def = tools[name];
60
+ const def = tools[canonical];
57
61
  if (!def)
58
- return `error: Unknown tool: ${name}`;
62
+ return `error: Unknown tool: ${canonical}`;
59
63
  try {
60
64
  const result = await def[2](args);
61
65
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ideacode",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "CLI TUI for AI agents via OpenRouter — agentic loop, tools, markdown",
5
5
  "type": "module",
6
6
  "repository": {