@pantheon.ai/agents 0.0.9 → 0.0.11

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 (3) hide show
  1. package/README.md +43 -6
  2. package/dist/index.js +540 -142
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,16 +1,43 @@
1
1
  # `@pantheon.ai/agents`
2
2
 
3
- This package contains:
3
+ This package provides the `pantheon-agents` CLI for managing agent configs and tasks backed by a TiDB/MySQL task list.
4
+
5
+ It includes:
4
6
 
5
7
  - A **runner loop** (`pantheon-agents run <agent>`) that executes queued tasks via Pantheon and persists status/output to TiDB.
6
8
  - A **client-facing server** (`pantheon-agents server`) that exposes the same task operations over **HTTP REST** and **MCP (HTTP+SSE)**.
7
9
 
8
- ## Prerequisites
10
+ ## Build
11
+
12
+ ```bash
13
+ npm run -w @pantheon.ai/agents build
14
+ ```
15
+
16
+ ## Environment variables
17
+
18
+ - `DATABASE_URL` (required): MySQL/TiDB connection string for the task list database.
19
+ - `PANTHEON_API_KEY` (required for some commands): Used to query/execute Pantheon branches.
20
+ - `PANTHEON_AGENTS_API_TOKEN` (optional): Bearer token for API auth when running `pantheon-agents server` (defaults for `--token`).
21
+
22
+ Command requirements:
23
+
24
+ - `pantheon-agents add-task|delete-task|show-config|show-tasks`: requires `DATABASE_URL`
25
+ - `pantheon-agents run`: requires `DATABASE_URL` + `PANTHEON_API_KEY`
26
+ - `pantheon-agents config`: requires `DATABASE_URL`, and requires `PANTHEON_API_KEY` when `--bootstrap` is enabled (default) or when `--root-branch-id` is not provided
27
+ - `pantheon-agents reconfig`: requires `DATABASE_URL` + `PANTHEON_API_KEY`
28
+ - `pantheon-agents server`: requires `DATABASE_URL`; auth uses `--token` or env `PANTHEON_AGENTS_API_TOKEN` (unless `--no-auth`)
9
29
 
10
- - `DATABASE_URL` (TiDB/MySQL connection string) must be set for all commands that touch the DB.
11
- - `PANTHEON_API_KEY` is required for the runner loop (`pantheon-agents run`) and bootstrap config flows.
30
+ Notes:
12
31
 
13
- ### DB schema / migrations
32
+ - `PANTHEON_API_KEY` is also required for config-related server endpoints/tools that read Pantheon branch output or re-bootstrap configs.
33
+
34
+ ## Show tasks output modes
35
+
36
+ - Default: concise one-line entries.
37
+ - Table output: use `--no-concise`.
38
+ - JSON output: use `--json`.
39
+
40
+ ## DB schema / migrations
14
41
 
15
42
  The schema lives in `packages/agents/src/db/schema/tidb.sql`.
16
43
 
@@ -59,6 +86,15 @@ curl -sS -H "Authorization: Bearer $TOKEN" \\
59
86
 
60
87
  curl -sS -H "Authorization: Bearer $TOKEN" \\
61
88
  -X DELETE http://127.0.0.1:8000/api/v1/agents/<agent>/tasks/<task_id>
89
+
90
+ # Get agent config (includes bootstrap branch output)
91
+ curl -sS -H "Authorization: Bearer $TOKEN" \\
92
+ http://127.0.0.1:8000/api/v1/agents/<agent>/configs/<project_id>
93
+
94
+ # Re-bootstrap agent config (like `pantheon-agents reconfig`)
95
+ curl -sS -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \\
96
+ -X POST http://127.0.0.1:8000/api/v1/agents/<agent>/configs/<project_id>/reconfig \\
97
+ -d '{ "role": "reviewer", "skills": ["style-reviewer"] }'
62
98
  ```
63
99
 
64
100
  ### MCP (HTTP+SSE)
@@ -73,4 +109,5 @@ Tools:
73
109
  - `tasks.create`
74
110
  - `tasks.cancel`
75
111
  - `tasks.delete`
76
-
112
+ - `configs.get`
113
+ - `configs.reconfig`
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import * as process$1 from "node:process";
4
- import { createCommand } from "commander";
3
+ import { Command, createCommand } from "commander";
4
+ import process$1 from "node:process";
5
5
  import * as fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { multistream, pino, transport } from "pino";
@@ -420,7 +420,7 @@ var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
420
420
 
421
421
  //#endregion
422
422
  //#region package.json
423
- var version$1 = "0.0.9";
423
+ var version$1 = "0.0.11";
424
424
 
425
425
  //#endregion
426
426
  //#region src/schemas/task-list.ts
@@ -611,6 +611,13 @@ var TaskListTidbProvider = class extends TaskListProvider {
611
611
  ...config
612
612
  }).execute();
613
613
  }
614
+ async updateAgentConfig({ skills, ...config }) {
615
+ const result = await this.db.updateTable("agent_project_config").set({
616
+ skills: JSON.stringify(skills),
617
+ ...config
618
+ }).where("agent", "=", this.agentName).where("project_id", "=", config.project_id).executeTakeFirst();
619
+ if (Number(result.numUpdatedRows ?? 0) === 0) throw new Error(`No config found to update for agent ${this.agentName} and project ${config.project_id}.`);
620
+ }
614
621
  async getTask(taskId) {
615
622
  const taskItem = await this.selectTask().where("id", "=", taskId).executeTakeFirst();
616
623
  if (taskItem == null) return null;
@@ -9701,6 +9708,69 @@ async function startPendingTask(provider, task, logger) {
9701
9708
 
9702
9709
  //#endregion
9703
9710
  //#region src/core/index.ts
9711
+ function normalizeSkills(value) {
9712
+ if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
9713
+ if (typeof value === "string") {
9714
+ try {
9715
+ const parsed = JSON.parse(value);
9716
+ if (Array.isArray(parsed)) return parsed.filter((item) => typeof item === "string");
9717
+ } catch {}
9718
+ return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
9719
+ }
9720
+ return [];
9721
+ }
9722
+ function buildRebootstrapDiffPrompt(options) {
9723
+ return `You must follow these instructions":
9724
+ 1. Clone the main branch from ${options.prototypeUrl} to a temporary directory (<pantheon-agents> in follow instructions references to this directory).
9725
+ 2. Copy the <pantheon-agents>/agents/${options.role}/AGENTS.md to \`<workspace>/AGENTS.md\`.
9726
+ 3. Copy the <pantheon-agents>/agents/${options.role}/skills directory to \`<workspace>/.codex/skills\` if the source directory exists.
9727
+ ${options.skills.length > 0 ? `4. Copy the pantheon-agents/skills/\{${options.skills.join(",")}} directory to \`<workspace>/.codex/skills/\`` : ""}
9728
+
9729
+ Validate <workspace>: check if files and directorys exists:
9730
+ - AGENTS.md
9731
+ - .codex/skills
9732
+
9733
+ **You MUST NOT generate AGENTS.md and skills by yourself if clone failed.**
9734
+
9735
+ Finally, output a DIFF-oriented summary (not raw file contents) between:
9736
+ - BEFORE: the workspace state before applying the steps above (previous base branch id: ${options.previousBaseBranchId})
9737
+ - AFTER: the workspace state after applying the steps above
9738
+
9739
+ The summary MUST include:
9740
+ - \`AGENTS.md\` changes (diff/stat only; do NOT print full file content)
9741
+ - \`.codex/skills\` added/removed/modified entries (filenames only; do NOT print file contents)
9742
+ - A concise summary with: changed files list + add/remove/modify counts
9743
+ `;
9744
+ }
9745
+ async function defaultSleep(ms) {
9746
+ await new Promise((resolve) => {
9747
+ setTimeout(resolve, ms);
9748
+ });
9749
+ }
9750
+ async function waitForPantheonBranchOutput(options) {
9751
+ let retried = 0;
9752
+ let i = 0;
9753
+ while (true) {
9754
+ await options.sleep(options.pollIntervalMs);
9755
+ const result = await options.getPantheonBranchFn({
9756
+ branchId: options.branchId,
9757
+ projectId: options.projectId,
9758
+ getOutputIfFinished: true
9759
+ }).then((result) => {
9760
+ retried = 0;
9761
+ return result;
9762
+ }).catch((reason) => {
9763
+ if (retried < options.maxRetries) {
9764
+ retried++;
9765
+ return { state: "others" };
9766
+ }
9767
+ throw new Error(`Failed to get bootstrap branch status. Retry ${retried} times. Last error: ${getErrorMessage(reason)}`);
9768
+ });
9769
+ if (result.state === "failed") throw new Error(`Bootstrap failed: ${result.error}`);
9770
+ if (result.state === "succeed") return result.output ?? "";
9771
+ console.log(`Bootstrap in progress... [${++i}]`);
9772
+ }
9773
+ }
9704
9774
  async function runAgent(name, options, logger) {
9705
9775
  const agentDir = path.join(options.dataDir, "agents", name);
9706
9776
  const pidFile = path.join(agentDir, "pid");
@@ -9710,38 +9780,37 @@ async function runAgent(name, options, logger) {
9710
9780
  }
9711
9781
  async function configAgent(name, options) {
9712
9782
  const provider = new TaskListTidbProvider(name, pino());
9713
- const previousConfig = await provider.getAgentConfig(options.projectId);
9714
- if (previousConfig) if (previousConfig.role === options.role) {
9715
- console.log(`Agent ${name} already configured as ${options.role} for project ${options.projectId}.`);
9716
- console.log(`Base branch id: ${previousConfig.base_branch_id}`);
9717
- await provider.close();
9718
- return;
9719
- } else {
9720
- console.error(`Agent ${name} already configured as ${previousConfig.role} for project ${options.projectId}.`);
9721
- console.error(`Base branch id: ${previousConfig.base_branch_id}`);
9722
- console.error(`Cannot change role to ${options.role}`);
9723
- await provider.close();
9724
- process.exitCode = 1;
9725
- return;
9726
- }
9727
- console.log(`Configuring agent ${name} as ${options.role} for project ${options.projectId}.`);
9728
- if (!options.rootBranchId) {
9729
- console.log("No root branch id specified, using project root branch.");
9730
- const project = await getPantheonProjectInfo({ projectId: options.projectId });
9731
- if (!project.root_branch_id) {
9732
- console.error(`Project ${options.projectId} has no root branch. Project status is ${project.status}`);
9733
- await provider.close();
9783
+ try {
9784
+ const previousConfig = await provider.getAgentConfig(options.projectId);
9785
+ if (previousConfig) {
9786
+ if (previousConfig.role === options.role) {
9787
+ console.log(`Agent ${name} already configured as ${options.role} for project ${options.projectId}.`);
9788
+ console.log(`Base branch id: ${previousConfig.base_branch_id}`);
9789
+ return;
9790
+ }
9791
+ console.error(`Agent ${name} already configured as ${previousConfig.role} for project ${options.projectId}.`);
9792
+ console.error(`Base branch id: ${previousConfig.base_branch_id}`);
9793
+ console.error(`Cannot change role to ${options.role}`);
9734
9794
  process.exitCode = 1;
9735
9795
  return;
9736
9796
  }
9737
- options.rootBranchId = project.root_branch_id;
9738
- }
9739
- if (options.bootstrap) {
9740
- const branchId = await executeOnPantheon({
9741
- projectId: options.projectId,
9742
- branchId: options.rootBranchId,
9743
- agent: "codex",
9744
- prompt: `You must follow these instructions":
9797
+ console.log(`Configuring agent ${name} as ${options.role} for project ${options.projectId}.`);
9798
+ if (!options.rootBranchId) {
9799
+ console.log("No root branch id specified, using project root branch.");
9800
+ const project = await getPantheonProjectInfo({ projectId: options.projectId });
9801
+ if (!project.root_branch_id) {
9802
+ console.error(`Project ${options.projectId} has no root branch. Project status is ${project.status}`);
9803
+ process.exitCode = 1;
9804
+ return;
9805
+ }
9806
+ options.rootBranchId = project.root_branch_id;
9807
+ }
9808
+ if (options.bootstrap) {
9809
+ const branchId = await executeOnPantheon({
9810
+ projectId: options.projectId,
9811
+ branchId: options.rootBranchId,
9812
+ agent: "codex",
9813
+ prompt: `You must follow these instructions":
9745
9814
  1. Clone the main branch from ${options.prototypeUrl} to a temporary directory (<pantheon-agents> in follow instructions references to this directory).
9746
9815
  2. Copy the <pantheon-agents>/agents/${options.role}/AGENTS.md to \`<workspace>/AGENTS.md\`.
9747
9816
  3. Copy the <pantheon-agents>/agents/${options.role}/skills directory to \`<workspace>/.codex/skills\` if the source directory exists.
@@ -9755,70 +9824,135 @@ Validate <workspace>: check if files and directorys exists:
9755
9824
 
9756
9825
  Finally, outputs the first 5 lines of <workspace>/AGENTS.md and the skills list in <workspace>/.codex/skills
9757
9826
  `
9758
- });
9759
- let retried = 0;
9760
- const maxRetries = 3;
9761
- let i = 0;
9762
- console.log(`Bootstrap branch created: ${branchId}. Waiting for ready... [poll interval = 10s]`);
9763
- while (true) {
9764
- await new Promise((resolve) => {
9765
- setTimeout(resolve, 1e4);
9766
9827
  });
9767
- const result = await getPantheonBranch({
9768
- branchId,
9769
- projectId: options.projectId,
9770
- getOutputIfFinished: true
9771
- }).then((result) => {
9772
- retried = 0;
9773
- return result;
9774
- }).catch((reason) => {
9775
- if (retried < maxRetries) {
9776
- retried++;
9777
- return { state: "others" };
9778
- } else throw new Error(`Failed to get bootstrap branch status. Retry ${retried} times. Last error: ${getErrorMessage(reason)}`);
9828
+ let retried = 0;
9829
+ const maxRetries = 3;
9830
+ let i = 0;
9831
+ console.log(`Bootstrap branch created: ${branchId}. Waiting for ready... [poll interval = 10s]`);
9832
+ while (true) {
9833
+ await new Promise((resolve) => {
9834
+ setTimeout(resolve, 1e4);
9835
+ });
9836
+ const result = await getPantheonBranch({
9837
+ branchId,
9838
+ projectId: options.projectId,
9839
+ getOutputIfFinished: true
9840
+ }).then((result) => {
9841
+ retried = 0;
9842
+ return result;
9843
+ }).catch((reason) => {
9844
+ if (retried < maxRetries) {
9845
+ retried++;
9846
+ return { state: "others" };
9847
+ }
9848
+ throw new Error(`Failed to get bootstrap branch status. Retry ${retried} times. Last error: ${getErrorMessage(reason)}`);
9849
+ });
9850
+ if (result.state === "failed") {
9851
+ console.error("Bootstrap failed: " + result.error);
9852
+ process.exitCode = 1;
9853
+ return;
9854
+ }
9855
+ if (result.state === "succeed") {
9856
+ console.log("Bootstrap succeeded. Output is:");
9857
+ console.log(result.output);
9858
+ break;
9859
+ }
9860
+ console.log(`Bootstrap in progress... [${++i}]`);
9861
+ }
9862
+ await provider.setAgentConfig({
9863
+ project_id: options.projectId,
9864
+ base_branch_id: branchId,
9865
+ execute_agent: options.executeAgent,
9866
+ role: options.role,
9867
+ skills: options.skills,
9868
+ prototype_url: options.prototypeUrl
9779
9869
  });
9780
- if (result.state === "failed") {
9781
- console.error("Bootstrap failed: " + result.error);
9782
- await provider.close();
9783
- process.exit(1);
9784
- }
9785
- if (result.state === "succeed") {
9786
- console.log("Bootstrap succeeded. Output is:");
9787
- console.log(result.output);
9788
- break;
9789
- }
9790
- console.log(`Bootstrap in progress... [${++i}]`);
9791
- }
9792
- await provider.setAgentConfig({
9870
+ } else await provider.setAgentConfig({
9793
9871
  project_id: options.projectId,
9794
- base_branch_id: branchId,
9872
+ base_branch_id: options.rootBranchId,
9795
9873
  execute_agent: options.executeAgent,
9796
9874
  role: options.role,
9797
9875
  skills: options.skills,
9798
9876
  prototype_url: options.prototypeUrl
9799
9877
  });
9800
- } else await provider.setAgentConfig({
9878
+ console.log(`Agent ${name} configured successfully.`);
9879
+ } finally {
9880
+ await provider.close();
9881
+ }
9882
+ }
9883
+ async function reconfigAgentWithDeps(name, options, deps) {
9884
+ const previousConfig = await deps.provider.getAgentConfig(options.projectId);
9885
+ if (!previousConfig) throw new Error(`No config found for agent ${name} and project ${options.projectId}.`);
9886
+ const resolvedRole = options.role?.trim() || previousConfig.role;
9887
+ if (options.role != null && !options.role.trim()) throw new Error("--role must be non-empty.");
9888
+ const resolvedSkills = options.skills ?? normalizeSkills(previousConfig.skills);
9889
+ const nextBaseBranchId = await deps.executeOnPantheonFn({
9890
+ projectId: options.projectId,
9891
+ branchId: previousConfig.base_branch_id,
9892
+ agent: "codex",
9893
+ prompt: buildRebootstrapDiffPrompt({
9894
+ prototypeUrl: previousConfig.prototype_url,
9895
+ role: resolvedRole,
9896
+ skills: resolvedSkills,
9897
+ previousBaseBranchId: previousConfig.base_branch_id
9898
+ })
9899
+ });
9900
+ console.log(`Bootstrap branch created: ${nextBaseBranchId}. Waiting for ready... [poll interval = ${Math.floor((deps.pollIntervalMs ?? 1e4) / 1e3)}s]`);
9901
+ const bootstrapOutput = await waitForPantheonBranchOutput({
9902
+ projectId: options.projectId,
9903
+ branchId: nextBaseBranchId,
9904
+ pollIntervalMs: deps.pollIntervalMs ?? 1e4,
9905
+ maxRetries: deps.maxRetries ?? 3,
9906
+ getPantheonBranchFn: deps.getPantheonBranchFn,
9907
+ sleep: deps.sleep ?? defaultSleep
9908
+ });
9909
+ await deps.provider.updateAgentConfig({
9801
9910
  project_id: options.projectId,
9802
- base_branch_id: options.rootBranchId,
9803
- execute_agent: options.executeAgent,
9804
- role: options.role,
9805
- skills: options.skills,
9806
- prototype_url: options.prototypeUrl
9911
+ base_branch_id: nextBaseBranchId,
9912
+ execute_agent: previousConfig.execute_agent,
9913
+ role: resolvedRole,
9914
+ skills: resolvedSkills,
9915
+ prototype_url: previousConfig.prototype_url
9807
9916
  });
9808
- console.log(`Agent ${name} configured successfully.`);
9809
- await provider.close();
9917
+ return {
9918
+ previousConfig,
9919
+ resolvedRole,
9920
+ resolvedSkills,
9921
+ nextBaseBranchId,
9922
+ bootstrapOutput
9923
+ };
9924
+ }
9925
+ async function reconfigAgent(name, options) {
9926
+ const provider = new TaskListTidbProvider(name, pino());
9927
+ try {
9928
+ console.log(`Reconfiguring agent ${name} for project ${options.projectId}.`);
9929
+ const result = await reconfigAgentWithDeps(name, options, {
9930
+ provider,
9931
+ executeOnPantheonFn: executeOnPantheon,
9932
+ getPantheonBranchFn: getPantheonBranch
9933
+ });
9934
+ console.log("Re-bootstrap succeeded. Output is:");
9935
+ console.log(result.bootstrapOutput);
9936
+ console.log(`New base branch id: ${result.nextBaseBranchId}`);
9937
+ console.log(`Agent ${name} reconfigured successfully.`);
9938
+ } finally {
9939
+ await provider.close();
9940
+ }
9810
9941
  }
9811
9942
  async function addTask(name, options) {
9812
9943
  const provider = new TaskListTidbProvider(name, pino());
9813
- const config = await provider.getAgentConfig(options.projectId);
9814
- if (!config) throw new Error(`Agent ${name} not configured for project ${options.projectId}`);
9815
- const task = await provider.createTask({
9816
- task: options.prompt,
9817
- project_id: options.projectId,
9818
- base_branch_id: config.base_branch_id
9819
- });
9820
- console.log(`Task ${task.task} queued successfully.`);
9821
- await provider.close();
9944
+ try {
9945
+ const config = await provider.getAgentConfig(options.projectId);
9946
+ if (!config) throw new Error(`Agent ${name} not configured for project ${options.projectId}`);
9947
+ const task = await provider.createTask({
9948
+ task: options.prompt,
9949
+ project_id: options.projectId,
9950
+ base_branch_id: config.base_branch_id
9951
+ });
9952
+ console.log(`Queued task ${task.id} successfully.`);
9953
+ } finally {
9954
+ await provider.close();
9955
+ }
9822
9956
  }
9823
9957
  async function deleteTask(agentName, taskId) {
9824
9958
  const provider = new TaskListTidbProvider(agentName, pino());
@@ -9973,11 +10107,22 @@ async function assertsSingleton(logger, pidFile) {
9973
10107
  }
9974
10108
  }
9975
10109
 
10110
+ //#endregion
10111
+ //#region src/cli/utils/env.ts
10112
+ function ensureEnv(keys) {
10113
+ const missing = keys.filter((key) => !process.env[key]);
10114
+ if (missing.length === 0) return true;
10115
+ for (const key of missing) console.error(`${key} environment variable is not set.`);
10116
+ process.exitCode = 1;
10117
+ return false;
10118
+ }
10119
+
9976
10120
  //#endregion
9977
10121
  //#region src/cli/commands/add-task.ts
9978
10122
  function createAddTaskCommand(version) {
9979
- return createCommand("pantheon-agents add-task").version(version).description("Add a task to an agent").argument("<name>", "The name of the agent.").argument("<project-id>", "The project id of the agent.").argument("<task-prompt>", "The prompt of the task.").action(async function() {
10123
+ return createCommand("add-task").version(version).description("Add a task to an agent").argument("<name>", "The name of the agent.").argument("<project-id>", "The project id of the agent.").argument("<task-prompt>", "The prompt of the task.").action(async function() {
9980
10124
  const [name, projectId, taskPrompt] = this.args;
10125
+ if (!ensureEnv(["DATABASE_URL"])) return;
9981
10126
  await addTask(name, {
9982
10127
  projectId,
9983
10128
  prompt: taskPrompt
@@ -9988,12 +10133,16 @@ function createAddTaskCommand(version) {
9988
10133
  //#endregion
9989
10134
  //#region src/cli/commands/config.ts
9990
10135
  function createConfigAgentCommand(version) {
9991
- return createCommand("pantheon-agents config").version(version).description("Configure agent for pantheon project").argument("<name>", "The name of the agent.").argument("<role>", "The role of the agent.").argument("<project-id>", "The project id of the agent.").option("--skills <skills>", "The skills of the agent. Multiple values are separated by comma.", (val) => val.split(",").map((s) => s.trim()).filter((s) => s !== ""), []).option("--execute-agent <agent>", "The execute agent of the agent.", "codex").option("--root-branch-id <branchId>", "The root branch id of the agent. Default to project root branch id.").option("--prototype-url <url>", "Role and skill definitions repo.", "https://github.com/pingcap-inc/pantheon-agents").option("--no-bootstrap", "Prevent bootstrap base branch for agent. Use the root branch as base branch.").action(async function() {
10136
+ return createCommand("config").version(version).description("Configure agent for pantheon project").argument("<name>", "The name of the agent.").argument("<role>", "The role of the agent.").argument("<project-id>", "The project id of the agent.").option("--skills <skills>", "The skills of the agent. Multiple values are separated by comma.", (val) => val.split(",").map((s) => s.trim()).filter((s) => s !== ""), []).option("--execute-agent <agent>", "The execute agent of the agent.", "codex").option("--root-branch-id <branchId>", "The root branch id of the agent. Default to project root branch id.").option("--prototype-url <url>", "Role and skill definitions repo.", "https://github.com/pingcap-inc/pantheon-agents").option("--no-bootstrap", "Prevent bootstrap base branch for agent. Use the root branch as base branch.").action(async function() {
9992
10137
  const [name, role, projectId] = this.args;
10138
+ const options = this.opts();
10139
+ const requiredEnvVars = ["DATABASE_URL"];
10140
+ if (options.bootstrap || !options.rootBranchId) requiredEnvVars.push("PANTHEON_API_KEY");
10141
+ if (!ensureEnv(requiredEnvVars)) return;
9993
10142
  await configAgent(name, {
9994
10143
  role,
9995
10144
  projectId,
9996
- ...this.opts()
10145
+ ...options
9997
10146
  });
9998
10147
  });
9999
10148
  }
@@ -10001,8 +10150,9 @@ function createConfigAgentCommand(version) {
10001
10150
  //#endregion
10002
10151
  //#region src/cli/commands/delete-task.ts
10003
10152
  function createDeleteTaskCommand(version) {
10004
- return createCommand("pantheon-agents delete-task").version(version).description("Delete a task for an agent").argument("<name>", "The name of the agent.").argument("<task-id>", "The id of the task.").action(async function() {
10153
+ return createCommand("delete-task").version(version).description("Delete a task for an agent").argument("<name>", "The name of the agent.").argument("<task-id>", "The id of the task.").action(async function() {
10005
10154
  const [name, taskId] = this.args;
10155
+ if (!ensureEnv(["DATABASE_URL"])) return;
10006
10156
  const rl = readline.createInterface({
10007
10157
  input: process$1.stdin,
10008
10158
  output: process$1.stdout
@@ -10010,16 +10160,19 @@ function createDeleteTaskCommand(version) {
10010
10160
  try {
10011
10161
  if ((await rl.question(`Type the task id (${taskId}) to confirm deletion: `)).trim() !== taskId) {
10012
10162
  console.error("Confirmation failed. Task id did not match.");
10013
- process$1.exit(1);
10163
+ process$1.exitCode = 1;
10164
+ return;
10014
10165
  }
10015
10166
  if ((await rl.question("Type DELETE to permanently remove this task: ")).trim() !== "DELETE") {
10016
10167
  console.error("Confirmation failed. Aborting deletion.");
10017
- process$1.exit(1);
10168
+ process$1.exitCode = 1;
10169
+ return;
10018
10170
  }
10019
10171
  const deletedTask = await deleteTask(name, taskId);
10020
10172
  if (!deletedTask) {
10021
10173
  console.error(`Task ${taskId} not found for agent ${name}.`);
10022
- process$1.exit(1);
10174
+ process$1.exitCode = 1;
10175
+ return;
10023
10176
  }
10024
10177
  console.log(`Deleted task ${taskId} for agent ${name}. Status was ${deletedTask.status}.`);
10025
10178
  } finally {
@@ -10028,12 +10181,51 @@ function createDeleteTaskCommand(version) {
10028
10181
  });
10029
10182
  }
10030
10183
 
10184
+ //#endregion
10185
+ //#region src/cli/utils/parse.ts
10186
+ function parseCommaList(value) {
10187
+ return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
10188
+ }
10189
+ function parseUniqueCommaList(value) {
10190
+ const items = parseCommaList(value);
10191
+ const seen = /* @__PURE__ */ new Set();
10192
+ const result = [];
10193
+ for (const item of items) {
10194
+ if (seen.has(item)) continue;
10195
+ seen.add(item);
10196
+ result.push(item);
10197
+ }
10198
+ return result;
10199
+ }
10200
+
10201
+ //#endregion
10202
+ //#region src/cli/commands/reconfig.ts
10203
+ function createReconfigAgentCommand(version) {
10204
+ return createCommand("reconfig").version(version).description("Re-bootstrap an existing agent configuration for a project").argument("<name>", "The name of the agent.").argument("<project-id>", "The project id of the agent.").option("--role <role>", "Override role for the agent.").option("--skills <skills>", "Override skills for the agent (comma-separated, replaces existing list).", parseUniqueCommaList).action(async function() {
10205
+ const [name, projectId] = this.args;
10206
+ const options = this.opts();
10207
+ if (!ensureEnv(["DATABASE_URL", "PANTHEON_API_KEY"])) return;
10208
+ const resolvedRole = options.role?.trim();
10209
+ if (options.role != null && !resolvedRole) {
10210
+ console.error("--role must be non-empty.");
10211
+ process$1.exitCode = 1;
10212
+ return;
10213
+ }
10214
+ await reconfigAgent(name, {
10215
+ projectId,
10216
+ role: resolvedRole,
10217
+ skills: options.skills
10218
+ });
10219
+ });
10220
+ }
10221
+
10031
10222
  //#endregion
10032
10223
  //#region src/cli/commands/run.ts
10033
10224
  function createRunAgentCommand(version) {
10034
- return createCommand("pantheon-agents run").version(version).description("Start a pantheon agents").argument("<name>", "The name of the agent.").option("--data-dir [dir]", "Data directory.", expandTilde("~/.pantheon-agents")).option("--mcp-port", "The port of the MCP server. Defaults to a random port.").option("--loop-interval <seconds>", "The interval of the loop in seconds. Defaults to 5.", (val) => parseInt(val, 10), 5).action(async function() {
10225
+ return createCommand("run").version(version).description("Start a pantheon agent").argument("<name>", "The name of the agent.").option("--data-dir [dir]", "Data directory.", expandTilde("~/.pantheon-agents")).option("--loop-interval <seconds>", "The interval of the loop in seconds. Defaults to 5.", (val) => parseInt(val, 10), 5).action(async function() {
10035
10226
  const [name] = this.args;
10036
10227
  const options = this.opts();
10228
+ if (!ensureEnv(["DATABASE_URL", "PANTHEON_API_KEY"])) return;
10037
10229
  const logFileTransport = transport({
10038
10230
  target: "pino-roll",
10039
10231
  options: {
@@ -21623,6 +21815,141 @@ function taskToDTO(agent, task) {
21623
21815
  error: task.status === "failed" ? task.error : null
21624
21816
  };
21625
21817
  }
21818
+ function agentConfigToDTO(config) {
21819
+ return {
21820
+ agent: config.agent,
21821
+ project_id: config.project_id,
21822
+ base_branch_id: config.base_branch_id,
21823
+ role: config.role,
21824
+ skills: config.skills,
21825
+ prototype_url: config.prototype_url,
21826
+ execute_agent: config.execute_agent
21827
+ };
21828
+ }
21829
+
21830
+ //#endregion
21831
+ //#region src/server/agent-config.ts
21832
+ function assertPantheonApiKeyConfigured() {
21833
+ if (!process.env.PANTHEON_API_KEY?.trim()) throw new ApiError({
21834
+ status: 503,
21835
+ code: "pantheon_api_key_missing",
21836
+ message: "Missing PANTHEON_API_KEY. This endpoint requires Pantheon API access."
21837
+ });
21838
+ }
21839
+ async function getBranchOutputOrThrow(options) {
21840
+ let state;
21841
+ try {
21842
+ state = await options.getPantheonBranchFn({
21843
+ projectId: options.projectId,
21844
+ branchId: options.branchId,
21845
+ getOutputIfFinished: true,
21846
+ manifestingAsSucceed: true
21847
+ });
21848
+ } catch (error) {
21849
+ throw new ApiError({
21850
+ status: 502,
21851
+ code: "pantheon_api_error",
21852
+ message: `Failed to fetch Pantheon branch output for ${options.branchId}.`,
21853
+ details: {
21854
+ project_id: options.projectId,
21855
+ branch_id: options.branchId,
21856
+ error: getErrorMessage(error)
21857
+ }
21858
+ });
21859
+ }
21860
+ if (state.state === "succeed") return state.output ?? "";
21861
+ if (state.state === "failed") throw new ApiError({
21862
+ status: 502,
21863
+ code: "pantheon_branch_failed",
21864
+ message: `Pantheon branch ${options.branchId} failed.`,
21865
+ details: {
21866
+ project_id: options.projectId,
21867
+ branch_id: options.branchId,
21868
+ error: state.error
21869
+ }
21870
+ });
21871
+ throw new ApiError({
21872
+ status: 502,
21873
+ code: "pantheon_branch_not_ready",
21874
+ message: `Pantheon branch ${options.branchId} is not finished yet.`,
21875
+ details: {
21876
+ project_id: options.projectId,
21877
+ branch_id: options.branchId
21878
+ }
21879
+ });
21880
+ }
21881
+ async function getAgentConfigWithBootstrapOutput(options) {
21882
+ assertPantheonApiKeyConfigured();
21883
+ const config = await options.provider.getAgentConfig(options.projectId);
21884
+ if (!config) throw new ApiError({
21885
+ status: 404,
21886
+ code: "config_not_found",
21887
+ message: `No config found for agent ${options.agent} and project ${options.projectId}.`
21888
+ });
21889
+ const bootstrapOutput = await getBranchOutputOrThrow({
21890
+ projectId: config.project_id,
21891
+ branchId: config.base_branch_id,
21892
+ getPantheonBranchFn: options.getPantheonBranchFn ?? getPantheonBranch
21893
+ });
21894
+ return {
21895
+ config: agentConfigToDTO(config),
21896
+ bootstrap_output: bootstrapOutput
21897
+ };
21898
+ }
21899
+ async function reconfigAgentConfigWithBootstrapOutput(options) {
21900
+ assertPantheonApiKeyConfigured();
21901
+ const getPantheonBranchFn = options.getPantheonBranchFn ?? getPantheonBranch;
21902
+ const wrappedGetPantheonBranch = (args) => getPantheonBranchFn({
21903
+ ...args,
21904
+ manifestingAsSucceed: true
21905
+ });
21906
+ let result;
21907
+ try {
21908
+ result = await reconfigAgentWithDeps(options.agent, options.reconfig, {
21909
+ provider: options.provider,
21910
+ executeOnPantheonFn: options.executeOnPantheonFn ?? executeOnPantheon,
21911
+ getPantheonBranchFn: wrappedGetPantheonBranch,
21912
+ sleep: options.sleep,
21913
+ pollIntervalMs: options.pollIntervalMs,
21914
+ maxRetries: options.maxRetries
21915
+ });
21916
+ } catch (error) {
21917
+ if (error instanceof ApiError) throw error;
21918
+ const message = getErrorMessage(error);
21919
+ if (message.startsWith("No config found for agent ")) throw new ApiError({
21920
+ status: 404,
21921
+ code: "config_not_found",
21922
+ message
21923
+ });
21924
+ if (message === "--role must be non-empty.") throw new ApiError({
21925
+ status: 400,
21926
+ code: "validation_error",
21927
+ message
21928
+ });
21929
+ if (message.startsWith("Bootstrap failed:")) throw new ApiError({
21930
+ status: 502,
21931
+ code: "bootstrap_failed",
21932
+ message
21933
+ });
21934
+ throw new ApiError({
21935
+ status: 502,
21936
+ code: "reconfig_failed",
21937
+ message,
21938
+ details: { error: message }
21939
+ });
21940
+ }
21941
+ const updated = await options.provider.getAgentConfig(result.previousConfig.project_id);
21942
+ if (!updated) throw new ApiError({
21943
+ status: 500,
21944
+ code: "config_update_lost",
21945
+ message: "Config disappeared after reconfig update."
21946
+ });
21947
+ return {
21948
+ previous_config: agentConfigToDTO(result.previousConfig),
21949
+ config: agentConfigToDTO(updated),
21950
+ bootstrap_output: result.bootstrapOutput
21951
+ };
21952
+ }
21626
21953
 
21627
21954
  //#endregion
21628
21955
  //#region src/server/api.ts
@@ -21661,6 +21988,10 @@ const createTaskBodySchema = z$1.object({
21661
21988
  project_id: z$1.string().min(1),
21662
21989
  task: z$1.string().min(1)
21663
21990
  });
21991
+ const reconfigBodySchema = z$1.object({
21992
+ role: z$1.string().trim().min(1).optional(),
21993
+ skills: z$1.array(z$1.string().trim().min(1)).optional()
21994
+ });
21664
21995
  function getAgentParam(value) {
21665
21996
  if (typeof value !== "string" || value.trim().length === 0) throw new ApiError({
21666
21997
  status: 400,
@@ -21669,6 +22000,14 @@ function getAgentParam(value) {
21669
22000
  });
21670
22001
  return value;
21671
22002
  }
22003
+ function getProjectIdParam(value) {
22004
+ if (typeof value !== "string" || value.trim().length === 0) throw new ApiError({
22005
+ status: 400,
22006
+ code: "validation_error",
22007
+ message: "Invalid project_id parameter."
22008
+ });
22009
+ return value;
22010
+ }
21672
22011
  function getTaskIdParam(value) {
21673
22012
  if (typeof value !== "string" || value.trim().length === 0) throw new ApiError({
21674
22013
  status: 400,
@@ -21684,6 +22023,31 @@ function createApiRouter(options) {
21684
22023
  const agents = await new TaskListTidbProvider("server", logger, { db }).listAgentNames();
21685
22024
  res.json({ agents });
21686
22025
  }));
22026
+ router.get("/agents/:agent/configs/:project_id", asyncHandler(async (req, res) => {
22027
+ const agent = getAgentParam(req.params.agent);
22028
+ const result = await getAgentConfigWithBootstrapOutput({
22029
+ agent,
22030
+ projectId: getProjectIdParam(req.params.project_id),
22031
+ provider: new TaskListTidbProvider(agent, logger, { db })
22032
+ });
22033
+ res.json(result);
22034
+ }));
22035
+ router.post("/agents/:agent/configs/:project_id/reconfig", asyncHandler(async (req, res) => {
22036
+ const agent = getAgentParam(req.params.agent);
22037
+ const projectId = getProjectIdParam(req.params.project_id);
22038
+ const body = reconfigBodySchema.parse(req.body);
22039
+ const provider = new TaskListTidbProvider(agent, logger, { db });
22040
+ const result = await reconfigAgentConfigWithBootstrapOutput({
22041
+ agent,
22042
+ reconfig: {
22043
+ projectId,
22044
+ role: body.role,
22045
+ skills: body.skills
22046
+ },
22047
+ provider
22048
+ });
22049
+ res.json(result);
22050
+ }));
21687
22051
  router.get("/agents/:agent/tasks", asyncHandler(async (req, res) => {
21688
22052
  const agent = getAgentParam(req.params.agent);
21689
22053
  const query = listTasksQuerySchema.parse(req.query);
@@ -31551,6 +31915,51 @@ function createAgentsMcpServer(options) {
31551
31915
  structuredContent: { deleted: true }
31552
31916
  };
31553
31917
  });
31918
+ server.registerTool("configs.get", {
31919
+ description: "Get an agent's config for a project, including the bootstrap branch output.",
31920
+ inputSchema: {
31921
+ agent: z$1.string().min(1),
31922
+ project_id: z$1.string().min(1)
31923
+ }
31924
+ }, async ({ agent, project_id }) => {
31925
+ return {
31926
+ content: [{
31927
+ type: "text",
31928
+ text: "OK"
31929
+ }],
31930
+ structuredContent: await getAgentConfigWithBootstrapOutput({
31931
+ agent,
31932
+ projectId: project_id,
31933
+ provider: new TaskListTidbProvider(agent, options.logger, { db: options.db })
31934
+ })
31935
+ };
31936
+ });
31937
+ server.registerTool("configs.reconfig", {
31938
+ description: "Re-bootstrap an existing agent configuration for a project (like `pantheon-agents reconfig`).",
31939
+ inputSchema: {
31940
+ agent: z$1.string().min(1),
31941
+ project_id: z$1.string().min(1),
31942
+ role: z$1.string().trim().min(1).optional(),
31943
+ skills: z$1.array(z$1.string().trim().min(1)).optional()
31944
+ }
31945
+ }, async ({ agent, project_id, role, skills }) => {
31946
+ const provider = new TaskListTidbProvider(agent, options.logger, { db: options.db });
31947
+ return {
31948
+ content: [{
31949
+ type: "text",
31950
+ text: "OK"
31951
+ }],
31952
+ structuredContent: await reconfigAgentConfigWithBootstrapOutput({
31953
+ agent,
31954
+ reconfig: {
31955
+ projectId: project_id,
31956
+ role,
31957
+ skills
31958
+ },
31959
+ provider
31960
+ })
31961
+ };
31962
+ });
31554
31963
  return server;
31555
31964
  }
31556
31965
 
@@ -31661,11 +32070,13 @@ async function startAgentsServer(options, logger, overrides = {}) {
31661
32070
  //#endregion
31662
32071
  //#region src/cli/commands/server.ts
31663
32072
  function createServerCommand(version) {
31664
- return createCommand("pantheon-agents server").version(version).description("Start the task HTTP API + MCP server").option("--host <host>", "Bind host. Defaults to 127.0.0.1.", "127.0.0.1").option("--port <port>", "Bind port. Defaults to 8000.", (v) => parseInt(v, 10), 8e3).option("--base-path <path>", "Mount base path prefix. Defaults to /.", "/").option("--token <token>", "Bearer token for API auth. Defaults to env PANTHEON_AGENTS_API_TOKEN.").option("--no-auth", "Disable auth (NOT recommended).").action(async function() {
32073
+ return createCommand("server").version(version).description("Start the task HTTP API + MCP server").option("--host <host>", "Bind host. Defaults to 127.0.0.1.", "127.0.0.1").option("--port <port>", "Bind port. Defaults to 8000.", (v) => parseInt(v, 10), 8e3).option("--base-path <path>", "Mount base path prefix. Defaults to /.", "/").option("--token <token>", "Bearer token for API auth. Defaults to env PANTHEON_AGENTS_API_TOKEN.").option("--no-auth", "Disable auth (NOT recommended).").action(async function() {
31665
32074
  const options = this.opts();
32075
+ if (!ensureEnv(["DATABASE_URL"])) return;
31666
32076
  if (!Number.isInteger(options.port) || options.port <= 0) {
31667
32077
  console.error("Invalid --port value. Must be a positive integer.");
31668
- process$1.exit(1);
32078
+ process$1.exitCode = 1;
32079
+ return;
31669
32080
  }
31670
32081
  const logger = pino({
31671
32082
  timestamp: pino.stdTimeFunctions.isoTime,
@@ -31688,14 +32099,16 @@ function createServerCommand(version) {
31688
32099
  //#endregion
31689
32100
  //#region src/cli/commands/show-config.ts
31690
32101
  function createShowConfigCommand(version) {
31691
- return createCommand("pantheon-agents show-config").version(version).description("Show agent config for a project").argument("<name>", "The name of the agent.").argument("[project-id]", "The project id.").option("--json", "Output config as JSON.").action(async function() {
32102
+ return createCommand("show-config").version(version).description("Show agent config for a project").argument("<name>", "The name of the agent.").argument("[project-id]", "The project id.").option("--json", "Output config as JSON.").action(async function() {
31692
32103
  const [name, projectId] = this.args;
31693
32104
  const options = this.opts();
32105
+ if (!ensureEnv(["DATABASE_URL"])) return;
31694
32106
  if (projectId) {
31695
32107
  const config = await showAgentConfig(name, projectId);
31696
32108
  if (!config) {
31697
32109
  console.error(`No config found for agent ${name} and project ${projectId}.`);
31698
- process$1.exit(1);
32110
+ process$1.exitCode = 1;
32111
+ return;
31699
32112
  }
31700
32113
  if (options.json) {
31701
32114
  console.log(JSON.stringify(config, null, 2));
@@ -31715,7 +32128,8 @@ function createShowConfigCommand(version) {
31715
32128
  const configs = await showAgentConfigs(name);
31716
32129
  if (!configs.length) {
31717
32130
  console.error(`No configs found for agent ${name}.`);
31718
- process$1.exit(1);
32131
+ process$1.exitCode = 1;
32132
+ return;
31719
32133
  }
31720
32134
  if (options.json) {
31721
32135
  console.log(JSON.stringify(configs, null, 2));
@@ -31749,55 +32163,53 @@ const orderByFields = [
31749
32163
  ];
31750
32164
  const orderDirections = ["asc", "desc"];
31751
32165
 
31752
- //#endregion
31753
- //#region src/cli/utils/parse.ts
31754
- function parseCommaList(value) {
31755
- return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
31756
- }
31757
-
31758
32166
  //#endregion
31759
32167
  //#region src/cli/commands/show-tasks.ts
31760
32168
  function createShowTasksCommand(version) {
31761
- return createCommand("pantheon-agents show-tasks").version(version).description("Show tasks for an agent").argument("[name]", "The name of the agent.").option("--agents <names>", "Comma-separated agent names to query in one call.", parseCommaList, []).option("--all", "Show tasks for all agents.").option("--status <status>", "Filter tasks by status. Multiple values are separated by comma.", parseCommaList, []).option("--order-by <field>", "Order by queued_at, started_at, or ended_at.", "queued_at").option("--order-direction <direction>", "Order direction: asc or desc.", "desc").option("--limit <number>", "Limit the number of tasks shown.", (val) => parseInt(val, 10)).option("--max-task-length <number>", "Maximum task length for table output.", (val) => parseInt(val, 10), 120).option("--full-task", "Do not truncate task text in output.").option("--concise", "Output concise one-line entries.", true).option("--no-color", "Disable colored output.").option("--json", "Output tasks as JSON.").action(async function() {
32169
+ return createCommand("show-tasks").version(version).description("Show tasks for an agent").argument("[name]", "The name of the agent.").option("--agents <names>", "Comma-separated agent names to query in one call.", parseCommaList, []).option("--all", "Show tasks for all agents.").option("--status <status>", "Filter tasks by status. Multiple values are separated by comma.", parseCommaList, []).option("--order-by <field>", "Order by queued_at, started_at, or ended_at.", "queued_at").option("--order-direction <direction>", "Order direction: asc or desc.", "desc").option("--limit <number>", "Limit the number of tasks shown.", (val) => parseInt(val, 10)).option("--max-task-length <number>", "Maximum task length for table output.", (val) => parseInt(val, 10), 120).option("--full-task", "Do not truncate task text in output.").option("--no-concise", "Disable concise one-line entries.").option("--no-color", "Disable colored output.").option("--json", "Output tasks as JSON.").action(async function() {
31762
32170
  const [name] = this.args;
31763
32171
  const options = this.opts();
32172
+ if (!ensureEnv(["DATABASE_URL"])) return;
31764
32173
  const agentNames = /* @__PURE__ */ new Set();
31765
32174
  if (name) agentNames.add(name);
31766
32175
  options.agents.forEach((agent) => agentNames.add(agent));
31767
32176
  if (options.all && agentNames.size > 0) {
31768
32177
  console.error("Use either a specific agent name or --all, not both.");
31769
- process$1.exit(1);
32178
+ process$1.exitCode = 1;
32179
+ return;
31770
32180
  }
31771
32181
  if (!options.all && agentNames.size === 0) {
31772
32182
  console.error("Provide an agent name, --agents, or --all.");
31773
- process$1.exit(1);
32183
+ process$1.exitCode = 1;
32184
+ return;
31774
32185
  }
31775
32186
  const invalidStatuses = options.status.filter((status) => !taskStatuses.includes(status));
31776
32187
  if (invalidStatuses.length > 0) {
31777
32188
  console.error(`Invalid status values: ${invalidStatuses.join(", ")}. Allowed: ${taskStatuses.join(", ")}.`);
31778
- process$1.exit(1);
32189
+ process$1.exitCode = 1;
32190
+ return;
31779
32191
  }
31780
32192
  if (!orderByFields.includes(options.orderBy)) {
31781
32193
  console.error(`Invalid order-by value: ${options.orderBy}. Allowed: ${orderByFields.join(", ")}.`);
31782
- process$1.exit(1);
32194
+ process$1.exitCode = 1;
32195
+ return;
31783
32196
  }
31784
32197
  if (!orderDirections.includes(options.orderDirection)) {
31785
32198
  console.error(`Invalid order-direction value: ${options.orderDirection}. Allowed: ${orderDirections.join(", ")}.`);
31786
- process$1.exit(1);
32199
+ process$1.exitCode = 1;
32200
+ return;
31787
32201
  }
31788
32202
  if (options.limit != null && Number.isNaN(options.limit)) {
31789
32203
  console.error("Invalid limit value. Must be a number.");
31790
- process$1.exit(1);
32204
+ process$1.exitCode = 1;
32205
+ return;
31791
32206
  }
31792
32207
  if (Number.isNaN(options.maxTaskLength) || options.maxTaskLength <= 0) {
31793
32208
  console.error("Invalid max-task-length value. Must be a positive number.");
31794
- process$1.exit(1);
32209
+ process$1.exitCode = 1;
32210
+ return;
31795
32211
  }
31796
32212
  if (options.fullTask && options.maxTaskLength) options.maxTaskLength = Number.POSITIVE_INFINITY;
31797
- if (options.json && options.concise) {
31798
- console.error("Use either --json or --concise, not both.");
31799
- process$1.exit(1);
31800
- }
31801
32213
  const limitedTasks = await showTasksForAgents({
31802
32214
  agents: Array.from(agentNames),
31803
32215
  allAgents: options.all,
@@ -31833,36 +32245,22 @@ function createShowTasksCommand(version) {
31833
32245
  });
31834
32246
  }
31835
32247
 
31836
- //#endregion
31837
- //#region src/cli/utils/env.ts
31838
- function warnIfMissingEnv(keys) {
31839
- for (const key of keys) if (!process.env[key]) console.error(`${key} environment variable is not set.`);
31840
- }
31841
-
31842
32248
  //#endregion
31843
32249
  //#region src/cli/index.ts
31844
- warnIfMissingEnv(["PANTHEON_API_KEY", "DATABASE_URL"]);
31845
- const commands = {
31846
- "add-task": createAddTaskCommand(version$1),
31847
- config: createConfigAgentCommand(version$1),
31848
- "delete-task": createDeleteTaskCommand(version$1),
31849
- run: createRunAgentCommand(version$1),
31850
- server: createServerCommand(version$1),
31851
- "show-config": createShowConfigCommand(version$1),
31852
- "show-tasks": createShowTasksCommand(version$1)
31853
- };
31854
- function printCommandHelpAndExit(command) {
31855
- console.error(`Invalid command: ${command}. Supported commands: ${Object.keys(commands).join(", ")}.`);
31856
- console.error(" Run pantheon-agents help <command> for more information.");
31857
- process$1.exit(1);
31858
- }
31859
- if (process$1.argv[2] === "help") {
31860
- const command = process$1.argv[3];
31861
- if (command in commands) commands[command].help({ error: false });
31862
- else printCommandHelpAndExit(command);
31863
- }
31864
- if (!commands[process$1.argv[2]]) printCommandHelpAndExit(process$1.argv[2]);
31865
- commands[process$1.argv[2]].parse(process$1.argv.slice(3), { from: "user" });
32250
+ const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version$1).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version$1)).addCommand(createConfigAgentCommand(version$1)).addCommand(createDeleteTaskCommand(version$1)).addCommand(createReconfigAgentCommand(version$1)).addCommand(createRunAgentCommand(version$1)).addCommand(createServerCommand(version$1)).addCommand(createShowConfigCommand(version$1)).addCommand(createShowTasksCommand(version$1));
32251
+ async function main() {
32252
+ if (process$1.argv.length <= 2) {
32253
+ program.outputHelp();
32254
+ return;
32255
+ }
32256
+ await program.parseAsync(process$1.argv);
32257
+ }
32258
+ try {
32259
+ await main();
32260
+ } catch (error) {
32261
+ console.error(getErrorMessage(error));
32262
+ process$1.exitCode = 1;
32263
+ }
31866
32264
 
31867
32265
  //#endregion
31868
32266
  export { };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pantheon.ai/agents",
3
3
  "type": "module",
4
- "version": "0.0.9",
4
+ "version": "0.0.11",
5
5
  "bin": {
6
6
  "pantheon-agents": "dist/index.js"
7
7
  },
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "build": "rolldown -c rolldown.config.ts && chmod +x dist/index.js",
16
+ "test": "bun test",
16
17
  "dev:db:start": "tiup playground --without-monitor --tag pantheon-agents",
17
18
  "dev:db:gen": "kysely-codegen "
18
19
  },