coherence-cli 0.7.1 → 0.8.1

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/bin/cc.mjs CHANGED
@@ -7,73 +7,57 @@
7
7
  * Zero dependencies. Node 18+ required.
8
8
  */
9
9
 
10
- import { createRequire } from "node:module";
11
10
  import { listIdeas, showIdea, shareIdea, stakeOnIdea, forkIdea, createIdea } from "../lib/commands/ideas.mjs";
12
11
  import { listSpecs, showSpec } from "../lib/commands/specs.mjs";
13
12
  import { contribute } from "../lib/commands/contribute.mjs";
14
13
  import { showStatus, showResonance } from "../lib/commands/status.mjs";
15
14
  import { showIdentity, linkIdentity, unlinkIdentity, lookupIdentity, setupIdentity, setIdentity } from "../lib/commands/identity.mjs";
16
- import { listNodes, sendMessage, readMessages, sendCommand } from "../lib/commands/nodes.mjs";
17
- import { listTasks, showTask, claimTask, claimNext, reportTask, seedTask } from "../lib/commands/tasks.mjs";
18
- import { setup } from "../lib/commands/setup.mjs";
19
-
20
- // Version check — non-blocking, runs in background
21
- const require = createRequire(import.meta.url);
22
- const pkg = require("../package.json");
23
- const LOCAL_VERSION = pkg.version;
24
-
25
- async function checkForUpdate() {
26
- try {
27
- const resp = await fetch("https://registry.npmjs.org/coherence-cli/latest", {
28
- signal: AbortSignal.timeout(3000),
29
- });
30
- if (!resp.ok) return;
31
- const data = await resp.json();
32
- const latest = data.version;
33
- if (latest && latest !== LOCAL_VERSION && latest > LOCAL_VERSION) {
34
- console.log(
35
- `\n\x1b[33m Update available: ${LOCAL_VERSION} → ${latest} — auto-updating...\x1b[0m`,
36
- );
37
- const { execSync } = await import("node:child_process");
38
- try {
39
- execSync(`npm i -g coherence-cli@${latest}`, { stdio: "pipe" });
40
- console.log(`\x1b[32m ✓ Updated to v${latest}\x1b[0m\n`);
41
- } catch {
42
- console.log(`\x1b[31m ✗ Auto-update failed. Run: npm i -g coherence-cli@${latest}\x1b[0m\n`);
43
- }
44
- }
45
- } catch {
46
- // Silent — don't block the CLI for a version check
47
- }
48
- }
49
-
50
- const updateCheck = checkForUpdate();
15
+ import { listNodes, sendMessage, readMessages } from "../lib/commands/nodes.mjs";
16
+ import { listContributors, showContributor, showContributions } from "../lib/commands/contributors.mjs";
17
+ import { listAssets, showAsset, createAsset } from "../lib/commands/assets.mjs";
18
+ import { showNewsFeed, showTrending, showSources, addSource, showNewsResonance } from "../lib/commands/news.mjs";
19
+ import { showTreasury, showDeposits, makeDeposit } from "../lib/commands/treasury.mjs";
20
+ import { listLinks, showLink, showValuation, payoutPreview } from "../lib/commands/lineage.mjs";
21
+ import { listChangeRequests, showChangeRequest, vote, propose } from "../lib/commands/governance.mjs";
22
+ import { listServices, showService, showServicesHealth, showServicesDeps } from "../lib/commands/services.mjs";
23
+ import { showFrictionReport, listFrictionEvents, showFrictionCategories } from "../lib/commands/friction.mjs";
24
+ import { listProviders, showProviderStats } from "../lib/commands/providers.mjs";
25
+ import { showTraceability, showCoverage, traceIdea, traceSpec } from "../lib/commands/traceability.mjs";
26
+ import { showDiag, showDiagHealth, showDiagIssues, showDiagRunners, showDiagVisibility } from "../lib/commands/diagnostics.mjs";
51
27
 
52
28
  const [command, ...args] = process.argv.slice(2);
53
29
 
54
30
  const COMMANDS = {
55
- ideas: () => listIdeas(args),
56
- idea: () => handleIdea(args),
57
- share: () => shareIdea(),
58
- stake: () => stakeOnIdea(args),
59
- fork: () => forkIdea(args),
60
- specs: () => listSpecs(args),
61
- spec: () => showSpec(args),
62
- contribute: () => contribute(args),
63
- status: () => showStatus(),
64
- resonance: () => showResonance(),
65
- identity: () => handleIdentity(args),
66
- nodes: () => listNodes(),
67
- msg: () => sendMessage(args),
68
- cmd: () => sendCommand(args),
69
- messages: () => readMessages(args),
70
- inbox: () => readMessages(args),
71
- tasks: () => listTasks(args),
72
- task: () => handleTask(args),
73
- setup: () => setup(args),
74
- update: () => selfUpdate(),
75
- version: () => console.log(`cc v${LOCAL_VERSION}`),
76
- help: () => showHelp(),
31
+ ideas: () => listIdeas(args),
32
+ idea: () => handleIdea(args),
33
+ share: () => shareIdea(),
34
+ stake: () => stakeOnIdea(args),
35
+ fork: () => forkIdea(args),
36
+ specs: () => listSpecs(args),
37
+ spec: () => showSpec(args),
38
+ contribute: () => contribute(args),
39
+ status: () => showStatus(),
40
+ resonance: () => showResonance(),
41
+ identity: () => handleIdentity(args),
42
+ nodes: () => listNodes(),
43
+ msg: () => sendMessage(args),
44
+ messages: () => readMessages(args),
45
+ inbox: () => readMessages(args),
46
+ contributors: () => listContributors(args),
47
+ contributor: () => handleContributor(args),
48
+ assets: () => listAssets(args),
49
+ asset: () => handleAsset(args),
50
+ news: () => handleNews(args),
51
+ treasury: () => handleTreasury(args),
52
+ lineage: () => handleLineage(args),
53
+ governance: () => handleGovernance(args),
54
+ services: () => handleServices(args),
55
+ service: () => showService(args),
56
+ friction: () => handleFriction(args),
57
+ providers: () => handleProviders(args),
58
+ trace: () => handleTrace(args),
59
+ diag: () => handleDiag(args),
60
+ help: () => showHelp(),
77
61
  };
78
62
 
79
63
  async function handleIdea(args) {
@@ -81,17 +65,6 @@ async function handleIdea(args) {
81
65
  return showIdea(args);
82
66
  }
83
67
 
84
- async function handleTask(args) {
85
- const sub = args[0];
86
- switch (sub) {
87
- case "next": return claimNext();
88
- case "claim": return claimTask(args.slice(1));
89
- case "report": return reportTask(args.slice(1));
90
- case "seed": return seedTask(args.slice(1));
91
- default: return showTask(args);
92
- }
93
- }
94
-
95
68
  async function handleIdentity(args) {
96
69
  const sub = args[0];
97
70
  const subArgs = args.slice(1);
@@ -105,26 +78,101 @@ async function handleIdentity(args) {
105
78
  }
106
79
  }
107
80
 
108
- async function selfUpdate() {
109
- const { execSync } = await import("node:child_process");
110
- console.log(`Current: v${LOCAL_VERSION}`);
111
- console.log("Checking npm for latest version...");
112
- try {
113
- const resp = await fetch("https://registry.npmjs.org/coherence-cli/latest", {
114
- signal: AbortSignal.timeout(5000),
115
- });
116
- const data = await resp.json();
117
- const latest = data.version;
118
- if (latest === LOCAL_VERSION) {
119
- console.log(`\x1b[32m✓\x1b[0m Already on latest version (${LOCAL_VERSION})`);
120
- return;
121
- }
122
- console.log(`Updating ${LOCAL_VERSION} → ${latest}...`);
123
- execSync(`npm i -g coherence-cli@${latest}`, { stdio: "inherit" });
124
- console.log(`\x1b[32m✓\x1b[0m Updated to v${latest}`);
125
- } catch (e) {
126
- console.error(`\x1b[31m✗\x1b[0m Update failed: ${e.message}`);
127
- console.log(" Manual: npm i -g coherence-cli@latest");
81
+ async function handleContributor(args) {
82
+ if (args[1] === "contributions") return showContributions(args);
83
+ return showContributor(args);
84
+ }
85
+
86
+ async function handleAsset(args) {
87
+ if (args[0] === "create") return createAsset(args.slice(1));
88
+ return showAsset(args);
89
+ }
90
+
91
+ async function handleNews(args) {
92
+ const sub = args[0];
93
+ switch (sub) {
94
+ case "trending": return showTrending();
95
+ case "sources": return showSources();
96
+ case "source":
97
+ if (args[1] === "add") return addSource(args.slice(2));
98
+ return showSources();
99
+ case "resonance": return showNewsResonance(args.slice(1));
100
+ default: return showNewsFeed();
101
+ }
102
+ }
103
+
104
+ async function handleTreasury(args) {
105
+ const sub = args[0];
106
+ switch (sub) {
107
+ case "deposits": return showDeposits(args.slice(1));
108
+ case "deposit": return makeDeposit(args.slice(1));
109
+ default: return showTreasury();
110
+ }
111
+ }
112
+
113
+ async function handleLineage(args) {
114
+ if (!args[0]) return listLinks([]);
115
+ if (args[1] === "valuation") return showValuation(args);
116
+ if (args[1] === "payout") return payoutPreview([args[0], args[2]]);
117
+ // If first arg is a number, treat as limit
118
+ if (/^\d+$/.test(args[0])) return listLinks(args);
119
+ return showLink(args);
120
+ }
121
+
122
+ async function handleGovernance(args) {
123
+ const sub = args[0];
124
+ switch (sub) {
125
+ case "vote": return vote(args.slice(1));
126
+ case "propose": return propose(args.slice(1));
127
+ case undefined: return listChangeRequests();
128
+ default: return showChangeRequest(args);
129
+ }
130
+ }
131
+
132
+ async function handleServices(args) {
133
+ const sub = args[0];
134
+ switch (sub) {
135
+ case "health": return showServicesHealth();
136
+ case "deps": return showServicesDeps();
137
+ default: return listServices();
138
+ }
139
+ }
140
+
141
+ async function handleFriction(args) {
142
+ const sub = args[0];
143
+ switch (sub) {
144
+ case "events": return listFrictionEvents(args.slice(1));
145
+ case "categories": return showFrictionCategories();
146
+ default: return showFrictionReport();
147
+ }
148
+ }
149
+
150
+ async function handleProviders(args) {
151
+ const sub = args[0];
152
+ switch (sub) {
153
+ case "stats": return showProviderStats();
154
+ default: return listProviders();
155
+ }
156
+ }
157
+
158
+ async function handleTrace(args) {
159
+ const sub = args[0];
160
+ switch (sub) {
161
+ case "coverage": return showCoverage();
162
+ case "idea": return traceIdea(args.slice(1));
163
+ case "spec": return traceSpec(args.slice(1));
164
+ default: return showTraceability();
165
+ }
166
+ }
167
+
168
+ async function handleDiag(args) {
169
+ const sub = args[0];
170
+ switch (sub) {
171
+ case "health": return showDiagHealth();
172
+ case "issues": return showDiagIssues();
173
+ case "runners": return showDiagRunners();
174
+ case "visibility": return showDiagVisibility();
175
+ default: return showDiag();
128
176
  }
129
177
  }
130
178
 
@@ -158,31 +206,72 @@ function showHelp() {
158
206
  identity unlink <p> Unlink a provider
159
207
  identity lookup <p> <id> Find contributor by identity
160
208
 
161
- \x1b[1mTasks (agent-to-agent):\x1b[0m
162
- tasks [status] [limit] List tasks (pending|running|completed)
163
- task <id> View task detail
164
- task next Claim next pending task (for AI agents)
165
- task claim <id> Claim a specific task
166
- task report <id> <status> [output] Report result (completed|failed)
167
- task seed <idea> [type] Create task from idea (spec|test|impl|review)
168
-
169
209
  \x1b[1mFederation:\x1b[0m
170
210
  nodes List federation nodes
171
211
  msg <node|broadcast> <text> Send message to a node
172
- cmd <node> <command> Remote command (update|status|diagnose|restart|ping)
173
212
  inbox Read your messages
174
213
 
175
- \x1b[1mSystem:\x1b[0m
176
- update Self-update to latest npm version
177
- version Show current version
178
- help Show this help
214
+ \x1b[1mContributors:\x1b[0m
215
+ contributors [limit] List contributors
216
+ contributor <id> View contributor detail
217
+ contributor <id> contributions View contributions
218
+
219
+ \x1b[1mAssets:\x1b[0m
220
+ assets [limit] List assets
221
+ asset <id> View asset detail
222
+ asset create <type> <desc> Create an asset
223
+
224
+ \x1b[1mNews:\x1b[0m
225
+ news News feed
226
+ news trending Trending news
227
+ news sources List news sources
228
+ news source add <url> <name> Add a news source
229
+ news resonance [contributor] News resonance
230
+
231
+ \x1b[1mTreasury:\x1b[0m
232
+ treasury Treasury overview
233
+ treasury deposits <id> Deposits for contributor
234
+ treasury deposit <amt> <asset> Make a deposit
235
+
236
+ \x1b[1mLineage:\x1b[0m
237
+ lineage [limit] Value lineage links
238
+ lineage <id> View lineage link
239
+ lineage <id> valuation Link valuation
240
+ lineage <id> payout <amt> Payout preview
241
+
242
+ \x1b[1mGovernance:\x1b[0m
243
+ governance List change requests
244
+ governance <id> View change request
245
+ governance vote <id> <yes|no> Vote on change request
246
+ governance propose <title> <desc> Create proposal
247
+
248
+ \x1b[1mServices:\x1b[0m
249
+ services List services
250
+ service <id> View service detail
251
+ services health Services health check
252
+ services deps Service dependencies
253
+
254
+ \x1b[1mFriction:\x1b[0m
255
+ friction Friction report
256
+ friction events [limit] Friction events
257
+ friction categories Friction categories
179
258
 
180
259
  \x1b[1mProviders:\x1b[0m
181
- github, x, discord, telegram, mastodon, bluesky, linkedin, reddit,
182
- youtube, twitch, instagram, tiktok, gitlab, bitbucket, npm, crates,
183
- pypi, hackernews, stackoverflow, ethereum, bitcoin, solana, cosmos,
184
- nostr, ens, lens, email, google, apple, microsoft, orcid, did,
185
- keybase, pgp, fediverse, openclaw
260
+ providers List providers
261
+ providers stats Provider statistics
262
+
263
+ \x1b[1mTraceability:\x1b[0m
264
+ trace Traceability overview
265
+ trace coverage Traceability coverage
266
+ trace idea <id> Trace an idea
267
+ trace spec <id> Trace a spec
268
+
269
+ \x1b[1mDiagnostics:\x1b[0m
270
+ diag Agent effectiveness + pipeline
271
+ diag health Collective health
272
+ diag issues Fatal + monitor issues
273
+ diag runners Agent runners
274
+ diag visibility Agent visibility
186
275
 
187
276
  \x1b[2mHub: https://api.coherencycoin.com\x1b[0m
188
277
  `);
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Assets commands: assets, asset
3
+ */
4
+
5
+ import { get, post } from "../api.mjs";
6
+
7
+ function truncate(str, len) {
8
+ if (!str) return "";
9
+ return str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
10
+ }
11
+
12
+ export async function listAssets(args) {
13
+ const limit = parseInt(args[0]) || 20;
14
+ const raw = await get("/api/assets", { limit });
15
+ const data = Array.isArray(raw) ? raw : raw?.assets;
16
+ if (!data || !Array.isArray(data)) {
17
+ console.log("Could not fetch assets.");
18
+ return;
19
+ }
20
+ if (data.length === 0) {
21
+ console.log("No assets found.");
22
+ return;
23
+ }
24
+
25
+ console.log();
26
+ console.log(`\x1b[1m ASSETS\x1b[0m (${data.length})`);
27
+ console.log(` ${"─".repeat(60)}`);
28
+ for (const a of data) {
29
+ const name = truncate(a.name || a.description || a.id, 35);
30
+ const type = (a.type || a.asset_type || "").padEnd(12);
31
+ console.log(` ${type} ${name}`);
32
+ }
33
+ console.log();
34
+ }
35
+
36
+ export async function showAsset(args) {
37
+ const id = args[0];
38
+ if (!id) {
39
+ console.log("Usage: cc asset <id>");
40
+ return;
41
+ }
42
+ const data = await get(`/api/assets/${encodeURIComponent(id)}`);
43
+ if (!data) {
44
+ console.log(`Asset '${id}' not found.`);
45
+ return;
46
+ }
47
+ console.log();
48
+ console.log(`\x1b[1m ${data.name || data.id}\x1b[0m`);
49
+ console.log(` ${"─".repeat(50)}`);
50
+ if (data.type || data.asset_type) console.log(` Type: ${data.type || data.asset_type}`);
51
+ if (data.description) console.log(` Description: ${truncate(data.description, 60)}`);
52
+ if (data.value != null) console.log(` Value: ${data.value}`);
53
+ if (data.created_at) console.log(` Created: ${data.created_at}`);
54
+ console.log();
55
+ }
56
+
57
+ export async function createAsset(args) {
58
+ const type = args[0];
59
+ const desc = args.slice(1).join(" ");
60
+ if (!type || !desc) {
61
+ console.log("Usage: cc asset create <type> <description>");
62
+ return;
63
+ }
64
+ const result = await post("/api/assets", {
65
+ type,
66
+ description: desc,
67
+ });
68
+ if (result) {
69
+ console.log(`\x1b[32m✓\x1b[0m Asset created: ${result.id || "(new)"}`);
70
+ } else {
71
+ console.log("Failed to create asset.");
72
+ }
73
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Contributors commands: contributors, contributor
3
+ */
4
+
5
+ import { get } from "../api.mjs";
6
+
7
+ function truncate(str, len) {
8
+ if (!str) return "";
9
+ return str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
10
+ }
11
+
12
+ export async function listContributors(args) {
13
+ const limit = parseInt(args[0]) || 20;
14
+ const raw = await get("/api/contributors", { limit });
15
+ const data = Array.isArray(raw) ? raw : raw?.contributors;
16
+ if (!data || !Array.isArray(data)) {
17
+ console.log("Could not fetch contributors.");
18
+ return;
19
+ }
20
+ if (data.length === 0) {
21
+ console.log("No contributors found.");
22
+ return;
23
+ }
24
+
25
+ console.log();
26
+ console.log(`\x1b[1m CONTRIBUTORS\x1b[0m (${data.length})`);
27
+ console.log(` ${"─".repeat(60)}`);
28
+ for (const c of data) {
29
+ const name = truncate(c.name || c.display_name || c.id, 30);
30
+ const cc = c.total_cc != null ? `${c.total_cc} CC` : "";
31
+ console.log(` ${name.padEnd(32)} ${cc}`);
32
+ }
33
+ console.log();
34
+ }
35
+
36
+ export async function showContributor(args) {
37
+ const id = args[0];
38
+ if (!id) {
39
+ console.log("Usage: cc contributor <id>");
40
+ return;
41
+ }
42
+ const data = await get(`/api/contributors/${encodeURIComponent(id)}`);
43
+ if (!data) {
44
+ console.log(`Contributor '${id}' not found.`);
45
+ return;
46
+ }
47
+ console.log();
48
+ console.log(`\x1b[1m ${data.name || data.display_name || data.id}\x1b[0m`);
49
+ console.log(` ${"─".repeat(50)}`);
50
+ if (data.id) console.log(` ID: ${data.id}`);
51
+ if (data.total_cc != null) console.log(` Total CC: ${data.total_cc}`);
52
+ if (data.role) console.log(` Role: ${data.role}`);
53
+ if (data.joined_at) console.log(` Joined: ${data.joined_at}`);
54
+ console.log();
55
+ }
56
+
57
+ export async function showContributions(args) {
58
+ const id = args[0];
59
+ if (!id) {
60
+ console.log("Usage: cc contributor <id> contributions");
61
+ return;
62
+ }
63
+ const data = await get(`/api/contributors/${encodeURIComponent(id)}/contributions`);
64
+ const list = Array.isArray(data) ? data : data?.contributions;
65
+ if (!list || !Array.isArray(list)) {
66
+ console.log(`No contributions found for '${id}'.`);
67
+ return;
68
+ }
69
+
70
+ console.log();
71
+ console.log(`\x1b[1m CONTRIBUTIONS\x1b[0m for ${id} (${list.length})`);
72
+ console.log(` ${"─".repeat(60)}`);
73
+ for (const c of list) {
74
+ const desc = truncate(c.description || c.type || "?", 45);
75
+ const cc = c.cc_amount != null ? `${c.cc_amount} CC` : "";
76
+ console.log(` ${desc.padEnd(47)} ${cc}`);
77
+ }
78
+ console.log();
79
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Diagnostics commands: diag, diag health, diag issues, diag runners, diag visibility
3
+ */
4
+
5
+ import { get } from "../api.mjs";
6
+
7
+ function truncate(str, len) {
8
+ if (!str) return "";
9
+ return str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
10
+ }
11
+
12
+ export async function showDiag() {
13
+ const [effectiveness, pipeline] = await Promise.all([
14
+ get("/api/agent/effectiveness"),
15
+ get("/api/agent/pipeline-status"),
16
+ ]);
17
+
18
+ console.log();
19
+ console.log(`\x1b[1m DIAGNOSTICS\x1b[0m`);
20
+ console.log(` ${"─".repeat(50)}`);
21
+
22
+ if (effectiveness) {
23
+ console.log(` \x1b[1mEffectiveness:\x1b[0m`);
24
+ for (const [key, val] of Object.entries(effectiveness)) {
25
+ console.log(` ${key}: ${JSON.stringify(val)}`);
26
+ }
27
+ } else {
28
+ console.log(` Effectiveness: \x1b[2munavailable\x1b[0m`);
29
+ }
30
+
31
+ if (pipeline) {
32
+ console.log(` \x1b[1mPipeline Status:\x1b[0m`);
33
+ for (const [key, val] of Object.entries(pipeline)) {
34
+ console.log(` ${key}: ${JSON.stringify(val)}`);
35
+ }
36
+ } else {
37
+ console.log(` Pipeline Status: \x1b[2munavailable\x1b[0m`);
38
+ }
39
+ console.log();
40
+ }
41
+
42
+ export async function showDiagHealth() {
43
+ const data = await get("/api/agent/collective-health");
44
+ if (!data) {
45
+ console.log("Could not fetch collective health.");
46
+ return;
47
+ }
48
+
49
+ console.log();
50
+ console.log(`\x1b[1m COLLECTIVE HEALTH\x1b[0m`);
51
+ console.log(` ${"─".repeat(50)}`);
52
+ for (const [key, val] of Object.entries(data)) {
53
+ console.log(` ${key}: ${JSON.stringify(val)}`);
54
+ }
55
+ console.log();
56
+ }
57
+
58
+ export async function showDiagIssues() {
59
+ const [fatal, monitor] = await Promise.all([
60
+ get("/api/agent/fatal-issues"),
61
+ get("/api/agent/monitor-issues"),
62
+ ]);
63
+
64
+ console.log();
65
+ console.log(`\x1b[1m ISSUES\x1b[0m`);
66
+ console.log(` ${"─".repeat(50)}`);
67
+
68
+ const fatalList = Array.isArray(fatal) ? fatal : fatal?.issues || [];
69
+ const monitorList = Array.isArray(monitor) ? monitor : monitor?.issues || [];
70
+
71
+ if (fatalList.length > 0) {
72
+ console.log(` \x1b[31mFatal (${fatalList.length}):\x1b[0m`);
73
+ for (const i of fatalList) {
74
+ const desc = truncate(i.description || i.message || i.id || JSON.stringify(i), 60);
75
+ console.log(` \x1b[31m✗\x1b[0m ${desc}`);
76
+ }
77
+ } else {
78
+ console.log(` \x1b[32mNo fatal issues\x1b[0m`);
79
+ }
80
+
81
+ if (monitorList.length > 0) {
82
+ console.log(` \x1b[33mMonitor (${monitorList.length}):\x1b[0m`);
83
+ for (const i of monitorList) {
84
+ const desc = truncate(i.description || i.message || i.id || JSON.stringify(i), 60);
85
+ console.log(` \x1b[33m!\x1b[0m ${desc}`);
86
+ }
87
+ } else {
88
+ console.log(` \x1b[32mNo monitor issues\x1b[0m`);
89
+ }
90
+ console.log();
91
+ }
92
+
93
+ export async function showDiagRunners() {
94
+ const raw = await get("/api/agent/runners");
95
+ const data = Array.isArray(raw) ? raw : raw?.runners;
96
+ if (!data || !Array.isArray(data)) {
97
+ console.log("Could not fetch runners.");
98
+ return;
99
+ }
100
+ if (data.length === 0) {
101
+ console.log("No runners found.");
102
+ return;
103
+ }
104
+
105
+ console.log();
106
+ console.log(`\x1b[1m RUNNERS\x1b[0m (${data.length})`);
107
+ console.log(` ${"─".repeat(50)}`);
108
+ for (const r of data) {
109
+ const name = truncate(r.name || r.id || "?", 25);
110
+ const status = r.status || "?";
111
+ const dot = status === "active" || status === "running" ? "\x1b[32m●\x1b[0m" : "\x1b[2m○\x1b[0m";
112
+ console.log(` ${dot} ${name.padEnd(27)} ${status}`);
113
+ }
114
+ console.log();
115
+ }
116
+
117
+ export async function showDiagVisibility() {
118
+ const data = await get("/api/agent/visibility");
119
+ if (!data) {
120
+ console.log("Could not fetch visibility.");
121
+ return;
122
+ }
123
+
124
+ console.log();
125
+ console.log(`\x1b[1m VISIBILITY\x1b[0m`);
126
+ console.log(` ${"─".repeat(50)}`);
127
+ for (const [key, val] of Object.entries(data)) {
128
+ console.log(` ${key}: ${JSON.stringify(val)}`);
129
+ }
130
+ console.log();
131
+ }