fellow-agents 0.0.5

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 ADDED
@@ -0,0 +1,76 @@
1
+ # fellow-agents
2
+
3
+ Multiple Claude Code agents collaborating via messaging.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npm install -g fellow-agents
9
+ fellow-agents start
10
+ ```
11
+
12
+ Downloads binaries on first run, starts services, opens browser. No git clone needed.
13
+
14
+ ### Options
15
+
16
+ ```bash
17
+ fellow-agents start --port 3700 --emcom-port 8800 # custom ports
18
+ fellow-agents start --no-browser # headless
19
+ fellow-agents start --update # force re-download binaries
20
+ fellow-agents stop # stop all services
21
+ ```
22
+
23
+ ## Prerequisites
24
+
25
+ - [Node.js](https://nodejs.org/) 18+
26
+ - [Claude Code](https://claude.ai/code) (optional for setup, required to run agents)
27
+
28
+ ## Alternative: Git Clone
29
+
30
+ ```bash
31
+ git clone https://github.com/rajan-chari/fellow-agents.git
32
+ cd fellow-agents
33
+ ./setup.sh # Mac/Linux
34
+ pwsh ./setup.ps1 # Windows (requires PowerShell 7+)
35
+ ```
36
+
37
+ ## Try It
38
+
39
+ 1. Open **coordinator** in pty-win (click play)
40
+ 2. Say: *"Have the coder write a fibonacci script and the reviewer check it."*
41
+ 3. Watch agents message each other in the emcom feed (right panel)
42
+
43
+ ## What's Included
44
+
45
+ | Component | Purpose |
46
+ |-----------|---------|
47
+ | **pty-win** | Browser terminal multiplexer — manage all agent sessions |
48
+ | **emcom** | Async messaging between agents |
49
+ | **templates/** | 3 starter agents: coordinator, coder, reviewer |
50
+
51
+ ## Add an Agent
52
+
53
+ ```bash
54
+ mkdir workspaces/myagent
55
+ # Copy CLAUDE.md + identity.json from an existing agent, customize
56
+ emcom --identity workspaces/myagent/identity.json register
57
+ ```
58
+
59
+ ## Architecture
60
+
61
+ ```
62
+ ~/.fellow-agents/ # Data directory (auto-created)
63
+ bin/{platform}/ # emcom, tracker, emcom-server binaries
64
+ pty-win/ # Terminal multiplexer
65
+ pid/ # PID files for running services
66
+ logs/ # Service logs
67
+
68
+ ./workspaces/ # Agent workspaces (scaffolded from templates)
69
+ coordinator/ # Task coordinator
70
+ coder/ # Code writer
71
+ reviewer/ # Code reviewer
72
+ ```
73
+
74
+ ## Stop
75
+
76
+ `fellow-agents stop` or `Ctrl+C` in the start terminal.
package/dist/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ const args = process.argv.slice(2);
3
+ const command = args[0] || "start";
4
+ function getFlag(name, fallback) {
5
+ const idx = args.indexOf(name);
6
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : fallback;
7
+ }
8
+ function hasFlag(name) {
9
+ return args.includes(name);
10
+ }
11
+ if (command === "start") {
12
+ const { start } = await import("./commands/start.js");
13
+ await start({
14
+ port: parseInt(getFlag("--port", "3700"), 10),
15
+ emcomPort: parseInt(getFlag("--emcom-port", "8800"), 10),
16
+ dir: getFlag("--dir", process.cwd()),
17
+ noBrowser: hasFlag("--no-browser"),
18
+ update: hasFlag("--update"),
19
+ });
20
+ }
21
+ else if (command === "stop") {
22
+ const { stop } = await import("./commands/stop.js");
23
+ stop();
24
+ }
25
+ else if (command === "--help" || command === "-h") {
26
+ console.log(`fellow-agents — multi-agent system for Claude Code
27
+
28
+ Usage:
29
+ fellow-agents start [options] Start services (download binaries on first run)
30
+ fellow-agents stop Stop all running services
31
+
32
+ Options (start):
33
+ --port <number> pty-win port (default: 3700)
34
+ --emcom-port <number> emcom-server port (default: 8800)
35
+ --dir <path> Working directory (default: current)
36
+ --no-browser Don't open browser
37
+ --update Force re-download binaries
38
+
39
+ -h, --help Show this help`);
40
+ }
41
+ else {
42
+ console.error(`Unknown command: ${command}. Run 'fellow-agents --help' for usage.`);
43
+ process.exit(1);
44
+ }
45
+ export {};
@@ -0,0 +1,135 @@
1
+ import { existsSync } from "fs";
2
+ import http from "http";
3
+ import { join, resolve } from "path";
4
+ import { execSync } from "child_process";
5
+ import { binDir, ptyWinDir, logsDir } from "../lib/paths.js";
6
+ import { downloadBinaries } from "../lib/download.js";
7
+ import { startEmcomServer, startPtyWin, stopAll, logPath } from "../lib/services.js";
8
+ import { scaffoldWorkspaces, registerAgents, writeHooks } from "../lib/workspaces.js";
9
+ import { binarySuffix } from "../lib/platform.js";
10
+ export async function start(opts) {
11
+ console.log("");
12
+ console.log(" fellow-agents");
13
+ console.log(" =============");
14
+ console.log("");
15
+ const workDir = resolve(opts.dir);
16
+ const workspacesDir = join(workDir, "workspaces");
17
+ const emcomUrl = `http://127.0.0.1:${opts.emcomPort}`;
18
+ // 1. Prerequisites
19
+ console.log("[1/7] Checking prerequisites...");
20
+ const nodeVer = process.versions.node;
21
+ const nodeMajor = parseInt(nodeVer.split(".")[0], 10);
22
+ if (nodeMajor < 18) {
23
+ console.error(` Node.js 18+ required (found ${nodeVer})`);
24
+ process.exit(1);
25
+ }
26
+ console.log(` Node.js ${nodeVer}`);
27
+ try {
28
+ execSync("claude --version", { stdio: "pipe" });
29
+ console.log(" Claude Code found");
30
+ }
31
+ catch {
32
+ console.log(" Claude Code not found (optional — install from https://claude.ai/code)");
33
+ }
34
+ // 2. Download binaries
35
+ console.log("[2/7] Downloading binaries...");
36
+ try {
37
+ await downloadBinaries(opts.update);
38
+ }
39
+ catch (err) {
40
+ console.error(` Download failed: ${err}`);
41
+ if (!existsSync(join(binDir, `emcom${binarySuffix()}`))) {
42
+ console.error(" No cached binaries available. Check your internet connection.");
43
+ process.exit(1);
44
+ }
45
+ console.log(" Using cached binaries");
46
+ }
47
+ // 3. Install pty-win dependencies
48
+ console.log("[3/7] Installing pty-win...");
49
+ if (existsSync(join(ptyWinDir, "package.json"))) {
50
+ if (!existsSync(join(ptyWinDir, "node_modules"))) {
51
+ execSync("npm install --production", { cwd: ptyWinDir, stdio: "pipe" });
52
+ }
53
+ console.log(" pty-win ready");
54
+ }
55
+ else {
56
+ console.error(" pty-win not found — download may have failed");
57
+ process.exit(1);
58
+ }
59
+ // 4. Scaffold workspaces
60
+ console.log("[4/7] Scaffolding workspaces...");
61
+ scaffoldWorkspaces(workDir);
62
+ // PATH trick: prepend bin dir so agents find emcom/tracker
63
+ const env = { ...process.env, PATH: `${binDir}${process.platform === "win32" ? ";" : ":"}${process.env.PATH}` };
64
+ // 5. Start emcom-server
65
+ console.log("[5/7] Starting emcom-server...");
66
+ const emcomPid = startEmcomServer(opts.emcomPort, env);
67
+ console.log(` emcom-server started (pid ${emcomPid})`);
68
+ // Wait for health
69
+ const healthy = await new Promise((resolve) => {
70
+ let attempts = 0;
71
+ const check = () => {
72
+ http.get(`${emcomUrl}/api/health`, (res) => {
73
+ if (res.statusCode === 200)
74
+ return resolve(true);
75
+ if (++attempts < 20)
76
+ setTimeout(check, 500);
77
+ else
78
+ resolve(false);
79
+ }).on("error", () => {
80
+ if (++attempts < 20)
81
+ setTimeout(check, 500);
82
+ else
83
+ resolve(false);
84
+ });
85
+ };
86
+ check();
87
+ });
88
+ if (healthy) {
89
+ console.log(` emcom-server running on :${opts.emcomPort}`);
90
+ }
91
+ else {
92
+ console.error(` Warning: emcom-server health check failed — it may not be running`);
93
+ console.error(` Check logs: ${logPath("emcom-server")}`);
94
+ }
95
+ // 6. Register agents
96
+ console.log("[6/7] Registering agents + configuring hooks...");
97
+ registerAgents(workspacesDir, env);
98
+ writeHooks(workspacesDir, opts.port);
99
+ // 7. Start pty-win
100
+ console.log("[7/7] Starting pty-win...");
101
+ const ptyPid = startPtyWin(opts.port, workspacesDir, emcomUrl, env);
102
+ console.log(` pty-win started (pid ${ptyPid})`);
103
+ // Open browser
104
+ if (!opts.noBrowser) {
105
+ setTimeout(() => {
106
+ const url = `http://127.0.0.1:${opts.port}`;
107
+ try {
108
+ if (process.platform === "win32")
109
+ execSync(`start "" "${url}"`, { stdio: "ignore" });
110
+ else if (process.platform === "darwin")
111
+ execSync(`open "${url}"`, { stdio: "ignore" });
112
+ else
113
+ execSync(`xdg-open "${url}" 2>/dev/null || true`, { stdio: "ignore" });
114
+ }
115
+ catch { }
116
+ }, 2000);
117
+ }
118
+ console.log("");
119
+ console.log(" Setup complete!");
120
+ console.log(` pty-win: http://127.0.0.1:${opts.port}`);
121
+ console.log(` emcom-server: ${emcomUrl}`);
122
+ console.log(` logs: ${logsDir}`);
123
+ console.log("");
124
+ console.log(" Press Ctrl+C to stop all services.");
125
+ console.log("");
126
+ // Wait for Ctrl+C
127
+ process.on("SIGINT", () => {
128
+ console.log("\n Shutting down...");
129
+ stopAll();
130
+ console.log(" Stopped.");
131
+ process.exit(0);
132
+ });
133
+ // Keep alive
134
+ setInterval(() => { }, 60000);
135
+ }
@@ -0,0 +1,8 @@
1
+ import { stopAll } from "../lib/services.js";
2
+ export function stop() {
3
+ console.log("");
4
+ console.log(" Stopping fellow-agents...");
5
+ stopAll();
6
+ console.log(" Done.");
7
+ console.log("");
8
+ }
@@ -0,0 +1,93 @@
1
+ import { get } from "https";
2
+ import { createWriteStream, mkdirSync, readFileSync, writeFileSync, existsSync, rmSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { execSync } from "child_process";
5
+ import { dataDir, binDir, ptyWinDir, versionFile } from "./paths.js";
6
+ import { detectPlatform } from "./platform.js";
7
+ const REPO = "rajan-chari/fellow-agents";
8
+ function httpsGet(url) {
9
+ return new Promise((resolve, reject) => {
10
+ get(url, { headers: { "User-Agent": "fellow-agents-cli" } }, (res) => {
11
+ if (res.statusCode === 301 || res.statusCode === 302) {
12
+ return httpsGet(res.headers.location).then(resolve, reject);
13
+ }
14
+ let data = "";
15
+ res.on("data", (chunk) => (data += chunk));
16
+ res.on("end", () => resolve(data));
17
+ res.on("error", reject);
18
+ }).on("error", reject);
19
+ });
20
+ }
21
+ function httpsDownload(url, dest) {
22
+ return new Promise((resolve, reject) => {
23
+ get(url, { headers: { "User-Agent": "fellow-agents-cli" } }, (res) => {
24
+ if (res.statusCode === 301 || res.statusCode === 302) {
25
+ return httpsDownload(res.headers.location, dest).then(resolve, reject);
26
+ }
27
+ const file = createWriteStream(dest);
28
+ res.pipe(file);
29
+ file.on("finish", () => { file.close(); resolve(); });
30
+ file.on("error", reject);
31
+ }).on("error", reject);
32
+ });
33
+ }
34
+ export async function downloadBinaries(force = false) {
35
+ // Fetch latest release
36
+ const json = await httpsGet(`https://api.github.com/repos/${REPO}/releases/latest`);
37
+ const release = JSON.parse(json);
38
+ const tag = release.tag_name;
39
+ // Check if up to date
40
+ if (!force && existsSync(versionFile) && existsSync(binDir)) {
41
+ const localVer = readFileSync(versionFile, "utf-8").trim();
42
+ if (localVer === tag) {
43
+ console.log(` Binaries up to date (${tag})`);
44
+ return tag;
45
+ }
46
+ console.log(` Update available: ${localVer} → ${tag}`);
47
+ }
48
+ console.log(` Downloading release ${tag}...`);
49
+ const platform = detectPlatform();
50
+ for (const asset of release.assets) {
51
+ if (asset.name.includes(platform)) {
52
+ console.log(` Downloading ${asset.name}...`);
53
+ const dest = join(dataDir, asset.name);
54
+ mkdirSync(dirname(dest), { recursive: true });
55
+ await httpsDownload(asset.browser_download_url, dest);
56
+ // Extract zip
57
+ mkdirSync(binDir, { recursive: true });
58
+ extractZip(dest, dataDir);
59
+ rmSync(dest);
60
+ }
61
+ else if (asset.name.includes("pty-win")) {
62
+ console.log(` Downloading ${asset.name}...`);
63
+ const dest = join(dataDir, asset.name);
64
+ mkdirSync(dirname(dest), { recursive: true });
65
+ // Remove old node_modules before extracting new pty-win
66
+ const nodeModules = join(ptyWinDir, "node_modules");
67
+ if (existsSync(nodeModules))
68
+ rmSync(nodeModules, { recursive: true });
69
+ await httpsDownload(asset.browser_download_url, dest);
70
+ extractZip(dest, dataDir);
71
+ rmSync(dest);
72
+ }
73
+ }
74
+ // Save version
75
+ mkdirSync(join(dataDir, "bin"), { recursive: true });
76
+ writeFileSync(versionFile, tag, "utf-8");
77
+ return tag;
78
+ }
79
+ function extractZip(zipPath, destDir) {
80
+ // Use tar on all platforms (Windows 10+ has tar built in)
81
+ try {
82
+ execSync(`tar -xf "${zipPath}" -C "${destDir}"`, { stdio: "pipe" });
83
+ }
84
+ catch {
85
+ // Fallback: PowerShell on Windows
86
+ if (process.platform === "win32") {
87
+ execSync(`powershell -NoProfile -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`, { stdio: "pipe" });
88
+ }
89
+ else {
90
+ execSync(`unzip -qo "${zipPath}" -d "${destDir}"`, { stdio: "pipe" });
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,19 @@
1
+ import { homedir } from "os";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { detectPlatform } from "./platform.js";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ /** ~/.fellow-agents/ */
7
+ export const dataDir = join(homedir(), ".fellow-agents");
8
+ /** ~/.fellow-agents/bin/{platform}/ */
9
+ export const binDir = join(dataDir, "bin", detectPlatform());
10
+ /** ~/.fellow-agents/pty-win/ */
11
+ export const ptyWinDir = join(dataDir, "pty-win");
12
+ /** ~/.fellow-agents/pid/ */
13
+ export const pidDir = join(dataDir, "pid");
14
+ /** ~/.fellow-agents/logs/ */
15
+ export const logsDir = join(dataDir, "logs");
16
+ /** ~/.fellow-agents/bin/.version */
17
+ export const versionFile = join(dataDir, "bin", ".version");
18
+ /** templates/ directory shipped with the npm package */
19
+ export const templatesDir = join(__dirname, "..", "..", "templates");
@@ -0,0 +1,15 @@
1
+ import { platform, arch } from "os";
2
+ export function detectPlatform() {
3
+ const os = platform();
4
+ const cpu = arch();
5
+ if (os === "win32")
6
+ return "win-x64";
7
+ if (os === "darwin")
8
+ return cpu === "arm64" ? "osx-arm64" : "osx-x64";
9
+ if (os === "linux")
10
+ return "linux-x64";
11
+ throw new Error(`Unsupported platform: ${os}-${cpu}`);
12
+ }
13
+ export function binarySuffix() {
14
+ return platform() === "win32" ? ".exe" : "";
15
+ }
@@ -0,0 +1,97 @@
1
+ import { spawn } from "child_process";
2
+ import http from "http";
3
+ import https from "https";
4
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, openSync } from "fs";
5
+ import { join } from "path";
6
+ import { pidDir, binDir, ptyWinDir, logsDir } from "./paths.js";
7
+ import { binarySuffix } from "./platform.js";
8
+ function writePid(name, pid) {
9
+ mkdirSync(pidDir, { recursive: true });
10
+ writeFileSync(join(pidDir, `${name}.pid`), String(pid), "utf-8");
11
+ }
12
+ function readPid(name) {
13
+ const file = join(pidDir, `${name}.pid`);
14
+ if (!existsSync(file))
15
+ return null;
16
+ const pid = parseInt(readFileSync(file, "utf-8").trim(), 10);
17
+ return isNaN(pid) ? null : pid;
18
+ }
19
+ function removePid(name) {
20
+ const file = join(pidDir, `${name}.pid`);
21
+ if (existsSync(file))
22
+ rmSync(file);
23
+ }
24
+ function openLog(name) {
25
+ mkdirSync(logsDir, { recursive: true });
26
+ return openSync(join(logsDir, `${name}.log`), "w");
27
+ }
28
+ export function logPath(name) {
29
+ return join(logsDir, `${name}.log`);
30
+ }
31
+ function isRunning(pid) {
32
+ try {
33
+ process.kill(pid, 0);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ export function startEmcomServer(emcomPort, env) {
41
+ const bin = join(binDir, `emcom-server${binarySuffix()}`);
42
+ const log = openLog("emcom-server");
43
+ const proc = spawn(bin, ["--port", String(emcomPort)], {
44
+ env: { ...env, EMCOM_PORT: String(emcomPort) },
45
+ detached: true,
46
+ stdio: ["ignore", log, log],
47
+ });
48
+ proc.unref();
49
+ writePid("emcom-server", proc.pid);
50
+ return proc.pid;
51
+ }
52
+ export function startPtyWin(port, workspacesDir, emcomUrl, env) {
53
+ const main = join(ptyWinDir, "dist", "index.js");
54
+ const log = openLog("pty-win");
55
+ const proc = spawn("node", [main, "--port", String(port), "--root", workspacesDir, "--emcom", emcomUrl], {
56
+ env,
57
+ detached: true,
58
+ stdio: ["ignore", log, log],
59
+ });
60
+ proc.unref();
61
+ writePid("pty-win", proc.pid);
62
+ return proc.pid;
63
+ }
64
+ export function stopAll() {
65
+ for (const name of ["emcom-server", "pty-win"]) {
66
+ const pid = readPid(name);
67
+ if (pid && isRunning(pid)) {
68
+ try {
69
+ process.kill(pid);
70
+ console.log(` Stopped ${name} (pid ${pid})`);
71
+ }
72
+ catch {
73
+ console.log(` Could not stop ${name} (pid ${pid})`);
74
+ }
75
+ }
76
+ else {
77
+ console.log(` ${name} not running`);
78
+ }
79
+ removePid(name);
80
+ }
81
+ }
82
+ export function waitForHealth(url, timeoutMs = 30000) {
83
+ const mod = url.startsWith("https") ? https : http;
84
+ const startTime = Date.now();
85
+ return new Promise((resolve) => {
86
+ const check = () => {
87
+ if (Date.now() - startTime > timeoutMs)
88
+ return resolve(false);
89
+ mod.get(url, (res) => {
90
+ resolve(res.statusCode === 200);
91
+ }).on("error", () => {
92
+ setTimeout(check, 500);
93
+ });
94
+ };
95
+ check();
96
+ });
97
+ }
@@ -0,0 +1,54 @@
1
+ import { cpSync, mkdirSync, existsSync, readdirSync, writeFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { execSync } from "child_process";
4
+ import { templatesDir, binDir } from "./paths.js";
5
+ import { binarySuffix } from "./platform.js";
6
+ export function scaffoldWorkspaces(targetDir) {
7
+ const workspacesDir = join(targetDir, "workspaces");
8
+ if (existsSync(workspacesDir) && readdirSync(workspacesDir).length > 0) {
9
+ console.log(" Workspaces already exist — skipping scaffold");
10
+ return;
11
+ }
12
+ console.log(" Scaffolding workspaces from templates...");
13
+ mkdirSync(workspacesDir, { recursive: true });
14
+ cpSync(templatesDir, workspacesDir, { recursive: true });
15
+ }
16
+ export function registerAgents(workspacesDir, env) {
17
+ const emcom = join(binDir, `emcom${binarySuffix()}`);
18
+ const dirs = readdirSync(workspacesDir, { withFileTypes: true })
19
+ .filter((d) => d.isDirectory());
20
+ for (const dir of dirs) {
21
+ const idFile = join(workspacesDir, dir.name, "identity.json");
22
+ if (!existsSync(idFile))
23
+ continue;
24
+ try {
25
+ execSync(`"${emcom}" --identity "${idFile}" register --force`, {
26
+ env,
27
+ stdio: "pipe",
28
+ timeout: 10000,
29
+ });
30
+ console.log(` Registered: ${dir.name}`);
31
+ }
32
+ catch {
33
+ console.log(` Warning: failed to register ${dir.name}`);
34
+ }
35
+ }
36
+ }
37
+ export function writeHooks(workspacesDir, ptyWinPort) {
38
+ const dirs = readdirSync(workspacesDir, { withFileTypes: true })
39
+ .filter((d) => d.isDirectory());
40
+ for (const dir of dirs) {
41
+ const claudeDir = join(workspacesDir, dir.name, ".claude");
42
+ mkdirSync(claudeDir, { recursive: true });
43
+ const settings = {
44
+ hooks: {
45
+ Stop: [{ matcher: "", hooks: [{ type: "http", url: `http://127.0.0.1:${ptyWinPort}/api/hook/stop`, timeout: 2 }] }],
46
+ Notification: [{ matcher: "idle_prompt|permission_prompt", hooks: [{ type: "http", url: `http://127.0.0.1:${ptyWinPort}/api/hook/notify`, timeout: 2 }] }],
47
+ UserPromptSubmit: [{ matcher: "", hooks: [{ type: "http", url: `http://127.0.0.1:${ptyWinPort}/api/hook/prompt-submit`, timeout: 2 }] }],
48
+ },
49
+ messageIdleNotifThresholdMs: 5000,
50
+ };
51
+ writeFileSync(join(claudeDir, "settings.local.json"), JSON.stringify(settings, null, 2), "utf-8");
52
+ console.log(` Hooks configured: ${dir.name}`);
53
+ }
54
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "fellow-agents",
3
+ "version": "0.0.5",
4
+ "description": "Multi-agent system — multiple Claude Code instances collaborating via messaging",
5
+ "type": "module",
6
+ "bin": {
7
+ "fellow-agents": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "templates/"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "keywords": [
21
+ "claude",
22
+ "agents",
23
+ "multi-agent",
24
+ "terminal",
25
+ "collaboration"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/rajan-chari/fellow-agents"
30
+ },
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@types/node": "^25.6.0",
34
+ "typescript": "^6.0.2"
35
+ }
36
+ }
@@ -0,0 +1,15 @@
1
+ # Coder
2
+
3
+ You write, fix, and improve code based on task descriptions from the coordinator.
4
+
5
+ ## On Load
6
+
7
+ 1. Register with emcom: `emcom --identity identity.json register 2>/dev/null || true`
8
+ 2. Check messages: `emcom --identity identity.json inbox`
9
+ 3. Greet the user and check for pending work
10
+
11
+ ## Communication
12
+
13
+ - `emcom --identity identity.json send --to coordinator --subject "..." --body "..."` — report results
14
+ - `emcom --identity identity.json inbox` — check for tasks
15
+ - When done, message the coordinator with what you did and relevant file paths
@@ -0,0 +1 @@
1
+ {"name": "coder", "description": "Coder agent — writes and fixes code", "server": "http://127.0.0.1:8800"}
@@ -0,0 +1,22 @@
1
+ # Coordinator
2
+
3
+ You coordinate a team of AI agents. Break down goals, delegate tasks, collect results.
4
+
5
+ ## On Load
6
+
7
+ 1. Register with emcom: `emcom --identity identity.json register 2>/dev/null || true`
8
+ 2. Check messages: `emcom --identity identity.json inbox`
9
+ 3. Ask the user what they'd like the team to work on
10
+
11
+ ## Communication
12
+
13
+ - `emcom --identity identity.json send --to <name> --subject "..." --body "..."` — send a message
14
+ - `emcom --identity identity.json inbox` — check messages
15
+ - `emcom who` — see all agents
16
+
17
+ ## Workflow
18
+
19
+ 1. User gives you a goal
20
+ 2. Delegate to **coder** (implementation) or **reviewer** (analysis)
21
+ 3. Collect results via emcom
22
+ 4. Report back to user
@@ -0,0 +1 @@
1
+ {"name": "coordinator", "description": "Task coordinator — delegates work and collects results", "server": "http://127.0.0.1:8800"}
@@ -0,0 +1,15 @@
1
+ # Reviewer
2
+
3
+ You review code for quality, bugs, and improvements.
4
+
5
+ ## On Load
6
+
7
+ 1. Register with emcom: `emcom --identity identity.json register 2>/dev/null || true`
8
+ 2. Check messages: `emcom --identity identity.json inbox`
9
+ 3. Greet the user and check for pending reviews
10
+
11
+ ## Communication
12
+
13
+ - `emcom --identity identity.json send --to coordinator --subject "..." --body "..."` — report findings
14
+ - `emcom --identity identity.json send --to coder --subject "..." --body "..."` — send feedback directly
15
+ - `emcom --identity identity.json inbox` — check for review requests
@@ -0,0 +1 @@
1
+ {"name": "reviewer", "description": "Reviewer agent — reviews code for quality and correctness", "server": "http://127.0.0.1:8800"}