Menu
popagent
publicLatest change 816546ed4e6d53487b3a8af5796e644cae908620 - fix: route repository tasks through governed workflow by AkurAI Build
import { describe, expect, test } from "bun:test";
const run = async (args: string[], url?: string) => {
const process = Bun.spawn(["./popagent", ...args], {
stdout: "pipe",
stderr: "pipe",
env: { ...Bun.env, ...(url ? { POPAGENT_URL: url } : {}) },
});
return {
exitCode: await process.exited,
stdout: await new Response(process.stdout).text(),
stderr: await new Response(process.stderr).text(),
};
};
describe("popagent CLI", () => {
test("gives AI agents a complete one-shot command index", async () => {
const result = await run(["help"]);
expect(result.exitCode).toBe(0);
expect(result.stderr).toBe("");
for (const command of [
"popagent agent-update <agent> <json>",
"popagent skill-create <agent> <json>",
"popagent workspace-create <json>",
"popagent task <task-id>",
"popagent workflow-tasks [workspace]",
"popagent task-resolve <task-id> <json>",
"popagent signals [limit]",
"popagent automation-status",
"popagent automation-run <workspace>",
"popagent automation-reconcile",
"popagent signal-ignore <signal-id>",
"popagent signal-retry <signal-id>",
"popagent doc-save <workspace> <json>",
"popagent repo-brief <workspace> <query>",
"popagent docs-reindex <workspace>",
"popagent task-create <workspace> <prompt> [model]",
"popagent workflow-create <workspace> <prompt> [model]",
"popagent task-delete <task-id>",
"popagent schedule-create <json>",
"popagent settings-update <section> <json>",
"popagent api <METHOD> <path> [json]",
]) expect(result.stdout).toContain(command);
expect(result.stdout).toContain("POPAGENT_URL");
expect(result.stdout).toContain("x-popagent-key");
expect(result.stdout).toContain("JSON arguments are passed unchanged");
expect(result.stdout).toContain("Popagent Docs is canonical");
});
test("routes named management commands without rewriting JSON", async () => {
const requests: Array<{ method: string; path: string; body: string }> = [];
const server = Bun.serve({
port: 0,
async fetch(request) {
requests.push({
method: request.method,
path: new URL(request.url).pathname,
body: await request.text(),
});
return Response.json({ ok: true });
},
});
try {
const body = '{"path":"guides/start.md","content":"# Start"}';
const result = await run(["doc-save", "team/a", body], server.url.toString());
expect(result.exitCode).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({ ok: true });
expect(requests).toEqual([{
method: "POST",
path: "/api/workspaces/team%2Fa/docs",
body,
}]);
} finally {
server.stop(true);
}
});
test("routes persisted Build agent inspection and edits unchanged", async () => {
const requests: Array<{ method: string; path: string; body: string }> = [];
const server = Bun.serve({
port: 0,
async fetch(request) {
requests.push({
method: request.method,
path: new URL(request.url).pathname,
body: await request.text(),
});
return Response.json({ id: "build-maintainer" });
},
});
try {
const body = '{"delegationEnabled":true}';
expect((await run(["agents"], server.url.toString())).exitCode).toBe(0);
expect((await run(["agent-update", "build-maintainer", body], server.url.toString())).exitCode).toBe(0);
expect(requests).toEqual([
{ method: "GET", path: "/api/agents", body: "" },
{ method: "PATCH", path: "/api/agents/build-maintainer", body },
]);
} finally {
server.stop(true);
}
});
test("routes repository briefs through the workspace API", async () => {
const requests: Array<{ method: string; path: string }> = [];
const server = Bun.serve({
port: 0,
fetch(request) {
requests.push({ method: request.method, path: new URL(request.url).pathname + new URL(request.url).search });
return Response.json({ candidates: [] });
},
});
try {
expect((await run(["repo-brief", "team/a", "fix payment"], server.url.toString())).exitCode).toBe(0);
expect(requests).toEqual([{
method: "GET",
path: "/api/workspaces/team%2Fa/repo-brief?q=fix%20payment",
}]);
} finally {
server.stop(true);
}
});
test("reads one task without scanning the task collection", async () => {
const requests: Array<{ method: string; path: string }> = [];
const server = Bun.serve({
port: 0,
fetch(request) {
requests.push({ method: request.method, path: new URL(request.url).pathname });
return Response.json({ id: "failed/task" });
},
});
try {
expect((await run(["task", "failed/task"], server.url.toString())).exitCode).toBe(0);
expect(requests).toEqual([{ method: "GET", path: "/api/tasks/failed%2Ftask" }]);
} finally {
server.stop(true);
}
});
test("reads bounded autonomous workflow tasks", async () => {
const requests: string[] = [];
const server = Bun.serve({
port: 0,
fetch(request) {
requests.push(new URL(request.url).pathname + new URL(request.url).search);
return Response.json({ tasks: [] });
},
});
try {
expect((await run(["workflow-tasks", "team/a"], server.url.toString())).exitCode).toBe(0);
expect(requests).toEqual(["/api/tasks?workflow=true&workspaceId=team%2Fa"]);
} finally {
server.stop(true);
}
});
test("creates a governed specialist workflow task", async () => {
const requests: Array<{ method: string; path: string; body: unknown }> = [];
const server = Bun.serve({
port: 0,
async fetch(request) {
requests.push({
method: request.method,
path: new URL(request.url).pathname,
body: await request.json(),
});
return Response.json({ source: "self-update" });
},
});
try {
const result = await run(["workflow-create", "team/a", "Fix verified behavior"], server.url.toString());
expect(result.exitCode).toBe(0);
expect(requests).toEqual([{
method: "POST",
path: "/api/tasks",
body: { workspaceId: "team/a", prompt: "Fix verified behavior", workflow: true },
}]);
} finally {
server.stop(true);
}
});
test("routes terminal task resolution evidence", async () => {
const requests: Array<{ method: string; path: string; body: string }> = [];
const server = Bun.serve({
port: 0,
async fetch(request) {
requests.push({ method: request.method, path: new URL(request.url).pathname, body: await request.text() });
return Response.json({ status: "completed" });
},
});
try {
const body = '{"output":"Reviewed inline"}';
const result = await run(["task-resolve", "failed/task", body], server.url.toString());
expect(result.exitCode).toBe(0);
expect(requests).toEqual([{ method: "POST", path: "/api/tasks/failed%2Ftask/resolve", body }]);
} finally {
server.stop(true);
}
});
test("routes system schedule reconciliation", async () => {
const requests: Array<{ method: string; path: string }> = [];
const server = Bun.serve({
port: 0,
fetch(request) {
requests.push({ method: request.method, path: new URL(request.url).pathname });
return Response.json({ reconciled: true });
},
});
try {
const result = await run(["automation-reconcile"], server.url.toString());
expect(result.exitCode).toBe(0);
expect(requests).toEqual([{ method: "POST", path: "/api/autonomy/reconcile" }]);
} finally {
server.stop(true);
}
});
test("routes signal inspection and management commands", async () => {
const requests: Array<{ method: string; path: string }> = [];
const server = Bun.serve({
port: 0,
fetch(request) {
requests.push({ method: request.method, path: new URL(request.url).pathname + new URL(request.url).search });
return Response.json({ ok: true });
},
});
try {
expect((await run(["signals", "25"], server.url.toString())).exitCode).toBe(0);
expect((await run(["signal-ignore", "signal/id"], server.url.toString())).exitCode).toBe(0);
expect((await run(["signal-retry", "signal/id"], server.url.toString())).exitCode).toBe(0);
expect(requests).toEqual([
{ method: "GET", path: "/api/autonomy/signals?limit=25" },
{ method: "POST", path: "/api/autonomy/signals/signal%2Fid/ignore" },
{ method: "POST", path: "/api/autonomy/signals/signal%2Fid/retry" },
]);
} finally {
server.stop(true);
}
});
});