@fiodos/cli 0.1.34 → 0.1.35

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +46 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiodos/cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Fiodos CLI — analyzes your app's source code and generates the in-app voice-agent manifest, then wires the orb. Powers `npx @fiodos/cli analyze`.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -220,11 +220,46 @@ function wiringSpinnerLabel(platform) {
220
220
  return `Wiring actions to your ${surfaceNoun(platform)}`;
221
221
  }
222
222
 
223
- /** Branded terminal spinner (Fiodos orb + colors when stderr is a TTY). */
223
+ /**
224
+ * Fit a colored line into ONE terminal row. The in-place refresh
225
+ * (`\r` + erase-line) only works if the rendered line never wraps: on a
226
+ * narrow terminal (IDE panes) a wrapped spinner line leaves one orphan row
227
+ * BEHIND per tick — hundreds of stacked "Analyzing your project…" lines
228
+ * instead of a single live counter. ANSI escape sequences are copied without
229
+ * counting toward the visible width, so colors survive the truncation.
230
+ */
231
+ function fitLineToWidth(line, columns) {
232
+ const cols = Number(columns);
233
+ if (!Number.isFinite(cols) || cols <= 0) return line;
234
+ const max = cols - 1; // last cell stays free: writing it can auto-wrap
235
+ let out = '';
236
+ let visible = 0;
237
+ for (let i = 0; i < line.length; ) {
238
+ if (line[i] === '\x1b') {
239
+ const m = /^\x1b\[[0-9;]*m/.exec(line.slice(i));
240
+ if (m) {
241
+ out += m[0];
242
+ i += m[0].length;
243
+ continue;
244
+ }
245
+ }
246
+ if (visible >= max) break;
247
+ out += line[i];
248
+ visible += 1;
249
+ i += 1;
250
+ }
251
+ // Close any open color so the truncated tail never bleeds styles.
252
+ return visible >= max ? `${out}\x1b[0m` : out;
253
+ }
254
+
255
+ /** Branded terminal spinner (Fiodos orb + colors when stderr is a TTY).
256
+ * Without a TTY (piped/CI output) there is no in-place line refresh, so the
257
+ * animation would stack one line per tick — print the label ONCE instead. */
224
258
  async function withSpinner(label, work) {
225
259
  const orbFrames = ['◴', '◷', '◶', '◵'];
226
260
  const brailleFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
227
261
  const { blue, cyan, dim, reset } = fyodosTermColors();
262
+ const isTTY = Boolean(process.stderr.isTTY);
228
263
  let currentLabel = label;
229
264
  let frame = 0;
230
265
  let id = null;
@@ -236,13 +271,18 @@ async function withSpinner(label, work) {
236
271
  const tail = brailleFrames[frame % brailleFrames.length];
237
272
  const line =
238
273
  `${cyan}${orb}${reset} ${blue}Fiodos${reset} ${dim}${tail}${reset} ${currentLabel}… ${dim}${sec}s${reset}`;
239
- process.stderr.write(`\r\x1b[K${line}`);
274
+ process.stderr.write(`\r\x1b[K${fitLineToWidth(line, process.stderr.columns)}`);
240
275
  frame += 1;
241
276
  };
242
277
 
243
278
  const clearLine = () => process.stderr.write('\r\x1b[K');
244
279
 
245
280
  const startSpinner = () => {
281
+ if (!isTTY) {
282
+ // One static line; no timer (nothing can be refreshed without a TTY).
283
+ process.stderr.write(`${cyan}◉${reset} ${blue}Fiodos${reset} ${currentLabel}…\n`);
284
+ return;
285
+ }
246
286
  render();
247
287
  id = setInterval(render, 140);
248
288
  };
@@ -256,11 +296,14 @@ async function withSpinner(label, work) {
256
296
  };
257
297
 
258
298
  const resume = () => {
259
- if (!id) startSpinner();
299
+ if (!id && isTTY) startSpinner();
260
300
  };
261
301
 
262
302
  const setLabel = (next) => {
263
303
  currentLabel = next;
304
+ // Without a TTY the label is only ever printed once — announce the change
305
+ // on its own line so progress is still visible in piped/CI output.
306
+ if (!isTTY) process.stderr.write(`${cyan}◉${reset} ${blue}Fiodos${reset} ${next}…\n`);
264
307
  };
265
308
 
266
309
  startSpinner();