@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.
- package/package.json +1 -1
- 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.
|
|
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
|
-
/**
|
|
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();
|