@tiny-fish/cli 0.29.1-next.240 → 0.29.1-next.241

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.
@@ -26,6 +26,8 @@ const NO_BROWSER_REASONS = new Set([
26
26
  "harness_not_installed",
27
27
  "command_not_found",
28
28
  ]);
29
+ // Naming a harness the user does not have is the noise PF-3708 cut.
30
+ const HIDDEN_ONCE_SOMETHING_WORKED = new Set(["deselected", "not_detected", "not_installed"]);
29
31
  const STEP_INDENT = " ";
30
32
  // Must undercut agent tool-call budgets (Claude Code Bash defaults to 120s).
31
33
  const OAUTH_SIGN_IN_TIMEOUT_MS = 90_000;
@@ -140,11 +142,11 @@ function uninstallHarness(harness, configPath) {
140
142
  async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliInstall, deferOutro) {
141
143
  const { harness, configPath, detected } = detection;
142
144
  const base = { harness, detected, configPath, installed: false };
143
- if (!detected)
144
- return { ...base, outcome: "not_detected" };
145
- if (!opts.uninstall && isStaleConfig(detection)) {
146
- return { ...base, outcome: "stale_config", fixCommand: `tinyfish connect ${harness}` };
147
- }
145
+ const noWork = noWorkOutcome(detection, opts.uninstall);
146
+ if (noWork === "not_detected")
147
+ return { ...base, outcome: noWork };
148
+ if (noWork)
149
+ return { ...base, outcome: noWork, fixCommand: `tinyfish connect ${harness}` };
148
150
  // dryRun first: --dry-run --uninstall must plan, never reach removeCursorMcpServer().
149
151
  if (opts.dryRun) {
150
152
  const plan = opts.uninstall
@@ -212,7 +214,7 @@ async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliIn
212
214
  }
213
215
  // Detection saw a name on PATH; the probe proved nothing is behind it.
214
216
  if (e instanceof ConnectStepError && e.failureReason === "harness_not_installed") {
215
- return { ...base, detected: false, outcome: "not_detected" };
217
+ return { ...base, detected: false, outcome: "not_installed" };
216
218
  }
217
219
  // Both reasons carry the same "flag not listed" message; the upgrade nudge is in it.
218
220
  if (e instanceof ConnectStepError && TOO_OLD_REASONS.has(e.failureReason)) {
@@ -249,6 +251,12 @@ async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliIn
249
251
  }
250
252
  function summaryLine(result, uninstall) {
251
253
  const name = DISPLAY_NAMES[result.harness];
254
+ if (result.outcome === "not_installed") {
255
+ const next = uninstall
256
+ ? "nothing to remove"
257
+ : `Install ${name}, then run: tinyfish connect ${result.harness}`;
258
+ return `- ${name} — not installed (\`${HARNESS_COMMANDS[result.harness]}\` is on PATH but does not run). ${next}`;
259
+ }
252
260
  if (!result.detected) {
253
261
  const next = uninstall
254
262
  ? "nothing to remove"
@@ -315,13 +323,21 @@ function renderSummary(results, uninstall) {
315
323
  // Ids, not display names: they double as the `connect` argument.
316
324
  const skipped = results.filter((r) => r.outcome === "deselected").map((r) => r.harness);
317
325
  const lines = results
318
- .filter((r) => r.outcome !== "deselected" && r.outcome !== "not_detected")
326
+ .filter((r) => !HIDDEN_ONCE_SOMETHING_WORKED.has(r.outcome))
319
327
  .map((r) => summaryLine(r, uninstall));
320
328
  if (skipped.length === 0)
321
329
  return lines;
322
330
  lines.push("", `Skipped (not selected): ${skipped.join(", ")}`);
323
331
  return lines;
324
332
  }
333
+ /** The states processHarness settles without work; stepTotal must agree with it. */
334
+ function noWorkOutcome(d, uninstall) {
335
+ if (!d.detected)
336
+ return "not_detected";
337
+ if (!uninstall && isStaleConfig(d))
338
+ return "stale_config";
339
+ return undefined;
340
+ }
325
341
  /** A config-writing connect needs no binary; every other harness spawns one. */
326
342
  function isStaleConfig(detection) {
327
343
  return (detection.detected &&
@@ -490,8 +506,8 @@ export async function runConnectAll(opts) {
490
506
  const outro = new Set();
491
507
  const results = [];
492
508
  const oauthBudget = { spent: false };
493
- // Mirrors processHarness's own short-circuits: absent has no work, stale only when removing.
494
- const stepped = (d) => !deselected.has(d.harness) && d.detected && (opts.uninstall || !isStaleConfig(d));
509
+ // Shares processHarness's no-work states so stepTotal cannot desync.
510
+ const stepped = (d) => !deselected.has(d.harness) && !noWorkOutcome(d, opts.uninstall);
495
511
  const stepTotal = detections.filter(stepped).length;
496
512
  let step = 0;
497
513
  for (const detection of detections) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.29.1-next.240",
3
+ "version": "0.29.1-next.241",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {