@standardagents/code 0.4.0 → 0.5.1
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/index.js +421 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import os6 from 'os';
|
|
2
|
+
import os6, { homedir } from 'os';
|
|
3
3
|
import fs4 from 'fs';
|
|
4
4
|
import path3 from 'path';
|
|
5
5
|
import readline2 from 'readline/promises';
|
|
@@ -572,6 +572,20 @@ function gradAt(t) {
|
|
|
572
572
|
Math.round(a[2] + (b[2] - a[2]) * k)
|
|
573
573
|
];
|
|
574
574
|
}
|
|
575
|
+
function cyclePos(t) {
|
|
576
|
+
const p = t - Math.floor(t);
|
|
577
|
+
return p < 0.5 ? p * 2 : 2 - p * 2;
|
|
578
|
+
}
|
|
579
|
+
function colorAtCycle(tri, tc) {
|
|
580
|
+
if (tc) {
|
|
581
|
+
const [r, g, b] = gradAt(tri);
|
|
582
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
583
|
+
}
|
|
584
|
+
return `\x1B[38;5;${GRAD_256[Math.min(GRAD_256.length - 1, Math.floor(tri * GRAD_256.length))]}m`;
|
|
585
|
+
}
|
|
586
|
+
function brandCycleColor(ms, periodMs = 1200) {
|
|
587
|
+
return colorAtCycle(cyclePos(ms / periodMs), truecolor());
|
|
588
|
+
}
|
|
575
589
|
function gradientText(text, phase = 0) {
|
|
576
590
|
const chars = [...text];
|
|
577
591
|
const visible = chars.filter((ch) => ch.trim().length > 0).length;
|
|
@@ -1832,6 +1846,130 @@ var SystemEvents = class {
|
|
|
1832
1846
|
}
|
|
1833
1847
|
};
|
|
1834
1848
|
|
|
1849
|
+
// src/wordmill.ts
|
|
1850
|
+
var MILL_WORDS = [
|
|
1851
|
+
"Working",
|
|
1852
|
+
"Thinking",
|
|
1853
|
+
"Building",
|
|
1854
|
+
"Brewing",
|
|
1855
|
+
"Crafting",
|
|
1856
|
+
"Scheming",
|
|
1857
|
+
"Tinkering",
|
|
1858
|
+
"Noodling",
|
|
1859
|
+
"Pondering",
|
|
1860
|
+
"Wrangling",
|
|
1861
|
+
"Conjuring",
|
|
1862
|
+
"Percolating",
|
|
1863
|
+
"Assembling",
|
|
1864
|
+
"Cogitating",
|
|
1865
|
+
"Hatching",
|
|
1866
|
+
"Forging",
|
|
1867
|
+
// The silly shelf — irreverent, never profane.
|
|
1868
|
+
"Pickling",
|
|
1869
|
+
"Spooning",
|
|
1870
|
+
"Marinating",
|
|
1871
|
+
"Squishing",
|
|
1872
|
+
"Wiggling",
|
|
1873
|
+
"Frolicking",
|
|
1874
|
+
"Moisturizing",
|
|
1875
|
+
"Bamboozling",
|
|
1876
|
+
"Skedaddling",
|
|
1877
|
+
"Discombobulating",
|
|
1878
|
+
"Waffling",
|
|
1879
|
+
"Yodeling",
|
|
1880
|
+
"Shimmying",
|
|
1881
|
+
"Galumphing",
|
|
1882
|
+
"Snorkeling",
|
|
1883
|
+
"Bedazzling"
|
|
1884
|
+
];
|
|
1885
|
+
var FLIP_POOL = "abcdefghjkmnopqrstuvwxyz#$%&@!*+=.:~";
|
|
1886
|
+
var HOLD_MS = 2600;
|
|
1887
|
+
var LAZY_MS = 320;
|
|
1888
|
+
var LAZY_PERIOD = 200;
|
|
1889
|
+
var FAST_PERIOD = 70;
|
|
1890
|
+
var BOLD = "\x1B[1m";
|
|
1891
|
+
var DIM2 = "\x1B[2m";
|
|
1892
|
+
var OFF = "\x1B[22m";
|
|
1893
|
+
function glyphAt(slot, bucket) {
|
|
1894
|
+
let h = (slot + 1) * 2654435761 ^ (bucket + 1) * 40503;
|
|
1895
|
+
h = Math.imul(h ^ h >>> 13, 1274126177);
|
|
1896
|
+
h ^= h >>> 16;
|
|
1897
|
+
return FLIP_POOL[(h >>> 0) % FLIP_POOL.length];
|
|
1898
|
+
}
|
|
1899
|
+
var WordMill = class {
|
|
1900
|
+
word = MILL_WORDS[0];
|
|
1901
|
+
target = null;
|
|
1902
|
+
// non-null while morphing
|
|
1903
|
+
phaseAt = 0;
|
|
1904
|
+
// clock time the current phase began
|
|
1905
|
+
slots = [];
|
|
1906
|
+
morphLen = 0;
|
|
1907
|
+
rng;
|
|
1908
|
+
constructor(rng = Math.random) {
|
|
1909
|
+
this.rng = rng;
|
|
1910
|
+
}
|
|
1911
|
+
/** Restart at "Working" (called when a turn begins). */
|
|
1912
|
+
reset(now) {
|
|
1913
|
+
this.word = MILL_WORDS[0];
|
|
1914
|
+
this.target = null;
|
|
1915
|
+
this.phaseAt = now;
|
|
1916
|
+
}
|
|
1917
|
+
/** The plain word currently displayed or being formed (for tests). */
|
|
1918
|
+
get current() {
|
|
1919
|
+
return this.target ?? this.word;
|
|
1920
|
+
}
|
|
1921
|
+
/** The styled label for this frame (contains only bold/dim ANSI). */
|
|
1922
|
+
text(now) {
|
|
1923
|
+
if (this.phaseAt === 0) this.phaseAt = now;
|
|
1924
|
+
if (!this.target) {
|
|
1925
|
+
if (now - this.phaseAt >= HOLD_MS) this.beginMorph(now);
|
|
1926
|
+
else return `${BOLD}${this.word}${OFF}`;
|
|
1927
|
+
}
|
|
1928
|
+
return this.morphFrame(now);
|
|
1929
|
+
}
|
|
1930
|
+
beginMorph(now) {
|
|
1931
|
+
const options = MILL_WORDS.filter((w) => w !== this.word);
|
|
1932
|
+
this.target = options[Math.floor(this.rng() * options.length)];
|
|
1933
|
+
this.phaseAt = now;
|
|
1934
|
+
const n = Math.max(this.word.length, this.target.length);
|
|
1935
|
+
this.slots = [];
|
|
1936
|
+
let maxLand = 0;
|
|
1937
|
+
for (let i = 0; i < n; i++) {
|
|
1938
|
+
const start = this.rng() * 500;
|
|
1939
|
+
const land = 750 + i * 90 + this.rng() * 350;
|
|
1940
|
+
this.slots.push({ start, land });
|
|
1941
|
+
if (land > maxLand) maxLand = land;
|
|
1942
|
+
}
|
|
1943
|
+
this.morphLen = maxLand + 60;
|
|
1944
|
+
}
|
|
1945
|
+
morphFrame(now) {
|
|
1946
|
+
const target = this.target;
|
|
1947
|
+
const t = now - this.phaseAt;
|
|
1948
|
+
if (t >= this.morphLen) {
|
|
1949
|
+
this.word = target;
|
|
1950
|
+
this.target = null;
|
|
1951
|
+
this.phaseAt = now;
|
|
1952
|
+
return `${BOLD}${this.word}${OFF}`;
|
|
1953
|
+
}
|
|
1954
|
+
let out = "";
|
|
1955
|
+
for (let i = 0; i < this.slots.length; i++) {
|
|
1956
|
+
const s = this.slots[i];
|
|
1957
|
+
if (t < s.start) {
|
|
1958
|
+
const ch = this.word[i];
|
|
1959
|
+
if (ch) out += `${BOLD}${ch}${OFF}`;
|
|
1960
|
+
} else if (t < s.land) {
|
|
1961
|
+
const age = t - s.start;
|
|
1962
|
+
const period = age < LAZY_MS ? LAZY_PERIOD : FAST_PERIOD;
|
|
1963
|
+
out += `${DIM2}${glyphAt(i, Math.floor(t / period))}${OFF}`;
|
|
1964
|
+
} else {
|
|
1965
|
+
const ch = target[i];
|
|
1966
|
+
if (ch) out += `${BOLD}${ch}${OFF}`;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return out;
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1835
1973
|
// src/types.ts
|
|
1836
1974
|
var LEVELS = [1, 2, 3, 4, 5];
|
|
1837
1975
|
var LEVEL_DETAIL = {
|
|
@@ -1950,6 +2088,7 @@ var C = {
|
|
|
1950
2088
|
teal: "\x1B[38;5;37m"
|
|
1951
2089
|
};
|
|
1952
2090
|
var FRAMES = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FB", "\u28FD", "\u28FE"];
|
|
2091
|
+
var SPINNER_MS = 70;
|
|
1953
2092
|
var SUBAGENT_COLORS = [
|
|
1954
2093
|
"\x1B[35m",
|
|
1955
2094
|
// magenta
|
|
@@ -1966,6 +2105,19 @@ var SUBAGENT_COLORS = [
|
|
|
1966
2105
|
];
|
|
1967
2106
|
var COMPACTION_COLOR = "\x1B[38;5;208m";
|
|
1968
2107
|
var COMPACTION_AGENT = "compaction_agent";
|
|
2108
|
+
function isWideCodePoint(cp) {
|
|
2109
|
+
return cp >= 4352 && cp <= 4447 || // Hangul Jamo
|
|
2110
|
+
cp >= 11904 && cp <= 42191 || // CJK radicals … Yi
|
|
2111
|
+
cp >= 44032 && cp <= 55203 || // Hangul syllables
|
|
2112
|
+
cp >= 63744 && cp <= 64255 || // CJK compatibility ideographs
|
|
2113
|
+
cp >= 65072 && cp <= 65103 || // CJK compatibility forms
|
|
2114
|
+
cp >= 65280 && cp <= 65376 || // fullwidth forms
|
|
2115
|
+
cp >= 65504 && cp <= 65510 || cp >= 127744 && cp <= 129791 || // emoji
|
|
2116
|
+
cp >= 131072;
|
|
2117
|
+
}
|
|
2118
|
+
function sanitizeHudRow(s) {
|
|
2119
|
+
return s.replace(/\x1b(?!\[)/g, "").replace(/[\x00-\x1a\x1c-\x1f\x7f]/g, " ");
|
|
2120
|
+
}
|
|
1969
2121
|
var Tui = class _Tui {
|
|
1970
2122
|
constructor(level = 1) {
|
|
1971
2123
|
this.level = level;
|
|
@@ -1986,6 +2138,8 @@ var Tui = class _Tui {
|
|
|
1986
2138
|
// caret's row offset from the input region top, last render
|
|
1987
2139
|
working = false;
|
|
1988
2140
|
workingStart = 0;
|
|
2141
|
+
// Slot-machine morph for the working label (Working → Brewing → …).
|
|
2142
|
+
wordMill = new WordMill();
|
|
1989
2143
|
spinnerTimer = null;
|
|
1990
2144
|
bgCount = 0;
|
|
1991
2145
|
// The `[⚙ n bg]` prompt badge can be selected with ← from the start of the
|
|
@@ -2495,7 +2649,8 @@ var Tui = class _Tui {
|
|
|
2495
2649
|
return `${(n / 1e6).toFixed(2)}M`;
|
|
2496
2650
|
}
|
|
2497
2651
|
spinnerFrame() {
|
|
2498
|
-
|
|
2652
|
+
const now = Date.now();
|
|
2653
|
+
return `${C.bold}${brandCycleColor(now)}${FRAMES[Math.floor(now / SPINNER_MS) % FRAMES.length]}${C.reset}`;
|
|
2499
2654
|
}
|
|
2500
2655
|
/** "↑X ↓Y" cumulative token totals (greyed — low-priority). */
|
|
2501
2656
|
tokensText() {
|
|
@@ -2568,7 +2723,7 @@ var Tui = class _Tui {
|
|
|
2568
2723
|
if (this.working) {
|
|
2569
2724
|
const el = this.formatElapsed(Date.now() - this.workingStart);
|
|
2570
2725
|
const right = `${C.dim}${el}${C.reset}${tk ? " " + tk : ""}`;
|
|
2571
|
-
const head = `${this.spinnerFrame()} ${
|
|
2726
|
+
const head = `${this.spinnerFrame()} ${this.wordMill.text(Date.now())}`;
|
|
2572
2727
|
const avail = Math.max(
|
|
2573
2728
|
0,
|
|
2574
2729
|
cols2 - this.visibleWidth(head) - this.visibleWidth(right) - 2 - gaugeReserve
|
|
@@ -2599,7 +2754,50 @@ var Tui = class _Tui {
|
|
|
2599
2754
|
return `${q}${bg}${this.levelColor()}\u276F${C.reset} `;
|
|
2600
2755
|
}
|
|
2601
2756
|
visibleWidth(s) {
|
|
2602
|
-
|
|
2757
|
+
let w = 0;
|
|
2758
|
+
for (const ch of s.replace(/\x1b\[[0-9;]*m/g, "")) {
|
|
2759
|
+
w += isWideCodePoint(ch.codePointAt(0)) ? 2 : 1;
|
|
2760
|
+
}
|
|
2761
|
+
return w;
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Truncate a styled string to at most `max` VISIBLE columns, copying ANSI
|
|
2765
|
+
* escape sequences through without counting them and appending a reset if it
|
|
2766
|
+
* cut mid-style. The redraw's whole height math assumes every HUD row is
|
|
2767
|
+
* exactly ONE physical terminal row — true only while each row's visible
|
|
2768
|
+
* width is strictly less than `cols`. A row of width == cols hits the
|
|
2769
|
+
* terminal's auto-wrap boundary: many terminals push the cursor to the next
|
|
2770
|
+
* line, so the following CR/LF yields an EXTRA physical row we didn't count,
|
|
2771
|
+
* and the region marches down the screen leaving a trail on every tick. Every
|
|
2772
|
+
* HUD row is clamped through here to keep that invariant no matter what any
|
|
2773
|
+
* individual line builder produces.
|
|
2774
|
+
*/
|
|
2775
|
+
clampVisible(s, max) {
|
|
2776
|
+
let out = "";
|
|
2777
|
+
let width = 0;
|
|
2778
|
+
let i = 0;
|
|
2779
|
+
let truncated = false;
|
|
2780
|
+
while (i < s.length) {
|
|
2781
|
+
if (s[i] === "\x1B" && s[i + 1] === "[") {
|
|
2782
|
+
let j = i + 2;
|
|
2783
|
+
while (j < s.length && !/[a-zA-Z]/.test(s[j])) j++;
|
|
2784
|
+
out += s.slice(i, j + 1);
|
|
2785
|
+
i = j + 1;
|
|
2786
|
+
continue;
|
|
2787
|
+
}
|
|
2788
|
+
const cp = s.codePointAt(i);
|
|
2789
|
+
const chLen = cp > 65535 ? 2 : 1;
|
|
2790
|
+
const chWidth = isWideCodePoint(cp) ? 2 : 1;
|
|
2791
|
+
if (width + chWidth > max) {
|
|
2792
|
+
truncated = true;
|
|
2793
|
+
break;
|
|
2794
|
+
}
|
|
2795
|
+
out += s.slice(i, i + chLen);
|
|
2796
|
+
width += chWidth;
|
|
2797
|
+
i += chLen;
|
|
2798
|
+
}
|
|
2799
|
+
if (truncated) out += C.reset;
|
|
2800
|
+
return out;
|
|
2603
2801
|
}
|
|
2604
2802
|
/**
|
|
2605
2803
|
* The slash-palette block rendered BELOW the input. While the palette is open
|
|
@@ -2612,7 +2810,7 @@ var Tui = class _Tui {
|
|
|
2612
2810
|
*/
|
|
2613
2811
|
paletteBlockLines(cols2) {
|
|
2614
2812
|
if (!this.paletteOpen()) return [];
|
|
2615
|
-
const rows = this.paletteLines(cols2);
|
|
2813
|
+
const rows = this.paletteLines(cols2).map((r) => this.clampVisible(r, Math.max(1, cols2 - 1)));
|
|
2616
2814
|
const reserved = Math.max(this.commands.length, rows.length);
|
|
2617
2815
|
while (rows.length < reserved) rows.push("");
|
|
2618
2816
|
return rows;
|
|
@@ -2681,11 +2879,13 @@ var Tui = class _Tui {
|
|
|
2681
2879
|
if (!this.started || this.takeoverHandler) return;
|
|
2682
2880
|
const cols2 = process.stdout.columns || 80;
|
|
2683
2881
|
this.moveToRegionTop();
|
|
2684
|
-
process.stdout.write("\x1B[J");
|
|
2685
2882
|
const hudWidths = [];
|
|
2883
|
+
const hudRows = [];
|
|
2884
|
+
const rowCap = Math.max(1, cols2 - 1);
|
|
2686
2885
|
const writeHudRow = (line) => {
|
|
2687
|
-
|
|
2688
|
-
|
|
2886
|
+
const row = this.clampVisible(sanitizeHudRow(line), rowCap);
|
|
2887
|
+
hudWidths.push(this.visibleWidth(row));
|
|
2888
|
+
hudRows.push(row);
|
|
2689
2889
|
};
|
|
2690
2890
|
const previewLines = this.streamPreviewLines(cols2);
|
|
2691
2891
|
for (const line of previewLines) writeHudRow(line);
|
|
@@ -2697,7 +2897,7 @@ var Tui = class _Tui {
|
|
|
2697
2897
|
const quitLine = this.quitArmed ? `${C.dim}Press Control-C again to exit${C.reset}` : null;
|
|
2698
2898
|
const quitRows = quitLine ? 1 : 0;
|
|
2699
2899
|
if (quitLine) writeHudRow(quitLine);
|
|
2700
|
-
const frame = FRAMES[Math.floor(Date.now() /
|
|
2900
|
+
const frame = FRAMES[Math.floor(Date.now() / SPINNER_MS) % FRAMES.length];
|
|
2701
2901
|
for (const sub of this.subagents) {
|
|
2702
2902
|
const color = sub.agentName === COMPACTION_AGENT ? COMPACTION_COLOR : SUBAGENT_COLORS[this.subagentColorByID.get(sub.id) ?? 0];
|
|
2703
2903
|
const budget = cols2 - 10;
|
|
@@ -2716,8 +2916,8 @@ var Tui = class _Tui {
|
|
|
2716
2916
|
const prefix = this.promptPrefix();
|
|
2717
2917
|
const pw = this.visibleWidth(prefix);
|
|
2718
2918
|
const lines = this.inputBuffer.split("\n");
|
|
2719
|
-
|
|
2720
|
-
for (let i = 1; i < lines.length; i++)
|
|
2919
|
+
const tail = [prefix + lines[0]];
|
|
2920
|
+
for (let i = 1; i < lines.length; i++) tail.push("\r\n" + lines[i]);
|
|
2721
2921
|
const rowsOf = (len, lead) => Math.max(1, Math.ceil((lead + len) / cols2));
|
|
2722
2922
|
const lineRows = lines.map((l, i) => rowsOf(l.length, i === 0 ? pw : 0));
|
|
2723
2923
|
const inputRows = lineRows.reduce((a, b) => a + b, 0);
|
|
@@ -2732,18 +2932,18 @@ var Tui = class _Tui {
|
|
|
2732
2932
|
for (let i = 0; i < caretLine; i++) caretRow += lineRows[i];
|
|
2733
2933
|
const caretCol = caretCell % cols2;
|
|
2734
2934
|
const paletteBlock = this.paletteBlockLines(cols2);
|
|
2735
|
-
for (const line of paletteBlock)
|
|
2935
|
+
for (const line of paletteBlock) tail.push("\r\n" + line);
|
|
2736
2936
|
if (paletteBlock.length > 0) {
|
|
2737
|
-
|
|
2937
|
+
tail.push("\r");
|
|
2738
2938
|
const up = inputRows - 1 + paletteBlock.length - caretRow;
|
|
2739
|
-
if (up > 0)
|
|
2740
|
-
if (caretCol > 0)
|
|
2939
|
+
if (up > 0) tail.push(`\x1B[${up}A`);
|
|
2940
|
+
if (caretCol > 0) tail.push(`\x1B[${caretCol}C`);
|
|
2741
2941
|
this.lastCursorRow = aboveRows + caretRow;
|
|
2742
2942
|
} else if (this.cursorPos < this.inputBuffer.length) {
|
|
2743
|
-
|
|
2943
|
+
tail.push("\r");
|
|
2744
2944
|
const up = inputRows - 1 - caretRow;
|
|
2745
|
-
if (up > 0)
|
|
2746
|
-
if (caretCol > 0)
|
|
2945
|
+
if (up > 0) tail.push(`\x1B[${up}A`);
|
|
2946
|
+
if (caretCol > 0) tail.push(`\x1B[${caretCol}C`);
|
|
2747
2947
|
this.lastCursorRow = aboveRows + caretRow;
|
|
2748
2948
|
} else {
|
|
2749
2949
|
this.lastCursorRow = aboveRows + (inputRows - 1);
|
|
@@ -2751,6 +2951,8 @@ var Tui = class _Tui {
|
|
|
2751
2951
|
this.drawnHudWidths = hudWidths;
|
|
2752
2952
|
this.lastDrawnCols = cols2;
|
|
2753
2953
|
this.bottomDrawn = true;
|
|
2954
|
+
const out = "\x1B[J" + hudRows.join("\r\n") + "\r\n" + tail.join("");
|
|
2955
|
+
process.stdout.write(out);
|
|
2754
2956
|
}
|
|
2755
2957
|
clearBottom() {
|
|
2756
2958
|
if (!this.bottomDrawn) return;
|
|
@@ -2986,6 +3188,7 @@ var Tui = class _Tui {
|
|
|
2986
3188
|
if (on && !this.working) {
|
|
2987
3189
|
this.working = true;
|
|
2988
3190
|
this.workingStart = Date.now();
|
|
3191
|
+
this.wordMill.reset(this.workingStart);
|
|
2989
3192
|
this.goalComplete = false;
|
|
2990
3193
|
} else if (!on) {
|
|
2991
3194
|
this.working = false;
|
|
@@ -3017,7 +3220,7 @@ var Tui = class _Tui {
|
|
|
3017
3220
|
syncSpinner() {
|
|
3018
3221
|
const spinning = this.working || this.subagents.length > 0;
|
|
3019
3222
|
if (spinning && !this.spinnerTimer) {
|
|
3020
|
-
this.spinnerTimer = setInterval(() => this.renderBottom(),
|
|
3223
|
+
this.spinnerTimer = setInterval(() => this.renderBottom(), SPINNER_MS);
|
|
3021
3224
|
} else if (!spinning && this.spinnerTimer) {
|
|
3022
3225
|
clearInterval(this.spinnerTimer);
|
|
3023
3226
|
this.spinnerTimer = null;
|
|
@@ -3063,7 +3266,7 @@ var Tui = class _Tui {
|
|
|
3063
3266
|
* resets whenever the label changes.
|
|
3064
3267
|
*/
|
|
3065
3268
|
setStep(label, outTokens) {
|
|
3066
|
-
const next = label && label.trim() ? label.trim() : null;
|
|
3269
|
+
const next = label && label.trim() ? label.replace(/\s+/g, " ").trim() : null;
|
|
3067
3270
|
if (next !== this.step) {
|
|
3068
3271
|
this.step = next;
|
|
3069
3272
|
this.stepStart = Date.now();
|
|
@@ -3296,8 +3499,8 @@ function appendHistory(store, threadId, history, text) {
|
|
|
3296
3499
|
// src/markdown.ts
|
|
3297
3500
|
var ESC = "\x1B[";
|
|
3298
3501
|
var R = ESC + "0m";
|
|
3299
|
-
var
|
|
3300
|
-
var
|
|
3502
|
+
var BOLD2 = ESC + "1m";
|
|
3503
|
+
var DIM3 = ESC + "2m";
|
|
3301
3504
|
var ITAL = ESC + "3m";
|
|
3302
3505
|
var UNDER = ESC + "4m";
|
|
3303
3506
|
var TEAL = ESC + "38;5;37m";
|
|
@@ -3319,11 +3522,11 @@ function inline(s) {
|
|
|
3319
3522
|
});
|
|
3320
3523
|
s = s.replace(
|
|
3321
3524
|
/\[([^\]]+)\]\(([^)\s]+)\)/g,
|
|
3322
|
-
(_, text, url) => `${CYAN}${UNDER}${text}${R} ${
|
|
3525
|
+
(_, text, url) => `${CYAN}${UNDER}${text}${R} ${DIM3}${url}${R}`
|
|
3323
3526
|
);
|
|
3324
|
-
s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${
|
|
3527
|
+
s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${BOLD2}${t}${R}`);
|
|
3325
3528
|
s = s.replace(/\*([^*\n]+)\*/g, (_, t) => `${ITAL}${t}${R}`);
|
|
3326
|
-
s = s.replace(/~~([^~]+)~~/g, (_, t) => `${
|
|
3529
|
+
s = s.replace(/~~([^~]+)~~/g, (_, t) => `${DIM3}${t}${R}`);
|
|
3327
3530
|
s = s.replace(/\x00(\d+)\x00/g, (_, i) => `${TEAL}${codes[+i].replace(/ /g, String.fromCharCode(160))}${R}`);
|
|
3328
3531
|
return s;
|
|
3329
3532
|
}
|
|
@@ -3376,7 +3579,7 @@ function renderTable(rows) {
|
|
|
3376
3579
|
const cells = [];
|
|
3377
3580
|
for (let c2 = 0; c2 < cols2; c2++) {
|
|
3378
3581
|
const raw = r[c2] ?? "";
|
|
3379
|
-
const styled = ri === 0 ? `${
|
|
3582
|
+
const styled = ri === 0 ? `${BOLD2}${inline(raw)}${R}` : inline(raw);
|
|
3380
3583
|
cells.push(padEndVisible(styled, widths[c2]));
|
|
3381
3584
|
}
|
|
3382
3585
|
out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
|
|
@@ -3416,7 +3619,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3416
3619
|
}
|
|
3417
3620
|
const heading = line.match(/^(#{1,6})\s+(.*)$/);
|
|
3418
3621
|
if (heading) {
|
|
3419
|
-
for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${
|
|
3622
|
+
for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${BOLD2}${TEAL}${ln}${R}`);
|
|
3420
3623
|
i++;
|
|
3421
3624
|
continue;
|
|
3422
3625
|
}
|
|
@@ -3428,7 +3631,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3428
3631
|
const quote = line.match(/^\s*>\s?(.*)$/);
|
|
3429
3632
|
if (quote) {
|
|
3430
3633
|
for (const ln of wrapStyled(inline(quote[1]), Math.max(8, cols2 - 2))) {
|
|
3431
|
-
out.push(`${GRAY}\u2502${R} ${
|
|
3634
|
+
out.push(`${GRAY}\u2502${R} ${DIM3}${ln}${R}`);
|
|
3432
3635
|
}
|
|
3433
3636
|
i++;
|
|
3434
3637
|
continue;
|
|
@@ -3444,7 +3647,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3444
3647
|
if (numbered) {
|
|
3445
3648
|
const marker = `${numbered[2]}${numbered[3]}`;
|
|
3446
3649
|
const leadWidth = numbered[1].length + marker.length + 1;
|
|
3447
|
-
wrapBlock(out, cols2, `${numbered[1]}${
|
|
3650
|
+
wrapBlock(out, cols2, `${numbered[1]}${BOLD2}${marker}${R} `, " ".repeat(leadWidth), leadWidth, inline(numbered[4]));
|
|
3448
3651
|
i++;
|
|
3449
3652
|
continue;
|
|
3450
3653
|
}
|
|
@@ -3733,7 +3936,7 @@ var McpManager = class {
|
|
|
3733
3936
|
}));
|
|
3734
3937
|
return {
|
|
3735
3938
|
ok: true,
|
|
3736
|
-
result: servers.length ? JSON.stringify({ servers }, null, 2) :
|
|
3939
|
+
result: servers.length ? JSON.stringify({ servers }, null, 2) : 'No MCP servers are connected. The user can add one with the /mcp command, or you can install one with the mcp tool (action "install").'
|
|
3737
3940
|
};
|
|
3738
3941
|
}
|
|
3739
3942
|
if (action === "list_tools") {
|
|
@@ -3889,6 +4092,107 @@ function saveDefaultEndpoint(endpoint) {
|
|
|
3889
4092
|
} catch {
|
|
3890
4093
|
}
|
|
3891
4094
|
}
|
|
4095
|
+
var PKG_NAME = "@standardagents/code";
|
|
4096
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PKG_NAME)}`;
|
|
4097
|
+
var CACHE_REL_DIR = ".config/standardagents-cli";
|
|
4098
|
+
var CACHE_FILE = "update-check.json";
|
|
4099
|
+
var CACHE_TTL_MS = 1e3 * 60 * 60 * 24;
|
|
4100
|
+
var CHECK_TIMEOUT_MS = 4e3;
|
|
4101
|
+
function cacheDir() {
|
|
4102
|
+
return path3.join(homedir(), CACHE_REL_DIR);
|
|
4103
|
+
}
|
|
4104
|
+
function cachePath() {
|
|
4105
|
+
return path3.join(cacheDir(), CACHE_FILE);
|
|
4106
|
+
}
|
|
4107
|
+
function readCache() {
|
|
4108
|
+
try {
|
|
4109
|
+
const raw = fs4.readFileSync(cachePath(), "utf-8");
|
|
4110
|
+
return JSON.parse(raw);
|
|
4111
|
+
} catch {
|
|
4112
|
+
return null;
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
function writeCache(latest) {
|
|
4116
|
+
try {
|
|
4117
|
+
const dir = cacheDir();
|
|
4118
|
+
if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
|
|
4119
|
+
fs4.writeFileSync(cachePath(), JSON.stringify({ latest, timestamp: Date.now() }));
|
|
4120
|
+
} catch {
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
async function checkForUpdate(currentVersion) {
|
|
4124
|
+
if (!currentVersion) return null;
|
|
4125
|
+
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
|
|
4126
|
+
const cached = readCache();
|
|
4127
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
4128
|
+
if (cached.latest !== currentVersion) {
|
|
4129
|
+
return { current: currentVersion, latest: cached.latest };
|
|
4130
|
+
}
|
|
4131
|
+
return null;
|
|
4132
|
+
}
|
|
4133
|
+
const controller = new AbortController();
|
|
4134
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
4135
|
+
try {
|
|
4136
|
+
const res = await fetch(REGISTRY_URL, {
|
|
4137
|
+
signal: controller.signal,
|
|
4138
|
+
headers: {
|
|
4139
|
+
Accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
|
|
4140
|
+
"User-Agent": `${PKG_NAME}/${currentVersion}`
|
|
4141
|
+
}
|
|
4142
|
+
});
|
|
4143
|
+
if (!res.ok) return null;
|
|
4144
|
+
const data = await res.json();
|
|
4145
|
+
const latest = data["dist-tags"]?.latest;
|
|
4146
|
+
if (!latest) return null;
|
|
4147
|
+
writeCache(latest);
|
|
4148
|
+
if (latest !== currentVersion) {
|
|
4149
|
+
return { current: currentVersion, latest };
|
|
4150
|
+
}
|
|
4151
|
+
return null;
|
|
4152
|
+
} catch {
|
|
4153
|
+
return null;
|
|
4154
|
+
} finally {
|
|
4155
|
+
clearTimeout(timeout);
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
async function forceCheckForUpdate(currentVersion) {
|
|
4159
|
+
if (!currentVersion) return null;
|
|
4160
|
+
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
|
|
4161
|
+
const controller = new AbortController();
|
|
4162
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
4163
|
+
try {
|
|
4164
|
+
const res = await fetch(REGISTRY_URL, {
|
|
4165
|
+
signal: controller.signal,
|
|
4166
|
+
headers: {
|
|
4167
|
+
Accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
|
|
4168
|
+
"User-Agent": `${PKG_NAME}/${currentVersion}`
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
4171
|
+
if (!res.ok) return null;
|
|
4172
|
+
const data = await res.json();
|
|
4173
|
+
const latest = data["dist-tags"]?.latest;
|
|
4174
|
+
if (!latest) return null;
|
|
4175
|
+
writeCache(latest);
|
|
4176
|
+
if (latest !== currentVersion) {
|
|
4177
|
+
return { current: currentVersion, latest };
|
|
4178
|
+
}
|
|
4179
|
+
return null;
|
|
4180
|
+
} catch {
|
|
4181
|
+
return null;
|
|
4182
|
+
} finally {
|
|
4183
|
+
clearTimeout(timeout);
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
function runNpmUpdate() {
|
|
4187
|
+
return new Promise((resolve) => {
|
|
4188
|
+
const child = spawn("npm", ["i", "-g", `${PKG_NAME}@latest`], {
|
|
4189
|
+
stdio: "inherit",
|
|
4190
|
+
shell: true
|
|
4191
|
+
});
|
|
4192
|
+
child.on("close", (code) => resolve(code === 0));
|
|
4193
|
+
child.on("error", () => resolve(false));
|
|
4194
|
+
});
|
|
4195
|
+
}
|
|
3892
4196
|
|
|
3893
4197
|
// src/index.ts
|
|
3894
4198
|
var AGENT_ID = "standard_code_agent";
|
|
@@ -3992,7 +4296,25 @@ function printAssistant(tui, text) {
|
|
|
3992
4296
|
}
|
|
3993
4297
|
tui.print("");
|
|
3994
4298
|
}
|
|
4299
|
+
function startLoader(label) {
|
|
4300
|
+
const frames = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FB", "\u28FD", "\u28FE"];
|
|
4301
|
+
stdout.write("\x1B[?25l");
|
|
4302
|
+
const draw = () => {
|
|
4303
|
+
const now = Date.now();
|
|
4304
|
+
const f = frames[Math.floor(now / 70) % frames.length];
|
|
4305
|
+
stdout.write(`\r\x1B[K${brandCycleColor(now)}${f}${c.reset} ${c.dim}${label}\u2026${c.reset}`);
|
|
4306
|
+
};
|
|
4307
|
+
draw();
|
|
4308
|
+
const timer = setInterval(draw, 70);
|
|
4309
|
+
return {
|
|
4310
|
+
stop: () => {
|
|
4311
|
+
clearInterval(timer);
|
|
4312
|
+
stdout.write("\r\x1B[K\x1B[?25h");
|
|
4313
|
+
}
|
|
4314
|
+
};
|
|
4315
|
+
}
|
|
3995
4316
|
function farewell(stoppedProcs = 0) {
|
|
4317
|
+
stdout.write("\x1B[?25h");
|
|
3996
4318
|
if (stoppedProcs > 0) {
|
|
3997
4319
|
stdout.write(
|
|
3998
4320
|
`
|
|
@@ -4041,7 +4363,7 @@ function printWelcome(endpoint, projectDir) {
|
|
|
4041
4363
|
const meta = [
|
|
4042
4364
|
`${c.bold}${gradientText("Standard Code")}${c.reset}${version ? ` ${c.dim}v${version}${c.reset}` : ""}`,
|
|
4043
4365
|
`${c.dim}terminal coding agent${c.reset}`,
|
|
4044
|
-
`${c.teal}${host}${c.reset}
|
|
4366
|
+
...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c.teal}${host}${c.reset}`],
|
|
4045
4367
|
`${c.dim}${dir}${c.reset}`
|
|
4046
4368
|
];
|
|
4047
4369
|
const markWidth = Math.max(...LOGO_MARK.map((l) => [...l].length));
|
|
@@ -4155,9 +4477,29 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4155
4477
|
|
|
4156
4478
|
`);
|
|
4157
4479
|
}
|
|
4480
|
+
const version = readVersion();
|
|
4481
|
+
let updateAvailable = null;
|
|
4482
|
+
{
|
|
4483
|
+
const loading = startLoader("Checking for updates");
|
|
4484
|
+
updateAvailable = await checkForUpdate(version);
|
|
4485
|
+
loading.stop();
|
|
4486
|
+
if (updateAvailable) {
|
|
4487
|
+
stdout.write(
|
|
4488
|
+
` ${c.teal}\u25C7${c.reset} ${c.dim}Update available:${c.reset} ${c.dim}v${updateAvailable.current}${c.reset} \u2192 ${c.bold}v${updateAvailable.latest}${c.reset}
|
|
4489
|
+
${c.dim}Run ${c.reset}${c.bold}npm i -g @standardagents/code@latest${c.reset}${c.dim} to update${c.reset}
|
|
4490
|
+
|
|
4491
|
+
`
|
|
4492
|
+
);
|
|
4493
|
+
}
|
|
4494
|
+
}
|
|
4158
4495
|
const stored = getCredential(endpoint);
|
|
4159
4496
|
let api = stored ? new ApiClient(endpoint, stored.access_token) : null;
|
|
4160
|
-
|
|
4497
|
+
let storedCheck = null;
|
|
4498
|
+
if (api) {
|
|
4499
|
+
const connecting = startLoader("Connecting to Standard Agents");
|
|
4500
|
+
storedCheck = await api.verifyDetailed();
|
|
4501
|
+
connecting.stop();
|
|
4502
|
+
}
|
|
4161
4503
|
if (!api || !storedCheck?.ok) {
|
|
4162
4504
|
const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
4163
4505
|
if (storedCheck && !storedCheck.ok) {
|
|
@@ -4199,7 +4541,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4199
4541
|
});
|
|
4200
4542
|
if (!got) continue;
|
|
4201
4543
|
api = new ApiClient(endpoint, got);
|
|
4544
|
+
const connecting2 = startLoader("Connecting to Standard Agents");
|
|
4202
4545
|
const check2 = await api.verifyDetailed();
|
|
4546
|
+
connecting2.stop();
|
|
4203
4547
|
if (check2.ok) {
|
|
4204
4548
|
saveCredential(
|
|
4205
4549
|
{ endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
|
|
@@ -4213,7 +4557,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4213
4557
|
continue;
|
|
4214
4558
|
}
|
|
4215
4559
|
api = new ApiClient(endpoint, token);
|
|
4560
|
+
const connecting = startLoader("Connecting to Standard Agents");
|
|
4216
4561
|
const check = await api.verifyDetailed();
|
|
4562
|
+
connecting.stop();
|
|
4217
4563
|
if (check.ok) {
|
|
4218
4564
|
saveCredential(
|
|
4219
4565
|
{ endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
|
|
@@ -4232,18 +4578,20 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4232
4578
|
handoffClosing = true;
|
|
4233
4579
|
reader.rl?.close();
|
|
4234
4580
|
const tags = [`path:${projectDir}`, `machine:${machine}`];
|
|
4581
|
+
const loadingSessions = startLoader("Loading sessions");
|
|
4235
4582
|
let existing = [];
|
|
4236
4583
|
try {
|
|
4237
4584
|
existing = await api.listThreads(AGENT_ID_VARIANTS, tags);
|
|
4238
4585
|
} catch {
|
|
4239
4586
|
existing = [];
|
|
4240
4587
|
}
|
|
4588
|
+
const summaries = existing.length > 0 ? await summarizeThreads(api, existing.slice(0, 8)) : [];
|
|
4589
|
+
loadingSessions.stop();
|
|
4241
4590
|
const tui = new Tui(1);
|
|
4242
4591
|
let threadId;
|
|
4243
4592
|
let resumed = false;
|
|
4244
4593
|
let historySeed;
|
|
4245
4594
|
if (existing.length > 0) {
|
|
4246
|
-
const summaries = await summarizeThreads(api, existing.slice(0, 8));
|
|
4247
4595
|
const items = summaries.map((s) => ({
|
|
4248
4596
|
label: s.label,
|
|
4249
4597
|
hint: s.hint,
|
|
@@ -4409,6 +4757,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
4409
4757
|
saveApprovals(api, threadId, perm);
|
|
4410
4758
|
void api.kvSet(threadId, "session_info", { cwd: projectDir, machine }).catch(() => {
|
|
4411
4759
|
});
|
|
4760
|
+
const attaching = startLoader("Attaching to thread");
|
|
4412
4761
|
let busy = false;
|
|
4413
4762
|
let interrupting = false;
|
|
4414
4763
|
const queued = [];
|
|
@@ -4502,7 +4851,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4502
4851
|
for (const s of subs) {
|
|
4503
4852
|
const status = (s.status || "").trim();
|
|
4504
4853
|
if (status === "idle" || status === "terminated") continue;
|
|
4505
|
-
const
|
|
4854
|
+
const oneLineStatus = status.replace(/\s+/g, " ");
|
|
4855
|
+
const detail = oneLineStatus && oneLineStatus !== "running" ? ` \u2014 ${oneLineStatus.slice(0, 80)}` : "";
|
|
4506
4856
|
activeSubagents.set(s.id, {
|
|
4507
4857
|
label: `${subagentLabel(s, agentTitles)}${detail}`,
|
|
4508
4858
|
agentName: s.agent_name ?? void 0
|
|
@@ -4599,7 +4949,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4599
4949
|
return mcpCtl.connect(cfg);
|
|
4600
4950
|
},
|
|
4601
4951
|
// Seed the request into the main chat — the agent researches + installs it
|
|
4602
|
-
// there (using research_agent +
|
|
4952
|
+
// there (using research_agent + the mcp tool's install action), visible in
|
|
4953
|
+
// the transcript.
|
|
4603
4954
|
requestInstall: (query) => {
|
|
4604
4955
|
void sendNow(
|
|
4605
4956
|
`Install an MCP server for me: ${query}. Research the best one and its exact launch command, then install it.`
|
|
@@ -4649,10 +5000,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4649
5000
|
setEnabled: (name, enabled) => api.setSkillEnabled(name, enabled),
|
|
4650
5001
|
remove: (name) => api.removeSkill(name),
|
|
4651
5002
|
// Seed the request into the main chat — the agent researches or authors
|
|
4652
|
-
// the skill there (research_agent +
|
|
5003
|
+
// the skill there (research_agent + the skill tool's install action),
|
|
5004
|
+
// visible in the transcript.
|
|
4653
5005
|
requestInstall: (query) => {
|
|
4654
5006
|
void sendNow(
|
|
4655
|
-
`Install a skill for me: ${query}. Find the skill's published files (or author a proper SKILL.md from your research), install it
|
|
5007
|
+
`Install a skill for me: ${query}. Find the skill's published files (or author a proper SKILL.md from your research), install it, then tell me what it can do.`
|
|
4656
5008
|
);
|
|
4657
5009
|
}
|
|
4658
5010
|
};
|
|
@@ -4696,6 +5048,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4696
5048
|
},
|
|
4697
5049
|
{ name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
|
|
4698
5050
|
{ name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
|
|
5051
|
+
{ name: "update", label: "Check for updates", hint: "check npm for a newer version", run: () => runUpdateCommand(tui) },
|
|
4699
5052
|
{ name: "logout", label: "Sign out", hint: "delete the saved token & quit", run: () => logout() },
|
|
4700
5053
|
{ name: "quit", label: "Quit", run: () => quit() }
|
|
4701
5054
|
]);
|
|
@@ -4751,6 +5104,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4751
5104
|
await Promise.all([bridge.connect(), stream.connect()]);
|
|
4752
5105
|
void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
|
|
4753
5106
|
});
|
|
5107
|
+
attaching.stop();
|
|
4754
5108
|
tui.banner([
|
|
4755
5109
|
`${c.bold}${c.magenta}Standard Code${c.reset} ${c.dim}\u2014 coding agent${c.reset}`,
|
|
4756
5110
|
`${c.gray}project:${c.reset} ${projectDir}`,
|
|
@@ -4951,6 +5305,36 @@ function showKeybindings(tui) {
|
|
|
4951
5305
|
tui.print(`${c.gray} \u2190${c.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
|
|
4952
5306
|
tui.print(`${c.gray} ctrl-c${c.reset} quit`);
|
|
4953
5307
|
}
|
|
5308
|
+
async function runUpdateCommand(tui) {
|
|
5309
|
+
const version = readVersion();
|
|
5310
|
+
const result = await forceCheckForUpdate(version);
|
|
5311
|
+
if (!result) {
|
|
5312
|
+
tui.print(`${c.green}\u2713${c.reset} ${c.gray}@standardagents/code${c.reset} is up to date (v${version})`);
|
|
5313
|
+
return;
|
|
5314
|
+
}
|
|
5315
|
+
const { latest } = result;
|
|
5316
|
+
tui.print(`
|
|
5317
|
+
${c.yellow}\u27F3${c.reset} Update available: ${c.gray}v${version}${c.reset} \u2192 ${c.green}v${latest}${c.reset}`);
|
|
5318
|
+
const choice = await tui.select(`Update now with \`npm i -g @standardagents/code@latest\`?`, [
|
|
5319
|
+
{ label: "Yes, update now", value: "yes" },
|
|
5320
|
+
{ label: "No, skip", value: "no" }
|
|
5321
|
+
]);
|
|
5322
|
+
if (choice === "yes") {
|
|
5323
|
+
tui.print(` ${c.gray}Running npm i -g @standardagents/code@latest\u2026${c.reset}`);
|
|
5324
|
+
try {
|
|
5325
|
+
const ok = await runNpmUpdate();
|
|
5326
|
+
if (ok) {
|
|
5327
|
+
tui.print(` ${c.green}\u2713${c.reset} Updated to v${latest}. Restart to use the new version.`);
|
|
5328
|
+
} else {
|
|
5329
|
+
tui.print(` ${c.red}\u2717${c.reset} Update failed.`);
|
|
5330
|
+
}
|
|
5331
|
+
} catch (e) {
|
|
5332
|
+
tui.print(` ${c.red}\u2717${c.reset} Update failed: ${e}`);
|
|
5333
|
+
}
|
|
5334
|
+
} else {
|
|
5335
|
+
tui.print(` ${c.gray}Skipped. Run /update later.${c.reset}`);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
4954
5338
|
async function runProcessMenu(tui, bg) {
|
|
4955
5339
|
const procs = await bg.list();
|
|
4956
5340
|
if (!procs.length) {
|