feat(ai-tools): catalog of AI coding CLIs, detection, enable/default, lanes for any tool (#40 A1)

server/lib/ai-tools.js loads server/data/ai-tools/*.json (38 tools, data from
Orca, MIT) plus DASHBOARD_AI_TOOLS_DIR, detects each CLI by walking PATH, and
keeps enable/default in <data dir>/ai-tools.json. shell-path.js merges the
login shell's PATH at startup and on Refresh. /api/ai-tools lists, refreshes
and toggles; /api/run/binary resolves through the catalog.

Lanes accept any catalog id (EDISABLED for a disabled one on creation, ENOTOOL
at start once a tool left the catalog). pty-run keeps the Claude/Codex
builders and builds every other tool's argv from its catalog file, pasting an
after-start prompt through a tmux buffer; every pane gets the hydrated PATH.
A per-run bypass switch appends the catalog's bypass args (Codex included).
POST /api/run now forwards sandbox/approval/resumeLast, which it dropped.
agenthub lanes add gains --tool.
This commit is contained in:
2026-09-28 10:57:10 +07:00
parent 0aa7fc232e
commit 77917203fd
59 changed files with 1804 additions and 84 deletions
+13 -4
View File
@@ -10,6 +10,7 @@
*/
const { db } = require("../db");
const aiTools = require("./ai-tools");
const {
DEFAULT_PIPELINE_ID,
listPipelines,
@@ -97,12 +98,20 @@ function validateKind(kind) {
}
}
const VALID_TOOLS = new Set(["claude", "codex", "antigravity"]);
/**
* A lane may be created for any AI Tools catalog id, but not for one the user
* disabled. Only creation asks — an existing lane on a tool disabled since
* keeps working.
*/
function validateTool(tool) {
if (!VALID_TOOLS.has(tool)) {
if (!aiTools.has(tool)) {
throw Object.assign(new Error(`unknown tool: ${tool}`), { code: "EBADTOOL" });
}
if (!aiTools.isEnabled(tool)) {
throw Object.assign(new Error(`${tool} is disabled in Settings → AI Tools`), {
code: "EDISABLED",
});
}
}
/**
@@ -888,7 +897,7 @@ function clearCodexPipelines() {
module.exports = {
DEAD_SEC,
VALID_TOOLS,
validateTool,
detectionIsStale,
canonicalCwd,
createLane,