@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.
- package/README.md +272 -0
- package/bin/agency.js +2 -0
- package/dashboard/out/404.html +1 -0
- package/dashboard/out/_next/static/chunks/255-67e8754147461423.js +1 -0
- package/dashboard/out/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/dashboard/out/_next/static/chunks/app/_not-found/page-ad40673d821037f6.js +1 -0
- package/dashboard/out/_next/static/chunks/app/layout-056f12675e691d12.js +1 -0
- package/dashboard/out/_next/static/chunks/app/page-80f01fdbb09b43c8.js +1 -0
- package/dashboard/out/_next/static/chunks/framework-de98b93a850cfc71.js +1 -0
- package/dashboard/out/_next/static/chunks/main-1a0dcce460eb61ce.js +1 -0
- package/dashboard/out/_next/static/chunks/main-app-1d848b791b823fa6.js +1 -0
- package/dashboard/out/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/dashboard/out/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dashboard/out/_next/static/chunks/webpack-4e6bf084ac60582b.js +1 -0
- package/dashboard/out/_next/static/css/27d1ea794f04e96a.css +1 -0
- package/dashboard/out/_next/static/pU1nwWH_dNUOCI8y4nl3C/_buildManifest.js +1 -0
- package/dashboard/out/_next/static/pU1nwWH_dNUOCI8y4nl3C/_ssgManifest.js +1 -0
- package/dashboard/out/index.html +1 -0
- package/dashboard/out/index.txt +19 -0
- package/docs/images/agency_cli_ps.png +0 -0
- package/docs/images/agency_ui_ai_prodivder_settings.png +0 -0
- package/docs/images/agency_ui_aws_settings.png +0 -0
- package/docs/images/agency_ui_identity_settings.png +0 -0
- package/docs/images/agency_ui_mission_control.png +0 -0
- package/docs/images/agent_ui_agent_config.png +0 -0
- package/package.json +31 -0
- package/src/api/db/client.ts +16 -0
- package/src/api/db/migrate.ts +37 -0
- package/src/api/db/migrations/001_initial.ts +193 -0
- package/src/api/db/migrations/002_configs.ts +76 -0
- package/src/api/db/migrations/003_settings_columns.ts +13 -0
- package/src/api/db/seed.ts +142 -0
- package/src/api/db/types.ts +126 -0
- package/src/api/index.ts +73 -0
- package/src/api/lib/activity.ts +13 -0
- package/src/api/lib/fleet-sync.ts +156 -0
- package/src/api/lib/mentions.ts +59 -0
- package/src/api/lib/processes.ts +45 -0
- package/src/api/lib/resolve-agent.ts +5 -0
- package/src/api/lib/tunnels.ts +99 -0
- package/src/api/routes/activities.ts +27 -0
- package/src/api/routes/agents.ts +311 -0
- package/src/api/routes/documents.ts +41 -0
- package/src/api/routes/knowledge.ts +60 -0
- package/src/api/routes/messages.ts +54 -0
- package/src/api/routes/notifications.ts +40 -0
- package/src/api/routes/oauth.ts +171 -0
- package/src/api/routes/role-configs.ts +71 -0
- package/src/api/routes/settings.ts +94 -0
- package/src/api/routes/skills.ts +76 -0
- package/src/api/routes/tasks.ts +154 -0
- package/src/cli/commands/config.ts +42 -0
- package/src/cli/commands/daemon.ts +173 -0
- package/src/cli/commands/doc.ts +47 -0
- package/src/cli/commands/init.ts +105 -0
- package/src/cli/commands/learn.ts +51 -0
- package/src/cli/commands/logs.ts +31 -0
- package/src/cli/commands/msg.ts +18 -0
- package/src/cli/commands/ps.ts +19 -0
- package/src/cli/commands/recall.ts +18 -0
- package/src/cli/commands/skills.ts +66 -0
- package/src/cli/commands/ssh.ts +68 -0
- package/src/cli/commands/start.ts +14 -0
- package/src/cli/commands/status.ts +33 -0
- package/src/cli/commands/stop.ts +11 -0
- package/src/cli/commands/tasks.ts +150 -0
- package/src/cli/index.ts +70 -0
- package/src/cli/lib/api.ts +16 -0
- package/src/cli/lib/config.ts +5 -0
- package/src/cli/lib/find-root.ts +32 -0
- package/src/cli/lib/prompt.ts +20 -0
- package/src/daemon.ts +83 -0
- package/src/templates/implementer/agents-config.md +44 -0
- package/src/templates/implementer/agents.md +32 -0
- package/src/templates/implementer/heartbeat.md +47 -0
- package/src/templates/implementer/tools.md +33 -0
- package/src/templates/orchestrator/agents-config.md +44 -0
- package/src/templates/orchestrator/agents.md +27 -0
- package/src/templates/orchestrator/heartbeat.md +40 -0
- package/src/templates/orchestrator/tools.md +40 -0
- package/src/templates/shared/environment.md +20 -0
- package/src/templates/shared/memory.md +20 -0
- package/src/templates/shared/soul.md +26 -0
- package/src/templates/shared/user.md +12 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { api } from "../lib/api.js";
|
|
2
|
+
|
|
3
|
+
function parseFlag(args: string[], flag: string): string | undefined {
|
|
4
|
+
const idx = args.indexOf(flag);
|
|
5
|
+
if (idx === -1 || idx + 1 >= args.length) return undefined;
|
|
6
|
+
return args[idx + 1];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default async function skills(args: string[]) {
|
|
10
|
+
const sub = args[0];
|
|
11
|
+
|
|
12
|
+
if (sub === "list" || !sub) {
|
|
13
|
+
const category = parseFlag(args, "--category");
|
|
14
|
+
const search = parseFlag(args, "--search");
|
|
15
|
+
const params = new URLSearchParams();
|
|
16
|
+
if (category) params.set("category", category);
|
|
17
|
+
if (search) params.set("search", search);
|
|
18
|
+
const qs = params.toString() ? `?${params}` : "";
|
|
19
|
+
const rows = await api(`/skills${qs}`);
|
|
20
|
+
if (rows.length === 0) {
|
|
21
|
+
console.log("No skills found.");
|
|
22
|
+
} else {
|
|
23
|
+
for (const s of rows) {
|
|
24
|
+
const tags = s.tags?.length ? ` [${s.tags.join(", ")}]` : "";
|
|
25
|
+
console.log(` ${s.id.slice(0, 8)} ${s.name}${tags}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (sub === "show") {
|
|
32
|
+
const id = args[1];
|
|
33
|
+
if (!id) { console.error("Usage: agency skills show <id>"); process.exit(1); }
|
|
34
|
+
const s = await api(`/skills/${id}`);
|
|
35
|
+
console.log(`Skill: ${s.name}`);
|
|
36
|
+
console.log(` Category: ${s.category}`);
|
|
37
|
+
console.log(` Tags: ${s.tags?.join(", ") || "none"}`);
|
|
38
|
+
console.log(` Body:\n${s.body}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (sub === "create") {
|
|
43
|
+
const name = args[1];
|
|
44
|
+
if (!name) { console.error("Usage: agency skills create <name> [--category C]"); process.exit(1); }
|
|
45
|
+
const category = parseFlag(args, "--category");
|
|
46
|
+
const body = await Bun.stdin.text();
|
|
47
|
+
const s = await api("/skills", {
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: JSON.stringify({ name, body: body || `# ${name}\n`, category }),
|
|
50
|
+
});
|
|
51
|
+
console.log(`Created skill ${s.id}: ${s.name}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (sub === "delete") {
|
|
56
|
+
const id = args[1];
|
|
57
|
+
if (!id) { console.error("Usage: agency skills delete <id>"); process.exit(1); }
|
|
58
|
+
await api(`/skills/${id}`, { method: "DELETE" });
|
|
59
|
+
console.log(`Deleted skill ${id}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.error("Unknown skills subcommand:", sub);
|
|
64
|
+
console.log("Subcommands: list, show, create, delete");
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { api } from "../lib/api.js";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
|
|
6
|
+
export default async function ssh(args: string[]) {
|
|
7
|
+
const name = args[0];
|
|
8
|
+
if (!name) {
|
|
9
|
+
console.error("Usage: agency ssh <agent-name>");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const agent = await api(`/agents/${name}`);
|
|
14
|
+
if (!agent) {
|
|
15
|
+
console.error(`Agent "${name}" not found.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (agent.location !== "ec2") {
|
|
20
|
+
console.error(`SSH is only supported for EC2 agents. "${name}" is ${agent.location ?? "local"}.`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Get SSH settings
|
|
25
|
+
const settings = await api("/settings?category=ssh");
|
|
26
|
+
const sshConfig: Record<string, string> = {};
|
|
27
|
+
for (const s of settings) {
|
|
28
|
+
sshConfig[s.key] = s.value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const privateKey = sshConfig["ssh.private_key"];
|
|
32
|
+
const user = sshConfig["ssh.user"] || "ubuntu";
|
|
33
|
+
|
|
34
|
+
if (!privateKey) {
|
|
35
|
+
console.error("SSH private key not configured. Set it in Settings → SSH.");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get host from fleet.json via agent detail or settings
|
|
40
|
+
// The agent's host should be in fleet.json
|
|
41
|
+
const configRes = await api("/settings?category=aws");
|
|
42
|
+
// We need the fleet host — fetch it from the API or read fleet.json directly
|
|
43
|
+
let host = "";
|
|
44
|
+
try {
|
|
45
|
+
const fleetPath = path.resolve(process.cwd(), ".agency", "fleet.json");
|
|
46
|
+
const fleet = JSON.parse(fs.readFileSync(fleetPath, "utf-8"));
|
|
47
|
+
host = fleet.agents?.[name]?.host ?? "";
|
|
48
|
+
} catch {}
|
|
49
|
+
|
|
50
|
+
if (!host) {
|
|
51
|
+
console.error(`No host configured for "${name}". Add "host" to fleet.json for this agent.`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Write key to temp file
|
|
56
|
+
const keyDir = path.join(os.tmpdir(), "agency-ssh");
|
|
57
|
+
fs.mkdirSync(keyDir, { recursive: true, mode: 0o700 });
|
|
58
|
+
const keyPath = path.join(keyDir, "agent_key");
|
|
59
|
+
fs.writeFileSync(keyPath, privateKey + "\n", { mode: 0o600 });
|
|
60
|
+
|
|
61
|
+
// Exec ssh
|
|
62
|
+
const proc = Bun.spawn(
|
|
63
|
+
["ssh", "-i", keyPath, "-o", "StrictHostKeyChecking=no", `${user}@${host}`, ...args.slice(1)],
|
|
64
|
+
{ stdout: "inherit", stderr: "inherit", stdin: "inherit" }
|
|
65
|
+
);
|
|
66
|
+
const code = await proc.exited;
|
|
67
|
+
process.exit(code);
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { api } from "../lib/api.js";
|
|
2
|
+
|
|
3
|
+
export default async function start(args: string[]) {
|
|
4
|
+
const name = args[0];
|
|
5
|
+
if (!name) {
|
|
6
|
+
console.error("Usage: agency start <agent-name>");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const result = await api(`/agents/${name}/deploy`, { method: "POST" });
|
|
10
|
+
console.log(`Started ${name}: ${result.status} (${result.method})`);
|
|
11
|
+
if (result.instructions) {
|
|
12
|
+
console.log(` ${result.instructions}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { api } from "../lib/api.js";
|
|
2
|
+
|
|
3
|
+
export default async function status(args: string[]) {
|
|
4
|
+
const name = args[0];
|
|
5
|
+
|
|
6
|
+
if (name) {
|
|
7
|
+
const agent = await api(`/agents/${name}`);
|
|
8
|
+
console.log(`Agent: ${agent.name}`);
|
|
9
|
+
console.log(` Role: ${agent.role ?? "n/a"}`);
|
|
10
|
+
console.log(` Status: ${agent.status}`);
|
|
11
|
+
console.log(` Current task: ${agent.current_task ?? "none"}`);
|
|
12
|
+
console.log(` Updated: ${agent.updated_at}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// General health check
|
|
17
|
+
try {
|
|
18
|
+
const data = await api("/health");
|
|
19
|
+
console.log(`API: ${data.status}`);
|
|
20
|
+
} catch (err: any) {
|
|
21
|
+
console.log(`API: unreachable (${err.message})`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const agents = await api("/agents");
|
|
26
|
+
const active = agents.filter((a: any) => a.status === "active" && a.name !== "human");
|
|
27
|
+
const total = agents.filter((a: any) => a.name !== "human");
|
|
28
|
+
console.log(`Agents: ${active.length}/${total.length} active`);
|
|
29
|
+
|
|
30
|
+
for (const a of total) {
|
|
31
|
+
console.log(` ${a.name.padEnd(15)} ${(a.status ?? "").padEnd(10)} ${a.role ?? ""}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { api } from "../lib/api.js";
|
|
2
|
+
|
|
3
|
+
export default async function stop(args: string[]) {
|
|
4
|
+
const name = args[0];
|
|
5
|
+
if (!name) {
|
|
6
|
+
console.error("Usage: agency stop <agent-name>");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const result = await api(`/agents/${name}/stop`, { method: "POST" });
|
|
10
|
+
console.log(`Stopped ${name}: ${result.status}`);
|
|
11
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
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 tasks(args: string[]) {
|
|
11
|
+
const { agentName } = getConfig();
|
|
12
|
+
const sub = args[0];
|
|
13
|
+
const jsonMode = args.includes("--json");
|
|
14
|
+
|
|
15
|
+
if (sub === "create") {
|
|
16
|
+
const title = args[1];
|
|
17
|
+
if (!title) { console.error("Usage: agency tasks create <title> [flags]"); process.exit(1); }
|
|
18
|
+
const taskType = parseFlag(args, "--type") ?? "task";
|
|
19
|
+
const priority = parseFlag(args, "--priority");
|
|
20
|
+
const assign = parseFlag(args, "--assign");
|
|
21
|
+
const design = parseFlag(args, "--design");
|
|
22
|
+
const acceptance = parseFlag(args, "--acceptance");
|
|
23
|
+
const description = parseFlag(args, "--description") ?? title;
|
|
24
|
+
const from = parseFlag(args, "--from") ?? (agentName || "human");
|
|
25
|
+
const parentId = parseFlag(args, "--parent");
|
|
26
|
+
|
|
27
|
+
const task = await api("/tasks", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
title, description, from,
|
|
31
|
+
task_type: taskType,
|
|
32
|
+
priority: priority ? Number(priority) : undefined,
|
|
33
|
+
assign: assign ?? undefined,
|
|
34
|
+
design: design ?? undefined,
|
|
35
|
+
acceptance: acceptance ?? undefined,
|
|
36
|
+
parent_id: parentId ?? undefined,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
console.log(`Created task ${task.id}: ${task.title} [${task.status}]`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (sub === "list") {
|
|
44
|
+
const status = parseFlag(args, "--status");
|
|
45
|
+
const assignee = parseFlag(args, "--assignee");
|
|
46
|
+
const type = parseFlag(args, "--type");
|
|
47
|
+
const parent = parseFlag(args, "--parent");
|
|
48
|
+
const params = new URLSearchParams();
|
|
49
|
+
if (status) params.set("status", status);
|
|
50
|
+
if (assignee) params.set("assignee", assignee);
|
|
51
|
+
if (type) params.set("type", type);
|
|
52
|
+
if (parent) params.set("parent_id", parent);
|
|
53
|
+
const qs = params.toString() ? `?${params}` : "";
|
|
54
|
+
const rows = await api(`/tasks${qs}`);
|
|
55
|
+
if (jsonMode) { console.log(JSON.stringify(rows)); return; }
|
|
56
|
+
if (rows.length === 0) {
|
|
57
|
+
console.log("No tasks found.");
|
|
58
|
+
} else {
|
|
59
|
+
for (const t of rows) {
|
|
60
|
+
console.log(` ${t.id} [${t.status.padEnd(10)}] P${t.priority} ${t.title}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (sub === "ready") {
|
|
67
|
+
const assignee = parseFlag(args, "--assignee") ?? agentName;
|
|
68
|
+
if (!assignee) { console.error("Provide --assignee or set AGENCY_AGENT_NAME"); process.exit(1); }
|
|
69
|
+
const rows = await api(`/tasks?status=assigned&assignee=${encodeURIComponent(assignee)}`);
|
|
70
|
+
if (jsonMode) { console.log(JSON.stringify(rows)); return; }
|
|
71
|
+
if (rows.length === 0) {
|
|
72
|
+
console.log("No ready tasks.");
|
|
73
|
+
} else {
|
|
74
|
+
for (const t of rows) {
|
|
75
|
+
console.log(` ${t.id} P${t.priority} ${t.title}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (sub === "show") {
|
|
82
|
+
const id = args[1];
|
|
83
|
+
if (!id) { console.error("Usage: agency tasks show <id>"); process.exit(1); }
|
|
84
|
+
const t = await api(`/tasks/${id}`);
|
|
85
|
+
if (jsonMode) { console.log(JSON.stringify(t)); return; }
|
|
86
|
+
console.log(`Task: ${t.id}`);
|
|
87
|
+
console.log(` Title: ${t.title}`);
|
|
88
|
+
console.log(` Status: ${t.status}`);
|
|
89
|
+
console.log(` Priority: ${t.priority}`);
|
|
90
|
+
console.log(` Type: ${t.task_type}`);
|
|
91
|
+
console.log(` Description: ${t.description}`);
|
|
92
|
+
if (t.design) console.log(` Design: ${t.design}`);
|
|
93
|
+
if (t.acceptance) console.log(` Acceptance: ${t.acceptance}`);
|
|
94
|
+
if (t.assignees?.length) {
|
|
95
|
+
console.log(` Assignees: ${t.assignees.map((a: any) => a.name).join(", ")}`);
|
|
96
|
+
}
|
|
97
|
+
if (t.messages?.length) {
|
|
98
|
+
console.log(` Messages:`);
|
|
99
|
+
for (const m of t.messages) {
|
|
100
|
+
console.log(` [${m.from_name ?? "?"}] ${m.content}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (sub === "update") {
|
|
107
|
+
const id = args[1];
|
|
108
|
+
if (!id) { console.error("Usage: agency tasks update <id> [flags]"); process.exit(1); }
|
|
109
|
+
const status = parseFlag(args, "--status");
|
|
110
|
+
const priority = parseFlag(args, "--priority");
|
|
111
|
+
const design = parseFlag(args, "--design");
|
|
112
|
+
const acceptance = parseFlag(args, "--acceptance");
|
|
113
|
+
const assign = parseFlag(args, "--assign");
|
|
114
|
+
const from = parseFlag(args, "--from") ?? (agentName || "human");
|
|
115
|
+
const body: any = { from };
|
|
116
|
+
if (status) body.status = status;
|
|
117
|
+
if (priority) body.priority = Number(priority);
|
|
118
|
+
if (design) body.design = design;
|
|
119
|
+
if (acceptance) body.acceptance = acceptance;
|
|
120
|
+
|
|
121
|
+
const t = await api(`/tasks/${id}`, { method: "PATCH", body: JSON.stringify(body) });
|
|
122
|
+
|
|
123
|
+
if (assign) {
|
|
124
|
+
await api(`/tasks/${id}/assign`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
body: JSON.stringify({ agentName: assign }),
|
|
127
|
+
});
|
|
128
|
+
console.log(`Updated task ${t.id}: [${t.status}] ${t.title} (assigned to ${assign})`);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(`Updated task ${t.id}: [${t.status}] ${t.title}`);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (sub === "close") {
|
|
136
|
+
const id = args[1];
|
|
137
|
+
if (!id) { console.error("Usage: agency tasks close <id>"); process.exit(1); }
|
|
138
|
+
const from = agentName || "human";
|
|
139
|
+
const t = await api(`/tasks/${id}`, {
|
|
140
|
+
method: "PATCH",
|
|
141
|
+
body: JSON.stringify({ status: "done", from }),
|
|
142
|
+
});
|
|
143
|
+
console.log(`Closed task ${t.id}: ${t.title}`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.error("Unknown tasks subcommand:", sub);
|
|
148
|
+
console.log("Subcommands: create, list, ready, show, update, close");
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const args = process.argv.slice(2);
|
|
2
|
+
const command = args[0];
|
|
3
|
+
|
|
4
|
+
const COMMANDS: Record<string, () => Promise<any>> = {
|
|
5
|
+
init: () => import("./commands/init.js"),
|
|
6
|
+
ps: () => import("./commands/ps.js"),
|
|
7
|
+
start: () => import("./commands/start.js"),
|
|
8
|
+
stop: () => import("./commands/stop.js"),
|
|
9
|
+
logs: () => import("./commands/logs.js"),
|
|
10
|
+
ssh: () => import("./commands/ssh.js"),
|
|
11
|
+
tasks: () => import("./commands/tasks.js"),
|
|
12
|
+
msg: () => import("./commands/msg.js"),
|
|
13
|
+
learn: () => import("./commands/learn.js"),
|
|
14
|
+
recall: () => import("./commands/recall.js"),
|
|
15
|
+
doc: () => import("./commands/doc.js"),
|
|
16
|
+
daemon: () => import("./commands/daemon.js"),
|
|
17
|
+
status: () => import("./commands/status.js"),
|
|
18
|
+
config: () => import("./commands/config.js"),
|
|
19
|
+
skills: () => import("./commands/skills.js"),
|
|
20
|
+
ping: () => import("./commands/status.js"), // alias
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
if (command === "--version" || command === "-v" || command === "-V") {
|
|
25
|
+
const pkg = await import("../../package.json");
|
|
26
|
+
console.log(pkg.default?.version ?? pkg.version ?? "0.1.0");
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!command || command === "--help" || command === "-h") {
|
|
31
|
+
console.log(`Agency — Multi-Agent AI Development Platform
|
|
32
|
+
|
|
33
|
+
Usage: agency <command> [args]
|
|
34
|
+
|
|
35
|
+
Commands:
|
|
36
|
+
init Set up .agency/ in current directory
|
|
37
|
+
ps List agents
|
|
38
|
+
start <name> Start an agent
|
|
39
|
+
stop <name> Stop an agent
|
|
40
|
+
logs <name> Tail agent logs
|
|
41
|
+
ssh <name> SSH into agent
|
|
42
|
+
tasks <subcommand> Task management (create/list/ready/show/update/close)
|
|
43
|
+
msg <task-id> <message> Post a task comment
|
|
44
|
+
learn <content> [--tags t,t] Store knowledge
|
|
45
|
+
recall <search> Search knowledge
|
|
46
|
+
doc <subcommand> Document management
|
|
47
|
+
daemon <subcommand> Daemon management (install/uninstall/start/stop/status/logs/run)
|
|
48
|
+
status Health check
|
|
49
|
+
config [key] [value] View/edit settings
|
|
50
|
+
skills <subcommand> Skills management (list/show/create/update/delete)`);
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const loader = COMMANDS[command];
|
|
55
|
+
if (!loader) {
|
|
56
|
+
console.error(`Unknown command: ${command}`);
|
|
57
|
+
console.error("Run 'agency --help' for usage.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const mod = await loader();
|
|
62
|
+
if (mod.default) {
|
|
63
|
+
await mod.default(args.slice(1));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
main().catch((err) => {
|
|
68
|
+
console.error(err.message ?? err);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getConfig } from "./config.js";
|
|
2
|
+
|
|
3
|
+
const { apiUrl } = getConfig();
|
|
4
|
+
|
|
5
|
+
export async function api(path: string, options?: RequestInit): Promise<any> {
|
|
6
|
+
const url = `${apiUrl}${path}`;
|
|
7
|
+
const res = await fetch(url, {
|
|
8
|
+
...options,
|
|
9
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
const body = await res.text();
|
|
13
|
+
throw new Error(`API error ${res.status}: ${body}`);
|
|
14
|
+
}
|
|
15
|
+
return res.json();
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Walk up from `startDir` (default CWD) looking for `.agency/` directory.
|
|
6
|
+
* Returns the absolute path to `.agency/`, or null if not found.
|
|
7
|
+
*/
|
|
8
|
+
export function findAgencyRoot(startDir?: string): string | null {
|
|
9
|
+
let dir = path.resolve(startDir ?? process.cwd());
|
|
10
|
+
const root = path.parse(dir).root;
|
|
11
|
+
|
|
12
|
+
while (true) {
|
|
13
|
+
const candidate = path.join(dir, ".agency");
|
|
14
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
if (dir === root) return null;
|
|
18
|
+
dir = path.dirname(dir);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Like findAgencyRoot but throws if not found.
|
|
24
|
+
*/
|
|
25
|
+
export function requireAgencyRoot(startDir?: string): string {
|
|
26
|
+
const root = findAgencyRoot(startDir);
|
|
27
|
+
if (!root) {
|
|
28
|
+
console.error("No .agency/ directory found. Run `agency init` first.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
return root;
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as readline from "readline";
|
|
2
|
+
|
|
3
|
+
export function ask(question: string, defaultValue?: string): Promise<string> {
|
|
4
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
8
|
+
rl.close();
|
|
9
|
+
resolve(answer.trim() || defaultValue || "");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function confirm(question: string, defaultYes = true): Promise<boolean> {
|
|
15
|
+
const hint = defaultYes ? "Y/n" : "y/N";
|
|
16
|
+
return ask(`${question} (${hint})`).then((a) => {
|
|
17
|
+
if (!a) return defaultYes;
|
|
18
|
+
return a.toLowerCase().startsWith("y");
|
|
19
|
+
});
|
|
20
|
+
}
|
package/src/daemon.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { findAgencyRoot } from "./cli/lib/find-root.js";
|
|
3
|
+
|
|
4
|
+
// Resolve .agency/ and set environment
|
|
5
|
+
const agencyRoot = findAgencyRoot();
|
|
6
|
+
if (agencyRoot) {
|
|
7
|
+
process.env.DATABASE_PATH ??= path.join(agencyRoot, "agency.db");
|
|
8
|
+
process.env.FLEET_PATH ??= path.join(agencyRoot, "fleet.json");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Start API server explicitly (Bun auto-serve only works for main entry)
|
|
12
|
+
console.log("[daemon] starting API server...");
|
|
13
|
+
const apiModule = await import("./api/index.js");
|
|
14
|
+
const server = Bun.serve({
|
|
15
|
+
port: apiModule.default.port,
|
|
16
|
+
hostname: apiModule.default.hostname,
|
|
17
|
+
fetch: apiModule.default.fetch,
|
|
18
|
+
});
|
|
19
|
+
console.log(`[daemon] API listening on http://${server.hostname}:${server.port}`);
|
|
20
|
+
|
|
21
|
+
// Run migrations on startup
|
|
22
|
+
const { runMigrations } = await import("./api/db/migrate.js");
|
|
23
|
+
await runMigrations();
|
|
24
|
+
|
|
25
|
+
function spawnChild(name: string, cmd: string[], cwd: string) {
|
|
26
|
+
let proc: ReturnType<typeof Bun.spawn> | null = null;
|
|
27
|
+
let stopped = false;
|
|
28
|
+
|
|
29
|
+
function start() {
|
|
30
|
+
console.log(`[daemon] starting ${name}...`);
|
|
31
|
+
proc = Bun.spawn(cmd, {
|
|
32
|
+
cwd,
|
|
33
|
+
stdout: "inherit",
|
|
34
|
+
stderr: "inherit",
|
|
35
|
+
env: { ...process.env },
|
|
36
|
+
});
|
|
37
|
+
console.log(`[daemon] ${name} pid=${proc.pid}`);
|
|
38
|
+
|
|
39
|
+
proc.exited.then((code) => {
|
|
40
|
+
if (stopped) return;
|
|
41
|
+
console.error(`[daemon] ${name} exited with code ${code}, restarting in 1s...`);
|
|
42
|
+
setTimeout(start, 1000);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
start();
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
kill() {
|
|
50
|
+
stopped = true;
|
|
51
|
+
proc?.kill();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const children: { kill(): void }[] = [];
|
|
57
|
+
|
|
58
|
+
// Dashboard is now served as static files from the API process — no child needed.
|
|
59
|
+
|
|
60
|
+
// Only spawn notify if it exists
|
|
61
|
+
const notifyEntry = path.resolve(import.meta.dir, "../packages/notify/src/index.ts");
|
|
62
|
+
if (await Bun.file(notifyEntry).exists()) {
|
|
63
|
+
children.push(
|
|
64
|
+
spawnChild(
|
|
65
|
+
"notify",
|
|
66
|
+
["bun", "run", "src/index.ts"],
|
|
67
|
+
path.resolve(import.meta.dir, "../packages/notify")
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function shutdown() {
|
|
73
|
+
console.log("[daemon] shutting down...");
|
|
74
|
+
const { stopAllTunnels } = await import("./api/lib/tunnels.js");
|
|
75
|
+
stopAllTunnels();
|
|
76
|
+
for (const child of children) {
|
|
77
|
+
child.kill();
|
|
78
|
+
}
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
process.on("SIGTERM", shutdown);
|
|
83
|
+
process.on("SIGINT", shutdown);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Implementer Configuration
|
|
2
|
+
|
|
3
|
+
## Communication
|
|
4
|
+
|
|
5
|
+
- Minimal communication. Silence is competence.
|
|
6
|
+
- Speak up when blocked, confused, or need a decision.
|
|
7
|
+
- Use task comments for all communication.
|
|
8
|
+
|
|
9
|
+
## Task Lifecycle
|
|
10
|
+
|
|
11
|
+
1. **Assigned** — Read task, understand requirements
|
|
12
|
+
2. **In Progress** — Do the work
|
|
13
|
+
3. **Needs Input** — Blocked, waiting for guidance (include clear question)
|
|
14
|
+
4. **Review** — Work complete, ready for review
|
|
15
|
+
5. **Done** — Accepted by orchestrator
|
|
16
|
+
|
|
17
|
+
## Asking for Help
|
|
18
|
+
|
|
19
|
+
Good: "I found X and Y approaches. X is simpler but Y handles edge case Z. Which should I use?"
|
|
20
|
+
Bad: "What should I do?"
|
|
21
|
+
|
|
22
|
+
Always include:
|
|
23
|
+
- What you've tried
|
|
24
|
+
- What options you see
|
|
25
|
+
- Your recommendation
|
|
26
|
+
|
|
27
|
+
## Knowledge Base
|
|
28
|
+
|
|
29
|
+
- Before starting work: `agency recall <topic>` to check for prior decisions
|
|
30
|
+
- After learning something: `agency learn "<what you learned>" --tags tag1,tag2`
|
|
31
|
+
|
|
32
|
+
## Testing
|
|
33
|
+
|
|
34
|
+
- Run tests before marking task as review
|
|
35
|
+
- Verify changes locally
|
|
36
|
+
- Don't push broken code
|
|
37
|
+
|
|
38
|
+
## Rules
|
|
39
|
+
|
|
40
|
+
1. One active task at a time
|
|
41
|
+
2. Always test before completing
|
|
42
|
+
3. Capture decisions in task comments
|
|
43
|
+
4. Don't duplicate work — check if it's already been done
|
|
44
|
+
5. Ask for help rather than guessing on important decisions
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Implementer Role
|
|
2
|
+
|
|
3
|
+
You are an implementer — you execute work assigned by the orchestrator.
|
|
4
|
+
|
|
5
|
+
## Session Startup
|
|
6
|
+
|
|
7
|
+
1. Read SOUL.md, USER.md, MEMORY.md
|
|
8
|
+
2. `agency recall "recent decisions"`
|
|
9
|
+
3. Run your heartbeat checklist
|
|
10
|
+
|
|
11
|
+
## Role Definition
|
|
12
|
+
|
|
13
|
+
- **Execute work** — Pick up assigned tasks and complete them
|
|
14
|
+
- **Don't investigate** — If the task is unclear, ask for clarification
|
|
15
|
+
- **Don't delegate** — You do the work yourself
|
|
16
|
+
- **Report back** — Update task status and comment with results
|
|
17
|
+
|
|
18
|
+
## Rules
|
|
19
|
+
|
|
20
|
+
1. Always use proper development tools and workflows
|
|
21
|
+
2. Run tests before marking work complete
|
|
22
|
+
3. Verify changes locally before pushing
|
|
23
|
+
4. Capture decisions in task comments
|
|
24
|
+
5. Don't duplicate work — check first
|
|
25
|
+
|
|
26
|
+
## Communication
|
|
27
|
+
|
|
28
|
+
All communication goes through task comments (`agency msg`).
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
Follow HEARTBEAT.md on every cycle.
|