@aigne/cli 1.74.0-beta.1 → 1.74.0-beta.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.
Files changed (128) hide show
  1. package/README.md +153 -14
  2. package/dist/commands/aigne.cjs +3 -4
  3. package/dist/commands/aigne.mjs +3 -4
  4. package/dist/commands/aigne.mjs.map +1 -1
  5. package/dist/commands/app/agent.cjs +2 -27
  6. package/dist/commands/app/agent.mjs +2 -26
  7. package/dist/commands/app/agent.mjs.map +1 -1
  8. package/dist/commands/create.cjs +1 -1
  9. package/dist/commands/create.mjs +1 -1
  10. package/dist/commands/eval.cjs +42 -3
  11. package/dist/commands/eval.mjs +43 -4
  12. package/dist/commands/eval.mjs.map +1 -1
  13. package/dist/commands/explain.cjs +340 -0
  14. package/dist/commands/explain.mjs +340 -0
  15. package/dist/commands/explain.mjs.map +1 -0
  16. package/dist/commands/hub.cjs +111 -14
  17. package/dist/commands/hub.mjs +111 -14
  18. package/dist/commands/hub.mjs.map +1 -1
  19. package/dist/commands/observe.cjs +44 -1
  20. package/dist/commands/observe.mjs +44 -1
  21. package/dist/commands/observe.mjs.map +1 -1
  22. package/dist/commands/run-skill.cjs +29 -13
  23. package/dist/commands/run-skill.mjs +29 -13
  24. package/dist/commands/run-skill.mjs.map +1 -1
  25. package/dist/commands/run.cjs +5 -4
  26. package/dist/commands/run.mjs +5 -4
  27. package/dist/commands/run.mjs.map +1 -1
  28. package/dist/commands/serve-mcp.cjs +49 -4
  29. package/dist/commands/serve-mcp.mjs +49 -3
  30. package/dist/commands/serve-mcp.mjs.map +1 -1
  31. package/dist/commands/shell.cjs +106 -0
  32. package/dist/commands/shell.mjs +105 -0
  33. package/dist/commands/shell.mjs.map +1 -0
  34. package/dist/shell/repl.cjs +544 -0
  35. package/dist/shell/repl.mjs +543 -0
  36. package/dist/shell/repl.mjs.map +1 -0
  37. package/dist/shell/tools/ask-user.cjs +191 -0
  38. package/dist/shell/tools/ask-user.mjs +187 -0
  39. package/dist/shell/tools/ask-user.mjs.map +1 -0
  40. package/dist/shell/tools/index.cjs +2 -0
  41. package/dist/shell/tools/index.mjs +4 -0
  42. package/dist/shell/tools/render.cjs +189 -0
  43. package/dist/shell/tools/render.mjs +186 -0
  44. package/dist/shell/tools/render.mjs.map +1 -0
  45. package/dist/tracer/terminal.cjs +120 -133
  46. package/dist/tracer/terminal.mjs +121 -134
  47. package/dist/tracer/terminal.mjs.map +1 -1
  48. package/dist/ui/utils/terminal-select.cjs +73 -0
  49. package/dist/ui/utils/terminal-select.mjs +72 -0
  50. package/dist/ui/utils/terminal-select.mjs.map +1 -0
  51. package/dist/utils/agent-v1.cjs +2 -2
  52. package/dist/utils/agent-v1.mjs +2 -2
  53. package/dist/utils/aigne-hub/credential.cjs +3 -3
  54. package/dist/utils/aigne-hub/credential.mjs +3 -3
  55. package/dist/utils/aigne-hub/model.cjs +2 -2
  56. package/dist/utils/aigne-hub/model.mjs +1 -1
  57. package/dist/utils/ascii-logo.cjs +12 -13
  58. package/dist/utils/ascii-logo.d.cts.map +1 -1
  59. package/dist/utils/ascii-logo.d.mts.map +1 -1
  60. package/dist/utils/ascii-logo.mjs +12 -13
  61. package/dist/utils/ascii-logo.mjs.map +1 -1
  62. package/dist/utils/evaluation/evaluator.cjs +1 -1
  63. package/dist/utils/evaluation/evaluator.mjs +1 -1
  64. package/dist/utils/evaluation/reporter.cjs +78 -1
  65. package/dist/utils/evaluation/reporter.mjs +76 -1
  66. package/dist/utils/evaluation/reporter.mjs.map +1 -1
  67. package/dist/utils/exit-codes.cjs +73 -0
  68. package/dist/utils/exit-codes.d.cts +52 -0
  69. package/dist/utils/exit-codes.d.cts.map +1 -0
  70. package/dist/utils/exit-codes.d.mts +52 -0
  71. package/dist/utils/exit-codes.d.mts.map +1 -0
  72. package/dist/utils/exit-codes.mjs +71 -0
  73. package/dist/utils/exit-codes.mjs.map +1 -0
  74. package/dist/utils/output.cjs +61 -0
  75. package/dist/utils/output.d.cts +43 -0
  76. package/dist/utils/output.d.cts.map +1 -0
  77. package/dist/utils/output.d.mts +43 -0
  78. package/dist/utils/output.d.mts.map +1 -0
  79. package/dist/utils/output.mjs +56 -0
  80. package/dist/utils/output.mjs.map +1 -0
  81. package/dist/utils/run-chat-loop.cjs +1 -1
  82. package/dist/utils/run-chat-loop.mjs +1 -1
  83. package/dist/utils/run-with-aigne.cjs +10 -3
  84. package/dist/utils/run-with-aigne.d.cts.map +1 -1
  85. package/dist/utils/run-with-aigne.d.mts.map +1 -1
  86. package/dist/utils/run-with-aigne.mjs +10 -3
  87. package/dist/utils/run-with-aigne.mjs.map +1 -1
  88. package/dist/utils/serve-mcp.cjs +1 -1
  89. package/dist/utils/serve-mcp.mjs +1 -1
  90. package/dist/utils/view.cjs +34 -0
  91. package/dist/utils/view.d.cts +47 -0
  92. package/dist/utils/view.d.cts.map +1 -0
  93. package/dist/utils/view.d.mts +47 -0
  94. package/dist/utils/view.d.mts.map +1 -0
  95. package/dist/utils/view.mjs +33 -0
  96. package/dist/utils/view.mjs.map +1 -0
  97. package/dist/utils/yargs.cjs +27 -5
  98. package/dist/utils/yargs.d.cts +13 -0
  99. package/dist/utils/yargs.d.cts.map +1 -1
  100. package/dist/utils/yargs.d.mts +14 -1
  101. package/dist/utils/yargs.d.mts.map +1 -1
  102. package/dist/utils/yargs.mjs +32 -10
  103. package/dist/utils/yargs.mjs.map +1 -1
  104. package/package.json +21 -17
  105. package/dist/commands/app/app.cjs +0 -92
  106. package/dist/commands/app/app.mjs +0 -90
  107. package/dist/commands/app/app.mjs.map +0 -1
  108. package/dist/commands/app/cli.cjs +0 -6
  109. package/dist/commands/app/cli.d.cts +0 -1
  110. package/dist/commands/app/cli.d.mts +0 -1
  111. package/dist/commands/app/cli.mjs +0 -8
  112. package/dist/commands/app/cli.mjs.map +0 -1
  113. package/dist/commands/app/upgrade.cjs +0 -243
  114. package/dist/commands/app/upgrade.mjs +0 -240
  115. package/dist/commands/app/upgrade.mjs.map +0 -1
  116. package/dist/commands/app.cjs +0 -53
  117. package/dist/commands/app.mjs +0 -53
  118. package/dist/commands/app.mjs.map +0 -1
  119. package/dist/commands/deploy.cjs +0 -237
  120. package/dist/commands/deploy.mjs +0 -237
  121. package/dist/commands/deploy.mjs.map +0 -1
  122. package/dist/utils/listr.cjs +0 -226
  123. package/dist/utils/listr.d.cts +0 -71
  124. package/dist/utils/listr.d.cts.map +0 -1
  125. package/dist/utils/listr.d.mts +0 -71
  126. package/dist/utils/listr.d.mts.map +0 -1
  127. package/dist/utils/listr.mjs +0 -222
  128. package/dist/utils/listr.mjs.map +0 -1
@@ -0,0 +1,544 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_utils_aigne_hub_model = require('../utils/aigne-hub/model.cjs');
3
+ const require_terminal_input = require('../ui/utils/terminal-input.cjs');
4
+ const require_terminal = require('../tracer/terminal.cjs');
5
+ let chalk = require("chalk");
6
+ chalk = require_rolldown_runtime.__toESM(chalk);
7
+ let node_path = require("node:path");
8
+ let _aigne_core = require("@aigne/core");
9
+ let _aigne_core_loader = require("@aigne/core/loader");
10
+
11
+ //#region src/shell/repl.ts
12
+ /**
13
+ * Get the prompt string based on current shell state
14
+ */
15
+ function getPrompt(state) {
16
+ if (state.config?.prompt) return state.config.prompt.replace("{mode}", state.mode).replace("{agent}", state.agent?.name || "");
17
+ if (state.mode === "agent" && state.agent?.name) return `aigne [${state.agent.name}]>`;
18
+ return "aigne>";
19
+ }
20
+ /**
21
+ * Create or recreate UserAgent for the current agent
22
+ * Call this when agent changes or when clearing conversation
23
+ */
24
+ function resetUserAgent(state) {
25
+ state.userAgent = state.aigne.invoke(state.agent);
26
+ }
27
+ /**
28
+ * Helper to resolve path
29
+ */
30
+ function resolvePath(path) {
31
+ return (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(process.cwd(), path);
32
+ }
33
+ /**
34
+ * /load command - Load an agent from a path
35
+ */
36
+ async function handleLoad(args, state) {
37
+ if (args.length === 0) return { message: `${chalk.default.red("Error:")} No path specified.\nUsage: /load <path>` };
38
+ const path = resolvePath(args[0]);
39
+ try {
40
+ const agent = await (0, _aigne_core_loader.loadAgent)(path, { model: (modelOptions) => require_utils_aigne_hub_model.loadChatModel({ ...modelOptions }) });
41
+ const name = agent.name || args[0] || "unnamed";
42
+ state.loadedAgents.set(name, {
43
+ agent,
44
+ path
45
+ });
46
+ state.agent = agent;
47
+ state.mode = "agent";
48
+ resetUserAgent(state);
49
+ return { message: chalk.default.green(`Loaded: ${name}`) };
50
+ } catch (error) {
51
+ return { message: `${chalk.default.red("Error:")} Failed to load agent from ${path}\n${error instanceof Error ? error.message : String(error)}` };
52
+ }
53
+ }
54
+ /**
55
+ * /list command - List all loaded agents
56
+ */
57
+ function handleList(_args, state) {
58
+ if (state.loadedAgents.size === 0) return { message: chalk.default.gray("No agents loaded. Use /load <path> to load an agent.") };
59
+ const lines = ["Loaded agents:"];
60
+ for (const [name, { path }] of state.loadedAgents) {
61
+ const prefix = state.mode === "agent" && state.agent?.name === name ? chalk.default.green("* ") : " ";
62
+ lines.push(`${prefix}${name} ${chalk.default.gray(`(${path})`)}`);
63
+ }
64
+ return { message: lines.join("\n") };
65
+ }
66
+ /**
67
+ * /switch command - Switch to a different loaded agent
68
+ */
69
+ function handleSwitch(args, state) {
70
+ if (state.loadedAgents.size === 0) return { message: chalk.default.gray("No agents loaded. Use /load <path> to load an agent first.") };
71
+ const name = args[0];
72
+ if (!name) {
73
+ const lines = ["Available agents:"];
74
+ let i = 1;
75
+ for (const [agentName] of state.loadedAgents) {
76
+ lines.push(` ${i}. ${agentName}`);
77
+ i++;
78
+ }
79
+ lines.push("\nUsage: /switch <name>");
80
+ return { message: lines.join("\n") };
81
+ }
82
+ const loaded = state.loadedAgents.get(name);
83
+ if (!loaded) return { message: `${chalk.default.red("Agent not found:")} ${name}` };
84
+ state.agent = loaded.agent;
85
+ state.mode = "agent";
86
+ resetUserAgent(state);
87
+ return { message: chalk.default.green(`Switched to: ${name}`) };
88
+ }
89
+ /**
90
+ * /unload command - Unload an agent
91
+ */
92
+ function handleUnload(args, state) {
93
+ if (state.loadedAgents.size === 0) return { message: chalk.default.gray("No agents loaded.") };
94
+ const name = args[0];
95
+ if (!name) return { message: `${chalk.default.red("Error:")} No agent name specified.\nUsage: /unload <name>` };
96
+ if (!state.loadedAgents.has(name)) return { message: `${chalk.default.red("Agent not found:")} ${name}` };
97
+ const isCurrentAgent = state.mode === "agent" && state.agent?.name === name;
98
+ state.loadedAgents.delete(name);
99
+ if (isCurrentAgent) {
100
+ const remaining = state.loadedAgents.values().next();
101
+ if (!remaining.done && remaining.value) state.agent = remaining.value.agent;
102
+ else {
103
+ state.agent = state.defaultAgent;
104
+ state.mode = "direct";
105
+ }
106
+ resetUserAgent(state);
107
+ }
108
+ return { message: chalk.default.green(`Unloaded: ${name}`) };
109
+ }
110
+ /**
111
+ * /info command - Show current agent info
112
+ */
113
+ function handleInfo(_args, state) {
114
+ const lines = [];
115
+ lines.push(`${chalk.default.cyan("Current Mode:")} ${state.mode}`);
116
+ if (state.mode === "agent" && state.agent) {
117
+ lines.push(`${chalk.default.cyan("Agent:")} ${state.agent.name || "unnamed"}`);
118
+ if (state.agent instanceof _aigne_core.AIAgent && state.agent.instructions) {
119
+ const instructions = typeof state.agent.instructions === "string" ? state.agent.instructions : "function-based instructions";
120
+ lines.push(`${chalk.default.cyan("Instructions:")} ${instructions.slice(0, 100)}...`);
121
+ }
122
+ } else lines.push(`${chalk.default.cyan("Agent:")} default assistant (direct mode)`);
123
+ lines.push(`${chalk.default.cyan("Loaded Agents:")} ${state.loadedAgents.size}`);
124
+ return { message: lines.join("\n") };
125
+ }
126
+ /**
127
+ * /mode command - Display or switch mode
128
+ */
129
+ function handleMode(args, state) {
130
+ if (args.length === 0) return { message: `${chalk.default.cyan("Current mode:")} ${state.mode}\n\nAvailable modes:\n direct - Direct LLM conversation\n agent - Conversation through loaded agent` };
131
+ const targetMode = args[0]?.toLowerCase();
132
+ if (targetMode === "direct") {
133
+ state.mode = "direct";
134
+ state.agent = state.defaultAgent;
135
+ resetUserAgent(state);
136
+ return { message: chalk.default.green("Switched to direct mode.") };
137
+ }
138
+ if (targetMode === "agent") {
139
+ if (state.loadedAgents.size === 0) return { message: `${chalk.default.red("Error:")} No agents loaded. Use /load <path> first.` };
140
+ if (state.mode === "agent" && state.agent !== state.defaultAgent) return { message: chalk.default.gray("Already in agent mode.") };
141
+ const firstAgent = state.loadedAgents.values().next().value;
142
+ if (firstAgent) {
143
+ state.agent = firstAgent.agent;
144
+ state.mode = "agent";
145
+ resetUserAgent(state);
146
+ return { message: chalk.default.green(`Switched to agent mode with: ${firstAgent.agent.name}`) };
147
+ }
148
+ return { message: `${chalk.default.red("Error:")} No agents loaded.` };
149
+ }
150
+ return { message: `${chalk.default.red("Error:")} Unknown mode: ${targetMode}\nAvailable modes: direct, agent` };
151
+ }
152
+ /**
153
+ * /run command - Run an agent from a path (single execution)
154
+ */
155
+ async function handleRun(args, state) {
156
+ if (args.length === 0) return { message: `${chalk.default.red("Error:")} No path specified.\nUsage: /run <path> [input]` };
157
+ const path = resolvePath(args[0]);
158
+ const input = args.slice(1).join(" ");
159
+ try {
160
+ const agent = await (0, _aigne_core_loader.loadAgent)(path, { model: (modelOptions) => require_utils_aigne_hub_model.loadChatModel({ ...modelOptions }) });
161
+ const { aigne } = state;
162
+ const tracer = new require_terminal.TerminalTracer(aigne.newContext(), {});
163
+ const message = input ? { message: input } : {};
164
+ await tracer.run(agent, message);
165
+ return {};
166
+ } catch (error) {
167
+ return { message: `${chalk.default.red("Error:")} Failed to run agent from ${path}\n${error instanceof Error ? error.message : String(error)}` };
168
+ }
169
+ }
170
+ /**
171
+ * /eval command - Placeholder for agent evaluation
172
+ */
173
+ function handleEval(args, _state) {
174
+ if (args.length === 0) return { message: `${chalk.default.red("Error:")} Missing arguments.\nUsage: /eval <agent> --dataset <path>\n\nFor full evaluation, use: aigne eval <path> --agent <name> --dataset <path>` };
175
+ return { message: `${chalk.default.yellow("Note:")} Full evaluation is better done via CLI.\nRun: ${chalk.default.cyan("aigne eval")} ${args.join(" ")}` };
176
+ }
177
+ /**
178
+ * CLI command documentation for /explain
179
+ */
180
+ const CLI_COMMAND_DOCS = {
181
+ run: {
182
+ purpose: "Run an AIGNE agent from a directory or URL",
183
+ usage: "aigne run [path] [--input <message>] [--model <model>] [--interactive] [--json]",
184
+ examples: [
185
+ "aigne run ./my-agent",
186
+ "aigne run ./my-agent --input 'Hello'",
187
+ "aigne run ./my-agent --interactive"
188
+ ]
189
+ },
190
+ eval: {
191
+ purpose: "Evaluate an AIGNE agent against test cases",
192
+ usage: "aigne eval [path] --agent <name> --dataset <path> [--output <path>]",
193
+ examples: ["aigne eval . --agent my-agent --dataset tests.json", "aigne eval . --agent my-agent --dataset tests.json --output results.csv"]
194
+ },
195
+ skill: {
196
+ purpose: "Run Agent Skills from specified directories",
197
+ usage: "aigne skill <paths..> [--input <message>] [--interactive]",
198
+ examples: ["aigne skill ./skill1 ./skill2", "aigne skill ./my-skill --interactive"]
199
+ },
200
+ test: {
201
+ purpose: "Run agent tests in the specified directory",
202
+ usage: "aigne test [--filter <pattern>] [--json]",
203
+ examples: ["aigne test", "aigne test --filter 'my-test'"]
204
+ },
205
+ create: {
206
+ purpose: "Create a new AIGNE agent project",
207
+ usage: "aigne create [path] [--template <name>]",
208
+ examples: ["aigne create my-agent", "aigne create my-agent --template chatbot"]
209
+ },
210
+ "serve-mcp": {
211
+ purpose: "Serve agents as an MCP (Model Context Protocol) server",
212
+ usage: "aigne serve-mcp [path] [--host <host>] [--port <port>]",
213
+ examples: ["aigne serve-mcp .", "aigne serve-mcp ./my-agent --port 8080"]
214
+ },
215
+ hub: {
216
+ purpose: "Manage AIGNE Hub connections",
217
+ usage: "aigne hub <command>",
218
+ examples: [
219
+ "aigne hub list",
220
+ "aigne hub connect",
221
+ "aigne hub status"
222
+ ]
223
+ },
224
+ shell: {
225
+ purpose: "Start an interactive shell for LLM conversation",
226
+ usage: "aigne shell [--model <model>]",
227
+ examples: ["aigne shell", "aigne shell --model gpt-4"]
228
+ }
229
+ };
230
+ /**
231
+ * /explain command - Show CLI command documentation
232
+ */
233
+ function handleExplain(args, _state) {
234
+ if (args.length === 0) {
235
+ const lines$1 = [chalk.default.bold("Available commands:")];
236
+ for (const [name, doc$1] of Object.entries(CLI_COMMAND_DOCS)) lines$1.push(` ${chalk.default.cyan(name.padEnd(12))} ${doc$1.purpose}`);
237
+ lines$1.push("");
238
+ lines$1.push("Use /explain <command> for detailed documentation.");
239
+ return { message: lines$1.join("\n") };
240
+ }
241
+ const cmdName = args[0]?.toLowerCase();
242
+ const doc = CLI_COMMAND_DOCS[cmdName];
243
+ if (!doc) return { message: `${chalk.default.red("Error:")} unknown command: ${cmdName}\n\nAvailable commands: ${Object.keys(CLI_COMMAND_DOCS).join(", ")}` };
244
+ const lines = [
245
+ `${chalk.default.bold("COMMAND")} aigne ${cmdName}`,
246
+ "",
247
+ chalk.default.bold("PURPOSE"),
248
+ ` ${doc.purpose}`,
249
+ "",
250
+ chalk.default.bold("USAGE"),
251
+ ` ${doc.usage}`
252
+ ];
253
+ if (doc.examples?.length) {
254
+ lines.push("", chalk.default.bold("EXAMPLES"));
255
+ for (const ex of doc.examples) lines.push(` ${chalk.default.gray("$")} ${ex}`);
256
+ }
257
+ return { message: lines.join("\n") };
258
+ }
259
+ /**
260
+ * /sessions command - List available sessions
261
+ */
262
+ function handleSessions(_args, state) {
263
+ if (!state.sessionId) return { message: chalk.default.yellow("Session history not configured. Start shell with history enabled to use this feature.") };
264
+ return { message: [
265
+ chalk.default.bold("Current Session:"),
266
+ ` ${chalk.default.cyan("ID:")} ${state.sessionId}`,
267
+ "",
268
+ chalk.default.gray("Note: Full session listing requires AFS History provider.")
269
+ ].join("\n") };
270
+ }
271
+ /**
272
+ * /history command - Show conversation history
273
+ */
274
+ function handleHistory(args, state) {
275
+ if (!state.sessionId) return { message: chalk.default.yellow("Session history not configured. Start shell with history enabled to use this feature.") };
276
+ const count = args[0] ? Number.parseInt(args[0], 10) : 10;
277
+ if (Number.isNaN(count)) return { message: `${chalk.default.red("Error:")} Invalid count: ${args[0]}\nUsage: /history [count]` };
278
+ return { message: chalk.default.gray(`Showing last ${count} messages (feature requires AFS History).`) };
279
+ }
280
+ /**
281
+ * /restore command - Restore a previous session
282
+ */
283
+ function handleRestore(args, state) {
284
+ if (args.length === 0) return { message: `${chalk.default.red("Error:")} No session ID specified.\nUsage: /restore <sessionId>` };
285
+ if (!state.sessionId) return { message: chalk.default.yellow("Session history not configured. Cannot restore sessions without history provider.") };
286
+ const sessionId = args[0];
287
+ return { message: chalk.default.yellow(`Session restore not yet implemented. Requested session: ${sessionId}`) };
288
+ }
289
+ /**
290
+ * /export command - Export conversation
291
+ */
292
+ function handleExport(args, state) {
293
+ const format = args[0] || "json";
294
+ const path = args[1];
295
+ if (!state.sessionId) return { message: chalk.default.yellow("Session history not configured. Nothing to export.") };
296
+ return { message: [
297
+ chalk.default.bold("Export Options:"),
298
+ ` Format: ${format} (json, markdown, text)`,
299
+ ` Path: ${path || "(stdout)"}`,
300
+ "",
301
+ "Usage: /export [format] [path]"
302
+ ].join("\n") };
303
+ }
304
+ /**
305
+ * /clear command - Clear conversation history
306
+ */
307
+ function handleClear(_args, state) {
308
+ if (state.sessionId) state.sessionId = crypto.randomUUID();
309
+ resetUserAgent(state);
310
+ return { message: chalk.default.green("Conversation cleared.") };
311
+ }
312
+ /**
313
+ * /model command - Display or change model
314
+ */
315
+ async function handleModel(args, state) {
316
+ const { aigne } = state;
317
+ if (args.length === 0) {
318
+ const model = aigne.model;
319
+ if (!model) return { message: chalk.default.yellow("No model configured.") };
320
+ try {
321
+ const credential = await model.credential;
322
+ const modelName = credential?.model || "default";
323
+ const lines = [chalk.default.bold("Current Model:"), ` ${chalk.default.cyan("Model:")} ${modelName}`];
324
+ if (credential?.apiKey) {
325
+ const masked = `${credential.apiKey.slice(0, 4)}...${credential.apiKey.slice(-4)}`;
326
+ lines.push(` ${chalk.default.cyan("API Key:")} ${masked}`);
327
+ }
328
+ return { message: lines.join("\n") };
329
+ } catch {
330
+ return { message: `${chalk.default.cyan("Model:")} configured (details unavailable)` };
331
+ }
332
+ }
333
+ return { message: chalk.default.yellow("Changing model at runtime is not yet supported.\nRestart shell with: aigne shell --model " + args[0]) };
334
+ }
335
+ /**
336
+ * Help command categories with their commands
337
+ */
338
+ const HELP_CATEGORIES = [
339
+ {
340
+ name: "Commands",
341
+ commands: [{
342
+ cmd: "/help",
343
+ desc: "Show this help message"
344
+ }, {
345
+ cmd: "/exit",
346
+ desc: "Exit the shell",
347
+ alias: "/quit"
348
+ }]
349
+ },
350
+ {
351
+ name: "Agent Operations",
352
+ commands: [
353
+ {
354
+ cmd: "/load <path>",
355
+ desc: "Load an agent from a path"
356
+ },
357
+ {
358
+ cmd: "/list",
359
+ desc: "List loaded agents"
360
+ },
361
+ {
362
+ cmd: "/switch <name>",
363
+ desc: "Switch to a different agent"
364
+ },
365
+ {
366
+ cmd: "/unload <name>",
367
+ desc: "Unload an agent"
368
+ },
369
+ {
370
+ cmd: "/info",
371
+ desc: "Show current agent info"
372
+ }
373
+ ]
374
+ },
375
+ {
376
+ name: "Mode",
377
+ commands: [
378
+ {
379
+ cmd: "/mode",
380
+ desc: "Show current mode"
381
+ },
382
+ {
383
+ cmd: "/mode direct",
384
+ desc: "Switch to direct LLM mode"
385
+ },
386
+ {
387
+ cmd: "/mode agent",
388
+ desc: "Switch to agent mode"
389
+ }
390
+ ]
391
+ },
392
+ {
393
+ name: "CLI Bridge",
394
+ commands: [
395
+ {
396
+ cmd: "/run <path>",
397
+ desc: "Run an agent (single execution)"
398
+ },
399
+ {
400
+ cmd: "/eval <path>",
401
+ desc: "Evaluate an agent"
402
+ },
403
+ {
404
+ cmd: "/explain <cmd>",
405
+ desc: "Explain CLI command"
406
+ }
407
+ ]
408
+ },
409
+ {
410
+ name: "Session",
411
+ commands: [
412
+ {
413
+ cmd: "/sessions",
414
+ desc: "List available sessions"
415
+ },
416
+ {
417
+ cmd: "/history [n]",
418
+ desc: "Show conversation history"
419
+ },
420
+ {
421
+ cmd: "/restore <id>",
422
+ desc: "Restore a previous session"
423
+ },
424
+ {
425
+ cmd: "/export",
426
+ desc: "Export conversation"
427
+ },
428
+ {
429
+ cmd: "/clear",
430
+ desc: "Clear conversation"
431
+ }
432
+ ]
433
+ },
434
+ {
435
+ name: "Settings",
436
+ commands: [{
437
+ cmd: "/model",
438
+ desc: "Show current model info"
439
+ }]
440
+ }
441
+ ];
442
+ /**
443
+ * Generate help message based on state config
444
+ */
445
+ function generateHelp(state) {
446
+ const hiddenCommands = new Set(state.config?.commands?.hide || []);
447
+ const lines = [];
448
+ for (const category of HELP_CATEGORIES) {
449
+ const visibleCommands = category.commands.filter((c) => {
450
+ const baseCmd = c.cmd.split(" ")[0];
451
+ return !hiddenCommands.has(baseCmd);
452
+ });
453
+ if (visibleCommands.length === 0) continue;
454
+ lines.push(chalk.default.bold(`${category.name}:`));
455
+ for (const c of visibleCommands) {
456
+ const alias = c.alias ? ` ${chalk.default.gray(`(or ${c.alias})`)}` : "";
457
+ lines.push(` ${chalk.default.cyan(c.cmd.padEnd(18))}${alias} ${c.desc}`);
458
+ }
459
+ lines.push("");
460
+ }
461
+ lines.push(chalk.default.bold("Tips:"));
462
+ lines.push(" - Type your message and press Enter to chat");
463
+ lines.push(" - Press Ctrl+C to cancel current input");
464
+ lines.push(" - Press Ctrl+D to exit");
465
+ return lines.join("\n").trim();
466
+ }
467
+ const SLASH_COMMANDS = {
468
+ "/exit": () => ({ exit: true }),
469
+ "/quit": () => ({ exit: true }),
470
+ "/help": (_args, state) => ({ message: generateHelp(state) }),
471
+ "/load": handleLoad,
472
+ "/list": handleList,
473
+ "/switch": handleSwitch,
474
+ "/unload": handleUnload,
475
+ "/info": handleInfo,
476
+ "/mode": handleMode,
477
+ "/run": handleRun,
478
+ "/eval": handleEval,
479
+ "/explain": handleExplain,
480
+ "/sessions": handleSessions,
481
+ "/history": handleHistory,
482
+ "/restore": handleRestore,
483
+ "/export": handleExport,
484
+ "/clear": handleClear,
485
+ "/model": handleModel
486
+ };
487
+ /**
488
+ * Parse and execute a slash command if the input starts with /
489
+ * Returns undefined if not a slash command
490
+ */
491
+ async function parseSlashCommand(input, state) {
492
+ const trimmed = input.trim();
493
+ if (!trimmed.startsWith("/")) return void 0;
494
+ const parts = trimmed.split(/\s+/);
495
+ let cmd = parts[0]?.toLowerCase() ?? "";
496
+ const args = parts.slice(1);
497
+ if (state.config?.commands?.alias && cmd in state.config.commands.alias) {
498
+ const aliasTarget = state.config.commands.alias[cmd];
499
+ if (aliasTarget) {
500
+ const aliasParts = aliasTarget.split(/\s+/);
501
+ cmd = aliasParts[0]?.toLowerCase() ?? "";
502
+ args.unshift(...aliasParts.slice(1));
503
+ }
504
+ }
505
+ const handler = SLASH_COMMANDS[cmd];
506
+ if (handler) return await handler(args, state);
507
+ return { message: `Unknown command: ${cmd}. Type /help for available commands.` };
508
+ }
509
+ /**
510
+ * Call the agent with user input and display response
511
+ */
512
+ async function callAgent(state, userMessage) {
513
+ const { userAgent } = state;
514
+ const input = { message: userMessage };
515
+ const tracer = new require_terminal.TerminalTracer(userAgent.context, { suppressToolCallLogs: true });
516
+ try {
517
+ await tracer.run(userAgent, input);
518
+ } catch (error) {
519
+ console.error(chalk.default.red(`\nError: ${error instanceof Error ? error.message : String(error)}`));
520
+ if (error instanceof Error && error.stack) console.error(chalk.default.gray(error.stack));
521
+ }
522
+ }
523
+ /**
524
+ * Run the shell REPL loop
525
+ */
526
+ async function runShellLoop(state) {
527
+ for (;;) {
528
+ const input = await require_terminal_input.terminalInput({
529
+ message: getPrompt(state),
530
+ clear: true
531
+ });
532
+ if (!input?.trim()) continue;
533
+ const cmdResult = await parseSlashCommand(input, state);
534
+ if (cmdResult) {
535
+ if (cmdResult.message) console.log(cmdResult.message);
536
+ if (cmdResult.exit) break;
537
+ continue;
538
+ }
539
+ await callAgent(state, input.trim());
540
+ }
541
+ }
542
+
543
+ //#endregion
544
+ exports.runShellLoop = runShellLoop;