@standardagents/code 0.3.2 → 0.5.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.
- package/dist/index.js +289 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -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
|
|
@@ -1986,8 +2125,13 @@ var Tui = class _Tui {
|
|
|
1986
2125
|
// caret's row offset from the input region top, last render
|
|
1987
2126
|
working = false;
|
|
1988
2127
|
workingStart = 0;
|
|
2128
|
+
// Slot-machine morph for the working label (Working → Brewing → …).
|
|
2129
|
+
wordMill = new WordMill();
|
|
1989
2130
|
spinnerTimer = null;
|
|
1990
2131
|
bgCount = 0;
|
|
2132
|
+
// The `[⚙ n bg]` prompt badge can be selected with ← from the start of the
|
|
2133
|
+
// input (rendered inverted); Enter then opens the background-process panel.
|
|
2134
|
+
bgBadgeSelected = false;
|
|
1991
2135
|
queuedCount = 0;
|
|
1992
2136
|
subagents = [];
|
|
1993
2137
|
// active subagents (one line each)
|
|
@@ -2069,6 +2213,9 @@ var Tui = class _Tui {
|
|
|
2069
2213
|
/** Up on the top row: return true to consume it (e.g. pull a queued message)
|
|
2070
2214
|
* before history recall gets a chance. */
|
|
2071
2215
|
onUpArrow = () => false;
|
|
2216
|
+
/** Enter while the `[⚙ n bg]` badge is selected — opens the bg process panel. */
|
|
2217
|
+
onBgBadge = () => {
|
|
2218
|
+
};
|
|
2072
2219
|
onQuit = () => process.exit(0);
|
|
2073
2220
|
levelListeners = [];
|
|
2074
2221
|
get colors() {
|
|
@@ -2146,6 +2293,21 @@ var Tui = class _Tui {
|
|
|
2146
2293
|
return;
|
|
2147
2294
|
}
|
|
2148
2295
|
if (!key) return;
|
|
2296
|
+
if (this.bgBadgeSelected) {
|
|
2297
|
+
if (key.name === "return" || key.name === "enter") {
|
|
2298
|
+
this.bgBadgeSelected = false;
|
|
2299
|
+
this.renderBottom();
|
|
2300
|
+
this.onBgBadge();
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
if (key.name === "left") return;
|
|
2304
|
+
this.bgBadgeSelected = false;
|
|
2305
|
+
if (key.name === "right" || key.name === "escape") {
|
|
2306
|
+
this.renderBottom();
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
this.renderBottom();
|
|
2310
|
+
}
|
|
2149
2311
|
if (this.paletteOpen()) {
|
|
2150
2312
|
const matches = this.filteredCommands();
|
|
2151
2313
|
const cur = matches.length ? Math.min(this.slashIdx, matches.length - 1) : 0;
|
|
@@ -2196,6 +2358,9 @@ var Tui = class _Tui {
|
|
|
2196
2358
|
if (this.cursorPos > 0) {
|
|
2197
2359
|
this.cursorPos--;
|
|
2198
2360
|
this.renderBottom();
|
|
2361
|
+
} else if (this.bgCount > 0) {
|
|
2362
|
+
this.bgBadgeSelected = true;
|
|
2363
|
+
this.renderBottom();
|
|
2199
2364
|
}
|
|
2200
2365
|
return;
|
|
2201
2366
|
}
|
|
@@ -2471,7 +2636,8 @@ var Tui = class _Tui {
|
|
|
2471
2636
|
return `${(n / 1e6).toFixed(2)}M`;
|
|
2472
2637
|
}
|
|
2473
2638
|
spinnerFrame() {
|
|
2474
|
-
|
|
2639
|
+
const now = Date.now();
|
|
2640
|
+
return `${C.bold}${brandCycleColor(now)}${FRAMES[Math.floor(now / SPINNER_MS) % FRAMES.length]}${C.reset}`;
|
|
2475
2641
|
}
|
|
2476
2642
|
/** "↑X ↓Y" cumulative token totals (greyed — low-priority). */
|
|
2477
2643
|
tokensText() {
|
|
@@ -2544,7 +2710,7 @@ var Tui = class _Tui {
|
|
|
2544
2710
|
if (this.working) {
|
|
2545
2711
|
const el = this.formatElapsed(Date.now() - this.workingStart);
|
|
2546
2712
|
const right = `${C.dim}${el}${C.reset}${tk ? " " + tk : ""}`;
|
|
2547
|
-
const head = `${this.spinnerFrame()} ${
|
|
2713
|
+
const head = `${this.spinnerFrame()} ${this.wordMill.text(Date.now())}`;
|
|
2548
2714
|
const avail = Math.max(
|
|
2549
2715
|
0,
|
|
2550
2716
|
cols2 - this.visibleWidth(head) - this.visibleWidth(right) - 2 - gaugeReserve
|
|
@@ -2570,7 +2736,8 @@ var Tui = class _Tui {
|
|
|
2570
2736
|
/** The prompt line prefix (with ANSI colour) that precedes the typed text. */
|
|
2571
2737
|
promptPrefix() {
|
|
2572
2738
|
const q = this.queuedCount > 0 ? `${C.yellow}[\u23F3 ${this.queuedCount} queued]${C.reset} ` : "";
|
|
2573
|
-
const
|
|
2739
|
+
const bgText = `[\u2699 ${this.bgCount} bg]`;
|
|
2740
|
+
const bg = this.bgCount > 0 ? this.bgBadgeSelected ? `${C.cyan}\x1B[7m${bgText}\x1B[27m${C.reset} ` : `${C.cyan}${bgText}${C.reset} ` : "";
|
|
2574
2741
|
return `${q}${bg}${this.levelColor()}\u276F${C.reset} `;
|
|
2575
2742
|
}
|
|
2576
2743
|
visibleWidth(s) {
|
|
@@ -2672,7 +2839,7 @@ var Tui = class _Tui {
|
|
|
2672
2839
|
const quitLine = this.quitArmed ? `${C.dim}Press Control-C again to exit${C.reset}` : null;
|
|
2673
2840
|
const quitRows = quitLine ? 1 : 0;
|
|
2674
2841
|
if (quitLine) writeHudRow(quitLine);
|
|
2675
|
-
const frame = FRAMES[Math.floor(Date.now() /
|
|
2842
|
+
const frame = FRAMES[Math.floor(Date.now() / SPINNER_MS) % FRAMES.length];
|
|
2676
2843
|
for (const sub of this.subagents) {
|
|
2677
2844
|
const color = sub.agentName === COMPACTION_AGENT ? COMPACTION_COLOR : SUBAGENT_COLORS[this.subagentColorByID.get(sub.id) ?? 0];
|
|
2678
2845
|
const budget = cols2 - 10;
|
|
@@ -2961,6 +3128,7 @@ var Tui = class _Tui {
|
|
|
2961
3128
|
if (on && !this.working) {
|
|
2962
3129
|
this.working = true;
|
|
2963
3130
|
this.workingStart = Date.now();
|
|
3131
|
+
this.wordMill.reset(this.workingStart);
|
|
2964
3132
|
this.goalComplete = false;
|
|
2965
3133
|
} else if (!on) {
|
|
2966
3134
|
this.working = false;
|
|
@@ -2992,7 +3160,7 @@ var Tui = class _Tui {
|
|
|
2992
3160
|
syncSpinner() {
|
|
2993
3161
|
const spinning = this.working || this.subagents.length > 0;
|
|
2994
3162
|
if (spinning && !this.spinnerTimer) {
|
|
2995
|
-
this.spinnerTimer = setInterval(() => this.renderBottom(),
|
|
3163
|
+
this.spinnerTimer = setInterval(() => this.renderBottom(), SPINNER_MS);
|
|
2996
3164
|
} else if (!spinning && this.spinnerTimer) {
|
|
2997
3165
|
clearInterval(this.spinnerTimer);
|
|
2998
3166
|
this.spinnerTimer = null;
|
|
@@ -3003,6 +3171,7 @@ var Tui = class _Tui {
|
|
|
3003
3171
|
}
|
|
3004
3172
|
setBackgroundCount(n) {
|
|
3005
3173
|
this.bgCount = n;
|
|
3174
|
+
if (n === 0) this.bgBadgeSelected = false;
|
|
3006
3175
|
this.renderBottom();
|
|
3007
3176
|
}
|
|
3008
3177
|
setQueuedCount(n) {
|
|
@@ -3047,6 +3216,7 @@ var Tui = class _Tui {
|
|
|
3047
3216
|
}
|
|
3048
3217
|
// ─── takeover helpers (approval / menus) ───────────────────────────────────
|
|
3049
3218
|
beginTakeover() {
|
|
3219
|
+
this.bgBadgeSelected = false;
|
|
3050
3220
|
this.clearBottom();
|
|
3051
3221
|
process.stdout.write("\r\x1B[K");
|
|
3052
3222
|
process.stdout.write("\x1B[?25l");
|
|
@@ -3269,8 +3439,8 @@ function appendHistory(store, threadId, history, text) {
|
|
|
3269
3439
|
// src/markdown.ts
|
|
3270
3440
|
var ESC = "\x1B[";
|
|
3271
3441
|
var R = ESC + "0m";
|
|
3272
|
-
var
|
|
3273
|
-
var
|
|
3442
|
+
var BOLD2 = ESC + "1m";
|
|
3443
|
+
var DIM3 = ESC + "2m";
|
|
3274
3444
|
var ITAL = ESC + "3m";
|
|
3275
3445
|
var UNDER = ESC + "4m";
|
|
3276
3446
|
var TEAL = ESC + "38;5;37m";
|
|
@@ -3292,11 +3462,11 @@ function inline(s) {
|
|
|
3292
3462
|
});
|
|
3293
3463
|
s = s.replace(
|
|
3294
3464
|
/\[([^\]]+)\]\(([^)\s]+)\)/g,
|
|
3295
|
-
(_, text, url) => `${CYAN}${UNDER}${text}${R} ${
|
|
3465
|
+
(_, text, url) => `${CYAN}${UNDER}${text}${R} ${DIM3}${url}${R}`
|
|
3296
3466
|
);
|
|
3297
|
-
s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${
|
|
3467
|
+
s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${BOLD2}${t}${R}`);
|
|
3298
3468
|
s = s.replace(/\*([^*\n]+)\*/g, (_, t) => `${ITAL}${t}${R}`);
|
|
3299
|
-
s = s.replace(/~~([^~]+)~~/g, (_, t) => `${
|
|
3469
|
+
s = s.replace(/~~([^~]+)~~/g, (_, t) => `${DIM3}${t}${R}`);
|
|
3300
3470
|
s = s.replace(/\x00(\d+)\x00/g, (_, i) => `${TEAL}${codes[+i].replace(/ /g, String.fromCharCode(160))}${R}`);
|
|
3301
3471
|
return s;
|
|
3302
3472
|
}
|
|
@@ -3349,7 +3519,7 @@ function renderTable(rows) {
|
|
|
3349
3519
|
const cells = [];
|
|
3350
3520
|
for (let c2 = 0; c2 < cols2; c2++) {
|
|
3351
3521
|
const raw = r[c2] ?? "";
|
|
3352
|
-
const styled = ri === 0 ? `${
|
|
3522
|
+
const styled = ri === 0 ? `${BOLD2}${inline(raw)}${R}` : inline(raw);
|
|
3353
3523
|
cells.push(padEndVisible(styled, widths[c2]));
|
|
3354
3524
|
}
|
|
3355
3525
|
out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
|
|
@@ -3389,7 +3559,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3389
3559
|
}
|
|
3390
3560
|
const heading = line.match(/^(#{1,6})\s+(.*)$/);
|
|
3391
3561
|
if (heading) {
|
|
3392
|
-
for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${
|
|
3562
|
+
for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${BOLD2}${TEAL}${ln}${R}`);
|
|
3393
3563
|
i++;
|
|
3394
3564
|
continue;
|
|
3395
3565
|
}
|
|
@@ -3401,7 +3571,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3401
3571
|
const quote = line.match(/^\s*>\s?(.*)$/);
|
|
3402
3572
|
if (quote) {
|
|
3403
3573
|
for (const ln of wrapStyled(inline(quote[1]), Math.max(8, cols2 - 2))) {
|
|
3404
|
-
out.push(`${GRAY}\u2502${R} ${
|
|
3574
|
+
out.push(`${GRAY}\u2502${R} ${DIM3}${ln}${R}`);
|
|
3405
3575
|
}
|
|
3406
3576
|
i++;
|
|
3407
3577
|
continue;
|
|
@@ -3417,7 +3587,7 @@ function renderMarkdown(src, cols2 = 80) {
|
|
|
3417
3587
|
if (numbered) {
|
|
3418
3588
|
const marker = `${numbered[2]}${numbered[3]}`;
|
|
3419
3589
|
const leadWidth = numbered[1].length + marker.length + 1;
|
|
3420
|
-
wrapBlock(out, cols2, `${numbered[1]}${
|
|
3590
|
+
wrapBlock(out, cols2, `${numbered[1]}${BOLD2}${marker}${R} `, " ".repeat(leadWidth), leadWidth, inline(numbered[4]));
|
|
3421
3591
|
i++;
|
|
3422
3592
|
continue;
|
|
3423
3593
|
}
|
|
@@ -3706,7 +3876,7 @@ var McpManager = class {
|
|
|
3706
3876
|
}));
|
|
3707
3877
|
return {
|
|
3708
3878
|
ok: true,
|
|
3709
|
-
result: servers.length ? JSON.stringify({ servers }, null, 2) :
|
|
3879
|
+
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").'
|
|
3710
3880
|
};
|
|
3711
3881
|
}
|
|
3712
3882
|
if (action === "list_tools") {
|
|
@@ -3839,6 +4009,16 @@ function saveCredential(cred, options = {}) {
|
|
|
3839
4009
|
} catch {
|
|
3840
4010
|
}
|
|
3841
4011
|
}
|
|
4012
|
+
function deleteCredential(endpoint) {
|
|
4013
|
+
const creds = loadCredentials();
|
|
4014
|
+
delete creds.instances[normalizeEndpoint(endpoint)];
|
|
4015
|
+
fs4.mkdirSync(DIR, { recursive: true });
|
|
4016
|
+
fs4.writeFileSync(FILE, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
4017
|
+
try {
|
|
4018
|
+
fs4.chmodSync(FILE, 384);
|
|
4019
|
+
} catch {
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
3842
4022
|
function defaultEndpoint() {
|
|
3843
4023
|
return loadCredentials().default_endpoint ?? null;
|
|
3844
4024
|
}
|
|
@@ -3955,7 +4135,25 @@ function printAssistant(tui, text) {
|
|
|
3955
4135
|
}
|
|
3956
4136
|
tui.print("");
|
|
3957
4137
|
}
|
|
4138
|
+
function startLoader(label) {
|
|
4139
|
+
const frames = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FB", "\u28FD", "\u28FE"];
|
|
4140
|
+
stdout.write("\x1B[?25l");
|
|
4141
|
+
const draw = () => {
|
|
4142
|
+
const now = Date.now();
|
|
4143
|
+
const f = frames[Math.floor(now / 70) % frames.length];
|
|
4144
|
+
stdout.write(`\r\x1B[K${brandCycleColor(now)}${f}${c.reset} ${c.dim}${label}\u2026${c.reset}`);
|
|
4145
|
+
};
|
|
4146
|
+
draw();
|
|
4147
|
+
const timer = setInterval(draw, 70);
|
|
4148
|
+
return {
|
|
4149
|
+
stop: () => {
|
|
4150
|
+
clearInterval(timer);
|
|
4151
|
+
stdout.write("\r\x1B[K\x1B[?25h");
|
|
4152
|
+
}
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
3958
4155
|
function farewell(stoppedProcs = 0) {
|
|
4156
|
+
stdout.write("\x1B[?25h");
|
|
3959
4157
|
if (stoppedProcs > 0) {
|
|
3960
4158
|
stdout.write(
|
|
3961
4159
|
`
|
|
@@ -4004,7 +4202,7 @@ function printWelcome(endpoint, projectDir) {
|
|
|
4004
4202
|
const meta = [
|
|
4005
4203
|
`${c.bold}${gradientText("Standard Code")}${c.reset}${version ? ` ${c.dim}v${version}${c.reset}` : ""}`,
|
|
4006
4204
|
`${c.dim}terminal coding agent${c.reset}`,
|
|
4007
|
-
`${c.teal}${host}${c.reset}
|
|
4205
|
+
...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c.teal}${host}${c.reset}`],
|
|
4008
4206
|
`${c.dim}${dir}${c.reset}`
|
|
4009
4207
|
];
|
|
4010
4208
|
const markWidth = Math.max(...LOGO_MARK.map((l) => [...l].length));
|
|
@@ -4120,7 +4318,12 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4120
4318
|
}
|
|
4121
4319
|
const stored = getCredential(endpoint);
|
|
4122
4320
|
let api = stored ? new ApiClient(endpoint, stored.access_token) : null;
|
|
4123
|
-
|
|
4321
|
+
let storedCheck = null;
|
|
4322
|
+
if (api) {
|
|
4323
|
+
const connecting = startLoader("Connecting to Standard Agents");
|
|
4324
|
+
storedCheck = await api.verifyDetailed();
|
|
4325
|
+
connecting.stop();
|
|
4326
|
+
}
|
|
4124
4327
|
if (!api || !storedCheck?.ok) {
|
|
4125
4328
|
const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
4126
4329
|
if (storedCheck && !storedCheck.ok) {
|
|
@@ -4162,7 +4365,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4162
4365
|
});
|
|
4163
4366
|
if (!got) continue;
|
|
4164
4367
|
api = new ApiClient(endpoint, got);
|
|
4368
|
+
const connecting2 = startLoader("Connecting to Standard Agents");
|
|
4165
4369
|
const check2 = await api.verifyDetailed();
|
|
4370
|
+
connecting2.stop();
|
|
4166
4371
|
if (check2.ok) {
|
|
4167
4372
|
saveCredential(
|
|
4168
4373
|
{ endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
|
|
@@ -4176,7 +4381,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4176
4381
|
continue;
|
|
4177
4382
|
}
|
|
4178
4383
|
api = new ApiClient(endpoint, token);
|
|
4384
|
+
const connecting = startLoader("Connecting to Standard Agents");
|
|
4179
4385
|
const check = await api.verifyDetailed();
|
|
4386
|
+
connecting.stop();
|
|
4180
4387
|
if (check.ok) {
|
|
4181
4388
|
saveCredential(
|
|
4182
4389
|
{ endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
|
|
@@ -4195,18 +4402,20 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4195
4402
|
handoffClosing = true;
|
|
4196
4403
|
reader.rl?.close();
|
|
4197
4404
|
const tags = [`path:${projectDir}`, `machine:${machine}`];
|
|
4405
|
+
const loadingSessions = startLoader("Loading sessions");
|
|
4198
4406
|
let existing = [];
|
|
4199
4407
|
try {
|
|
4200
4408
|
existing = await api.listThreads(AGENT_ID_VARIANTS, tags);
|
|
4201
4409
|
} catch {
|
|
4202
4410
|
existing = [];
|
|
4203
4411
|
}
|
|
4412
|
+
const summaries = existing.length > 0 ? await summarizeThreads(api, existing.slice(0, 8)) : [];
|
|
4413
|
+
loadingSessions.stop();
|
|
4204
4414
|
const tui = new Tui(1);
|
|
4205
4415
|
let threadId;
|
|
4206
4416
|
let resumed = false;
|
|
4207
4417
|
let historySeed;
|
|
4208
4418
|
if (existing.length > 0) {
|
|
4209
|
-
const summaries = await summarizeThreads(api, existing.slice(0, 8));
|
|
4210
4419
|
const items = summaries.map((s) => ({
|
|
4211
4420
|
label: s.label,
|
|
4212
4421
|
hint: s.hint,
|
|
@@ -4230,7 +4439,12 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4230
4439
|
} else {
|
|
4231
4440
|
threadId = await api.createThread(AGENT_ID, tags);
|
|
4232
4441
|
}
|
|
4233
|
-
|
|
4442
|
+
for (; ; ) {
|
|
4443
|
+
await runInteractive(tui, api, threadId, projectDir, machine, resumed, historySeed);
|
|
4444
|
+
historySeed = threadId;
|
|
4445
|
+
threadId = await api.createThread(AGENT_ID, tags);
|
|
4446
|
+
resumed = false;
|
|
4447
|
+
}
|
|
4234
4448
|
}
|
|
4235
4449
|
async function summarizeThreads(api, threads) {
|
|
4236
4450
|
return Promise.all(
|
|
@@ -4367,6 +4581,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
4367
4581
|
saveApprovals(api, threadId, perm);
|
|
4368
4582
|
void api.kvSet(threadId, "session_info", { cwd: projectDir, machine }).catch(() => {
|
|
4369
4583
|
});
|
|
4584
|
+
const attaching = startLoader("Attaching to thread");
|
|
4370
4585
|
let busy = false;
|
|
4371
4586
|
let interrupting = false;
|
|
4372
4587
|
const queued = [];
|
|
@@ -4502,27 +4717,30 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4502
4717
|
if (activeSubagents.size > 0) scheduleReconcile();
|
|
4503
4718
|
}, 5e3);
|
|
4504
4719
|
heartbeatPoll.unref();
|
|
4720
|
+
let endSession;
|
|
4721
|
+
const sessionEnded = new Promise((r) => endSession = r);
|
|
4505
4722
|
const quit = async () => {
|
|
4506
4723
|
tui.end();
|
|
4507
|
-
const
|
|
4724
|
+
const stopped2 = api.stop(threadId).catch(() => {
|
|
4508
4725
|
});
|
|
4509
|
-
const
|
|
4726
|
+
const procsStopped2 = host.stopAllLocalProcesses().catch(() => 0);
|
|
4510
4727
|
bridge.close();
|
|
4511
4728
|
stream.close();
|
|
4512
4729
|
events.close();
|
|
4513
4730
|
mcp.closeAll();
|
|
4514
|
-
const [,
|
|
4515
|
-
Promise.all([
|
|
4731
|
+
const [, killed2] = await Promise.race([
|
|
4732
|
+
Promise.all([stopped2, procsStopped2]),
|
|
4516
4733
|
new Promise((r) => setTimeout(() => r([void 0, 0]), 1500))
|
|
4517
4734
|
]);
|
|
4518
|
-
farewell(
|
|
4735
|
+
farewell(killed2);
|
|
4519
4736
|
process.exit(0);
|
|
4520
4737
|
};
|
|
4521
4738
|
tui.setQuitHandler(quit);
|
|
4522
|
-
const
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
tui.print(`${c.gray}
|
|
4739
|
+
const logout = async () => {
|
|
4740
|
+
deleteCredential(api.origin);
|
|
4741
|
+
const instanceHost = api.origin.replace(/^https?:\/\//, "");
|
|
4742
|
+
tui.print(`${c.gray}Signed out \u2014 removed the saved token for ${c.teal}${instanceHost}${c.reset}${c.gray}. Run standardcode to sign in again.${c.reset}`);
|
|
4743
|
+
await quit();
|
|
4526
4744
|
};
|
|
4527
4745
|
const bgMgr = {
|
|
4528
4746
|
list: () => registry.list(),
|
|
@@ -4554,7 +4772,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4554
4772
|
return mcpCtl.connect(cfg);
|
|
4555
4773
|
},
|
|
4556
4774
|
// Seed the request into the main chat — the agent researches + installs it
|
|
4557
|
-
// there (using research_agent +
|
|
4775
|
+
// there (using research_agent + the mcp tool's install action), visible in
|
|
4776
|
+
// the transcript.
|
|
4558
4777
|
requestInstall: (query) => {
|
|
4559
4778
|
void sendNow(
|
|
4560
4779
|
`Install an MCP server for me: ${query}. Research the best one and its exact launch command, then install it.`
|
|
@@ -4604,14 +4823,21 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4604
4823
|
setEnabled: (name, enabled) => api.setSkillEnabled(name, enabled),
|
|
4605
4824
|
remove: (name) => api.removeSkill(name),
|
|
4606
4825
|
// Seed the request into the main chat — the agent researches or authors
|
|
4607
|
-
// the skill there (research_agent +
|
|
4826
|
+
// the skill there (research_agent + the skill tool's install action),
|
|
4827
|
+
// visible in the transcript.
|
|
4608
4828
|
requestInstall: (query) => {
|
|
4609
4829
|
void sendNow(
|
|
4610
|
-
`Install a skill for me: ${query}. Find the skill's published files (or author a proper SKILL.md from your research), install it
|
|
4830
|
+
`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.`
|
|
4611
4831
|
);
|
|
4612
4832
|
}
|
|
4613
4833
|
};
|
|
4614
4834
|
tui.setCommands([
|
|
4835
|
+
{
|
|
4836
|
+
name: "clear",
|
|
4837
|
+
label: "Clear conversation",
|
|
4838
|
+
hint: "start a fresh session",
|
|
4839
|
+
run: () => endSession()
|
|
4840
|
+
},
|
|
4615
4841
|
{
|
|
4616
4842
|
name: "compact",
|
|
4617
4843
|
label: "Compact conversation now",
|
|
@@ -4644,8 +4870,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4644
4870
|
run: () => runSkillsMenu(tui, skillsCtl)
|
|
4645
4871
|
},
|
|
4646
4872
|
{ name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
|
|
4647
|
-
{ name: "view", label: "View thread in AgentBuilder", run: () => viewThread() },
|
|
4648
4873
|
{ name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
|
|
4874
|
+
{ name: "logout", label: "Sign out", hint: "delete the saved token & quit", run: () => logout() },
|
|
4649
4875
|
{ name: "quit", label: "Quit", run: () => quit() }
|
|
4650
4876
|
]);
|
|
4651
4877
|
const history = await loadHistory(api, threadId, historySeedThreadId);
|
|
@@ -4684,6 +4910,10 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4684
4910
|
});
|
|
4685
4911
|
}
|
|
4686
4912
|
};
|
|
4913
|
+
tui.onBgBadge = () => {
|
|
4914
|
+
void runProcessMenu(tui, bgMgr).catch(() => {
|
|
4915
|
+
});
|
|
4916
|
+
};
|
|
4687
4917
|
tui.onUpArrow = () => {
|
|
4688
4918
|
if (tui.getInput().trim() || queued.length === 0) return false;
|
|
4689
4919
|
const q = queued.pop();
|
|
@@ -4696,6 +4926,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4696
4926
|
await Promise.all([bridge.connect(), stream.connect()]);
|
|
4697
4927
|
void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
|
|
4698
4928
|
});
|
|
4929
|
+
attaching.stop();
|
|
4699
4930
|
tui.banner([
|
|
4700
4931
|
`${c.bold}${c.magenta}Standard Code${c.reset} ${c.dim}\u2014 coding agent${c.reset}`,
|
|
4701
4932
|
`${c.gray}project:${c.reset} ${projectDir}`,
|
|
@@ -4795,10 +5026,33 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4795
5026
|
} catch {
|
|
4796
5027
|
}
|
|
4797
5028
|
};
|
|
4798
|
-
setInterval(() => void poll().catch(() => {
|
|
5029
|
+
const pollTimer = setInterval(() => void poll().catch(() => {
|
|
4799
5030
|
}), 1200);
|
|
4800
|
-
await
|
|
5031
|
+
await sessionEnded;
|
|
5032
|
+
clearInterval(pollTimer);
|
|
5033
|
+
clearInterval(heartbeatPoll);
|
|
5034
|
+
const stopped = api.stop(threadId).catch(() => {
|
|
4801
5035
|
});
|
|
5036
|
+
const procsStopped = host.stopAllLocalProcesses().catch(() => 0);
|
|
5037
|
+
bridge.close();
|
|
5038
|
+
stream.close();
|
|
5039
|
+
events.close();
|
|
5040
|
+
mcp.closeAll();
|
|
5041
|
+
const [, killed] = await Promise.race([
|
|
5042
|
+
Promise.all([stopped, procsStopped]),
|
|
5043
|
+
new Promise((r) => setTimeout(() => r([void 0, 0]), 1500))
|
|
5044
|
+
]);
|
|
5045
|
+
tui.setWorking(false);
|
|
5046
|
+
tui.setSubagents([]);
|
|
5047
|
+
tui.setGoal(null);
|
|
5048
|
+
tui.setQueuedCount(0);
|
|
5049
|
+
tui.setContextPct(null);
|
|
5050
|
+
tui.setStep(null, 0);
|
|
5051
|
+
tui.setBackgroundCount(0);
|
|
5052
|
+
if (killed > 0) {
|
|
5053
|
+
tui.print(`${c.cyan}\u2699${c.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
|
|
5054
|
+
}
|
|
5055
|
+
tui.print(`${c.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c.reset}`);
|
|
4802
5056
|
}
|
|
4803
5057
|
async function runSkillsMenu(tui, skills) {
|
|
4804
5058
|
let list;
|
|
@@ -4870,6 +5124,7 @@ function showKeybindings(tui) {
|
|
|
4870
5124
|
tui.print(`${c.gray} /${c.reset} open the command palette (type to filter)`);
|
|
4871
5125
|
tui.print(`${c.gray} ctrl-v${c.reset} paste an image from the clipboard ([#Image 1])`);
|
|
4872
5126
|
tui.print(`${c.gray} \u2191 / \u2193${c.reset} cycle past messages (on the input's top line)`);
|
|
5127
|
+
tui.print(`${c.gray} \u2190${c.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
|
|
4873
5128
|
tui.print(`${c.gray} ctrl-c${c.reset} quit`);
|
|
4874
5129
|
}
|
|
4875
5130
|
async function runProcessMenu(tui, bg) {
|