@nghyane/arcane 0.1.19 → 0.1.20

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +7 -7
  3. package/src/lsp/clients/biome-client.ts +1 -1
  4. package/src/lsp/edits.ts +1 -1
  5. package/src/lsp/index.ts +1 -1
  6. package/src/lsp/render.ts +3 -2
  7. package/src/lsp/utils.ts +1 -1
  8. package/src/main.ts +2 -2
  9. package/src/modes/components/assistant-message.ts +55 -25
  10. package/src/modes/components/bash-execution.ts +31 -0
  11. package/src/modes/components/context-group.ts +30 -3
  12. package/src/modes/components/model-selector.ts +35 -9
  13. package/src/modes/components/python-execution.ts +37 -0
  14. package/src/modes/components/tool-execution.ts +3 -4
  15. package/src/modes/controllers/event-controller.ts +43 -11
  16. package/src/modes/utils/ui-helpers.ts +1 -1
  17. package/src/patch/edit-tool.ts +13 -24
  18. package/src/patch/hashline.ts +105 -3
  19. package/src/patch/schemas.ts +2 -2
  20. package/src/prompts/agents/explore.md +1 -1
  21. package/src/prompts/agents/librarian.md +1 -1
  22. package/src/prompts/system/system-prompt.md +0 -1
  23. package/src/session/agent-session.ts +28 -27
  24. package/src/task/index.ts +1 -9
  25. package/src/task/render.ts +3 -3
  26. package/src/tools/ask.ts +0 -2
  27. package/src/tools/bash.ts +6 -3
  28. package/src/tools/browser.ts +1 -1
  29. package/src/tools/default-renderer.ts +7 -5
  30. package/src/tools/fetch.ts +5 -2
  31. package/src/tools/find-thread.ts +5 -2
  32. package/src/tools/find.ts +3 -3
  33. package/src/tools/gemini-image.ts +18 -10
  34. package/src/tools/github.ts +2 -2
  35. package/src/tools/grep.ts +3 -3
  36. package/src/tools/notebook.ts +8 -2
  37. package/src/tools/python.ts +3 -2
  38. package/src/tools/read-thread.ts +5 -2
  39. package/src/tools/read.ts +6 -3
  40. package/src/tools/render-mermaid.ts +3 -7
  41. package/src/tools/save-memory.ts +6 -3
  42. package/src/tools/ssh.ts +6 -3
  43. package/src/tools/todo-write.ts +6 -3
  44. package/src/tools/undo-edit.ts +5 -2
  45. package/src/ui/render-utils.ts +1 -1
  46. package/src/utils/file-mentions.ts +1 -1
  47. package/src/web/github-client.ts +2 -1
  48. package/src/web/scrapers/youtube.ts +1 -1
  49. package/src/web/search/render.ts +11 -2
  50. package/src/prompts/tools/render-mermaid.md +0 -9
@@ -429,10 +429,10 @@ export class GitHubTool implements AgentTool<typeof schema, GitHubToolDetails, T
429
429
 
430
430
  async execute(
431
431
  _toolCallId: string,
432
- params: Record<string, unknown>,
432
+ params: GitHubInput,
433
433
  signal?: AbortSignal,
434
434
  ): Promise<AgentToolResult<GitHubToolDetails>> {
435
- const input = params as unknown as GitHubInput;
435
+ const input = params;
436
436
  const details: GitHubToolDetails = {
437
437
  action: input.action,
438
438
  owner: input.owner,
package/src/tools/grep.ts CHANGED
@@ -18,7 +18,7 @@ import { resolveToCwd } from "./path-utils";
18
18
  import { ToolError } from "./tool-errors";
19
19
 
20
20
  const grepSchema = Type.Object({
21
- pattern: Type.String({ description: "Regex pattern to search for" }),
21
+ pattern: Type.String({ description: "Regex pattern to search for", minLength: 1 }),
22
22
  path: Type.Optional(Type.String({ description: "Directory or file to search (default: cwd)" })),
23
23
  glob: Type.Optional(Type.String({ description: 'Glob filter for file paths (e.g. "*.ts")' })),
24
24
  type: Type.Optional(Type.String({ description: 'File extension filter without dot (e.g. "ts")' })),
@@ -278,7 +278,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails, T
278
278
  });
279
279
  }
280
280
 
281
- renderCall(args: GrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
281
+ renderCall(args: GrepRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
282
282
  const meta: string[] = [];
283
283
  if (args.path) meta.push(`in ${args.path}`);
284
284
  if (args.glob) meta.push(`glob:${args.glob}`);
@@ -295,7 +295,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails, T
295
295
  if (args.offset !== undefined && args.offset > 0) meta.push(`offset:${args.offset}`);
296
296
 
297
297
  const text = renderStatusLine(
298
- { icon: "pending", title: "Grep", description: args.pattern || "?", meta },
298
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Grep", description: args.pattern || "?", meta },
299
299
  uiTheme,
300
300
  );
301
301
  return new Text(text, 0, 0);
@@ -178,7 +178,7 @@ export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookTo
178
178
  });
179
179
  }
180
180
 
181
- renderCall(args: NotebookRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
181
+ renderCall(args: NotebookRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
182
182
  const meta: string[] = [];
183
183
  const notebookPath = args.notebookPath ?? args.notebook_path;
184
184
  const cellNumber = args.cellNumber ?? args.cell_index;
@@ -188,7 +188,13 @@ export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookTo
188
188
  if (cellType) meta.push(`type:${cellType}`);
189
189
 
190
190
  const text = renderStatusLine(
191
- { icon: "pending", title: "Notebook", description: args.action || "?", meta },
191
+ {
192
+ icon: "running",
193
+ spinnerFrame: options.spinnerFrame,
194
+ title: "Notebook",
195
+ description: args.action || "?",
196
+ meta,
197
+ },
192
198
  uiTheme,
193
199
  );
194
200
  return new Text(text, 0, 0);
@@ -476,7 +476,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema, any, Theme> {
476
476
  return context;
477
477
  }
478
478
 
479
- renderCall(args: PythonRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
479
+ renderCall(args: PythonRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
480
480
  const cells = args.cells ?? [];
481
481
  const cwd = getProjectDir();
482
482
  let displayWorkdir = args.cwd;
@@ -526,8 +526,9 @@ export class PythonTool implements AgentTool<typeof pythonSchema, any, Theme> {
526
526
  index: i,
527
527
  total: cells.length,
528
528
  title: combinedTitle,
529
- status: "pending",
529
+ status: "running",
530
530
  width,
531
+ spinnerFrame: options.spinnerFrame,
531
532
  codeMaxLines: PYTHON_DEFAULT_PREVIEW_LINES,
532
533
  expanded: true,
533
534
  },
@@ -377,9 +377,12 @@ export class ReadThreadTool implements AgentTool<typeof readThreadSchema, ReadTh
377
377
  };
378
378
  }
379
379
 
380
- renderCall(args: ReadThreadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
380
+ renderCall(args: ReadThreadRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
381
381
  const meta = args.threadId ? [args.threadId] : [];
382
- const text = renderStatusLine({ icon: "pending", title: "Read Thread", meta }, uiTheme);
382
+ const text = renderStatusLine(
383
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Read Thread", meta },
384
+ uiTheme,
385
+ );
383
386
  return new Text(text, 0, 0);
384
387
  }
385
388
 
package/src/tools/read.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs/promises";
2
- import path from "node:path";
2
+ import * as path from "node:path";
3
3
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@nghyane/arcane-agent";
4
4
  import type { ImageContent, TextContent } from "@nghyane/arcane-ai";
5
5
  import { FileType, glob } from "@nghyane/arcane-natives";
@@ -1055,7 +1055,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails, T
1055
1055
  return resultBuilder.done();
1056
1056
  }
1057
1057
 
1058
- renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
1058
+ renderCall(args: ReadRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
1059
1059
  const rawPath = args.file_path || args.path || "";
1060
1060
  const filePath = shortenPath(rawPath);
1061
1061
  const offset = args.offset;
@@ -1068,7 +1068,10 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails, T
1068
1068
  pathDisplay += `:${startLine}${endLine ? `-${endLine}` : ""}`;
1069
1069
  }
1070
1070
 
1071
- const text = renderStatusLine({ icon: "pending", title: "Read", description: pathDisplay }, uiTheme);
1071
+ const text = renderStatusLine(
1072
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Read", description: pathDisplay },
1073
+ uiTheme,
1074
+ );
1072
1075
  return new Text(text, 0, 0);
1073
1076
  }
1074
1077
 
@@ -1,8 +1,6 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@nghyane/arcane-agent";
2
2
  import { type AsciiRenderOptions, renderMermaidAscii } from "@nghyane/arcane-utils";
3
3
  import { type Static, Type } from "@sinclair/typebox";
4
- import { renderPromptTemplate } from "../config/prompt-templates";
5
- import renderMermaidDescription from "../prompts/tools/render-mermaid.md" with { type: "text" };
6
4
  import type { ToolSession } from "./index";
7
5
  import { allocateOutputArtifact } from "./output-utils";
8
6
 
@@ -38,14 +36,12 @@ export interface RenderMermaidToolDetails {
38
36
  export class RenderMermaidTool implements AgentTool<typeof renderMermaidSchema, RenderMermaidToolDetails> {
39
37
  readonly name = "render_mermaid";
40
38
  readonly label = "RenderMermaid";
41
- readonly description: string;
39
+ readonly description =
40
+ "Convert Mermaid graph source into ASCII diagram output. Returns ASCII diagram text. Saves full output to an artifact URL when artifact storage is available.";
42
41
  readonly parameters = renderMermaidSchema;
43
42
  readonly strict = true;
44
43
 
45
- constructor(private readonly session: ToolSession) {
46
- this.description = renderPromptTemplate(renderMermaidDescription);
47
- }
48
-
44
+ constructor(private readonly session: ToolSession) {}
49
45
  async execute(
50
46
  _toolCallId: string,
51
47
  params: RenderMermaidParams,
@@ -11,7 +11,7 @@ import { shortenPath, TRUNCATE_LENGTHS, truncateToWidth } from "../ui/render-uti
11
11
  import type { ToolSession } from ".";
12
12
 
13
13
  const saveMemorySchema = Type.Object({
14
- fact: Type.String({ description: "A clear, self-contained statement to remember across sessions" }),
14
+ fact: Type.String({ description: "A clear, self-contained statement to remember across sessions", minLength: 1 }),
15
15
  });
16
16
 
17
17
  type SaveMemoryParams = Static<typeof saveMemorySchema>;
@@ -153,10 +153,13 @@ export class SaveMemoryTool implements AgentTool<typeof saveMemorySchema, SaveMe
153
153
  };
154
154
  }
155
155
 
156
- renderCall(args: SaveMemoryRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
156
+ renderCall(args: SaveMemoryRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
157
157
  const preview = args.fact ? truncateToWidth(args.fact, TRUNCATE_LENGTHS.CONTENT) : "";
158
158
  const meta = preview ? [preview] : [];
159
- const text = renderStatusLine({ icon: "pending", title: "Save Memory", meta }, uiTheme);
159
+ const text = renderStatusLine(
160
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Save Memory", meta },
161
+ uiTheme,
162
+ );
160
163
  return new Text(text, 0, 0);
161
164
  }
162
165
 
package/src/tools/ssh.ts CHANGED
@@ -22,7 +22,7 @@ const sshSchema = Type.Object({
22
22
  host: Type.String({ description: "SSH host alias (from ~/.ssh/config)" }),
23
23
  command: Type.String({ description: "Shell command to execute on the remote host" }),
24
24
  cwd: Type.Optional(Type.String({ description: "Working directory on the remote host" })),
25
- timeout: Type.Optional(Type.Number({ description: "Timeout in milliseconds" })),
25
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds" })),
26
26
  });
27
27
 
28
28
  export interface SSHToolDetails {
@@ -187,10 +187,13 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails, Them
187
187
  return resultBuilder.done();
188
188
  }
189
189
 
190
- renderCall(args: SshRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
190
+ renderCall(args: SshRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
191
191
  const host = args.host || "…";
192
192
  const command = args.command || "…";
193
- const text = renderStatusLine({ icon: "pending", title: "SSH", description: `[${host}] $ ${command}` }, uiTheme);
193
+ const text = renderStatusLine(
194
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "SSH", description: `[${host}] $ ${command}` },
195
+ uiTheme,
196
+ );
194
197
  return new Text(text, 0, 0);
195
198
  }
196
199
 
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import * as path from "node:path";
2
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@nghyane/arcane-agent";
3
3
  import { StringEnum } from "@nghyane/arcane-ai";
4
4
  import type { Component } from "@nghyane/arcane-tui";
@@ -162,10 +162,13 @@ export class TodoWriteTool implements AgentTool<typeof todoWriteSchema, TodoWrit
162
162
  };
163
163
  }
164
164
 
165
- renderCall(args: TodoWriteRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
165
+ renderCall(args: TodoWriteRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
166
166
  const count = args.todos?.length ?? 0;
167
167
  const meta = count > 0 ? [`${count} items`] : ["empty"];
168
- const text = renderStatusLine({ icon: "pending", title: "Todo Write", meta }, uiTheme);
168
+ const text = renderStatusLine(
169
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Todo Write", meta },
170
+ uiTheme,
171
+ );
169
172
  return new Text(text, 0, 0);
170
173
  }
171
174
 
@@ -75,10 +75,13 @@ export class UndoEditTool implements AgentTool<typeof undoEditSchema, UndoEditTo
75
75
  });
76
76
  }
77
77
 
78
- renderCall(args: UndoEditRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
78
+ renderCall(args: UndoEditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
79
79
  const filePath = shortenPath(args.path ?? "");
80
80
  const pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
81
- const text = renderStatusLine({ icon: "pending", title: "Undo", description: pathDisplay }, uiTheme);
81
+ const text = renderStatusLine(
82
+ { icon: "running", spinnerFrame: options.spinnerFrame, title: "Undo", description: pathDisplay },
83
+ uiTheme,
84
+ );
82
85
  return new Text(text, 0, 0);
83
86
  }
84
87
 
@@ -25,7 +25,7 @@ const TOOL_TIERS: Record<string, ToolTier> = {
25
25
  search_code: "quiet",
26
26
  lsp: "quiet",
27
27
  browser: "quiet",
28
- github: "quiet",
28
+ github: "action",
29
29
  notebook: "quiet",
30
30
  undo_edit: "quiet",
31
31
  generate_image: "quiet",
@@ -6,7 +6,7 @@
6
6
  * so the agent doesn't need to read them manually.
7
7
  */
8
8
  import * as fs from "node:fs/promises";
9
- import path from "node:path";
9
+ import * as path from "node:path";
10
10
  import type { AgentMessage } from "@nghyane/arcane-agent";
11
11
  import { glob } from "@nghyane/arcane-natives";
12
12
  import { formatHashLines } from "../patch/hashline";
@@ -197,7 +197,8 @@ async function request<T = unknown>(endpoint: string, options: RequestOptions =
197
197
  return { data: null as T, ok: false, status: response.status, rateLimit };
198
198
  }
199
199
 
200
- const data = (await response.json()) as T;
200
+ const isRaw = response.headers.get("content-type")?.includes("application/json") === false;
201
+ const data = (isRaw ? await response.text() : await response.json()) as T;
201
202
 
202
203
  // Cache with ETag
203
204
  const etag = response.headers.get("etag");
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as os from "node:os";
3
- import path from "node:path";
3
+ import * as path from "node:path";
4
4
  import { ptree, Snowflake } from "@nghyane/arcane-utils";
5
5
  import { throwIfAborted } from "../../tools/tool-errors";
6
6
  import { ensureTool } from "../../utils/tools-manager";
@@ -102,11 +102,20 @@ export function renderSearchResult(
102
102
  /** Render web search call (query preview) */
103
103
  export function renderSearchCall(
104
104
  args: { query?: string; provider?: string; [key: string]: unknown },
105
- _options: RenderResultOptions,
105
+ options: RenderResultOptions,
106
106
  theme: Theme,
107
107
  ): Component {
108
108
  const provider = args.provider ?? "auto";
109
109
  const query = truncateToWidth(args.query ?? "", 80);
110
- const text = renderStatusLine({ icon: "pending", title: "Web Search", description: query, meta: [provider] }, theme);
110
+ const text = renderStatusLine(
111
+ {
112
+ icon: "running",
113
+ spinnerFrame: options.spinnerFrame,
114
+ title: "Web Search",
115
+ description: query,
116
+ meta: [provider],
117
+ },
118
+ theme,
119
+ );
111
120
  return new Text(text, 0, 0);
112
121
  }
@@ -1,9 +0,0 @@
1
- Convert Mermaid graph source into ASCII diagram output.
2
-
3
- Parameters:
4
- - `mermaid` (required): Mermaid graph text to render.
5
- - `config` (optional): JSON render configuration (spacing and layout options).
6
- Behavior:
7
- - Returns ASCII diagram text.
8
- - Saves full ASCII output to an artifact URL (`artifact://<id>`) when artifact storage is available.
9
- - Returns an error when the Mermaid input is invalid or rendering fails.