@jx0/agency 0.2.0

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 (85) hide show
  1. package/README.md +272 -0
  2. package/bin/agency.js +2 -0
  3. package/dashboard/out/404.html +1 -0
  4. package/dashboard/out/_next/static/chunks/255-67e8754147461423.js +1 -0
  5. package/dashboard/out/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  6. package/dashboard/out/_next/static/chunks/app/_not-found/page-ad40673d821037f6.js +1 -0
  7. package/dashboard/out/_next/static/chunks/app/layout-056f12675e691d12.js +1 -0
  8. package/dashboard/out/_next/static/chunks/app/page-80f01fdbb09b43c8.js +1 -0
  9. package/dashboard/out/_next/static/chunks/framework-de98b93a850cfc71.js +1 -0
  10. package/dashboard/out/_next/static/chunks/main-1a0dcce460eb61ce.js +1 -0
  11. package/dashboard/out/_next/static/chunks/main-app-1d848b791b823fa6.js +1 -0
  12. package/dashboard/out/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  13. package/dashboard/out/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  14. package/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  15. package/dashboard/out/_next/static/chunks/webpack-4e6bf084ac60582b.js +1 -0
  16. package/dashboard/out/_next/static/css/27d1ea794f04e96a.css +1 -0
  17. package/dashboard/out/_next/static/pU1nwWH_dNUOCI8y4nl3C/_buildManifest.js +1 -0
  18. package/dashboard/out/_next/static/pU1nwWH_dNUOCI8y4nl3C/_ssgManifest.js +1 -0
  19. package/dashboard/out/index.html +1 -0
  20. package/dashboard/out/index.txt +19 -0
  21. package/docs/images/agency_cli_ps.png +0 -0
  22. package/docs/images/agency_ui_ai_prodivder_settings.png +0 -0
  23. package/docs/images/agency_ui_aws_settings.png +0 -0
  24. package/docs/images/agency_ui_identity_settings.png +0 -0
  25. package/docs/images/agency_ui_mission_control.png +0 -0
  26. package/docs/images/agent_ui_agent_config.png +0 -0
  27. package/package.json +31 -0
  28. package/src/api/db/client.ts +16 -0
  29. package/src/api/db/migrate.ts +37 -0
  30. package/src/api/db/migrations/001_initial.ts +193 -0
  31. package/src/api/db/migrations/002_configs.ts +76 -0
  32. package/src/api/db/migrations/003_settings_columns.ts +13 -0
  33. package/src/api/db/seed.ts +142 -0
  34. package/src/api/db/types.ts +126 -0
  35. package/src/api/index.ts +73 -0
  36. package/src/api/lib/activity.ts +13 -0
  37. package/src/api/lib/fleet-sync.ts +156 -0
  38. package/src/api/lib/mentions.ts +59 -0
  39. package/src/api/lib/processes.ts +45 -0
  40. package/src/api/lib/resolve-agent.ts +5 -0
  41. package/src/api/lib/tunnels.ts +99 -0
  42. package/src/api/routes/activities.ts +27 -0
  43. package/src/api/routes/agents.ts +311 -0
  44. package/src/api/routes/documents.ts +41 -0
  45. package/src/api/routes/knowledge.ts +60 -0
  46. package/src/api/routes/messages.ts +54 -0
  47. package/src/api/routes/notifications.ts +40 -0
  48. package/src/api/routes/oauth.ts +171 -0
  49. package/src/api/routes/role-configs.ts +71 -0
  50. package/src/api/routes/settings.ts +94 -0
  51. package/src/api/routes/skills.ts +76 -0
  52. package/src/api/routes/tasks.ts +154 -0
  53. package/src/cli/commands/config.ts +42 -0
  54. package/src/cli/commands/daemon.ts +173 -0
  55. package/src/cli/commands/doc.ts +47 -0
  56. package/src/cli/commands/init.ts +105 -0
  57. package/src/cli/commands/learn.ts +51 -0
  58. package/src/cli/commands/logs.ts +31 -0
  59. package/src/cli/commands/msg.ts +18 -0
  60. package/src/cli/commands/ps.ts +19 -0
  61. package/src/cli/commands/recall.ts +18 -0
  62. package/src/cli/commands/skills.ts +66 -0
  63. package/src/cli/commands/ssh.ts +68 -0
  64. package/src/cli/commands/start.ts +14 -0
  65. package/src/cli/commands/status.ts +33 -0
  66. package/src/cli/commands/stop.ts +11 -0
  67. package/src/cli/commands/tasks.ts +150 -0
  68. package/src/cli/index.ts +70 -0
  69. package/src/cli/lib/api.ts +16 -0
  70. package/src/cli/lib/config.ts +5 -0
  71. package/src/cli/lib/find-root.ts +32 -0
  72. package/src/cli/lib/prompt.ts +20 -0
  73. package/src/daemon.ts +83 -0
  74. package/src/templates/implementer/agents-config.md +44 -0
  75. package/src/templates/implementer/agents.md +32 -0
  76. package/src/templates/implementer/heartbeat.md +47 -0
  77. package/src/templates/implementer/tools.md +33 -0
  78. package/src/templates/orchestrator/agents-config.md +44 -0
  79. package/src/templates/orchestrator/agents.md +27 -0
  80. package/src/templates/orchestrator/heartbeat.md +40 -0
  81. package/src/templates/orchestrator/tools.md +40 -0
  82. package/src/templates/shared/environment.md +20 -0
  83. package/src/templates/shared/memory.md +20 -0
  84. package/src/templates/shared/soul.md +26 -0
  85. package/src/templates/shared/user.md +12 -0
@@ -0,0 +1,154 @@
1
+ import { Hono } from "hono";
2
+ import { db } from "../db/client.js";
3
+ import { resolveAgent } from "../lib/resolve-agent.js";
4
+ import { logActivity } from "../lib/activity.js";
5
+
6
+ export const tasks = new Hono();
7
+
8
+ tasks.get("/", async (c) => {
9
+ const { status, assignee, type, parent_id } = c.req.query();
10
+ let q = db.selectFrom("tasks").selectAll();
11
+
12
+ if (status) q = q.where("status", "=", status);
13
+ if (type) q = q.where("task_type", "=", type);
14
+ if (parent_id) q = q.where("parent_id", "=", parent_id);
15
+
16
+ if (assignee) {
17
+ const agent = await resolveAgent(assignee);
18
+ if (!agent) return c.json([]);
19
+ q = q.where("id", "in",
20
+ db.selectFrom("task_assignees").where("agent_id", "=", agent.id).select("task_id")
21
+ );
22
+ }
23
+
24
+ const rows = await q.orderBy("created_at", "desc").execute();
25
+ return c.json(rows);
26
+ });
27
+
28
+ tasks.get("/:id", async (c) => {
29
+ const id = c.req.param("id");
30
+ const task = await db.selectFrom("tasks").where("id", "=", id).selectAll().executeTakeFirst();
31
+ if (!task) return c.json({ error: "not found" }, 404);
32
+
33
+ const assignees = await db
34
+ .selectFrom("task_assignees")
35
+ .innerJoin("agents", "agents.id", "task_assignees.agent_id")
36
+ .where("task_id", "=", id)
37
+ .selectAll("agents")
38
+ .execute();
39
+
40
+ const messages = await db
41
+ .selectFrom("messages")
42
+ .leftJoin("agents", "agents.id", "messages.from_agent")
43
+ .where("task_id", "=", id)
44
+ .select([
45
+ "messages.id", "messages.task_id", "messages.from_agent",
46
+ "messages.content", "messages.created_at",
47
+ "agents.name as from_name",
48
+ ])
49
+ .orderBy("messages.created_at", "asc")
50
+ .execute();
51
+
52
+ const documents = await db
53
+ .selectFrom("documents")
54
+ .where("task_id", "=", id)
55
+ .selectAll()
56
+ .execute();
57
+
58
+ return c.json({ ...task, assignees, messages, documents });
59
+ });
60
+
61
+ tasks.post("/", async (c) => {
62
+ const body = await c.req.json<{
63
+ title: string; description: string; from: string;
64
+ design?: string; acceptance?: string; priority?: number;
65
+ task_type?: string; parent_id?: string; assign?: string;
66
+ }>();
67
+ const agent = await resolveAgent(body.from);
68
+ if (!agent) return c.json({ error: "unknown agent" }, 400);
69
+
70
+ const task = await db.insertInto("tasks").values({
71
+ id: crypto.randomUUID(),
72
+ title: body.title,
73
+ description: body.description,
74
+ created_by: agent.id,
75
+ design: body.design ?? null,
76
+ acceptance: body.acceptance ?? null,
77
+ priority: body.priority ?? 2,
78
+ task_type: body.task_type ?? "task",
79
+ parent_id: body.parent_id ?? null,
80
+ status: body.assign ? "assigned" : "inbox",
81
+ }).returningAll().executeTakeFirstOrThrow();
82
+
83
+ await logActivity("task_created", agent.id, `Created task: ${task.title}`, task.id);
84
+
85
+ if (body.assign) {
86
+ const assignee = await resolveAgent(body.assign);
87
+ if (assignee) {
88
+ await db.insertInto("task_assignees").values({ task_id: task.id, agent_id: assignee.id }).execute();
89
+ }
90
+ }
91
+
92
+ return c.json(task, 201);
93
+ });
94
+
95
+ tasks.patch("/:id", async (c) => {
96
+ const id = c.req.param("id");
97
+ const body = await c.req.json<{
98
+ status?: string; priority?: number; description?: string;
99
+ design?: string; acceptance?: string; title?: string; from: string;
100
+ }>();
101
+ const agent = await resolveAgent(body.from);
102
+ if (!agent) return c.json({ error: "unknown agent" }, 400);
103
+
104
+ let q = db.updateTable("tasks").where("id", "=", id);
105
+ if (body.status !== undefined) q = q.set("status", body.status);
106
+ if (body.priority !== undefined) q = q.set("priority", body.priority);
107
+ if (body.description !== undefined) q = q.set("description", body.description);
108
+ if (body.design !== undefined) q = q.set("design", body.design);
109
+ if (body.acceptance !== undefined) q = q.set("acceptance", body.acceptance);
110
+ if (body.title !== undefined) q = q.set("title", body.title);
111
+ q = q.set("updated_at", new Date().toISOString());
112
+
113
+ const updated = await q.returningAll().executeTakeFirstOrThrow();
114
+
115
+ if (body.status) {
116
+ await logActivity("status_changed", agent.id, `Status → ${body.status}`, id);
117
+ }
118
+
119
+ return c.json(updated);
120
+ });
121
+
122
+ tasks.post("/:id/assign", async (c) => {
123
+ const id = c.req.param("id");
124
+ const { agentName } = await c.req.json<{ agentName: string }>();
125
+ const agent = await resolveAgent(agentName);
126
+ if (!agent) return c.json({ error: "unknown agent" }, 400);
127
+
128
+ await db.insertInto("task_assignees").values({ task_id: id, agent_id: agent.id }).execute();
129
+
130
+ // inbox → assigned
131
+ await db.updateTable("tasks")
132
+ .where("id", "=", id)
133
+ .where("status", "=", "inbox")
134
+ .set("status", "assigned")
135
+ .set("updated_at", new Date().toISOString())
136
+ .execute();
137
+
138
+ await logActivity("assigned", agent.id, `Assigned to ${agentName}`, id);
139
+ return c.json({ ok: true });
140
+ });
141
+
142
+ tasks.delete("/:id/assign/:agent", async (c) => {
143
+ const id = c.req.param("id");
144
+ const agentName = c.req.param("agent");
145
+ const agent = await resolveAgent(agentName);
146
+ if (!agent) return c.json({ error: "unknown agent" }, 400);
147
+
148
+ await db.deleteFrom("task_assignees")
149
+ .where("task_id", "=", id)
150
+ .where("agent_id", "=", agent.id)
151
+ .execute();
152
+
153
+ return c.json({ ok: true });
154
+ });
@@ -0,0 +1,42 @@
1
+ import { api } from "../lib/api.js";
2
+
3
+ export default async function config(args: string[]) {
4
+ const key = args[0];
5
+ const value = args[1];
6
+
7
+ // No args — list all settings
8
+ if (!key) {
9
+ const settings = await api("/settings");
10
+ let currentCategory = "";
11
+ for (const s of settings) {
12
+ if (s.category !== currentCategory) {
13
+ currentCategory = s.category;
14
+ console.log(`\n [${currentCategory}]`);
15
+ }
16
+ const val = s.value || "(empty)";
17
+ console.log(` ${s.key} = ${val}`);
18
+ }
19
+ console.log();
20
+ return;
21
+ }
22
+
23
+ // One arg — show specific setting
24
+ if (!value) {
25
+ try {
26
+ const s = await api(`/settings/${key}`);
27
+ console.log(`${s.key} = ${s.value}`);
28
+ if (s.description) console.log(` ${s.description}`);
29
+ } catch {
30
+ console.error(`Setting "${key}" not found.`);
31
+ process.exit(1);
32
+ }
33
+ return;
34
+ }
35
+
36
+ // Two args — set value
37
+ await api(`/settings/${key}`, {
38
+ method: "PUT",
39
+ body: JSON.stringify({ value }),
40
+ });
41
+ console.log(`Set ${key} = ${value}`);
42
+ }
@@ -0,0 +1,173 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs";
3
+ import * as os from "os";
4
+ import { requireAgencyRoot } from "../lib/find-root.js";
5
+
6
+ export default async function daemon(args: string[]) {
7
+ const sub = args[0];
8
+ if (!sub) {
9
+ console.error("Usage: agency daemon <install|uninstall|start|stop|status|logs|run>");
10
+ process.exit(1);
11
+ }
12
+
13
+ // "run" is special — it starts the daemon in-process (what systemd/launchd actually calls)
14
+ if (sub === "run") {
15
+ const agencyRoot = requireAgencyRoot();
16
+ process.env.DATABASE_PATH = path.join(agencyRoot, "agency.db");
17
+ process.env.FLEET_PATH = path.join(agencyRoot, "fleet.json");
18
+ await import("../../daemon.js");
19
+ return;
20
+ }
21
+
22
+ const { execSync } = await import("child_process");
23
+ const agencyRoot = requireAgencyRoot();
24
+ const projectDir = path.dirname(agencyRoot); // parent of .agency/
25
+ const bunPath = process.argv[0] || "bun";
26
+ const serviceName = "agency";
27
+ const isMac = process.platform === "darwin";
28
+
29
+ // Resolve the path to daemon entry point
30
+ // When installed as npm package, it's relative to the package
31
+ const daemonScript = path.resolve(import.meta.dir, "../../daemon.ts");
32
+
33
+ if (isMac) {
34
+ const label = "com.agency.daemon";
35
+ const plistDir = path.join(os.homedir(), "Library/LaunchAgents");
36
+ const plistFile = path.join(plistDir, `${label}.plist`);
37
+ const logDir = path.join(os.homedir(), "Library/Logs/agency");
38
+ const uid = process.getuid?.() ?? 501;
39
+ const domain = `gui/${uid}`;
40
+
41
+ if (sub === "install") {
42
+ fs.mkdirSync(plistDir, { recursive: true });
43
+ fs.mkdirSync(logDir, { recursive: true });
44
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
45
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
46
+ <plist version="1.0">
47
+ <dict>
48
+ <key>Label</key>
49
+ <string>${label}</string>
50
+ <key>ProgramArguments</key>
51
+ <array>
52
+ <string>${bunPath}</string>
53
+ <string>${daemonScript}</string>
54
+ </array>
55
+ <key>WorkingDirectory</key>
56
+ <string>${projectDir}</string>
57
+ <key>EnvironmentVariables</key>
58
+ <dict>
59
+ <key>PATH</key>
60
+ <string>${process.env.PATH}</string>
61
+ <key>HOME</key>
62
+ <string>${os.homedir()}</string>
63
+ <key>DATABASE_PATH</key>
64
+ <string>${path.join(agencyRoot, "agency.db")}</string>
65
+ <key>FLEET_PATH</key>
66
+ <string>${path.join(agencyRoot, "fleet.json")}</string>
67
+ </dict>
68
+ <key>KeepAlive</key>
69
+ <dict>
70
+ <key>SuccessfulExit</key>
71
+ <false/>
72
+ </dict>
73
+ <key>StandardOutPath</key>
74
+ <string>${logDir}/stdout.log</string>
75
+ <key>StandardErrorPath</key>
76
+ <string>${logDir}/stderr.log</string>
77
+ </dict>
78
+ </plist>`;
79
+ fs.writeFileSync(plistFile, plist);
80
+ console.log(`Installed ${plistFile}`);
81
+ return;
82
+ }
83
+ if (sub === "uninstall") {
84
+ try { execSync(`launchctl bootout ${domain}/${label}`, { stdio: "inherit" }); } catch {}
85
+ try { fs.unlinkSync(plistFile); } catch {}
86
+ console.log("Uninstalled agency service.");
87
+ return;
88
+ }
89
+ if (sub === "start") {
90
+ try { execSync(`launchctl bootstrap ${domain} ${plistFile}`, { stdio: "inherit" }); } catch {
91
+ try { execSync(`launchctl kickstart ${domain}/${label}`, { stdio: "inherit" }); } catch {}
92
+ }
93
+ console.log("Started agency daemon.");
94
+ return;
95
+ }
96
+ if (sub === "stop") {
97
+ try { execSync(`launchctl kill SIGTERM ${domain}/${label}`, { stdio: "inherit" }); } catch {}
98
+ console.log("Stopped agency daemon.");
99
+ return;
100
+ }
101
+ if (sub === "status") {
102
+ try { execSync(`launchctl print ${domain}/${label}`, { stdio: "inherit" }); } catch {
103
+ console.log("Service not loaded.");
104
+ }
105
+ return;
106
+ }
107
+ if (sub === "logs") {
108
+ try { execSync(`tail -f ${logDir}/stdout.log ${logDir}/stderr.log`, { stdio: "inherit" }); } catch {}
109
+ return;
110
+ }
111
+ } else {
112
+ // Linux — systemd
113
+ const serviceDir = path.join(os.homedir(), ".config/systemd/user");
114
+ const serviceFile = path.join(serviceDir, `${serviceName}.service`);
115
+
116
+ if (sub === "install") {
117
+ const unit = `[Unit]
118
+ Description=Agency Daemon
119
+ After=network.target
120
+
121
+ [Service]
122
+ Type=simple
123
+ WorkingDirectory=${projectDir}
124
+ ExecStart=${bunPath} ${daemonScript}
125
+ Restart=on-failure
126
+ RestartSec=5
127
+ Environment="PATH=${process.env.PATH}"
128
+ Environment="HOME=${os.homedir()}"
129
+ Environment="DATABASE_PATH=${path.join(agencyRoot, "agency.db")}"
130
+ Environment="FLEET_PATH=${path.join(agencyRoot, "fleet.json")}"
131
+
132
+ [Install]
133
+ WantedBy=default.target
134
+ `;
135
+ fs.mkdirSync(serviceDir, { recursive: true });
136
+ fs.writeFileSync(serviceFile, unit);
137
+ execSync("systemctl --user daemon-reload");
138
+ execSync(`systemctl --user enable ${serviceName}`);
139
+ console.log(`Installed and enabled ${serviceFile}`);
140
+ return;
141
+ }
142
+ if (sub === "uninstall") {
143
+ try { execSync(`systemctl --user stop ${serviceName}`, { stdio: "inherit" }); } catch {}
144
+ try { execSync(`systemctl --user disable ${serviceName}`, { stdio: "inherit" }); } catch {}
145
+ try { fs.unlinkSync(serviceFile); } catch {}
146
+ execSync("systemctl --user daemon-reload");
147
+ console.log("Uninstalled agency service.");
148
+ return;
149
+ }
150
+ if (sub === "start") {
151
+ execSync(`systemctl --user start ${serviceName}`, { stdio: "inherit" });
152
+ console.log("Started agency daemon.");
153
+ return;
154
+ }
155
+ if (sub === "stop") {
156
+ execSync(`systemctl --user stop ${serviceName}`, { stdio: "inherit" });
157
+ console.log("Stopped agency daemon.");
158
+ return;
159
+ }
160
+ if (sub === "status") {
161
+ try { execSync(`systemctl --user status ${serviceName}`, { stdio: "inherit" }); } catch {}
162
+ return;
163
+ }
164
+ if (sub === "logs") {
165
+ try { execSync(`journalctl --user -u ${serviceName} -f`, { stdio: "inherit" }); } catch {}
166
+ return;
167
+ }
168
+ }
169
+
170
+ console.error("Unknown daemon subcommand:", sub);
171
+ console.log("Subcommands: install, uninstall, start, stop, status, logs, run");
172
+ process.exit(1);
173
+ }
@@ -0,0 +1,47 @@
1
+ import { api } from "../lib/api.js";
2
+ import { getConfig } from "../lib/config.js";
3
+
4
+ function parseFlag(args: string[], flag: string): string | undefined {
5
+ const idx = args.indexOf(flag);
6
+ if (idx === -1 || idx + 1 >= args.length) return undefined;
7
+ return args[idx + 1];
8
+ }
9
+
10
+ export default async function doc(args: string[]) {
11
+ const sub = args[0];
12
+ const { agentName } = getConfig();
13
+
14
+ if (sub === "create") {
15
+ const title = args[1];
16
+ if (!title) {
17
+ console.error("Usage: agency doc create <title> --task <id> [--type T]");
18
+ process.exit(1);
19
+ }
20
+ const taskId = parseFlag(args, "--task");
21
+ const docType = parseFlag(args, "--type") ?? "general";
22
+ const from = agentName || "human";
23
+ const content = await Bun.stdin.text();
24
+ const d = await api("/documents", {
25
+ method: "POST",
26
+ body: JSON.stringify({ title, content, doc_type: docType, task_id: taskId, from }),
27
+ });
28
+ console.log(`Created document ${d.id}: ${d.title} (${d.doc_type})`);
29
+ return;
30
+ }
31
+
32
+ if (sub === "show") {
33
+ const id = args[1];
34
+ if (!id) { console.error("Usage: agency doc show <id>"); process.exit(1); }
35
+ const d = await api(`/documents/${id}`);
36
+ console.log(`Document: ${d.id}`);
37
+ console.log(` Title: ${d.title}`);
38
+ console.log(` Type: ${d.doc_type}`);
39
+ console.log(` Task: ${d.task_id ?? "none"}`);
40
+ console.log(` Content:\n${d.content}`);
41
+ return;
42
+ }
43
+
44
+ console.error("Unknown doc subcommand:", sub);
45
+ console.log("Subcommands: create, show");
46
+ process.exit(1);
47
+ }
@@ -0,0 +1,105 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { ask, confirm } from "../lib/prompt.js";
4
+
5
+ export default async function init(_args: string[]) {
6
+ console.log("\n Agency — Multi-Agent AI Development Platform\n");
7
+ console.log(" Setting up .agency/ in current directory...\n");
8
+
9
+ const agencyDir = path.resolve(process.cwd(), ".agency");
10
+
11
+ if (fs.existsSync(agencyDir)) {
12
+ const proceed = await confirm(".agency/ already exists. Re-initialize?", false);
13
+ if (!proceed) {
14
+ console.log("Aborted.");
15
+ process.exit(0);
16
+ }
17
+ }
18
+
19
+ const userName = await ask("? Your name", "Human");
20
+ const teamName = await ask("? Team name", "My Team");
21
+ const orchestratorName = await ask("? Orchestrator agent name", "sonny");
22
+
23
+ const doSlack = await confirm("? Configure Slack?", false);
24
+ let slackBotToken = "";
25
+ let slackAppToken = "";
26
+ let slackChannel = "";
27
+ let slackUserId = "";
28
+ if (doSlack) {
29
+ slackBotToken = await ask(" ? Slack bot token");
30
+ slackAppToken = await ask(" ? Slack app token");
31
+ slackChannel = await ask(" ? Slack channel ID");
32
+ slackUserId = await ask(" ? Your Slack user ID");
33
+ }
34
+
35
+ const workerRolesInput = await ask("? Add worker roles? (comma-separated names, or enter to skip)");
36
+ const workerRoles = workerRolesInput
37
+ ? workerRolesInput.split(",").map((r) => r.trim()).filter(Boolean)
38
+ : [];
39
+
40
+ const allRoles = ["orchestrator", ...workerRoles.map(() => "implementer")];
41
+ const allRoleNames = [orchestratorName, ...workerRoles];
42
+
43
+ // Create .agency directory
44
+ console.log("\n Creating .agency/ ...");
45
+ fs.mkdirSync(agencyDir, { recursive: true });
46
+
47
+ // Create fleet.json
48
+ const fleet: any = {
49
+ agents: {
50
+ [orchestratorName]: { role: "orchestrator", location: "local" },
51
+ },
52
+ };
53
+ for (const worker of workerRoles) {
54
+ fleet.agents[worker] = { role: "implementer", location: "local" };
55
+ }
56
+ fs.writeFileSync(
57
+ path.join(agencyDir, "fleet.json"),
58
+ JSON.stringify(fleet, null, 2) + "\n"
59
+ );
60
+
61
+ // Run migrations
62
+ console.log(" Running migrations ...");
63
+ process.env.DATABASE_PATH = path.join(agencyDir, "agency.db");
64
+
65
+ const { runMigrations } = await import("../../api/db/migrate.js");
66
+ await runMigrations();
67
+
68
+ // Seed defaults
69
+ console.log(" Seeding defaults ...");
70
+
71
+ // We need to re-import db after setting DATABASE_PATH
72
+ const { seedDefaults } = await import("../../api/db/seed.js");
73
+ await seedDefaults({
74
+ userName,
75
+ teamName,
76
+ roles: [...new Set(allRoles)],
77
+ });
78
+
79
+ // Store Slack settings if provided
80
+ if (doSlack) {
81
+ const { db } = await import("../../api/db/client.js");
82
+ const slackSettings = [
83
+ { key: "slack.team_channel", value: slackChannel },
84
+ { key: "slack.human_user_id", value: slackUserId },
85
+ ];
86
+ for (const s of slackSettings) {
87
+ await db.updateTable("settings").where("key", "=", s.key).set({ value: s.value, updated_at: new Date().toISOString() }).execute();
88
+ }
89
+ }
90
+
91
+ console.log(` Creating orchestrator: ${orchestratorName} ...`);
92
+ for (const worker of workerRoles) {
93
+ console.log(` Creating worker role: ${worker} ...`);
94
+ }
95
+
96
+ // Offer to install daemon
97
+ const doDaemon = await confirm("\n? Install and start the daemon?", true);
98
+ if (doDaemon) {
99
+ const daemonMod = await import("./daemon.js");
100
+ await daemonMod.default(["install"]);
101
+ await daemonMod.default(["start"]);
102
+ }
103
+
104
+ console.log("\n Done! Dashboard: http://localhost:3001 | API: http://localhost:3100\n");
105
+ }
@@ -0,0 +1,51 @@
1
+ import { api } from "../lib/api.js";
2
+ import { getConfig } from "../lib/config.js";
3
+
4
+ function parseFlag(args: string[], flag: string): string | undefined {
5
+ const idx = args.indexOf(flag);
6
+ if (idx === -1 || idx + 1 >= args.length) return undefined;
7
+ return args[idx + 1];
8
+ }
9
+
10
+ function removeFlagPairs(args: string[], flags: string[]): string[] {
11
+ const result: string[] = [];
12
+ let i = 0;
13
+ while (i < args.length) {
14
+ if (flags.includes(args[i]) && i + 1 < args.length) {
15
+ i += 2;
16
+ } else {
17
+ result.push(args[i]);
18
+ i++;
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+
24
+ function slugify(text: string): string {
25
+ return text
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9]+/g, "-")
28
+ .replace(/^-|-$/g, "")
29
+ .slice(0, 60);
30
+ }
31
+
32
+ export default async function learn(args: string[]) {
33
+ const tags = parseFlag(args, "--tags");
34
+ const contentArgs = removeFlagPairs(args, ["--tags"]);
35
+ const content = contentArgs.join(" ");
36
+ if (!content) {
37
+ console.error("Usage: agency learn <content...> [--tags t1,t2]");
38
+ process.exit(1);
39
+ }
40
+ const { agentName } = getConfig();
41
+ const key = slugify(content);
42
+ const from = agentName || "human";
43
+ const row = await api("/knowledge", {
44
+ method: "POST",
45
+ body: JSON.stringify({
46
+ key, content, from,
47
+ tags: tags ? tags.split(",") : [],
48
+ }),
49
+ });
50
+ console.log(`Learned: ${row.key} (${row.tags?.join(", ") || "no tags"})`);
51
+ }
@@ -0,0 +1,31 @@
1
+ import { requireAgencyRoot } from "../lib/find-root.js";
2
+ import * as path from "path";
3
+ import * as fs from "fs";
4
+
5
+ export default async function logs(args: string[]) {
6
+ const name = args[0];
7
+ if (!name) {
8
+ console.error("Usage: agency logs <agent-name>");
9
+ process.exit(1);
10
+ }
11
+
12
+ // For now, try to tail logs from common locations
13
+ const agencyRoot = requireAgencyRoot();
14
+ const logFile = path.join(agencyRoot, "logs", `${name}.log`);
15
+
16
+ if (fs.existsSync(logFile)) {
17
+ const proc = Bun.spawn(["tail", "-f", logFile], { stdio: ["inherit", "inherit", "inherit"] });
18
+ await proc.exited;
19
+ } else {
20
+ // Try journalctl for systemd-managed agents
21
+ try {
22
+ const proc = Bun.spawn(["journalctl", "--user", "-u", `agency-${name}`, "-f"], {
23
+ stdio: ["inherit", "inherit", "inherit"],
24
+ });
25
+ await proc.exited;
26
+ } catch {
27
+ console.error(`No logs found for agent "${name}".`);
28
+ process.exit(1);
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,18 @@
1
+ import { api } from "../lib/api.js";
2
+ import { getConfig } from "../lib/config.js";
3
+
4
+ export default async function msg(args: string[]) {
5
+ const taskId = args[0];
6
+ const content = args.slice(1).join(" ");
7
+ if (!taskId || !content) {
8
+ console.error("Usage: agency msg <task-id> <message...>");
9
+ process.exit(1);
10
+ }
11
+ const { agentName } = getConfig();
12
+ const from = agentName || "human";
13
+ const m = await api(`/tasks/${taskId}/messages`, {
14
+ method: "POST",
15
+ body: JSON.stringify({ from, content }),
16
+ });
17
+ console.log(`Message posted on task ${taskId} (id: ${m.id})`);
18
+ }
@@ -0,0 +1,19 @@
1
+ import { api } from "../lib/api.js";
2
+
3
+ export default async function ps(_args: string[]) {
4
+ const agents = await api("/agents");
5
+ if (agents.length === 0) {
6
+ console.log("No agents found.");
7
+ return;
8
+ }
9
+ console.log(" NAME STATUS ROLE LOCATION");
10
+ console.log(" " + "─".repeat(60));
11
+ for (const a of agents) {
12
+ if (a.name === "human") continue;
13
+ const name = (a.name ?? "").padEnd(15);
14
+ const status = (a.status ?? "").padEnd(10);
15
+ const role = (a.role ?? "").padEnd(13);
16
+ const location = a.location ?? "";
17
+ console.log(` ${name} ${status} ${role} ${location}`);
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ import { api } from "../lib/api.js";
2
+
3
+ export default async function recall(args: string[]) {
4
+ const search = args.join(" ");
5
+ if (!search) {
6
+ console.error("Usage: agency recall <search...>");
7
+ process.exit(1);
8
+ }
9
+ const rows = await api(`/knowledge?search=${encodeURIComponent(search)}`);
10
+ if (rows.length === 0) {
11
+ console.log("No knowledge found.");
12
+ } else {
13
+ for (const r of rows) {
14
+ console.log(` [${r.key}] ${r.content}`);
15
+ if (r.tags?.length) console.log(` tags: ${r.tags.join(", ")}`);
16
+ }
17
+ }
18
+ }