fellow-agents 0.0.18 → 0.0.19

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.
@@ -97,17 +97,20 @@ export async function start(opts) {
97
97
  // 5. Install AI skills (SKILL.md files) to known CLI paths
98
98
  console.log("[5/8] Installing skills...");
99
99
  const skillResult = installSkills();
100
- if (skillResult.written.length > 0) {
101
- console.log(` Installed ${skillResult.written.length} skill file(s)`);
100
+ const skillTotal = skillResult.written.length + skillResult.refreshed.length + skillResult.skipped.length;
101
+ if (skillTotal === 0) {
102
+ console.log(" No bundled skills");
102
103
  }
103
- if (skillResult.refreshed.length > 0) {
104
- console.log(` Refreshed ${skillResult.refreshed.length} skill file(s) to latest`);
105
- }
106
- if (skillResult.skipped.length > 0) {
107
- console.log(` Preserved ${skillResult.skipped.length} existing skill file(s) — customized or unowned`);
108
- }
109
- if (skillResult.written.length === 0 && skillResult.refreshed.length === 0 && skillResult.skipped.length === 0) {
110
- console.log(" No skills bundled");
104
+ else {
105
+ if (skillResult.written.length > 0) {
106
+ console.log(` Installed ${skillResult.written.length} skill file(s)`);
107
+ }
108
+ if (skillResult.refreshed.length > 0) {
109
+ console.log(` Refreshed ${skillResult.refreshed.length} skill file(s) to latest`);
110
+ }
111
+ if (skillResult.skipped.length > 0) {
112
+ console.log(` Preserved ${skillResult.skipped.length} existing skill file(s) — customized or unowned`);
113
+ }
111
114
  }
112
115
  // PATH trick: prepend bin dir so agents find emcom/tracker
113
116
  const env = { ...process.env, PATH: `${binDir}${process.platform === "win32" ? ";" : ":"}${process.env.PATH}` };
@@ -86,11 +86,17 @@ export function uninstall(opts) {
86
86
  // Remove skills we installed (only the ones that match the shipped bytes —
87
87
  // user-customized files are preserved).
88
88
  const skillResult = uninstallSkills();
89
- if (skillResult.removed.length > 0) {
90
- console.log(` Removed ${skillResult.removed.length} skill file(s)`);
89
+ const skillTotal = skillResult.removed.length + skillResult.preserved.length;
90
+ if (skillTotal === 0) {
91
+ console.log(" No fellow-agents-tracked skill files found");
91
92
  }
92
- if (skillResult.preserved.length > 0) {
93
- console.log(` Preserved ${skillResult.preserved.length} customized skill file(s)`);
93
+ else {
94
+ if (skillResult.removed.length > 0) {
95
+ console.log(` Removed ${skillResult.removed.length} skill file(s)`);
96
+ }
97
+ if (skillResult.preserved.length > 0) {
98
+ console.log(` Preserved ${skillResult.preserved.length} customized/unowned skill file(s)`);
99
+ }
94
100
  }
95
101
  for (const t of targets) {
96
102
  try {
@@ -89,6 +89,52 @@ function killTree(pid) {
89
89
  process.kill(pid);
90
90
  }
91
91
  }
92
+ // Scan a port for any listening PID and kill it. Used as a fallback when our
93
+ // PID files are missing (e.g., user ran clean) but a previous service is still
94
+ // holding the port — produces orphans that block re-start and re-uninstall.
95
+ function killOnPort(port) {
96
+ const killed = [];
97
+ try {
98
+ if (process.platform === "win32") {
99
+ const output = execSync(`netstat -ano -p tcp`, { stdio: ["ignore", "pipe", "ignore"] }).toString();
100
+ const pids = new Set();
101
+ for (const line of output.split("\n")) {
102
+ const trimmed = line.trim();
103
+ // Match "127.0.0.1:8800 ... LISTENING <pid>"
104
+ if (trimmed.includes(`:${port}`) && trimmed.includes("LISTENING")) {
105
+ const parts = trimmed.split(/\s+/);
106
+ const pid = parseInt(parts[parts.length - 1], 10);
107
+ if (!isNaN(pid) && pid > 4)
108
+ pids.add(pid); // skip system PIDs 0/4
109
+ }
110
+ }
111
+ for (const pid of pids) {
112
+ try {
113
+ execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore" });
114
+ killed.push(pid);
115
+ }
116
+ catch { }
117
+ }
118
+ }
119
+ else {
120
+ const output = execSync(`lsof -ti:${port}`, { stdio: ["ignore", "pipe", "ignore"] }).toString();
121
+ for (const line of output.split("\n")) {
122
+ const pid = parseInt(line.trim(), 10);
123
+ if (!isNaN(pid)) {
124
+ try {
125
+ process.kill(pid, "SIGKILL");
126
+ killed.push(pid);
127
+ }
128
+ catch { }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ catch {
134
+ // No output (no process listening) or command failed — return empty
135
+ }
136
+ return killed;
137
+ }
92
138
  export function stopAll() {
93
139
  for (const name of ["emcom-server", "pty-win"]) {
94
140
  const pid = readPid(name);
@@ -106,6 +152,14 @@ export function stopAll() {
106
152
  }
107
153
  removePid(name);
108
154
  }
155
+ // Fallback: scan default ports for orphans whose PID files have been lost.
156
+ // Common after `fellow-agents clean` or manual `Remove-Item` of pid/ dir.
157
+ for (const port of [3700, 8800]) {
158
+ const orphans = killOnPort(port);
159
+ if (orphans.length > 0) {
160
+ console.log(` Killed orphan(s) on :${port} (pid ${orphans.join(", ")})`);
161
+ }
162
+ }
109
163
  }
110
164
  export function waitForHealth(url, timeoutMs = 30000) {
111
165
  const mod = url.startsWith("https") ? https : http;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fellow-agents",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "Multi-agent system — multiple Claude Code instances collaborating via messaging",
5
5
  "type": "module",
6
6
  "bin": {