fullstackgtm 0.43.0 → 0.45.0

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 (130) hide show
  1. package/CHANGELOG.md +378 -1
  2. package/README.md +37 -14
  3. package/dist/audit.d.ts +3 -1
  4. package/dist/audit.js +29 -2
  5. package/dist/cli/audit.d.ts +15 -0
  6. package/dist/cli/audit.js +392 -0
  7. package/dist/cli/auth.d.ts +80 -0
  8. package/dist/cli/auth.js +500 -0
  9. package/dist/cli/call.d.ts +1 -0
  10. package/dist/cli/call.js +373 -0
  11. package/dist/cli/capabilities.d.ts +30 -0
  12. package/dist/cli/capabilities.js +197 -0
  13. package/dist/cli/draft.d.ts +7 -0
  14. package/dist/cli/draft.js +87 -0
  15. package/dist/cli/enrich.d.ts +8 -0
  16. package/dist/cli/enrich.js +788 -0
  17. package/dist/cli/fix.d.ts +33 -0
  18. package/dist/cli/fix.js +344 -0
  19. package/dist/cli/help.d.ts +25 -0
  20. package/dist/cli/help.js +609 -0
  21. package/dist/cli/icp.d.ts +7 -0
  22. package/dist/cli/icp.js +229 -0
  23. package/dist/cli/init.d.ts +7 -0
  24. package/dist/cli/init.js +58 -0
  25. package/dist/cli/market.d.ts +1 -0
  26. package/dist/cli/market.js +391 -0
  27. package/dist/cli/plans.d.ts +5 -0
  28. package/dist/cli/plans.js +454 -0
  29. package/dist/cli/schedule.d.ts +8 -0
  30. package/dist/cli/schedule.js +479 -0
  31. package/dist/cli/shared.d.ts +70 -0
  32. package/dist/cli/shared.js +331 -0
  33. package/dist/cli/signals.d.ts +7 -0
  34. package/dist/cli/signals.js +412 -0
  35. package/dist/cli/suggest.d.ts +49 -0
  36. package/dist/cli/suggest.js +135 -0
  37. package/dist/cli/tam.d.ts +9 -0
  38. package/dist/cli/tam.js +387 -0
  39. package/dist/cli/ui.d.ts +121 -0
  40. package/dist/cli/ui.js +375 -0
  41. package/dist/cli.d.ts +1 -57
  42. package/dist/cli.js +104 -4556
  43. package/dist/connector.d.ts +15 -0
  44. package/dist/connector.js +35 -0
  45. package/dist/connectors/hubspot.d.ts +3 -1
  46. package/dist/connectors/hubspot.js +10 -3
  47. package/dist/connectors/outboxChannel.d.ts +64 -0
  48. package/dist/connectors/outboxChannel.js +170 -0
  49. package/dist/connectors/prospectSources.d.ts +22 -0
  50. package/dist/connectors/prospectSources.js +43 -0
  51. package/dist/connectors/salesforce.d.ts +3 -1
  52. package/dist/connectors/salesforce.js +12 -5
  53. package/dist/connectors/signalSources.d.ts +105 -0
  54. package/dist/connectors/signalSources.js +264 -0
  55. package/dist/connectors/theirstack.d.ts +84 -0
  56. package/dist/connectors/theirstack.js +125 -0
  57. package/dist/icp.d.ts +52 -4
  58. package/dist/icp.js +112 -35
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +1 -0
  61. package/dist/init.js +3 -0
  62. package/dist/judge.d.ts +2 -0
  63. package/dist/judge.js +6 -0
  64. package/dist/judgeEval.d.ts +7 -0
  65. package/dist/judgeEval.js +8 -1
  66. package/dist/marketClassify.d.ts +2 -0
  67. package/dist/marketClassify.js +7 -1
  68. package/dist/mcp-bin.js +2 -2
  69. package/dist/mcp.js +259 -166
  70. package/dist/runReport.d.ts +26 -0
  71. package/dist/runReport.js +15 -0
  72. package/dist/schedule.d.ts +80 -2
  73. package/dist/schedule.js +272 -5
  74. package/dist/signals.d.ts +54 -0
  75. package/dist/signals.js +64 -0
  76. package/dist/spoolFiles.d.ts +44 -0
  77. package/dist/spoolFiles.js +114 -0
  78. package/dist/tam.d.ts +225 -0
  79. package/dist/tam.js +470 -0
  80. package/dist/types.d.ts +11 -0
  81. package/docs/api.md +91 -11
  82. package/docs/outbox-format.md +92 -0
  83. package/docs/recipes.md +37 -0
  84. package/docs/roadmap-to-1.0.md +31 -1
  85. package/docs/signal-spool-format.md +175 -0
  86. package/docs/tam.md +195 -0
  87. package/llms.txt +83 -11
  88. package/package.json +1 -1
  89. package/skills/fullstackgtm/SKILL.md +4 -3
  90. package/src/audit.ts +27 -1
  91. package/src/cli/audit.ts +447 -0
  92. package/src/cli/auth.ts +549 -0
  93. package/src/cli/call.ts +398 -0
  94. package/src/cli/capabilities.ts +215 -0
  95. package/src/cli/draft.ts +101 -0
  96. package/src/cli/enrich.ts +885 -0
  97. package/src/cli/fix.ts +372 -0
  98. package/src/cli/help.ts +664 -0
  99. package/src/cli/icp.ts +265 -0
  100. package/src/cli/init.ts +65 -0
  101. package/src/cli/market.ts +423 -0
  102. package/src/cli/plans.ts +523 -0
  103. package/src/cli/schedule.ts +526 -0
  104. package/src/cli/shared.ts +375 -0
  105. package/src/cli/signals.ts +434 -0
  106. package/src/cli/suggest.ts +151 -0
  107. package/src/cli/tam.ts +435 -0
  108. package/src/cli/ui.ts +426 -0
  109. package/src/cli.ts +103 -5170
  110. package/src/connector.ts +46 -0
  111. package/src/connectors/hubspot.ts +14 -2
  112. package/src/connectors/outboxChannel.ts +202 -0
  113. package/src/connectors/prospectSources.ts +57 -0
  114. package/src/connectors/salesforce.ts +18 -2
  115. package/src/connectors/signalSources.ts +314 -0
  116. package/src/connectors/theirstack.ts +170 -0
  117. package/src/icp.ts +120 -34
  118. package/src/index.ts +32 -0
  119. package/src/init.ts +3 -0
  120. package/src/judge.ts +7 -0
  121. package/src/judgeEval.ts +8 -1
  122. package/src/marketClassify.ts +8 -1
  123. package/src/mcp-bin.ts +2 -2
  124. package/src/mcp.ts +130 -57
  125. package/src/runReport.ts +39 -0
  126. package/src/schedule.ts +330 -7
  127. package/src/signals.ts +90 -0
  128. package/src/spoolFiles.ts +116 -0
  129. package/src/tam.ts +654 -0
  130. package/src/types.ts +12 -0
package/dist/cli/ui.js ADDED
@@ -0,0 +1,375 @@
1
+ // Terminal presentation layer — zero-dependency by design (this package ships
2
+ // with no runtime deps and holds CRM credentials; every avoided dependency is
3
+ // supply-chain surface we don't carry).
4
+ //
5
+ // The contract that keeps the agent surface safe:
6
+ // - stdout NEVER carries ANSI bytes unless stdout itself is an interactive
7
+ // TTY. Piped/redirected/--json output is byte-identical to the unstyled CLI.
8
+ // - Animation (spinners, progress, checklists) goes to stderr ONLY, and only
9
+ // when stderr is an interactive TTY. Non-TTY runs render nothing — no
10
+ // frame spam in CI logs or agent transcripts.
11
+ // - NO_COLOR (https://no-color.org), TERM=dumb, and CI disable styling;
12
+ // FORCE_COLOR (non-"0") re-enables it, matching the wider ecosystem.
13
+ /** Should `stream` receive color/ANSI styling at all? */
14
+ export function colorEnabled(stream = process.stdout, env = process.env) {
15
+ if (env.FORCE_COLOR === "0")
16
+ return false;
17
+ if (env.FORCE_COLOR !== undefined)
18
+ return true;
19
+ if (env.NO_COLOR !== undefined)
20
+ return false;
21
+ if (env.TERM === "dumb")
22
+ return false;
23
+ if (env.CI !== undefined)
24
+ return false;
25
+ return Boolean(stream?.isTTY);
26
+ }
27
+ /**
28
+ * Should `stream` receive live single-line repaints (spinners, bars)?
29
+ * Stricter than color: FORCE_COLOR opts into color codes, not into cursor
30
+ * animation — repaint sequences in a captured log are never what anyone wants.
31
+ */
32
+ export function animationEnabled(stream = process.stderr, env = process.env) {
33
+ if (env.NO_COLOR !== undefined)
34
+ return false;
35
+ if (env.TERM === "dumb")
36
+ return false;
37
+ if (env.CI !== undefined)
38
+ return false;
39
+ return Boolean(stream?.isTTY);
40
+ }
41
+ const CODES = {
42
+ reset: "\u001b[0m",
43
+ bold: "\u001b[1m",
44
+ dim: "\u001b[2m",
45
+ red: "\u001b[31m",
46
+ green: "\u001b[32m",
47
+ yellow: "\u001b[33m",
48
+ blue: "\u001b[34m",
49
+ magenta: "\u001b[35m",
50
+ cyan: "\u001b[36m",
51
+ };
52
+ /**
53
+ * Style functions for one stream: identity functions when styling is off, so
54
+ * call sites never branch. `paint(colorEnabled(process.stdout))`.
55
+ */
56
+ export function paint(enabled) {
57
+ const entry = (code) => enabled ? (text) => `${code}${text}${CODES.reset}` : (text) => text;
58
+ return {
59
+ enabled,
60
+ bold: entry(CODES.bold),
61
+ dim: entry(CODES.dim),
62
+ red: entry(CODES.red),
63
+ green: entry(CODES.green),
64
+ yellow: entry(CODES.yellow),
65
+ blue: entry(CODES.blue),
66
+ magenta: entry(CODES.magenta),
67
+ cyan: entry(CODES.cyan),
68
+ };
69
+ }
70
+ export const GLYPH = {
71
+ ok: "✓",
72
+ warn: "!",
73
+ fail: "✗",
74
+ pending: "○",
75
+ bullet: "●",
76
+ };
77
+ /** `✓` in green / `!` in yellow / `✗` in red — or the bare glyph unstyled. */
78
+ export function statusGlyph(state, p) {
79
+ if (state === "ok")
80
+ return p.green(GLYPH.ok);
81
+ if (state === "warn")
82
+ return p.yellow(GLYPH.warn);
83
+ if (state === "fail")
84
+ return p.red(GLYPH.fail);
85
+ return p.dim(GLYPH.pending);
86
+ }
87
+ /** Color banding for 0–100 scores: ≥80 green, ≥60 yellow, else red. */
88
+ export function scoreColor(score, p) {
89
+ const text = String(score);
90
+ if (score >= 80)
91
+ return p.green(text);
92
+ if (score >= 60)
93
+ return p.yellow(text);
94
+ return p.red(text);
95
+ }
96
+ const SPARKS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
97
+ /** Min–max normalized sparkline: [42, 51, 68, 81] → "▁▃▅█". */
98
+ export function sparkline(values) {
99
+ if (values.length === 0)
100
+ return "";
101
+ const min = Math.min(...values);
102
+ const max = Math.max(...values);
103
+ const span = max - min;
104
+ return values
105
+ .map((value) => {
106
+ const fraction = span === 0 ? 0.5 : (value - min) / span;
107
+ return SPARKS[Math.min(SPARKS.length - 1, Math.floor(fraction * SPARKS.length))];
108
+ })
109
+ .join("");
110
+ }
111
+ /** `formatBar(0.67)` → "▰▰▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱▱▱▱" (clamped to [0,1]). */
112
+ export function formatBar(fraction, width = 20) {
113
+ const clamped = Math.max(0, Math.min(1, fraction));
114
+ const filled = Math.round(clamped * width);
115
+ return "▰".repeat(filled) + "▱".repeat(width - filled);
116
+ }
117
+ /** 950 → "950ms"; 12_400 → "12s"; 84_000 → "1m 24s". */
118
+ export function formatDuration(ms) {
119
+ if (ms < 1000)
120
+ return `${Math.max(0, Math.round(ms))}ms`;
121
+ const seconds = Math.round(ms / 1000);
122
+ if (seconds < 60)
123
+ return `${seconds}s`;
124
+ const minutes = Math.floor(seconds / 60);
125
+ const rest = seconds % 60;
126
+ return `${minutes}m ${String(rest).padStart(2, "0")}s`;
127
+ }
128
+ /** 12345 → "12,345" (fixed en-US grouping — output must not vary by host locale). */
129
+ export function formatCount(value) {
130
+ return value.toLocaleString("en-US");
131
+ }
132
+ /**
133
+ * Rounded-corner frame around pre-rendered lines. `width` is measured on the
134
+ * raw text, so style the CONTENT only after framing (or pass unstyled lines) —
135
+ * ANSI codes inside `lines` would inflate the measured width.
136
+ */
137
+ export function box(lines, p, title) {
138
+ const inner = Math.max(...lines.map((line) => line.length), title ? title.length + 2 : 0);
139
+ const top = title
140
+ ? `╭─ ${title} ${"─".repeat(Math.max(0, inner - title.length - 1))}╮`
141
+ : `╭${"─".repeat(inner + 2)}╮`;
142
+ const bottom = `╰${"─".repeat(inner + 2)}╯`;
143
+ return [
144
+ p.dim(top),
145
+ ...lines.map((line) => `${p.dim("│")} ${line.padEnd(inner)} ${p.dim("│")}`),
146
+ p.dim(bottom),
147
+ ];
148
+ }
149
+ /**
150
+ * Left-aligned columns with two-space gutters. Rows may be ragged; widths are
151
+ * measured on raw cell text, so pass unstyled cells and style whole lines, or
152
+ * style via the optional per-column painters (applied AFTER padding).
153
+ */
154
+ export function table(rows, painters) {
155
+ const widths = [];
156
+ for (const row of rows) {
157
+ row.forEach((cell, index) => {
158
+ widths[index] = Math.max(widths[index] ?? 0, cell.length);
159
+ });
160
+ }
161
+ return rows.map((row) => row
162
+ .map((cell, index) => {
163
+ const padded = index === row.length - 1 ? cell : cell.padEnd(widths[index]);
164
+ const painterFn = painters?.[index];
165
+ return painterFn ? painterFn(padded) : padded;
166
+ })
167
+ .join(" ")
168
+ .trimEnd());
169
+ }
170
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
171
+ const SPINNER_INTERVAL_MS = 80;
172
+ const CLEAR_LINE = "\r\u001b[2K";
173
+ const NOOP_STATUS = { set() { }, done() { }, active: false };
174
+ /**
175
+ * One live spinner line on stderr, repainted in place. Returns an inert no-op
176
+ * outside an interactive TTY, so call sites never branch. `done()` always
177
+ * clears the animation; pass `finalText` to leave a plain summary line behind.
178
+ */
179
+ export function createStatusLine(stream = process.stderr, env = process.env) {
180
+ if (!animationEnabled(stream, env))
181
+ return NOOP_STATUS;
182
+ let text = "";
183
+ let frame = 0;
184
+ let timer = null;
185
+ const render = () => {
186
+ const columns = stream.columns ?? 80;
187
+ const line = `${SPINNER_FRAMES[frame % SPINNER_FRAMES.length]} ${text}`;
188
+ stream.write(CLEAR_LINE + (line.length >= columns ? `${line.slice(0, columns - 2)}…` : line));
189
+ };
190
+ const status = {
191
+ set(next) {
192
+ text = next;
193
+ if (!timer) {
194
+ // The timer only advances the spinner frame; text changes repaint
195
+ // immediately so progress is never a tick behind the work.
196
+ timer = setInterval(() => {
197
+ frame += 1;
198
+ render();
199
+ }, SPINNER_INTERVAL_MS);
200
+ // Never hold the event loop open: a forgotten spinner must not stop
201
+ // the CLI from exiting.
202
+ timer.unref?.();
203
+ }
204
+ render();
205
+ },
206
+ done(finalText) {
207
+ if (timer)
208
+ clearInterval(timer);
209
+ timer = null;
210
+ stream.write(CLEAR_LINE);
211
+ if (finalText !== undefined)
212
+ stream.write(`${finalText}\n`);
213
+ },
214
+ active: true,
215
+ };
216
+ return status;
217
+ }
218
+ /**
219
+ * A status line that re-renders itself every second with the elapsed time —
220
+ * for single long waits (an LLM call) where nothing else ticks. `label`
221
+ * receives the formatted elapsed time. Inert outside an interactive TTY.
222
+ */
223
+ export function startElapsedStatus(label, stream = process.stderr, env = process.env) {
224
+ const status = createStatusLine(stream, env);
225
+ if (!status.active)
226
+ return { done() { }, active: false };
227
+ const started = Date.now();
228
+ const refresh = () => status.set(label(formatDuration(Date.now() - started)));
229
+ refresh();
230
+ const timer = setInterval(refresh, 1000);
231
+ timer.unref?.();
232
+ return {
233
+ done(finalText) {
234
+ clearInterval(timer);
235
+ status.done(finalText);
236
+ },
237
+ active: true,
238
+ };
239
+ }
240
+ const NOOP_CHECKLIST = { update() { }, done() { }, active: false };
241
+ /**
242
+ * A multi-line live board — one line per item, each flipping ○ → ⠹ → ✓ as work
243
+ * progresses (the audit rule registry renders through this). Repaints with
244
+ * cursor-up; erases itself on done() so the verb's real output owns stdout.
245
+ */
246
+ export function createChecklist(items, stream = process.stderr, env = process.env) {
247
+ if (!animationEnabled(stream, env) || items.length === 0)
248
+ return NOOP_CHECKLIST;
249
+ const p = paint(true);
250
+ const states = new Map(items.map((item) => [item.id, { state: "pending" }]));
251
+ let frame = 0;
252
+ let painted = 0;
253
+ let timer = null;
254
+ const labelWidth = Math.max(...items.map((item) => item.label.length));
255
+ const render = () => {
256
+ const spin = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
257
+ const lines = items.map((item) => {
258
+ const entry = states.get(item.id);
259
+ const glyph = entry.state === "running" ? p.cyan(spin) : statusGlyph(entry.state, p);
260
+ const note = entry.note ? ` ${p.dim(entry.note)}` : "";
261
+ const label = entry.state === "pending" ? p.dim(item.label.padEnd(labelWidth)) : item.label.padEnd(labelWidth);
262
+ return ` ${glyph} ${label}${note}`;
263
+ });
264
+ const up = painted > 0 ? `\u001b[${painted}A` : "";
265
+ stream.write(`${up}${lines.map((line) => `\u001b[2K${line}`).join("\n")}\n`);
266
+ painted = lines.length;
267
+ };
268
+ const start = () => {
269
+ if (!timer) {
270
+ timer = setInterval(() => {
271
+ frame += 1;
272
+ render();
273
+ }, SPINNER_INTERVAL_MS);
274
+ timer.unref?.();
275
+ }
276
+ };
277
+ return {
278
+ update(id, state, note) {
279
+ const entry = states.get(id);
280
+ if (!entry)
281
+ return;
282
+ entry.state = state;
283
+ entry.note = note;
284
+ start();
285
+ render();
286
+ },
287
+ done() {
288
+ if (timer)
289
+ clearInterval(timer);
290
+ timer = null;
291
+ if (painted > 0) {
292
+ // Erase the board: cursor up over every painted line, clear each.
293
+ stream.write(`\u001b[${painted}A${"\u001b[2K\n".repeat(painted)}\u001b[${painted}A`);
294
+ painted = 0;
295
+ }
296
+ },
297
+ active: true,
298
+ };
299
+ }
300
+ /** Truncate to `max` display characters, ending in "…" when cut. */
301
+ export function truncateToWidth(text, max) {
302
+ if (max <= 0)
303
+ return "";
304
+ if (text.length <= max)
305
+ return text;
306
+ if (max === 1)
307
+ return "…";
308
+ return `${text.slice(0, max - 1).trimEnd()}…`;
309
+ }
310
+ /** critical → red, warning → yellow, info → dim. */
311
+ export function severityWord(word, p) {
312
+ if (word === "critical")
313
+ return p.red(word);
314
+ if (word === "warning")
315
+ return p.yellow(word);
316
+ return p.dim(word);
317
+ }
318
+ /** needs_approval → yellow, approved/applied → green, rejected → red. */
319
+ export function planStatusWord(status, p) {
320
+ if (status === "needs_approval")
321
+ return p.yellow(status);
322
+ if (status === "approved" || status === "applied")
323
+ return p.green(status);
324
+ if (status === "rejected")
325
+ return p.red(status);
326
+ return status;
327
+ }
328
+ /**
329
+ * Interactive-terminal styling for the patch-plan markdown that `audit` and
330
+ * `plans show` print: headings bold, severities banded, and the core diff —
331
+ * current value red, proposed value green (placeholders yellow: they need a
332
+ * human before they can be written). Line-level and additive — with paint
333
+ * disabled the text passes through untouched, so piped output is unchanged.
334
+ * One deliberate text change in rich mode: finding headlines render
335
+ * terminal-bold instead of markdown-bold (the ** markers drop).
336
+ */
337
+ export function stylizePlanMarkdown(text, p) {
338
+ if (!p.enabled)
339
+ return text;
340
+ return text
341
+ .split("\n")
342
+ .map((line) => {
343
+ if (line.startsWith("# ") || line.startsWith("## "))
344
+ return p.bold(line);
345
+ // Rule-summary table severity cell: "| stale-deal | 50 | warning |"
346
+ const cell = line.match(/^(\| .+ \| \d+ \| )(critical|warning|info)( \|)$/);
347
+ if (cell)
348
+ return `${cell[1]}${severityWord(cell[2], p)}${cell[3]}`;
349
+ // Finding headline: "- **Deal has no valid owner** (warning, planned)"
350
+ const headline = line.match(/^- \*\*(.+)\*\* \((critical|warning|info)(, .+)$/);
351
+ if (headline)
352
+ return `- ${p.bold(headline[1])} (${severityWord(headline[2], p)}${headline[3]}`;
353
+ // The diff at the heart of the plan: what is → what would be written.
354
+ const current = line.match(/^(\s*- Current CRM value: )(.*)$/);
355
+ if (current)
356
+ return `${current[1]}${p.red(current[2])}`;
357
+ const proposed = line.match(/^(\s*- Proposed value: )(.*)$/);
358
+ if (proposed) {
359
+ const value = proposed[2];
360
+ return `${proposed[1]}${value.startsWith("requires_human") ? p.yellow(value) : p.green(value)}`;
361
+ }
362
+ if (line.startsWith("Status: ")) {
363
+ return `Status: ${planStatusWord(line.slice("Status: ".length), p)}`;
364
+ }
365
+ if (line.startsWith("Dry run: "))
366
+ return p.dim(line);
367
+ return line;
368
+ })
369
+ .join("\n");
370
+ }
371
+ /** Strip ANSI escape sequences (test helper + safety net for width math). */
372
+ export function stripAnsi(text) {
373
+ // eslint-disable-next-line no-control-regex
374
+ return text.replace(/\u001b\[[0-9;]*[A-Za-z]/g, "");
375
+ }
package/dist/cli.d.ts CHANGED
@@ -1,58 +1,2 @@
1
- import { type LlmProvider } from "./llm.ts";
2
- /**
3
- * The broker channel carries a long-lived pairing bearer and receives freshly
4
- * minted live-CRM tokens, so it must be TLS unless it's an explicit localhost
5
- * dev target. Refuse http:// (and non-http schemes) otherwise — single-quote
6
- * shell escaping does nothing for a token sent in cleartext.
7
- */
8
- export declare function assertSecureBrokerUrl(raw: string): URL;
9
- type ProviderDoctorStatus = {
10
- source: "env" | "stored" | "broker" | "none";
11
- detail: string;
12
- };
13
- export declare function doctorReport(env?: Record<string, string | undefined>): {
14
- package: {
15
- name: string;
16
- version: string;
17
- };
18
- node: {
19
- version: string;
20
- ok: boolean;
21
- required: string;
22
- };
23
- profile: string;
24
- credentialStore: {
25
- path: string;
26
- exists: boolean;
27
- };
28
- config: {
29
- path: string;
30
- exists: boolean;
31
- };
32
- providers: Record<string, ProviderDoctorStatus>;
33
- broker: {
34
- paired: boolean;
35
- baseUrl: string;
36
- } | {
37
- paired: boolean;
38
- baseUrl?: undefined;
39
- };
40
- llm: {
41
- configured: boolean;
42
- provider: LlmProvider;
43
- source: "stored" | "env";
44
- detail?: undefined;
45
- } | {
46
- configured: boolean;
47
- detail: string;
48
- provider?: undefined;
49
- source?: undefined;
50
- };
51
- mcp: {
52
- peersInstalled: boolean;
53
- missing: string[];
54
- };
55
- nextSteps: string[];
56
- };
57
1
  export declare function runCli(argv: string[]): Promise<void>;
58
- export {};
2
+ export { assertSecureBrokerUrl, doctorReport } from "./cli/auth.ts";