@bli-cockpit/cli 0.2.137 → 0.2.140
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/commands/docs-read.js +197 -0
- package/dist/commands/docs.js +26 -32
- package/dist/commands/issue-directory.js +63 -0
- package/dist/commands/issue-show.js +147 -0
- package/dist/commands/issue.js +54 -71
- package/dist/commands/local-args-tower-admin.js +25 -0
- package/dist/commands/local-args-tower-docs-msg.js +95 -12
- package/dist/commands/local-args-tower-work.js +17 -5
- package/dist/commands/local-help-commands-tower.js +20 -14
- package/dist/commands/local-help-commands.js +14 -2
- package/dist/commands/local-help.js +3 -2
- package/dist/commands/local.js +8 -0
- package/dist/commands/mail.js +5 -1
- package/dist/commands/memory-store.js +11 -1
- package/dist/commands/msg.js +14 -3
- package/dist/commands/notes-reads.js +29 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/slack.js +59 -15
- package/dist/tower-client.js +29 -7
- package/package.json +3 -3
package/dist/commands/slack.js
CHANGED
|
@@ -29,9 +29,15 @@ export async function runSlack(command, io) {
|
|
|
29
29
|
: runReadAction(command, io, tower);
|
|
30
30
|
}
|
|
31
31
|
async function runCoverageAction(command, io, tower) {
|
|
32
|
-
const
|
|
32
|
+
const params = new URLSearchParams();
|
|
33
|
+
if (command.workspace)
|
|
34
|
+
params.set("workspace", command.workspace);
|
|
35
|
+
// BLI-10450: counts alone could not answer "is there a Kaiben channel?".
|
|
36
|
+
if (command.list)
|
|
37
|
+
params.set("list", "1");
|
|
38
|
+
const query = params.toString();
|
|
33
39
|
const result = await callTower(tower, {
|
|
34
|
-
path: `/api/ops/slack/coverage${query}`,
|
|
40
|
+
path: `/api/ops/slack/coverage${query ? `?${query}` : ""}`,
|
|
35
41
|
label: "slack-coverage",
|
|
36
42
|
});
|
|
37
43
|
if (!result.ok) {
|
|
@@ -58,7 +64,7 @@ function writeCoverageReply(command, io, body, coverage, workspaces) {
|
|
|
58
64
|
return;
|
|
59
65
|
}
|
|
60
66
|
const styled = colorEnabled(io);
|
|
61
|
-
for (const line of coverageLines(coverage, workspaces, command.staleOnly === true, (text) => dim(text, styled))) {
|
|
67
|
+
for (const line of coverageLines(coverage, workspaces, command.staleOnly === true, command.list === true, (text) => dim(text, styled))) {
|
|
62
68
|
writeLine(io.stdout, line);
|
|
63
69
|
}
|
|
64
70
|
}
|
|
@@ -71,27 +77,43 @@ function logCoverageRead(command, io, workspaces) {
|
|
|
71
77
|
stale: staleTotal,
|
|
72
78
|
open_skips: workspaces.reduce((sum, row) => sum + numberOf(row["openSkipsTotal"]), 0),
|
|
73
79
|
stale_only: command.staleOnly === true,
|
|
80
|
+
list: command.list === true,
|
|
74
81
|
})}`);
|
|
75
82
|
}
|
|
76
|
-
function coverageLines(coverage, workspaces, staleOnly, paint) {
|
|
83
|
+
function coverageLines(coverage, workspaces, staleOnly, list, paint) {
|
|
77
84
|
const lines = [];
|
|
78
85
|
const staleAfter = numberOf(coverage["staleAfterHours"]);
|
|
79
86
|
for (const workspace of workspaces) {
|
|
80
|
-
writeWorkspaceCoverageLines(lines, workspace, staleAfter, staleOnly, paint);
|
|
87
|
+
writeWorkspaceCoverageLines(lines, workspace, staleAfter, staleOnly, list, paint);
|
|
81
88
|
}
|
|
82
89
|
writeCoverageFooter(lines, coverage, paint);
|
|
83
90
|
return lines;
|
|
84
91
|
}
|
|
85
|
-
function writeWorkspaceCoverageLines(lines, workspace, staleAfter, staleOnly, paint) {
|
|
92
|
+
function writeWorkspaceCoverageLines(lines, workspace, staleAfter, staleOnly, list, paint) {
|
|
86
93
|
const key = String(workspace["workspace"] ?? "?");
|
|
87
94
|
const known = numberOf(workspace["channelsKnown"]);
|
|
88
95
|
const covered = numberOf(workspace["covered"]);
|
|
89
96
|
lines.push(`${key.toUpperCase()} ${covered} of ${known} channels readable`);
|
|
90
97
|
writeVisibilityLines(lines, workspace, staleOnly, paint);
|
|
98
|
+
if (list)
|
|
99
|
+
writeChannelListLines(lines, workspace, paint);
|
|
91
100
|
writeStaleChannelLines(lines, workspace, staleAfter, paint);
|
|
92
101
|
writeOpenSkipLines(lines, workspace, paint);
|
|
93
102
|
lines.push("");
|
|
94
103
|
}
|
|
104
|
+
/** BLI-10450: `--list` names every channel, not just the per-state counts. */
|
|
105
|
+
function writeChannelListLines(lines, workspace, paint) {
|
|
106
|
+
const channels = Array.isArray(workspace["channels"])
|
|
107
|
+
? workspace["channels"]
|
|
108
|
+
: [];
|
|
109
|
+
if (channels.length === 0)
|
|
110
|
+
return;
|
|
111
|
+
for (const channel of channels) {
|
|
112
|
+
const meaning = String(channel["meaning"] ?? "?");
|
|
113
|
+
const line = ` #${String(channel["name"] ?? "?")} ${meaning}`;
|
|
114
|
+
lines.push(meaning === "readable" ? line : paint(line));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
95
117
|
function writeCoverageFooter(lines, coverage, paint) {
|
|
96
118
|
const orphanChannels = numberOf(coverage["orphanChannels"]);
|
|
97
119
|
if (orphanChannels > 0) {
|
|
@@ -175,6 +197,8 @@ function buildReadBody(command) {
|
|
|
175
197
|
body["channel"] = command.channel;
|
|
176
198
|
if (command.query)
|
|
177
199
|
body["query"] = command.query;
|
|
200
|
+
if (command.query && command.word)
|
|
201
|
+
body["word"] = true;
|
|
178
202
|
if (command.since)
|
|
179
203
|
body["since"] = command.since;
|
|
180
204
|
if (command.until)
|
|
@@ -183,16 +207,32 @@ function buildReadBody(command) {
|
|
|
183
207
|
body["limit"] = command.limit;
|
|
184
208
|
return body;
|
|
185
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* BLI-10450: every result used to print twice. `read["summary"]` is built by
|
|
212
|
+
* `apps/dashboard/src/lib/jarvis/chat-v2/tools-slack-render.ts` `renderSummary`
|
|
213
|
+
* for the JARVIS chat tool, which needs ONE self-contained string — so it
|
|
214
|
+
* already embeds a header, one bullet line per message, AND the note. This
|
|
215
|
+
* CLI then rendered the SAME messages a second time as full blocks and
|
|
216
|
+
* appended the note a THIRD time. Only the header (summary's first line) is
|
|
217
|
+
* still printed verbatim, because it is the sentence that keeps an empty
|
|
218
|
+
* answer from reading as "nobody said anything"; the messages are rendered
|
|
219
|
+
* exactly once, in the format the caller asked for, and the note prints once.
|
|
220
|
+
*/
|
|
186
221
|
function writeReadReply(command, io, payload, read, messages) {
|
|
187
222
|
if (command.json) {
|
|
188
223
|
writeLine(io.stdout, JSON.stringify(payload));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const styled = colorEnabled(io);
|
|
227
|
+
const summary = typeof read["summary"] === "string" ? read["summary"] : "";
|
|
228
|
+
const header = summary.split("\n")[0] ?? "";
|
|
229
|
+
if (header)
|
|
230
|
+
writeLine(io.stdout, header);
|
|
231
|
+
if (command.format === "compact") {
|
|
232
|
+
for (const message of messages)
|
|
233
|
+
writeLine(io.stdout, compactMessageLine(message));
|
|
189
234
|
}
|
|
190
235
|
else {
|
|
191
|
-
const styled = colorEnabled(io);
|
|
192
|
-
// The server's own summary, first and verbatim: it is the sentence that
|
|
193
|
-
// keeps an empty answer from reading as "nobody said anything".
|
|
194
|
-
if (typeof read["summary"] === "string")
|
|
195
|
-
writeLine(io.stdout, read["summary"]);
|
|
196
236
|
if (messages.length > 0)
|
|
197
237
|
writeLine(io.stdout, "");
|
|
198
238
|
for (const message of messages) {
|
|
@@ -202,11 +242,15 @@ function writeReadReply(command, io, payload, read, messages) {
|
|
|
202
242
|
writeLine(io.stdout, dim(` ${message["permalink"]}`, styled));
|
|
203
243
|
}
|
|
204
244
|
}
|
|
205
|
-
if (typeof read["note"] === "string" && read["note"]) {
|
|
206
|
-
writeLine(io.stdout, "");
|
|
207
|
-
writeLine(io.stdout, dim(read["note"], styled));
|
|
208
|
-
}
|
|
209
245
|
}
|
|
246
|
+
if (typeof read["note"] === "string" && read["note"]) {
|
|
247
|
+
writeLine(io.stdout, "");
|
|
248
|
+
writeLine(io.stdout, dim(read["note"], styled));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function compactMessageLine(message) {
|
|
252
|
+
return `- #${String(message["channel"] ?? "?")} — ${String(message["author"] ?? "?")} — `
|
|
253
|
+
+ `${String(message["messageTs"] ?? "?")} — "${String(message["text"] ?? "")}" — ${String(message["permalink"] ?? "")}`;
|
|
210
254
|
}
|
|
211
255
|
function logReadResult(command, io, read, messages) {
|
|
212
256
|
const coverage = asRecord(read["coverage"]);
|
package/dist/tower-client.js
CHANGED
|
@@ -29,6 +29,20 @@ import { normalizeDashboardUrl, readResponseJson, responseErrorMessage, } from "
|
|
|
29
29
|
const defaultLog = (line) => {
|
|
30
30
|
console.error(line);
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* BLI-10450: every call printed `[tower client] responded {...}` on stderr,
|
|
34
|
+
* which mixed into content whenever a caller redirected `2>&1` — the exact
|
|
35
|
+
* shape an agent piping `cockpit docs read ... 2>&1` hit. The line is a
|
|
36
|
+
* per-request trace, not a decision (the failure branch below is the one the
|
|
37
|
+
* repo's "never fail silently" rule actually protects), so it is the one
|
|
38
|
+
* allowed to go quiet: under `--json` (the line is never meant for a person
|
|
39
|
+
* reading machine output), a bare `--quiet` (stripped by the router in
|
|
40
|
+
* `local.ts` and mirrored onto `COCKPIT_QUIET` so this module needs no extra
|
|
41
|
+
* plumbing), or the env var directly.
|
|
42
|
+
*/
|
|
43
|
+
export function shouldSuppressTowerSuccessLog(argv = process.argv) {
|
|
44
|
+
return argv.includes("--json") || Boolean(process.env.COCKPIT_QUIET);
|
|
45
|
+
}
|
|
32
46
|
/**
|
|
33
47
|
* Loads the paired device session, or explains in one sentence why it cannot.
|
|
34
48
|
*
|
|
@@ -82,13 +96,21 @@ export async function towerRequest(options) {
|
|
|
82
96
|
try {
|
|
83
97
|
const response = await options.fetch(url, init);
|
|
84
98
|
const respondedMs = Date.now() - startedAt;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
// The success branch always decided something (BLI-10450 kept the
|
|
100
|
+
// "log the success branch too" rule intact) — it is just that under
|
|
101
|
+
// `--json`/`--quiet` the reader already gets that decision back as the
|
|
102
|
+
// command's own stdout object, so a second copy on stderr is noise, not
|
|
103
|
+
// signal. The failure branch below never quiets: a request that reached
|
|
104
|
+
// no response at all is the one case AGENTS.md's logging rule protects.
|
|
105
|
+
if (!shouldSuppressTowerSuccessLog()) {
|
|
106
|
+
log(`[tower client] responded ${JSON.stringify({
|
|
107
|
+
request: options.label,
|
|
108
|
+
http_status: response.status,
|
|
109
|
+
content_type: response.headers.get("content-type") ?? "none",
|
|
110
|
+
streamed: (response.headers.get("content-type") ?? "").includes("application/x-ndjson"),
|
|
111
|
+
elapsed_ms: respondedMs,
|
|
112
|
+
})}`);
|
|
113
|
+
}
|
|
92
114
|
return { ok: true, response, startedAt, respondedMs };
|
|
93
115
|
}
|
|
94
116
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.140",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-verb-help.mjs && node ../../scripts/assert-public-cli-surface-doors.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-exit-contract.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@bli-cockpit/memory-mcp": "0.1.
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
30
|
+
"@bli-cockpit/memory-mcp": "0.1.36",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.65",
|
|
32
32
|
"@bli-cockpit/telemetry-core": "0.1.48"
|
|
33
33
|
}
|
|
34
34
|
}
|