@aigne/cli 1.49.2-beta → 1.50.0-beta.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.50.0-beta.1](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.49.2-beta.1...cli-v1.50.0-beta.1) (2025-09-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * **cli:** support define nested commands for sub apps ([#568](https://github.com/AIGNE-io/aigne-framework/issues/568)) ([0693b80](https://github.com/AIGNE-io/aigne-framework/commit/0693b807e0f8d335010e6ad00763b07cf095e65b))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @aigne/agent-library bumped to 1.21.47-beta.1
16
+ * @aigne/agentic-memory bumped to 1.0.47-beta.1
17
+ * @aigne/aigne-hub bumped to 0.10.1-beta.1
18
+ * @aigne/core bumped to 1.62.0-beta
19
+ * @aigne/default-memory bumped to 1.2.10-beta.1
20
+ * @aigne/openai bumped to 0.16.1-beta.1
21
+ * devDependencies
22
+ * @aigne/test-utils bumped to 0.5.54-beta.1
23
+
24
+ ## [1.49.2-beta.1](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.49.2-beta...cli-v1.49.2-beta.1) (2025-09-29)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **cli:** improve app loading reliability with force upgrade on error ([#566](https://github.com/AIGNE-io/aigne-framework/issues/566)) ([d7c49cf](https://github.com/AIGNE-io/aigne-framework/commit/d7c49cfdfdc72c0d1a98c3033babe1392cb707c1))
30
+
3
31
  ## [1.49.2-beta](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.49.1...cli-v1.49.2-beta) (2025-09-29)
4
32
 
5
33
 
package/dist/cli.js CHANGED
@@ -16,7 +16,8 @@ function getAIGNEFilePath() {
16
16
  }
17
17
  }
18
18
  const aigneFilePath = getAIGNEFilePath();
19
- export default createAIGNECommand({ aigneFilePath })
19
+ const argv = process.argv.slice(aigneFilePath ? 3 : 2);
20
+ export default createAIGNECommand({ argv, aigneFilePath })
20
21
  .fail((message, error, yargs) => {
21
22
  // We catch all errors below, here just print the help message non-error case like demandCommand
22
23
  if (!error) {
@@ -25,7 +26,7 @@ export default createAIGNECommand({ aigneFilePath })
25
26
  process.exit(1);
26
27
  }
27
28
  })
28
- .parseAsync(process.argv.slice(aigneFilePath ? 3 : 2))
29
+ .parseAsync(argv)
29
30
  .catch((error) => {
30
31
  console.log(""); // Add an empty line for better readability
31
32
  console.error(`${chalk.red("Error:")} ${highlightUrl(error.message)}`);
@@ -1,4 +1,5 @@
1
1
  import yargs from "yargs";
2
2
  export declare function createAIGNECommand(options?: {
3
+ argv?: string[];
3
4
  aigneFilePath?: string;
4
5
  }): yargs.Argv<{}>;
@@ -21,7 +21,7 @@ export function createAIGNECommand(options) {
21
21
  .command(createCreateCommand())
22
22
  .command(createServeMCPCommand(options))
23
23
  .command(createObservabilityCommand())
24
- .command(createAppCommands())
24
+ .command(createAppCommands(options))
25
25
  .command(createHubCommand())
26
26
  .command(createDeployCommands())
27
27
  .demandCommand()
@@ -1,11 +1,18 @@
1
1
  import type { CommandModule } from "yargs";
2
- import { type AgentInChildProcess, type LoadAIGNEInChildProcessResult } from "../utils/workers/run-aigne-in-child-process.js";
2
+ import { type AgentInChildProcess, type CLIAgentInChildProcess, type LoadAIGNEInChildProcessResult } from "../utils/workers/run-aigne-in-child-process.js";
3
3
  import { type AgentRunCommonOptions } from "../utils/yargs.js";
4
- export declare function createAppCommands(): CommandModule[];
4
+ export declare function createAppCommands({ argv }?: {
5
+ argv?: string[];
6
+ }): CommandModule[];
5
7
  export declare const agentCommandModule: ({ dir, agent, }: {
6
8
  dir: string;
7
9
  agent: AgentInChildProcess;
8
10
  }) => CommandModule<unknown, AgentRunCommonOptions>;
11
+ export declare const cliAgentCommandModule: ({ dir, parent, cliAgent, }: {
12
+ dir: string;
13
+ parent?: string[];
14
+ cliAgent: CLIAgentInChildProcess;
15
+ }) => CommandModule<unknown, AgentRunCommonOptions>;
9
16
  interface LoadApplicationOptions {
10
17
  packageName: string;
11
18
  dir: string;
@@ -1,3 +1,4 @@
1
+ import assert from "node:assert";
1
2
  import { spawn } from "node:child_process";
2
3
  import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
3
4
  import { homedir } from "node:os";
@@ -7,6 +8,7 @@ import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
7
8
  import { Listr, PRESET_TIMER } from "@aigne/listr2";
8
9
  import { joinURL } from "ufo";
9
10
  import { downloadAndExtract } from "../utils/download.js";
11
+ import { withSpinner } from "../utils/spinner.js";
10
12
  import { runAIGNEInChildProcess, } from "../utils/workers/run-aigne-in-child-process.js";
11
13
  import { withAgentInputSchema } from "../utils/yargs.js";
12
14
  import { serveMCPServerFromDir } from "./serve-mcp.js";
@@ -32,36 +34,36 @@ const builtinApps = [
32
34
  aliases: ["websmith", "web"],
33
35
  },
34
36
  ];
35
- export function createAppCommands() {
37
+ export function createAppCommands({ argv } = {}) {
36
38
  return builtinApps.map((app) => ({
37
39
  command: app.name,
38
40
  describe: app.describe,
39
41
  aliases: app.aliases,
40
- builder: async (yargs) => {
42
+ builder: async (y) => {
41
43
  const dir = join(homedir(), ".aigne", "registry.npmjs.org", app.packageName);
42
- const { aigne, version } = await loadApplication({
43
- dir,
44
- packageName: app.packageName,
45
- install: true,
46
- });
47
- if (aigne.cli?.chat) {
48
- yargs.command({
49
- ...agentCommandModule({ dir, agent: aigne.cli.chat }),
50
- command: "$0",
44
+ y.command(upgradeCommandModule({ packageName: app.packageName, dir }));
45
+ if (!argv || !isUpgradeCommand(argv)) {
46
+ const { aigne, version } = await loadApplication({
47
+ dir,
48
+ packageName: app.packageName,
49
+ install: true,
51
50
  });
51
+ if (aigne.cli?.chat) {
52
+ y.command({
53
+ ...agentCommandModule({ dir, agent: aigne.cli.chat }),
54
+ command: "$0",
55
+ });
56
+ }
57
+ for (const cliAgent of aigne.cli?.agents ?? []) {
58
+ y.command(cliAgentCommandModule({ dir, cliAgent }));
59
+ }
60
+ y.option("model", {
61
+ type: "string",
62
+ description: "Model to use for the application, example: openai:gpt-4.1 or google:gemini-2.5-flash",
63
+ }).command(serveMcpCommandModule({ name: app.name, dir }));
64
+ y.version(`${app.name} v${version}`).alias("version", "v");
52
65
  }
53
- for (const agent of aigne.cli?.agents ?? []) {
54
- yargs.command(agentCommandModule({ dir, agent }));
55
- }
56
- yargs
57
- .option("model", {
58
- type: "string",
59
- description: "Model to use for the application, example: openai:gpt-4.1 or google:gemini-2.5-flash",
60
- })
61
- .command(serveMcpCommandModule({ name: app.name, dir }))
62
- .command(upgradeCommandModule({ packageName: app.packageName, dir }));
63
- yargs.version(`${app.name} v${version}`).alias("version", "v");
64
- return yargs.demandCommand();
66
+ return y.demandCommand();
65
67
  },
66
68
  handler: () => { },
67
69
  }));
@@ -90,11 +92,15 @@ const serveMcpCommandModule = ({ name, dir, }) => ({
90
92
  await serveMCPServerFromDir({ ...options, dir });
91
93
  },
92
94
  });
95
+ function isUpgradeCommand(argv) {
96
+ const skipGlobalOptions = ["-v", "--version"];
97
+ return argv[1] === "upgrade" && !argv.some((arg) => skipGlobalOptions.includes(arg));
98
+ }
93
99
  const upgradeCommandModule = ({ packageName, dir, }) => ({
94
100
  command: "upgrade",
95
101
  describe: `Upgrade ${packageName} to the latest version`,
96
- builder: (argv) => {
97
- return argv
102
+ builder: (yargs) => {
103
+ return yargs
98
104
  .option("beta", {
99
105
  type: "boolean",
100
106
  describe: "Use beta versions if available",
@@ -112,11 +118,13 @@ const upgradeCommandModule = ({ packageName, dir, }) => ({
112
118
  },
113
119
  handler: async ({ beta, targetVersion, force }) => {
114
120
  beta ??= shouldUseBetaApps();
115
- let app = await loadApplication({ packageName, dir });
116
- const npm = await getNpmTgzInfo(packageName, { beta, version: targetVersion });
117
- if (!app || force || npm.version !== app.version) {
121
+ const npm = await withSpinner("", async () => {
118
122
  if (force)
119
123
  await rm(dir, { force: true, recursive: true });
124
+ return await getNpmTgzInfo(packageName, { beta, version: targetVersion });
125
+ });
126
+ let app = await loadApplication({ packageName, dir });
127
+ if (!app || force || npm.version !== app.version) {
120
128
  await installApp({ packageName, dir, beta, version: targetVersion });
121
129
  app = await loadApplication({ dir, packageName, install: true });
122
130
  console.log(`\n✅ Upgraded ${packageName} to version ${app.version}`);
@@ -145,12 +153,52 @@ export const agentCommandModule = ({ dir, agent, }) => {
145
153
  },
146
154
  };
147
155
  };
156
+ export const cliAgentCommandModule = ({ dir, parent, cliAgent, }) => {
157
+ const { agent, agents } = cliAgent;
158
+ const name = cliAgent.name || agent?.name;
159
+ assert(name, "CLI agent must have a name");
160
+ return {
161
+ command: name,
162
+ aliases: cliAgent.alias || agent?.alias || [],
163
+ describe: cliAgent.description || agent?.description || "",
164
+ builder: async (yargs) => {
165
+ if (agent) {
166
+ withAgentInputSchema(yargs, { inputSchema: jsonSchemaToZod(agent.inputSchema) });
167
+ }
168
+ if (agents?.length) {
169
+ for (const cmd of agents) {
170
+ yargs.command(cliAgentCommandModule({ dir, parent: (parent ?? []).concat(name), cliAgent: cmd }));
171
+ }
172
+ }
173
+ if (!agent)
174
+ yargs.demandCommand();
175
+ return yargs;
176
+ },
177
+ handler: async (options) => {
178
+ if (!agent)
179
+ throw new Error("CLI agent is not defined");
180
+ if (options.logLevel)
181
+ logger.level = options.logLevel;
182
+ await runAIGNEInChildProcess("invokeCLIAgentFromDir", {
183
+ dir,
184
+ parent,
185
+ agent: name,
186
+ input: options,
187
+ });
188
+ process.exit(0);
189
+ },
190
+ };
191
+ };
148
192
  export async function loadApplication(options) {
149
193
  const { dir, packageName } = options;
150
194
  const check = await checkInstallation(dir);
151
195
  if (check && !check.expired) {
152
- const aigne = await runAIGNEInChildProcess("loadAIGNE", dir).catch((error) => {
153
- console.warn(`⚠️ Failed to load ${packageName}, trying to reinstall:`, error.message);
196
+ const aigne = await runAIGNEInChildProcess("loadAIGNE", dir).catch(async (error) => {
197
+ logger.error(`⚠️ Failed to load ${packageName}, trying to reinstall:`, error.message);
198
+ await withSpinner("", async () => {
199
+ await rm(options.dir, { recursive: true, force: true });
200
+ await mkdir(options.dir, { recursive: true });
201
+ });
154
202
  });
155
203
  if (aigne) {
156
204
  return { aigne, version: check.version, isCache: true };
@@ -1,5 +1,6 @@
1
1
  import { isAbsolute, resolve } from "node:path";
2
2
  import { exists } from "@aigne/agent-library/utils/fs.js";
3
+ import { findCliAgent } from "@aigne/core/utils/agent-utils.js";
3
4
  import { z } from "zod";
4
5
  import { runEvaluationPipeline } from "../utils/evaluation/core.js";
5
6
  import { FileDataset } from "../utils/evaluation/dataset.js";
@@ -70,7 +71,7 @@ export function createEvalCommand({ aigneFilePath, } = {}) {
70
71
  const { chat } = aigne.cli;
71
72
  const agent = chat && chat.name === entryAgent
72
73
  ? chat
73
- : aigne.cli.agents[entryAgent] ||
74
+ : findCliAgent(aigne.cli, "*", entryAgent) ||
74
75
  aigne.agents[entryAgent] ||
75
76
  aigne.skills[entryAgent] ||
76
77
  aigne.mcpServer.agents[entryAgent];
@@ -80,7 +81,7 @@ export function createEvalCommand({ aigneFilePath, } = {}) {
80
81
  let evaluatorAgent;
81
82
  if (evaluatorName) {
82
83
  evaluatorAgent =
83
- aigne.cli.agents[evaluatorName] ||
84
+ findCliAgent(aigne.cli, "*", evaluatorName) ||
84
85
  aigne.agents[evaluatorName] ||
85
86
  aigne.skills[evaluatorName] ||
86
87
  aigne.mcpServer.agents[evaluatorName];
@@ -2,6 +2,7 @@ import { cp, mkdir, rm } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { isAbsolute, join, resolve } from "node:path";
4
4
  import { exists } from "@aigne/agent-library/utils/fs.js";
5
+ import { mapCliAgent } from "@aigne/core/utils/agent-utils.js";
5
6
  import { flat, isNonNullable } from "@aigne/core/utils/type-utils.js";
6
7
  import { Listr, PRESET_TIMER } from "@aigne/listr2";
7
8
  import { config } from "dotenv-flow";
@@ -11,7 +12,7 @@ import { downloadAndExtract } from "../utils/download.js";
11
12
  import { loadAIGNE } from "../utils/load-aigne.js";
12
13
  import { isUrl } from "../utils/url.js";
13
14
  import { serializeAgent } from "../utils/workers/run-aigne-in-child-process.js";
14
- import { agentCommandModule } from "./app.js";
15
+ import { agentCommandModule, cliAgentCommandModule } from "./app.js";
15
16
  export function createRunCommand({ aigneFilePath, } = {}) {
16
17
  return {
17
18
  command: "run [path] [entry-agent]",
@@ -47,10 +48,16 @@ export function createRunCommand({ aigneFilePath, } = {}) {
47
48
  });
48
49
  }
49
50
  // Allow user to run all of agents in the AIGNE instances
50
- const allAgents = flat(aigne.cli.agents, aigne.agents, aigne.skills, aigne.cli.chat, aigne.mcpServer.agents);
51
+ const allAgents = flat(aigne.agents, aigne.skills, aigne.cli.chat, aigne.mcpServer.agents);
51
52
  for (const agent of allAgents) {
52
53
  subYargs.command(agentCommandModule({ dir: path, agent: serializeAgent(agent) }));
53
54
  }
55
+ for (const cliAgent of aigne.cli.agents ?? []) {
56
+ subYargs.command(cliAgentCommandModule({
57
+ dir: path,
58
+ cliAgent: mapCliAgent(cliAgent, (a) => (a ? serializeAgent(a) : undefined)),
59
+ }));
60
+ }
54
61
  const argv = process.argv.slice(aigneFilePath ? 3 : 2);
55
62
  if (argv[0] === "run")
56
63
  argv.shift(); // remove 'run' command
@@ -62,8 +69,9 @@ export function createRunCommand({ aigneFilePath, } = {}) {
62
69
  if (argv[0] === "--entry-agent")
63
70
  argv.shift();
64
71
  const firstAgent = aigne.agents[0]?.name;
65
- if (!options.entryAgent && firstAgent)
72
+ if (!options.entryAgent && firstAgent && !argv.some((i) => ["-h", "--help"].includes(i))) {
66
73
  argv.unshift(firstAgent);
74
+ }
67
75
  await subYargs
68
76
  .strict()
69
77
  .demandCommand()
@@ -0,0 +1 @@
1
+ export declare function withSpinner<T>(message: string, fn: () => Promise<T>): Promise<T>;
@@ -0,0 +1,14 @@
1
+ import { Spinner } from "@aigne/listr2";
2
+ export async function withSpinner(message, fn) {
3
+ const spinner = new Spinner();
4
+ spinner.start(() => {
5
+ process.stdout.write(`\r\x1b[K${spinner.fetch()} ${message}`);
6
+ });
7
+ try {
8
+ return await fn();
9
+ }
10
+ finally {
11
+ spinner.stop();
12
+ process.stdout.write("\r\x1b[K"); // Clear the spinner line
13
+ }
14
+ }
@@ -1,11 +1,11 @@
1
1
  import { AIGNE, type Message } from "@aigne/core";
2
2
  import { type AgentRunCommonOptions } from "../yargs.js";
3
- import { type AgentInChildProcess } from "./run-aigne-in-child-process.js";
3
+ import { type AgentInChildProcess, type CLIAgentInChildProcess } from "./run-aigne-in-child-process.js";
4
4
  export declare function loadAIGNEInChildProcess(...args: Parameters<typeof AIGNE.load>): Promise<{
5
5
  agents?: AgentInChildProcess[];
6
6
  cli?: {
7
7
  chat?: AgentInChildProcess;
8
- agents?: AgentInChildProcess[];
8
+ agents?: CLIAgentInChildProcess[];
9
9
  };
10
10
  mcpServer?: {
11
11
  agents?: AgentInChildProcess[];
@@ -13,6 +13,7 @@ export declare function loadAIGNEInChildProcess(...args: Parameters<typeof AIGNE
13
13
  }>;
14
14
  export declare function invokeCLIAgentFromDirInChildProcess(options: {
15
15
  dir: string;
16
+ parent?: string[];
16
17
  agent: string;
17
18
  input: Message & AgentRunCommonOptions;
18
19
  }): Promise<void>;
@@ -1,9 +1,10 @@
1
1
  import assert from "node:assert";
2
2
  import { AIGNE } from "@aigne/core";
3
+ import { findCliAgent, mapCliAgent } from "@aigne/core/utils/agent-utils.js";
3
4
  import { loadAIGNE } from "../load-aigne.js";
4
5
  import { runAgentWithAIGNE } from "../run-with-aigne.js";
5
6
  import { parseAgentInput } from "../yargs.js";
6
- import { serializeAgent } from "./run-aigne-in-child-process.js";
7
+ import { serializeAgent, } from "./run-aigne-in-child-process.js";
7
8
  const METHODS = {
8
9
  loadAIGNE: loadAIGNEInChildProcess,
9
10
  invokeCLIAgentFromDir: invokeCLIAgentFromDirInChildProcess,
@@ -38,7 +39,7 @@ export async function loadAIGNEInChildProcess(...args) {
38
39
  agents: aigne.agents.map(serializeAgent),
39
40
  cli: {
40
41
  chat: aigne.cli.chat ? serializeAgent(aigne.cli.chat) : undefined,
41
- agents: aigne.cli.agents.map(serializeAgent),
42
+ agents: aigne.cli.agents?.map((item) => mapCliAgent(item, serializeAgent)),
42
43
  },
43
44
  mcpServer: {
44
45
  agents: aigne.mcpServer.agents.map(serializeAgent),
@@ -52,12 +53,20 @@ export async function invokeCLIAgentFromDirInChildProcess(options) {
52
53
  });
53
54
  try {
54
55
  const { chat } = aigne.cli;
55
- const agent = chat && chat.name === options.agent
56
- ? chat
57
- : aigne.cli.agents[options.agent] ||
58
- aigne.agents[options.agent] ||
59
- aigne.skills[options.agent] ||
60
- aigne.mcpServer.agents[options.agent];
56
+ let agent;
57
+ if (chat && chat.name === options.agent) {
58
+ agent = chat;
59
+ }
60
+ else if (options.parent) {
61
+ agent = findCliAgent(aigne.cli, options.parent, options.agent);
62
+ }
63
+ else {
64
+ agent =
65
+ findCliAgent(aigne.cli, [], options.agent) ||
66
+ aigne.agents[options.agent] ||
67
+ aigne.skills[options.agent] ||
68
+ aigne.mcpServer.agents[options.agent];
69
+ }
61
70
  assert(agent, `Agent ${options.agent} not found in ${options.dir}`);
62
71
  const input = await parseAgentInput(options.input, agent);
63
72
  await runAgentWithAIGNE(aigne, agent, {
@@ -1,4 +1,5 @@
1
1
  import type { Agent } from "@aigne/core";
2
+ import type { AIGNECLIAgent } from "@aigne/core/aigne/type.js";
2
3
  import type { JsonSchema } from "@aigne/json-schema-to-zod";
3
4
  import type { invokeCLIAgentFromDirInChildProcess, loadAIGNEInChildProcess } from "./run-aigne-in-child-process-worker.js";
4
5
  export type LoadAIGNEInChildProcessResult = Awaited<ReturnType<typeof loadAIGNEInChildProcess>>;
@@ -6,6 +7,10 @@ export interface AgentInChildProcess extends Pick<Agent, "name" | "description"
6
7
  inputSchema: JsonSchema;
7
8
  outputSchema: JsonSchema;
8
9
  }
10
+ export interface CLIAgentInChildProcess extends Omit<AIGNECLIAgent, "agent" | "agents"> {
11
+ agent?: AgentInChildProcess;
12
+ agents?: CLIAgentInChildProcess[];
13
+ }
9
14
  export declare function serializeAgent(agent: Agent): AgentInChildProcess;
10
15
  export interface ChildProcessAIGNEMethods {
11
16
  loadAIGNE: typeof loadAIGNEInChildProcess;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/cli",
3
- "version": "1.49.2-beta",
3
+ "version": "1.50.0-beta.1",
4
4
  "description": "Your command center for agent development",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -84,13 +84,13 @@
84
84
  "yoctocolors-cjs": "^2.1.3",
85
85
  "zod": "^3.25.67",
86
86
  "zod-to-json-schema": "^3.24.6",
87
- "@aigne/agent-library": "^1.21.47-beta",
88
- "@aigne/aigne-hub": "^0.10.1-beta",
89
- "@aigne/core": "^1.61.1-beta",
90
- "@aigne/default-memory": "^1.2.10-beta",
91
- "@aigne/agentic-memory": "^1.0.47-beta",
92
- "@aigne/observability-api": "^0.11.1-beta",
93
- "@aigne/openai": "^0.16.1-beta"
87
+ "@aigne/agent-library": "^1.21.47-beta.1",
88
+ "@aigne/aigne-hub": "^0.10.1-beta.1",
89
+ "@aigne/agentic-memory": "^1.0.47-beta.1",
90
+ "@aigne/core": "^1.62.0-beta",
91
+ "@aigne/default-memory": "^1.2.10-beta.1",
92
+ "@aigne/openai": "^0.16.1-beta.1",
93
+ "@aigne/observability-api": "^0.11.1-beta"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@inquirer/testing": "^2.1.50",
@@ -107,7 +107,7 @@
107
107
  "rimraf": "^6.0.1",
108
108
  "typescript": "^5.9.2",
109
109
  "ufo": "^1.6.1",
110
- "@aigne/test-utils": "^0.5.54-beta"
110
+ "@aigne/test-utils": "^0.5.54-beta.1"
111
111
  },
112
112
  "scripts": {
113
113
  "lint": "tsc --noEmit",