fellow-agents 0.0.10 → 0.0.12

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/dist/cli.js CHANGED
@@ -2,8 +2,10 @@
2
2
  const args = process.argv.slice(2);
3
3
  // Default to "start" if no command given or first arg is a flag
4
4
  const command = args[0] === "stop" ? "stop"
5
- : (args[0] === "--help" || args[0] === "-h") ? "help"
6
- : "start";
5
+ : args[0] === "clean" ? "clean"
6
+ : args[0] === "uninstall" ? "uninstall"
7
+ : (args[0] === "--help" || args[0] === "-h") ? "help"
8
+ : "start";
7
9
  function getFlag(name, fallback) {
8
10
  const idx = args.indexOf(name);
9
11
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : fallback;
@@ -25,12 +27,25 @@ else if (command === "stop") {
25
27
  const { stop } = await import("./commands/stop.js");
26
28
  stop();
27
29
  }
30
+ else if (command === "clean") {
31
+ const { clean } = await import("./commands/clean.js");
32
+ clean();
33
+ }
34
+ else if (command === "uninstall") {
35
+ const { uninstall } = await import("./commands/uninstall.js");
36
+ uninstall({
37
+ dir: getFlag("--dir", process.cwd()),
38
+ yes: hasFlag("--yes"),
39
+ });
40
+ }
28
41
  else {
29
42
  console.log(`fellow-agents — multi-agent system for Claude Code
30
43
 
31
44
  Usage:
32
45
  fellow-agents [options] Start services (default)
33
46
  fellow-agents stop Stop all running services
47
+ fellow-agents clean Wipe cached binaries + pty-win, preserve logs
48
+ fellow-agents uninstall [--yes] Remove all fellow-agents state (data dir + workspaces)
34
49
 
35
50
  Options:
36
51
  --port <number> pty-win port (default: 3700)
@@ -38,6 +53,7 @@ Options:
38
53
  --dir <path> Working directory (default: current)
39
54
  --no-browser Don't open browser
40
55
  --update Force re-download binaries
56
+ --yes Skip confirmation prompt (uninstall only)
41
57
 
42
58
  -h, --help Show this help`);
43
59
  }
@@ -0,0 +1,72 @@
1
+ import { existsSync, rmSync, statSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ import { dataDir, binDir, ptyWinDir, pidDir } from "../lib/paths.js";
4
+ import { stopAll } from "../lib/services.js";
5
+ function dirSize(path) {
6
+ if (!existsSync(path))
7
+ return 0;
8
+ let total = 0;
9
+ const stack = [path];
10
+ while (stack.length > 0) {
11
+ const current = stack.pop();
12
+ try {
13
+ const stat = statSync(current);
14
+ if (stat.isDirectory()) {
15
+ for (const entry of readdirSync(current))
16
+ stack.push(join(current, entry));
17
+ }
18
+ else {
19
+ total += stat.size;
20
+ }
21
+ }
22
+ catch { }
23
+ }
24
+ return total;
25
+ }
26
+ function formatBytes(bytes) {
27
+ if (bytes < 1024)
28
+ return `${bytes} B`;
29
+ if (bytes < 1024 * 1024)
30
+ return `${(bytes / 1024).toFixed(1)} KB`;
31
+ if (bytes < 1024 * 1024 * 1024)
32
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
33
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
34
+ }
35
+ export function clean() {
36
+ console.log("");
37
+ console.log(" Cleaning fellow-agents state...");
38
+ stopAll();
39
+ // Wipe these — logs/ is preserved for postmortem
40
+ const targets = [
41
+ { label: "bin", path: binDir },
42
+ { label: "pty-win", path: ptyWinDir },
43
+ { label: "pid", path: pidDir },
44
+ ];
45
+ let totalFreed = 0;
46
+ for (const t of targets) {
47
+ if (existsSync(t.path)) {
48
+ const size = dirSize(t.path);
49
+ try {
50
+ rmSync(t.path, { recursive: true, force: true });
51
+ console.log(` Removed ${t.label}/ (${formatBytes(size)})`);
52
+ totalFreed += size;
53
+ }
54
+ catch (err) {
55
+ console.error(` Failed to remove ${t.label}/: ${err.message}`);
56
+ }
57
+ }
58
+ }
59
+ // Reset version stamp (separate from bin/ since it's nested but worth calling out)
60
+ const versionPath = join(dataDir, "bin", ".version");
61
+ if (existsSync(versionPath)) {
62
+ try {
63
+ rmSync(versionPath);
64
+ }
65
+ catch { }
66
+ }
67
+ console.log("");
68
+ console.log(` Cleaned ${formatBytes(totalFreed)} from ${dataDir}`);
69
+ console.log(` Logs preserved at ${join(dataDir, "logs")}`);
70
+ console.log(` Run 'fellow-agents' to reinstall.`);
71
+ console.log("");
72
+ }
@@ -1,4 +1,4 @@
1
- import { existsSync } from "fs";
1
+ import { existsSync, readFileSync } from "fs";
2
2
  import http from "http";
3
3
  import { join, resolve } from "path";
4
4
  import { execSync } from "child_process";
@@ -7,6 +7,15 @@ import { downloadBinaries } from "../lib/download.js";
7
7
  import { startEmcomServer, startPtyWin, stopAll, logPath } from "../lib/services.js";
8
8
  import { scaffoldWorkspaces, registerAgents, writeHooks } from "../lib/workspaces.js";
9
9
  import { binarySuffix } from "../lib/platform.js";
10
+ // Minimal engines.node range check — handles ">=N", "<N", and combinations.
11
+ function nodeInRange(version, range) {
12
+ const major = parseInt(version.split(".")[0], 10);
13
+ const minMatch = range.match(/>=?\s*(\d+)/);
14
+ const maxMatch = range.match(/<\s*(\d+)/);
15
+ const min = minMatch ? parseInt(minMatch[1], 10) : 0;
16
+ const max = maxMatch ? parseInt(maxMatch[1], 10) : Infinity;
17
+ return major >= min && major < max;
18
+ }
10
19
  export async function start(opts) {
11
20
  console.log("");
12
21
  console.log(" fellow-agents");
@@ -53,16 +62,34 @@ export async function start(opts) {
53
62
  }
54
63
  // 3. Install pty-win dependencies
55
64
  console.log("[3/7] Installing pty-win...");
56
- if (existsSync(join(ptyWinDir, "package.json"))) {
57
- if (!existsSync(join(ptyWinDir, "node_modules"))) {
58
- execSync("npm install --production", { cwd: ptyWinDir, stdio: "pipe" });
59
- }
60
- console.log(" pty-win ready");
61
- }
62
- else {
65
+ const ptyPkgPath = join(ptyWinDir, "package.json");
66
+ if (!existsSync(ptyPkgPath)) {
63
67
  console.error(" pty-win not found — download may have failed");
64
68
  process.exit(1);
65
69
  }
70
+ // Warn fail-fast if Node is outside pty-win's supported range
71
+ try {
72
+ const ptyPkg = JSON.parse(readFileSync(ptyPkgPath, "utf-8"));
73
+ const range = ptyPkg.engines?.node;
74
+ if (range && !nodeInRange(nodeVer, range)) {
75
+ console.error(` WARNING: pty-win supports Node ${range}, you have ${nodeVer}.`);
76
+ console.error(` Install may fail. Consider using Node 22 LTS.`);
77
+ }
78
+ }
79
+ catch { }
80
+ // Probe for a real dep instead of just node_modules/ — partial installs leave the dir but miss packages
81
+ const expressInstalled = existsSync(join(ptyWinDir, "node_modules", "express", "package.json"));
82
+ if (!expressInstalled) {
83
+ try {
84
+ execSync("npm install --omit=dev", { cwd: ptyWinDir, stdio: "inherit" });
85
+ }
86
+ catch (err) {
87
+ console.error(` pty-win install failed — see output above`);
88
+ console.error(` Logs will be written to: ${logPath("pty-win")}`);
89
+ process.exit(1);
90
+ }
91
+ }
92
+ console.log(" pty-win ready");
66
93
  // 4. Scaffold workspaces
67
94
  console.log("[4/7] Scaffolding workspaces...");
68
95
  scaffoldWorkspaces(workDir);
@@ -0,0 +1,99 @@
1
+ import { existsSync, rmSync, statSync, readdirSync } from "fs";
2
+ import { join, resolve } from "path";
3
+ import { dataDir } from "../lib/paths.js";
4
+ import { stopAll } from "../lib/services.js";
5
+ function dirSize(path) {
6
+ if (!existsSync(path))
7
+ return 0;
8
+ let total = 0;
9
+ const stack = [path];
10
+ while (stack.length > 0) {
11
+ const current = stack.pop();
12
+ try {
13
+ const stat = statSync(current);
14
+ if (stat.isDirectory()) {
15
+ for (const entry of readdirSync(current))
16
+ stack.push(join(current, entry));
17
+ }
18
+ else {
19
+ total += stat.size;
20
+ }
21
+ }
22
+ catch { }
23
+ }
24
+ return total;
25
+ }
26
+ function formatBytes(bytes) {
27
+ if (bytes < 1024)
28
+ return `${bytes} B`;
29
+ if (bytes < 1024 * 1024)
30
+ return `${(bytes / 1024).toFixed(1)} KB`;
31
+ if (bytes < 1024 * 1024 * 1024)
32
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
33
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
34
+ }
35
+ export function uninstall(opts) {
36
+ const workDir = resolve(opts.dir);
37
+ // Two possible workspace locations — start.ts uses either depending on whether cwd has workspaces/
38
+ const wsCandidates = [
39
+ join(workDir, "workspaces"),
40
+ join(workDir, "fellow-agents", "workspaces"),
41
+ ];
42
+ const targets = [];
43
+ if (existsSync(dataDir)) {
44
+ targets.push({ label: "Data directory", path: dataDir, size: dirSize(dataDir) });
45
+ }
46
+ for (const ws of wsCandidates) {
47
+ if (existsSync(ws)) {
48
+ targets.push({ label: "Workspaces", path: ws, size: dirSize(ws) });
49
+ }
50
+ }
51
+ console.log("");
52
+ console.log(" fellow-agents uninstall");
53
+ console.log(" =======================");
54
+ console.log("");
55
+ if (targets.length === 0) {
56
+ console.log(" Nothing to remove — no fellow-agents state found.");
57
+ console.log("");
58
+ console.log(" To uninstall the npm package itself, run:");
59
+ console.log(" npm uninstall -g fellow-agents");
60
+ console.log("");
61
+ return;
62
+ }
63
+ const totalSize = targets.reduce((sum, t) => sum + t.size, 0);
64
+ console.log(" The following will be permanently removed:");
65
+ console.log("");
66
+ for (const t of targets) {
67
+ console.log(` ${t.path} (${formatBytes(t.size)})`);
68
+ }
69
+ console.log("");
70
+ console.log(` Total: ${formatBytes(totalSize)}`);
71
+ console.log("");
72
+ if (!opts.yes) {
73
+ console.log(" This is a dry run. To proceed, add --yes:");
74
+ console.log(" fellow-agents uninstall --yes");
75
+ console.log("");
76
+ console.log(" Tip: pass --dir <path> if your workspaces are elsewhere.");
77
+ console.log("");
78
+ return;
79
+ }
80
+ // Actually do the uninstall
81
+ console.log(" Stopping services...");
82
+ stopAll();
83
+ console.log("");
84
+ for (const t of targets) {
85
+ try {
86
+ rmSync(t.path, { recursive: true, force: true });
87
+ console.log(` Removed ${t.path}`);
88
+ }
89
+ catch (err) {
90
+ console.error(` Failed to remove ${t.path}: ${err.message}`);
91
+ }
92
+ }
93
+ console.log("");
94
+ console.log(" fellow-agents state removed.");
95
+ console.log("");
96
+ console.log(" To uninstall the npm package itself, run:");
97
+ console.log(" npm uninstall -g fellow-agents");
98
+ console.log("");
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fellow-agents",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Multi-agent system — multiple Claude Code instances collaborating via messaging",
5
5
  "type": "module",
6
6
  "bin": {