spidersan 0.2.2

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 (245) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/LICENSE +21 -0
  3. package/LICENSE.md +85 -0
  4. package/README.md +217 -0
  5. package/dist/bin/spidersan.d.ts +9 -0
  6. package/dist/bin/spidersan.d.ts.map +1 -0
  7. package/dist/bin/spidersan.js +152 -0
  8. package/dist/bin/spidersan.js.map +1 -0
  9. package/dist/commands/abandon.d.ts +8 -0
  10. package/dist/commands/abandon.d.ts.map +1 -0
  11. package/dist/commands/abandon.js +49 -0
  12. package/dist/commands/abandon.js.map +1 -0
  13. package/dist/commands/activate.d.ts +9 -0
  14. package/dist/commands/activate.d.ts.map +1 -0
  15. package/dist/commands/activate.js +45 -0
  16. package/dist/commands/activate.js.map +1 -0
  17. package/dist/commands/active-windows.d.ts +16 -0
  18. package/dist/commands/active-windows.d.ts.map +1 -0
  19. package/dist/commands/active-windows.js +373 -0
  20. package/dist/commands/active-windows.js.map +1 -0
  21. package/dist/commands/audit-mark.d.ts +12 -0
  22. package/dist/commands/audit-mark.d.ts.map +1 -0
  23. package/dist/commands/audit-mark.js +204 -0
  24. package/dist/commands/audit-mark.js.map +1 -0
  25. package/dist/commands/cleanup.d.ts +8 -0
  26. package/dist/commands/cleanup.d.ts.map +1 -0
  27. package/dist/commands/cleanup.js +41 -0
  28. package/dist/commands/cleanup.js.map +1 -0
  29. package/dist/commands/close.d.ts +9 -0
  30. package/dist/commands/close.d.ts.map +1 -0
  31. package/dist/commands/close.js +129 -0
  32. package/dist/commands/close.js.map +1 -0
  33. package/dist/commands/collab-sync.d.ts +14 -0
  34. package/dist/commands/collab-sync.d.ts.map +1 -0
  35. package/dist/commands/collab-sync.js +240 -0
  36. package/dist/commands/collab-sync.js.map +1 -0
  37. package/dist/commands/collab.d.ts +6 -0
  38. package/dist/commands/collab.d.ts.map +1 -0
  39. package/dist/commands/collab.js +103 -0
  40. package/dist/commands/collab.js.map +1 -0
  41. package/dist/commands/completions.d.ts +8 -0
  42. package/dist/commands/completions.d.ts.map +1 -0
  43. package/dist/commands/completions.js +83 -0
  44. package/dist/commands/completions.js.map +1 -0
  45. package/dist/commands/conflicts.d.ts +12 -0
  46. package/dist/commands/conflicts.d.ts.map +1 -0
  47. package/dist/commands/conflicts.js +426 -0
  48. package/dist/commands/conflicts.js.map +1 -0
  49. package/dist/commands/demo.d.ts +3 -0
  50. package/dist/commands/demo.d.ts.map +1 -0
  51. package/dist/commands/demo.js +113 -0
  52. package/dist/commands/demo.js.map +1 -0
  53. package/dist/commands/depends.d.ts +8 -0
  54. package/dist/commands/depends.d.ts.map +1 -0
  55. package/dist/commands/depends.js +44 -0
  56. package/dist/commands/depends.js.map +1 -0
  57. package/dist/commands/doctor.d.ts +8 -0
  58. package/dist/commands/doctor.d.ts.map +1 -0
  59. package/dist/commands/doctor.js +249 -0
  60. package/dist/commands/doctor.js.map +1 -0
  61. package/dist/commands/inbox.d.ts +3 -0
  62. package/dist/commands/inbox.d.ts.map +1 -0
  63. package/dist/commands/inbox.js +66 -0
  64. package/dist/commands/inbox.js.map +1 -0
  65. package/dist/commands/index.d.ts +43 -0
  66. package/dist/commands/index.d.ts.map +1 -0
  67. package/dist/commands/index.js +61 -0
  68. package/dist/commands/index.js.map +1 -0
  69. package/dist/commands/init.d.ts +8 -0
  70. package/dist/commands/init.d.ts.map +1 -0
  71. package/dist/commands/init.js +23 -0
  72. package/dist/commands/init.js.map +1 -0
  73. package/dist/commands/intent-scan.d.ts +17 -0
  74. package/dist/commands/intent-scan.d.ts.map +1 -0
  75. package/dist/commands/intent-scan.js +336 -0
  76. package/dist/commands/intent-scan.js.map +1 -0
  77. package/dist/commands/key-import.d.ts +3 -0
  78. package/dist/commands/key-import.d.ts.map +1 -0
  79. package/dist/commands/key-import.js +54 -0
  80. package/dist/commands/key-import.js.map +1 -0
  81. package/dist/commands/keygen.d.ts +3 -0
  82. package/dist/commands/keygen.d.ts.map +1 -0
  83. package/dist/commands/keygen.js +53 -0
  84. package/dist/commands/keygen.js.map +1 -0
  85. package/dist/commands/keys.d.ts +3 -0
  86. package/dist/commands/keys.d.ts.map +1 -0
  87. package/dist/commands/keys.js +56 -0
  88. package/dist/commands/keys.js.map +1 -0
  89. package/dist/commands/list.d.ts +3 -0
  90. package/dist/commands/list.d.ts.map +1 -0
  91. package/dist/commands/list.js +45 -0
  92. package/dist/commands/list.js.map +1 -0
  93. package/dist/commands/lock.d.ts +11 -0
  94. package/dist/commands/lock.d.ts.map +1 -0
  95. package/dist/commands/lock.js +220 -0
  96. package/dist/commands/lock.js.map +1 -0
  97. package/dist/commands/log.d.ts +8 -0
  98. package/dist/commands/log.d.ts.map +1 -0
  99. package/dist/commands/log.js +95 -0
  100. package/dist/commands/log.js.map +1 -0
  101. package/dist/commands/mcp-health.d.ts +3 -0
  102. package/dist/commands/mcp-health.d.ts.map +1 -0
  103. package/dist/commands/mcp-health.js +305 -0
  104. package/dist/commands/mcp-health.js.map +1 -0
  105. package/dist/commands/mcp-restart.d.ts +3 -0
  106. package/dist/commands/mcp-restart.d.ts.map +1 -0
  107. package/dist/commands/mcp-restart.js +176 -0
  108. package/dist/commands/mcp-restart.js.map +1 -0
  109. package/dist/commands/merge-order.d.ts +9 -0
  110. package/dist/commands/merge-order.d.ts.map +1 -0
  111. package/dist/commands/merge-order.js +138 -0
  112. package/dist/commands/merge-order.js.map +1 -0
  113. package/dist/commands/merged.d.ts +8 -0
  114. package/dist/commands/merged.d.ts.map +1 -0
  115. package/dist/commands/merged.js +39 -0
  116. package/dist/commands/merged.js.map +1 -0
  117. package/dist/commands/msg-read.d.ts +3 -0
  118. package/dist/commands/msg-read.d.ts.map +1 -0
  119. package/dist/commands/msg-read.js +70 -0
  120. package/dist/commands/msg-read.js.map +1 -0
  121. package/dist/commands/pulse.d.ts +9 -0
  122. package/dist/commands/pulse.d.ts.map +1 -0
  123. package/dist/commands/pulse.js +232 -0
  124. package/dist/commands/pulse.js.map +1 -0
  125. package/dist/commands/radar.d.ts +3 -0
  126. package/dist/commands/radar.d.ts.map +1 -0
  127. package/dist/commands/radar.js +147 -0
  128. package/dist/commands/radar.js.map +1 -0
  129. package/dist/commands/ready-check.d.ts +9 -0
  130. package/dist/commands/ready-check.d.ts.map +1 -0
  131. package/dist/commands/ready-check.js +156 -0
  132. package/dist/commands/ready-check.js.map +1 -0
  133. package/dist/commands/register.d.ts +3 -0
  134. package/dist/commands/register.d.ts.map +1 -0
  135. package/dist/commands/register.js +124 -0
  136. package/dist/commands/register.js.map +1 -0
  137. package/dist/commands/send.d.ts +3 -0
  138. package/dist/commands/send.d.ts.map +1 -0
  139. package/dist/commands/send.js +134 -0
  140. package/dist/commands/send.js.map +1 -0
  141. package/dist/commands/stale.d.ts +8 -0
  142. package/dist/commands/stale.d.ts.map +1 -0
  143. package/dist/commands/stale.js +38 -0
  144. package/dist/commands/stale.js.map +1 -0
  145. package/dist/commands/status.d.ts +9 -0
  146. package/dist/commands/status.d.ts.map +1 -0
  147. package/dist/commands/status.js +41 -0
  148. package/dist/commands/status.js.map +1 -0
  149. package/dist/commands/sync-all.d.ts +9 -0
  150. package/dist/commands/sync-all.d.ts.map +1 -0
  151. package/dist/commands/sync-all.js +180 -0
  152. package/dist/commands/sync-all.js.map +1 -0
  153. package/dist/commands/sync.d.ts +8 -0
  154. package/dist/commands/sync.d.ts.map +1 -0
  155. package/dist/commands/sync.js +49 -0
  156. package/dist/commands/sync.js.map +1 -0
  157. package/dist/commands/tension.d.ts +12 -0
  158. package/dist/commands/tension.d.ts.map +1 -0
  159. package/dist/commands/tension.js +242 -0
  160. package/dist/commands/tension.js.map +1 -0
  161. package/dist/commands/torrent.d.ts +9 -0
  162. package/dist/commands/torrent.d.ts.map +1 -0
  163. package/dist/commands/torrent.js +431 -0
  164. package/dist/commands/torrent.js.map +1 -0
  165. package/dist/commands/tui.d.ts +8 -0
  166. package/dist/commands/tui.d.ts.map +1 -0
  167. package/dist/commands/tui.js +15 -0
  168. package/dist/commands/tui.js.map +1 -0
  169. package/dist/commands/wake.d.ts +9 -0
  170. package/dist/commands/wake.d.ts.map +1 -0
  171. package/dist/commands/wake.js +188 -0
  172. package/dist/commands/wake.js.map +1 -0
  173. package/dist/commands/watch.d.ts +3 -0
  174. package/dist/commands/watch.d.ts.map +1 -0
  175. package/dist/commands/watch.js +245 -0
  176. package/dist/commands/watch.js.map +1 -0
  177. package/dist/commands/who-touched.d.ts +9 -0
  178. package/dist/commands/who-touched.d.ts.map +1 -0
  179. package/dist/commands/who-touched.js +186 -0
  180. package/dist/commands/who-touched.js.map +1 -0
  181. package/dist/index.d.ts +6 -0
  182. package/dist/index.d.ts.map +1 -0
  183. package/dist/index.js +6 -0
  184. package/dist/index.js.map +1 -0
  185. package/dist/lib/agent-errors.d.ts +41 -0
  186. package/dist/lib/agent-errors.d.ts.map +1 -0
  187. package/dist/lib/agent-errors.js +143 -0
  188. package/dist/lib/agent-errors.js.map +1 -0
  189. package/dist/lib/ast.d.ts +56 -0
  190. package/dist/lib/ast.d.ts.map +1 -0
  191. package/dist/lib/ast.js +134 -0
  192. package/dist/lib/ast.js.map +1 -0
  193. package/dist/lib/config.d.ts +33 -0
  194. package/dist/lib/config.d.ts.map +1 -0
  195. package/dist/lib/config.js +127 -0
  196. package/dist/lib/config.js.map +1 -0
  197. package/dist/lib/crdt.d.ts +83 -0
  198. package/dist/lib/crdt.d.ts.map +1 -0
  199. package/dist/lib/crdt.js +160 -0
  200. package/dist/lib/crdt.js.map +1 -0
  201. package/dist/lib/crypto.d.ts +71 -0
  202. package/dist/lib/crypto.d.ts.map +1 -0
  203. package/dist/lib/crypto.js +172 -0
  204. package/dist/lib/crypto.js.map +1 -0
  205. package/dist/lib/errors.d.ts +35 -0
  206. package/dist/lib/errors.d.ts.map +1 -0
  207. package/dist/lib/errors.js +71 -0
  208. package/dist/lib/errors.js.map +1 -0
  209. package/dist/lib/license.d.ts +61 -0
  210. package/dist/lib/license.d.ts.map +1 -0
  211. package/dist/lib/license.js +171 -0
  212. package/dist/lib/license.js.map +1 -0
  213. package/dist/lib/security.d.ts +53 -0
  214. package/dist/lib/security.d.ts.map +1 -0
  215. package/dist/lib/security.js +122 -0
  216. package/dist/lib/security.js.map +1 -0
  217. package/dist/lib/update-check.d.ts +11 -0
  218. package/dist/lib/update-check.d.ts.map +1 -0
  219. package/dist/lib/update-check.js +113 -0
  220. package/dist/lib/update-check.js.map +1 -0
  221. package/dist/storage/adapter.d.ts +58 -0
  222. package/dist/storage/adapter.d.ts.map +1 -0
  223. package/dist/storage/adapter.js +8 -0
  224. package/dist/storage/adapter.js.map +1 -0
  225. package/dist/storage/factory.d.ts +6 -0
  226. package/dist/storage/factory.d.ts.map +1 -0
  227. package/dist/storage/factory.js +22 -0
  228. package/dist/storage/factory.js.map +1 -0
  229. package/dist/storage/index.d.ts +5 -0
  230. package/dist/storage/index.d.ts.map +1 -0
  231. package/dist/storage/index.js +5 -0
  232. package/dist/storage/index.js.map +1 -0
  233. package/dist/storage/local.d.ts +24 -0
  234. package/dist/storage/local.d.ts.map +1 -0
  235. package/dist/storage/local.js +101 -0
  236. package/dist/storage/local.js.map +1 -0
  237. package/dist/storage/supabase.d.ts +52 -0
  238. package/dist/storage/supabase.d.ts.map +1 -0
  239. package/dist/storage/supabase.js +221 -0
  240. package/dist/storage/supabase.js.map +1 -0
  241. package/dist/tui/screen.d.ts +14 -0
  242. package/dist/tui/screen.d.ts.map +1 -0
  243. package/dist/tui/screen.js +136 -0
  244. package/dist/tui/screen.js.map +1 -0
  245. package/package.json +89 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * spidersan lock
3
+ *
4
+ * Claim or release locks on symbols (functions, classes, etc.)
5
+ * Uses CRDT for decentralized coordination, syncs via Hub.
6
+ *
7
+ * Part of Codex 5.2 implementation.
8
+ */
9
+ import { Command } from 'commander';
10
+ import { readFileSync, existsSync } from 'fs';
11
+ import { ASTParser } from '../lib/ast.js';
12
+ import { createSwarmState } from '../lib/crdt.js';
13
+ const HUB_URL = process.env.HUB_URL || 'https://hub.treebird.uk';
14
+ const AGENT_ID = process.env.SPIDERSAN_AGENT || process.env.MYCELIUMAIL_AGENT_ID || 'unknown';
15
+ // Singleton swarm state (persists across commands in same session)
16
+ let swarmState = null;
17
+ function getSwarmState() {
18
+ if (!swarmState) {
19
+ swarmState = createSwarmState(AGENT_ID, async (update) => {
20
+ // Sync updates to Hub
21
+ try {
22
+ await syncToHub(update);
23
+ }
24
+ catch {
25
+ // Hub offline - silent fail
26
+ }
27
+ });
28
+ }
29
+ return swarmState;
30
+ }
31
+ /**
32
+ * Sync CRDT update to Hub for other agents
33
+ */
34
+ async function syncToHub(update) {
35
+ const base64Update = Buffer.from(update).toString('base64');
36
+ await fetch(`${HUB_URL}/api/spidersan/crdt-sync`, {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({
40
+ agent: AGENT_ID,
41
+ update: base64Update,
42
+ timestamp: Date.now(),
43
+ }),
44
+ });
45
+ }
46
+ /**
47
+ * Pull latest state from Hub
48
+ */
49
+ async function pullFromHub() {
50
+ try {
51
+ const response = await fetch(`${HUB_URL}/api/spidersan/crdt-state`);
52
+ if (response.ok) {
53
+ const data = await response.json();
54
+ const state = getSwarmState();
55
+ for (const base64Update of data.updates || []) {
56
+ const update = Buffer.from(base64Update, 'base64');
57
+ state.applyUpdate(new Uint8Array(update));
58
+ }
59
+ }
60
+ }
61
+ catch {
62
+ // Hub offline - continue with local state
63
+ }
64
+ }
65
+ /**
66
+ * Extract symbol ID from file:symbol format
67
+ */
68
+ function parseSymbolId(symbolId) {
69
+ const parts = symbolId.split(':');
70
+ if (parts.length === 2) {
71
+ return { file: parts[0], symbol: parts[1] };
72
+ }
73
+ return null;
74
+ }
75
+ /**
76
+ * Generate symbol ID from file path and symbol name
77
+ */
78
+ function makeSymbolId(file, symbol) {
79
+ return `${file}:${symbol}`;
80
+ }
81
+ export const lockCommand = new Command('lock')
82
+ .description('Claim or release locks on symbols for coordination')
83
+ .argument('[symbolId]', 'Symbol to lock (format: file.ts:functionName)')
84
+ .option('--release', 'Release a lock instead of acquiring')
85
+ .option('--release-all', 'Release all locks held by this agent')
86
+ .option('--list', 'List all active locks')
87
+ .option('--file <path>', 'Lock all symbols in a file')
88
+ .option('--intent <description>', 'Describe what you intend to do with the symbol')
89
+ .option('--sync', 'Force sync with Hub before operation')
90
+ .action(async (symbolId, options) => {
91
+ const state = getSwarmState();
92
+ // Sync with Hub first if requested
93
+ if (options.sync) {
94
+ console.log('๐Ÿ”„ Syncing with Hub...');
95
+ await pullFromHub();
96
+ }
97
+ // List all locks
98
+ if (options.list) {
99
+ const locks = state.getAllLocks();
100
+ if (locks.length === 0) {
101
+ console.log('๐Ÿ•ท๏ธ No active locks');
102
+ return;
103
+ }
104
+ console.log(`\n๐Ÿ•ท๏ธ ACTIVE LOCKS (${locks.length}):`);
105
+ console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n');
106
+ // Group by agent
107
+ const byAgent = new Map();
108
+ for (const lock of locks) {
109
+ const existing = byAgent.get(lock.agentId) || [];
110
+ existing.push(lock);
111
+ byAgent.set(lock.agentId, existing);
112
+ }
113
+ for (const [agentId, agentLocks] of byAgent) {
114
+ const isMe = agentId === AGENT_ID;
115
+ console.log(`${isMe ? '๐Ÿ‘ค' : '๐Ÿ‘ฅ'} ${agentId}${isMe ? ' (you)' : ''}:`);
116
+ for (const lock of agentLocks) {
117
+ const age = Math.floor((Date.now() - lock.timestamp) / 1000 / 60);
118
+ console.log(` ๐Ÿ”’ ${lock.symbolId} (${age}m ago)`);
119
+ if (lock.description) {
120
+ console.log(` โ””โ”€ ${lock.description}`);
121
+ }
122
+ }
123
+ console.log('');
124
+ }
125
+ return;
126
+ }
127
+ // Release all locks
128
+ if (options.releaseAll) {
129
+ const released = state.releaseAllLocks();
130
+ console.log(`๐Ÿ”“ Released ${released} lock(s)`);
131
+ return;
132
+ }
133
+ // Lock all symbols in a file
134
+ if (options.file) {
135
+ const filePath = options.file;
136
+ if (!existsSync(filePath)) {
137
+ console.error(`โŒ File not found: ${filePath}`);
138
+ process.exit(1);
139
+ }
140
+ const code = readFileSync(filePath, 'utf-8');
141
+ const parser = new ASTParser();
142
+ const fileSymbols = parser.parseCode(code, filePath);
143
+ console.log(`\n๐Ÿ•ท๏ธ LOCKING SYMBOLS IN ${filePath}:`);
144
+ console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n');
145
+ let acquired = 0;
146
+ let blocked = 0;
147
+ for (const symbol of fileSymbols.symbols) {
148
+ const id = makeSymbolId(filePath, symbol.name);
149
+ const success = state.acquireLock(id, symbol.hash, options.intent);
150
+ if (success) {
151
+ console.log(` ๐Ÿ”’ ${symbol.type} '${symbol.name}' โ€” locked`);
152
+ acquired++;
153
+ }
154
+ else {
155
+ const existingLock = state.getLock(id);
156
+ console.log(` โŒ ${symbol.type} '${symbol.name}' โ€” locked by ${existingLock?.agentId}`);
157
+ blocked++;
158
+ }
159
+ }
160
+ console.log(`\nโœ… Acquired: ${acquired} | โŒ Blocked: ${blocked}`);
161
+ if (options.intent) {
162
+ state.setIntent(options.intent);
163
+ console.log(`\n๐Ÿ“ Intent set: "${options.intent}"`);
164
+ }
165
+ return;
166
+ }
167
+ // Single symbol lock/release
168
+ if (!symbolId) {
169
+ console.error('โŒ Symbol ID required (format: file.ts:functionName)');
170
+ console.error(' Or use: --file <path> to lock all symbols in a file');
171
+ console.error(' Or use: --list to see active locks');
172
+ process.exit(1);
173
+ }
174
+ if (options.release) {
175
+ const success = state.releaseLock(symbolId);
176
+ if (success) {
177
+ console.log(`๐Ÿ”“ Released lock on '${symbolId}'`);
178
+ }
179
+ else {
180
+ console.log(`โš ๏ธ Could not release '${symbolId}' (not locked by you)`);
181
+ }
182
+ return;
183
+ }
184
+ // Acquire lock
185
+ const parsed = parseSymbolId(symbolId);
186
+ let hash = 'manual';
187
+ // If it's a file:symbol format, try to compute actual hash
188
+ if (parsed && existsSync(parsed.file)) {
189
+ try {
190
+ const code = readFileSync(parsed.file, 'utf-8');
191
+ const parser = new ASTParser();
192
+ const fileSymbols = parser.parseCode(code, parsed.file);
193
+ const symbol = fileSymbols.symbols.find(s => s.name === parsed.symbol);
194
+ if (symbol) {
195
+ hash = symbol.hash;
196
+ }
197
+ }
198
+ catch {
199
+ // Use manual hash
200
+ }
201
+ }
202
+ const success = state.acquireLock(symbolId, hash, options.intent);
203
+ if (success) {
204
+ console.log(`๐Ÿ”’ Acquired lock on '${symbolId}'`);
205
+ if (options.intent) {
206
+ state.setIntent(options.intent);
207
+ console.log(`๐Ÿ“ Intent: "${options.intent}"`);
208
+ }
209
+ }
210
+ else {
211
+ const existingLock = state.getLock(symbolId);
212
+ console.log(`โŒ Could not lock '${symbolId}'`);
213
+ console.log(` Already locked by: ${existingLock?.agentId}`);
214
+ if (existingLock?.description) {
215
+ console.log(` Their intent: "${existingLock.description}"`);
216
+ }
217
+ process.exit(1);
218
+ }
219
+ });
220
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * spidersan log
3
+ *
4
+ * Record successful multi-agent collaboration sessions for future reference.
5
+ */
6
+ import { Command } from 'commander';
7
+ export declare const logCommand: Command;
8
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,eAAO,MAAM,UAAU,SAsEjB,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * spidersan log
3
+ *
4
+ * Record successful multi-agent collaboration sessions for future reference.
5
+ */
6
+ import { Command } from 'commander';
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+ const LOG_FILE = path.join(os.homedir(), '.spidersan', 'sessions.json');
11
+ function ensureLogFile() {
12
+ const dir = path.dirname(LOG_FILE);
13
+ if (!fs.existsSync(dir)) {
14
+ fs.mkdirSync(dir, { recursive: true });
15
+ }
16
+ if (!fs.existsSync(LOG_FILE)) {
17
+ fs.writeFileSync(LOG_FILE, '[]', 'utf-8');
18
+ return [];
19
+ }
20
+ try {
21
+ return JSON.parse(fs.readFileSync(LOG_FILE, 'utf-8'));
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ }
27
+ function saveLog(logs) {
28
+ fs.writeFileSync(LOG_FILE, JSON.stringify(logs, null, 2), 'utf-8');
29
+ }
30
+ function generateId() {
31
+ return `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
32
+ }
33
+ export const logCommand = new Command('log')
34
+ .description('Record multi-agent collaboration sessions')
35
+ .argument('[message]', 'Session description to log')
36
+ .option('--list', 'List all logged sessions')
37
+ .option('--last <n>', 'Show last N sessions', '10')
38
+ .option('--tags <tags>', 'Comma-separated tags for the session')
39
+ .option('--json', 'Output as JSON')
40
+ .option('--clear', 'Clear all session logs')
41
+ .action(async (message, options) => {
42
+ const logs = ensureLogFile();
43
+ // Clear logs
44
+ if (options.clear) {
45
+ saveLog([]);
46
+ console.log('๐Ÿ•ท๏ธ Session logs cleared.');
47
+ return;
48
+ }
49
+ // List mode
50
+ if (options.list || !message) {
51
+ if (logs.length === 0) {
52
+ console.log('๐Ÿ•ท๏ธ No sessions logged yet.');
53
+ console.log(' Run: spidersan log "Your session description"');
54
+ return;
55
+ }
56
+ const limit = parseInt(options.last, 10) || 10;
57
+ const recent = logs.slice(-limit).reverse();
58
+ if (options.json) {
59
+ console.log(JSON.stringify(recent, null, 2));
60
+ return;
61
+ }
62
+ console.log('๐Ÿ•ท๏ธ Session History:\n');
63
+ for (const session of recent) {
64
+ const date = new Date(session.timestamp).toLocaleString();
65
+ const tags = session.tags?.length ? ` [${session.tags.join(', ')}]` : '';
66
+ console.log(` ๐Ÿ“ ${date}${tags}`);
67
+ console.log(` ${session.message}`);
68
+ console.log(` ID: ${session.id}\n`);
69
+ }
70
+ console.log(`Showing ${recent.length} of ${logs.length} sessions.`);
71
+ return;
72
+ }
73
+ // Add new log
74
+ const tags = options.tags ? options.tags.split(',').map((t) => t.trim()) : [];
75
+ const newLog = {
76
+ id: generateId(),
77
+ timestamp: new Date().toISOString(),
78
+ message,
79
+ tags: tags.length > 0 ? tags : undefined
80
+ };
81
+ logs.push(newLog);
82
+ saveLog(logs);
83
+ if (options.json) {
84
+ console.log(JSON.stringify(newLog, null, 2));
85
+ return;
86
+ }
87
+ console.log('๐Ÿ•ท๏ธ Session logged!');
88
+ console.log(` ID: ${newLog.id}`);
89
+ console.log(` Message: ${message}`);
90
+ if (tags.length > 0) {
91
+ console.log(` Tags: ${tags.join(', ')}`);
92
+ }
93
+ console.log(`\n Total sessions: ${logs.length}`);
94
+ });
95
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const mcpHealthCommand: Command;
3
+ //# sourceMappingURL=mcp-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-health.d.ts","sourceRoot":"","sources":["../../src/commands/mcp-health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsLpC,eAAO,MAAM,gBAAgB,SAkKvB,CAAC"}
@@ -0,0 +1,305 @@
1
+ import { Command } from 'commander';
2
+ import { execSync, spawn } from 'child_process';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import * as os from 'os';
6
+ // Config
7
+ const HUB_URL = process.env.HUB_URL || 'https://hub.treebird.uk';
8
+ const MCP_CONFIG_PATH = path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json');
9
+ /**
10
+ * Read MCP config from Antigravity config file
11
+ */
12
+ function readMcpConfig() {
13
+ try {
14
+ if (!fs.existsSync(MCP_CONFIG_PATH)) {
15
+ console.log(`โš ๏ธ MCP config not found: ${MCP_CONFIG_PATH}`);
16
+ return null;
17
+ }
18
+ const content = fs.readFileSync(MCP_CONFIG_PATH, 'utf-8');
19
+ return JSON.parse(content);
20
+ }
21
+ catch (err) {
22
+ console.log(`โš ๏ธ Failed to read MCP config: ${err}`);
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * Start an MCP server from config
28
+ */
29
+ function startMcpServer(name, config) {
30
+ try {
31
+ const env = { ...process.env, ...config.env };
32
+ const child = spawn(config.command, config.args, {
33
+ detached: true,
34
+ stdio: 'ignore',
35
+ env
36
+ });
37
+ child.unref();
38
+ console.log(`๐Ÿš€ Started ${name} (PID: ${child.pid})`);
39
+ return true;
40
+ }
41
+ catch (err) {
42
+ console.log(`โŒ Failed to start ${name}: ${err}`);
43
+ return false;
44
+ }
45
+ }
46
+ // Known MCP servers in the Treebird ecosystem
47
+ const KNOWN_MCP_SERVERS = [
48
+ { name: 'watsan-mcp', pattern: 'watsan.*mcp', required: true },
49
+ { name: 'myceliumail-mcp', pattern: 'myceliumail.*mcp', required: true },
50
+ { name: 'spidersan-mcp', pattern: 'spidersan.*mcp', required: false },
51
+ { name: 'supabase-mcp', pattern: 'supabase.*mcp', required: false },
52
+ { name: 'perplexity-mcp', pattern: 'perplexity', required: false },
53
+ ];
54
+ function findMcpProcesses() {
55
+ const results = new Map();
56
+ try {
57
+ // Get all MCP-related processes
58
+ const output = execSync('ps aux | grep -E "mcp|MCP" | grep -v grep', {
59
+ encoding: 'utf-8',
60
+ stdio: ['pipe', 'pipe', 'pipe']
61
+ }).trim();
62
+ if (!output)
63
+ return results;
64
+ const lines = output.split('\n');
65
+ for (const line of lines) {
66
+ const parts = line.split(/\s+/);
67
+ if (parts.length < 11)
68
+ continue;
69
+ const pid = parseInt(parts[1], 10);
70
+ const startTime = parts[8]; // TIME column
71
+ const command = parts.slice(10).join(' ');
72
+ // Match against known servers
73
+ for (const server of KNOWN_MCP_SERVERS) {
74
+ const regex = new RegExp(server.pattern, 'i');
75
+ if (regex.test(command)) {
76
+ if (!results.has(server.name)) {
77
+ results.set(server.name, []);
78
+ }
79
+ results.get(server.name).push({
80
+ pid,
81
+ command: command.substring(0, 60) + (command.length > 60 ? '...' : ''),
82
+ uptime: startTime
83
+ });
84
+ }
85
+ }
86
+ // Check for unknown MCP servers
87
+ if (!Array.from(results.values()).some(procs => procs.some(p => p.pid === pid))) {
88
+ if (/mcp|MCP/.test(command)) {
89
+ const unknownKey = 'unknown-mcp';
90
+ if (!results.has(unknownKey)) {
91
+ results.set(unknownKey, []);
92
+ }
93
+ results.get(unknownKey).push({
94
+ pid,
95
+ command: command.substring(0, 60) + (command.length > 60 ? '...' : ''),
96
+ uptime: startTime
97
+ });
98
+ }
99
+ }
100
+ }
101
+ }
102
+ catch (err) {
103
+ // No processes found - that's okay
104
+ }
105
+ return results;
106
+ }
107
+ function getProcessUptime(pid) {
108
+ try {
109
+ const output = execSync(`ps -p ${pid} -o etime=`, { encoding: 'utf-8' }).trim();
110
+ return output || 'unknown';
111
+ }
112
+ catch {
113
+ return 'unknown';
114
+ }
115
+ }
116
+ /**
117
+ * Post MCP health summary to Hub chat
118
+ */
119
+ async function postToHubChat(healthReport, hasIssues) {
120
+ const running = Object.entries(healthReport).filter(([_, v]) => v.status === 'running').length;
121
+ const stopped = Object.entries(healthReport).filter(([_, v]) => v.status === 'stopped').length;
122
+ const zombies = Object.entries(healthReport).filter(([_, v]) => v.status === 'zombie').length;
123
+ const statusIcon = hasIssues ? 'โš ๏ธ' : 'โœ…';
124
+ const statusText = hasIssues ? 'Issues detected' : 'All healthy';
125
+ const serverLines = Object.entries(healthReport)
126
+ .map(([name, info]) => {
127
+ const icon = info.status === 'running' ? 'โœ…' : info.status === 'zombie' ? 'โš ๏ธ' : 'โŒ';
128
+ return `${icon} ${name}: ${info.status}`;
129
+ })
130
+ .join('\n');
131
+ const message = `๐Ÿฉบ **MCP Health Report** ${statusIcon}\n\n${serverLines}\n\n**Summary:** ${running} running, ${stopped} stopped, ${zombies} zombies`;
132
+ try {
133
+ const response = await fetch(`${HUB_URL}/api/chat`, {
134
+ method: 'POST',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ body: JSON.stringify({
137
+ agent: 'ssan',
138
+ name: 'Spidersan',
139
+ message,
140
+ glyph: '๐Ÿ•ท๏ธ'
141
+ })
142
+ });
143
+ if (response.ok) {
144
+ console.log('๐Ÿ“ค Posted health report to Hub chat');
145
+ }
146
+ else {
147
+ console.log(`โš ๏ธ Failed to post to Hub: ${response.status}`);
148
+ }
149
+ }
150
+ catch {
151
+ console.log('โš ๏ธ Could not connect to Hub');
152
+ }
153
+ }
154
+ export const mcpHealthCommand = new Command('mcp-health')
155
+ .description('๐Ÿฉบ Check health of MCP servers in the ecosystem')
156
+ .option('--json', 'Output as JSON for Hub events')
157
+ .option('--hub', 'Post health report to Hub chat')
158
+ .option('--kill-zombies', 'Kill duplicate MCP processes (keep only newest)')
159
+ .option('--auto-restart', 'Automatically restart stopped required MCP servers')
160
+ .action(async (options) => {
161
+ console.log(`
162
+ ๐Ÿฉบ MCP Health Check
163
+ โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
164
+ `);
165
+ const processes = findMcpProcesses();
166
+ const healthReport = {};
167
+ let hasIssues = false;
168
+ let zombiesKilled = 0;
169
+ let serversRestarted = 0;
170
+ const stoppedServers = [];
171
+ // Load MCP config for potential restart
172
+ const mcpConfig = options.autoRestart ? readMcpConfig() : null;
173
+ // Check each known server
174
+ for (const server of KNOWN_MCP_SERVERS) {
175
+ const procs = processes.get(server.name) || [];
176
+ const count = procs.length;
177
+ let status;
178
+ let icon;
179
+ if (count === 0) {
180
+ if (server.required) {
181
+ stoppedServers.push(server.name);
182
+ // Try to auto-restart if flag is set
183
+ if (options.autoRestart && mcpConfig?.mcpServers[server.name]) {
184
+ const serverConfig = mcpConfig.mcpServers[server.name];
185
+ if (startMcpServer(server.name, serverConfig)) {
186
+ serversRestarted++;
187
+ status = 'RESTARTED';
188
+ icon = '๐Ÿš€';
189
+ }
190
+ else {
191
+ status = 'RESTART FAILED';
192
+ icon = 'โŒ';
193
+ hasIssues = true;
194
+ }
195
+ }
196
+ else {
197
+ status = 'NOT RUNNING';
198
+ icon = 'โŒ';
199
+ hasIssues = true;
200
+ }
201
+ }
202
+ else {
203
+ status = 'not running (optional)';
204
+ icon = 'โšช';
205
+ }
206
+ }
207
+ else if (count === 1) {
208
+ const uptime = getProcessUptime(procs[0].pid);
209
+ status = `running (PID: ${procs[0].pid}, uptime: ${uptime})`;
210
+ icon = 'โœ…';
211
+ }
212
+ else {
213
+ status = `${count} ZOMBIES FOUND!`;
214
+ icon = 'โš ๏ธ';
215
+ hasIssues = true;
216
+ if (options.killZombies) {
217
+ // Kill all but the newest (highest PID)
218
+ // IMPORTANT: Exclude current process and parent to avoid self-termination
219
+ const currentPid = process.pid;
220
+ const parentPid = process.ppid;
221
+ const protectedPids = new Set([currentPid, parentPid]);
222
+ const sortedProcs = [...procs].sort((a, b) => b.pid - a.pid);
223
+ const toKill = sortedProcs.slice(1).filter(p => !protectedPids.has(p.pid));
224
+ for (const proc of toKill) {
225
+ try {
226
+ execSync(`kill ${proc.pid}`, { stdio: 'ignore' });
227
+ zombiesKilled++;
228
+ }
229
+ catch {
230
+ // Process might already be dead
231
+ }
232
+ }
233
+ status = `${count} found, ${toKill.length} killed (kept PID: ${sortedProcs[0].pid})`;
234
+ icon = '๐Ÿงน';
235
+ }
236
+ }
237
+ healthReport[server.name] = {
238
+ status: count === 0 ? 'stopped' : count === 1 ? 'running' : 'zombie',
239
+ pids: procs.map(p => p.pid),
240
+ zombieCount: count > 1 ? count - 1 : 0
241
+ };
242
+ if (!options.json) {
243
+ console.log(`${icon} ${server.name}: ${status}`);
244
+ }
245
+ }
246
+ // Check for unknown MCP processes
247
+ const unknownProcs = processes.get('unknown-mcp') || [];
248
+ if (unknownProcs.length > 0) {
249
+ if (!options.json) {
250
+ console.log(`\nโš ๏ธ Unknown MCP processes found:`);
251
+ for (const proc of unknownProcs) {
252
+ console.log(` PID ${proc.pid}: ${proc.command}`);
253
+ }
254
+ }
255
+ healthReport['unknown'] = {
256
+ status: 'unknown',
257
+ pids: unknownProcs.map(p => p.pid),
258
+ zombieCount: unknownProcs.length
259
+ };
260
+ }
261
+ if (!options.json) {
262
+ console.log(`
263
+ โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`);
264
+ if (options.killZombies && zombiesKilled > 0) {
265
+ console.log(`๐Ÿงน Killed ${zombiesKilled} zombie processes`);
266
+ }
267
+ if (serversRestarted > 0) {
268
+ console.log(`๐Ÿš€ Restarted ${serversRestarted} MCP server(s)`);
269
+ }
270
+ if (hasIssues) {
271
+ const hints = [];
272
+ if (stoppedServers.length > 0 && !options.autoRestart) {
273
+ hints.push('spidersan mcp-health --auto-restart');
274
+ }
275
+ hints.push('spidersan mcp-health --kill-zombies');
276
+ console.log(`
277
+ โš ๏ธ Issues found! Try:
278
+ ${hints.join('\n ')}
279
+ `);
280
+ }
281
+ else {
282
+ console.log(`
283
+ โœ… All MCP servers healthy!
284
+ `);
285
+ }
286
+ }
287
+ if (options.json) {
288
+ const event = {
289
+ event: 'mcp:health',
290
+ payload: {
291
+ servers: healthReport,
292
+ hasIssues,
293
+ zombiesKilled,
294
+ timestamp: new Date().toISOString()
295
+ },
296
+ emitter: 'spidersan'
297
+ };
298
+ console.log(JSON.stringify(event, null, 2));
299
+ }
300
+ // Post to Hub if --hub flag is set
301
+ if (options.hub) {
302
+ await postToHubChat(healthReport, hasIssues);
303
+ }
304
+ });
305
+ //# sourceMappingURL=mcp-health.js.map