@standardagents/code 0.4.0 → 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 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,6 +2125,8 @@ 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;
1991
2132
  // The `[⚙ n bg]` prompt badge can be selected with ← from the start of the
@@ -2495,7 +2636,8 @@ var Tui = class _Tui {
2495
2636
  return `${(n / 1e6).toFixed(2)}M`;
2496
2637
  }
2497
2638
  spinnerFrame() {
2498
- return `${C.bold}${C.cyan}${FRAMES[Math.floor(Date.now() / 100) % FRAMES.length]}${C.reset}`;
2639
+ const now = Date.now();
2640
+ return `${C.bold}${brandCycleColor(now)}${FRAMES[Math.floor(now / SPINNER_MS) % FRAMES.length]}${C.reset}`;
2499
2641
  }
2500
2642
  /** "↑X ↓Y" cumulative token totals (greyed — low-priority). */
2501
2643
  tokensText() {
@@ -2568,7 +2710,7 @@ var Tui = class _Tui {
2568
2710
  if (this.working) {
2569
2711
  const el = this.formatElapsed(Date.now() - this.workingStart);
2570
2712
  const right = `${C.dim}${el}${C.reset}${tk ? " " + tk : ""}`;
2571
- const head = `${this.spinnerFrame()} ${C.bold}Working${C.reset}`;
2713
+ const head = `${this.spinnerFrame()} ${this.wordMill.text(Date.now())}`;
2572
2714
  const avail = Math.max(
2573
2715
  0,
2574
2716
  cols2 - this.visibleWidth(head) - this.visibleWidth(right) - 2 - gaugeReserve
@@ -2697,7 +2839,7 @@ var Tui = class _Tui {
2697
2839
  const quitLine = this.quitArmed ? `${C.dim}Press Control-C again to exit${C.reset}` : null;
2698
2840
  const quitRows = quitLine ? 1 : 0;
2699
2841
  if (quitLine) writeHudRow(quitLine);
2700
- const frame = FRAMES[Math.floor(Date.now() / 100) % FRAMES.length];
2842
+ const frame = FRAMES[Math.floor(Date.now() / SPINNER_MS) % FRAMES.length];
2701
2843
  for (const sub of this.subagents) {
2702
2844
  const color = sub.agentName === COMPACTION_AGENT ? COMPACTION_COLOR : SUBAGENT_COLORS[this.subagentColorByID.get(sub.id) ?? 0];
2703
2845
  const budget = cols2 - 10;
@@ -2986,6 +3128,7 @@ var Tui = class _Tui {
2986
3128
  if (on && !this.working) {
2987
3129
  this.working = true;
2988
3130
  this.workingStart = Date.now();
3131
+ this.wordMill.reset(this.workingStart);
2989
3132
  this.goalComplete = false;
2990
3133
  } else if (!on) {
2991
3134
  this.working = false;
@@ -3017,7 +3160,7 @@ var Tui = class _Tui {
3017
3160
  syncSpinner() {
3018
3161
  const spinning = this.working || this.subagents.length > 0;
3019
3162
  if (spinning && !this.spinnerTimer) {
3020
- this.spinnerTimer = setInterval(() => this.renderBottom(), 100);
3163
+ this.spinnerTimer = setInterval(() => this.renderBottom(), SPINNER_MS);
3021
3164
  } else if (!spinning && this.spinnerTimer) {
3022
3165
  clearInterval(this.spinnerTimer);
3023
3166
  this.spinnerTimer = null;
@@ -3296,8 +3439,8 @@ function appendHistory(store, threadId, history, text) {
3296
3439
  // src/markdown.ts
3297
3440
  var ESC = "\x1B[";
3298
3441
  var R = ESC + "0m";
3299
- var BOLD = ESC + "1m";
3300
- var DIM2 = ESC + "2m";
3442
+ var BOLD2 = ESC + "1m";
3443
+ var DIM3 = ESC + "2m";
3301
3444
  var ITAL = ESC + "3m";
3302
3445
  var UNDER = ESC + "4m";
3303
3446
  var TEAL = ESC + "38;5;37m";
@@ -3319,11 +3462,11 @@ function inline(s) {
3319
3462
  });
3320
3463
  s = s.replace(
3321
3464
  /\[([^\]]+)\]\(([^)\s]+)\)/g,
3322
- (_, text, url) => `${CYAN}${UNDER}${text}${R} ${DIM2}${url}${R}`
3465
+ (_, text, url) => `${CYAN}${UNDER}${text}${R} ${DIM3}${url}${R}`
3323
3466
  );
3324
- s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${BOLD}${t}${R}`);
3467
+ s = s.replace(/\*\*([^*]+)\*\*/g, (_, t) => `${BOLD2}${t}${R}`);
3325
3468
  s = s.replace(/\*([^*\n]+)\*/g, (_, t) => `${ITAL}${t}${R}`);
3326
- s = s.replace(/~~([^~]+)~~/g, (_, t) => `${DIM2}${t}${R}`);
3469
+ s = s.replace(/~~([^~]+)~~/g, (_, t) => `${DIM3}${t}${R}`);
3327
3470
  s = s.replace(/\x00(\d+)\x00/g, (_, i) => `${TEAL}${codes[+i].replace(/ /g, String.fromCharCode(160))}${R}`);
3328
3471
  return s;
3329
3472
  }
@@ -3376,7 +3519,7 @@ function renderTable(rows) {
3376
3519
  const cells = [];
3377
3520
  for (let c2 = 0; c2 < cols2; c2++) {
3378
3521
  const raw = r[c2] ?? "";
3379
- const styled = ri === 0 ? `${BOLD}${inline(raw)}${R}` : inline(raw);
3522
+ const styled = ri === 0 ? `${BOLD2}${inline(raw)}${R}` : inline(raw);
3380
3523
  cells.push(padEndVisible(styled, widths[c2]));
3381
3524
  }
3382
3525
  out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
@@ -3416,7 +3559,7 @@ function renderMarkdown(src, cols2 = 80) {
3416
3559
  }
3417
3560
  const heading = line.match(/^(#{1,6})\s+(.*)$/);
3418
3561
  if (heading) {
3419
- for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${BOLD}${TEAL}${ln}${R}`);
3562
+ for (const ln of wrapStyled(heading[2].trim(), cols2)) out.push(`${BOLD2}${TEAL}${ln}${R}`);
3420
3563
  i++;
3421
3564
  continue;
3422
3565
  }
@@ -3428,7 +3571,7 @@ function renderMarkdown(src, cols2 = 80) {
3428
3571
  const quote = line.match(/^\s*>\s?(.*)$/);
3429
3572
  if (quote) {
3430
3573
  for (const ln of wrapStyled(inline(quote[1]), Math.max(8, cols2 - 2))) {
3431
- out.push(`${GRAY}\u2502${R} ${DIM2}${ln}${R}`);
3574
+ out.push(`${GRAY}\u2502${R} ${DIM3}${ln}${R}`);
3432
3575
  }
3433
3576
  i++;
3434
3577
  continue;
@@ -3444,7 +3587,7 @@ function renderMarkdown(src, cols2 = 80) {
3444
3587
  if (numbered) {
3445
3588
  const marker = `${numbered[2]}${numbered[3]}`;
3446
3589
  const leadWidth = numbered[1].length + marker.length + 1;
3447
- wrapBlock(out, cols2, `${numbered[1]}${BOLD}${marker}${R} `, " ".repeat(leadWidth), leadWidth, inline(numbered[4]));
3590
+ wrapBlock(out, cols2, `${numbered[1]}${BOLD2}${marker}${R} `, " ".repeat(leadWidth), leadWidth, inline(numbered[4]));
3448
3591
  i++;
3449
3592
  continue;
3450
3593
  }
@@ -3733,7 +3876,7 @@ var McpManager = class {
3733
3876
  }));
3734
3877
  return {
3735
3878
  ok: true,
3736
- 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 install_mcp."
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").'
3737
3880
  };
3738
3881
  }
3739
3882
  if (action === "list_tools") {
@@ -3992,7 +4135,25 @@ function printAssistant(tui, text) {
3992
4135
  }
3993
4136
  tui.print("");
3994
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
+ }
3995
4155
  function farewell(stoppedProcs = 0) {
4156
+ stdout.write("\x1B[?25h");
3996
4157
  if (stoppedProcs > 0) {
3997
4158
  stdout.write(
3998
4159
  `
@@ -4041,7 +4202,7 @@ function printWelcome(endpoint, projectDir) {
4041
4202
  const meta = [
4042
4203
  `${c.bold}${gradientText("Standard Code")}${c.reset}${version ? ` ${c.dim}v${version}${c.reset}` : ""}`,
4043
4204
  `${c.dim}terminal coding agent${c.reset}`,
4044
- `${c.teal}${host}${c.reset}`,
4205
+ ...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c.teal}${host}${c.reset}`],
4045
4206
  `${c.dim}${dir}${c.reset}`
4046
4207
  ];
4047
4208
  const markWidth = Math.max(...LOGO_MARK.map((l) => [...l].length));
@@ -4157,7 +4318,12 @@ ${c.dim}Press Control-C again to exit${c.reset}
4157
4318
  }
4158
4319
  const stored = getCredential(endpoint);
4159
4320
  let api = stored ? new ApiClient(endpoint, stored.access_token) : null;
4160
- const storedCheck = api ? await api.verifyDetailed() : null;
4321
+ let storedCheck = null;
4322
+ if (api) {
4323
+ const connecting = startLoader("Connecting to Standard Agents");
4324
+ storedCheck = await api.verifyDetailed();
4325
+ connecting.stop();
4326
+ }
4161
4327
  if (!api || !storedCheck?.ok) {
4162
4328
  const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
4163
4329
  if (storedCheck && !storedCheck.ok) {
@@ -4199,7 +4365,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
4199
4365
  });
4200
4366
  if (!got) continue;
4201
4367
  api = new ApiClient(endpoint, got);
4368
+ const connecting2 = startLoader("Connecting to Standard Agents");
4202
4369
  const check2 = await api.verifyDetailed();
4370
+ connecting2.stop();
4203
4371
  if (check2.ok) {
4204
4372
  saveCredential(
4205
4373
  { endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
@@ -4213,7 +4381,9 @@ ${c.dim}Press Control-C again to exit${c.reset}
4213
4381
  continue;
4214
4382
  }
4215
4383
  api = new ApiClient(endpoint, token);
4384
+ const connecting = startLoader("Connecting to Standard Agents");
4216
4385
  const check = await api.verifyDetailed();
4386
+ connecting.stop();
4217
4387
  if (check.ok) {
4218
4388
  saveCredential(
4219
4389
  { endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
@@ -4232,18 +4402,20 @@ ${c.dim}Press Control-C again to exit${c.reset}
4232
4402
  handoffClosing = true;
4233
4403
  reader.rl?.close();
4234
4404
  const tags = [`path:${projectDir}`, `machine:${machine}`];
4405
+ const loadingSessions = startLoader("Loading sessions");
4235
4406
  let existing = [];
4236
4407
  try {
4237
4408
  existing = await api.listThreads(AGENT_ID_VARIANTS, tags);
4238
4409
  } catch {
4239
4410
  existing = [];
4240
4411
  }
4412
+ const summaries = existing.length > 0 ? await summarizeThreads(api, existing.slice(0, 8)) : [];
4413
+ loadingSessions.stop();
4241
4414
  const tui = new Tui(1);
4242
4415
  let threadId;
4243
4416
  let resumed = false;
4244
4417
  let historySeed;
4245
4418
  if (existing.length > 0) {
4246
- const summaries = await summarizeThreads(api, existing.slice(0, 8));
4247
4419
  const items = summaries.map((s) => ({
4248
4420
  label: s.label,
4249
4421
  hint: s.hint,
@@ -4409,6 +4581,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
4409
4581
  saveApprovals(api, threadId, perm);
4410
4582
  void api.kvSet(threadId, "session_info", { cwd: projectDir, machine }).catch(() => {
4411
4583
  });
4584
+ const attaching = startLoader("Attaching to thread");
4412
4585
  let busy = false;
4413
4586
  let interrupting = false;
4414
4587
  const queued = [];
@@ -4599,7 +4772,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
4599
4772
  return mcpCtl.connect(cfg);
4600
4773
  },
4601
4774
  // Seed the request into the main chat — the agent researches + installs it
4602
- // there (using research_agent + install_mcp), visible in the transcript.
4775
+ // there (using research_agent + the mcp tool's install action), visible in
4776
+ // the transcript.
4603
4777
  requestInstall: (query) => {
4604
4778
  void sendNow(
4605
4779
  `Install an MCP server for me: ${query}. Research the best one and its exact launch command, then install it.`
@@ -4649,10 +4823,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
4649
4823
  setEnabled: (name, enabled) => api.setSkillEnabled(name, enabled),
4650
4824
  remove: (name) => api.removeSkill(name),
4651
4825
  // Seed the request into the main chat — the agent researches or authors
4652
- // the skill there (research_agent + install_skill), visible in the transcript.
4826
+ // the skill there (research_agent + the skill tool's install action),
4827
+ // visible in the transcript.
4653
4828
  requestInstall: (query) => {
4654
4829
  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 with install_skill, then tell me what it can do.`
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.`
4656
4831
  );
4657
4832
  }
4658
4833
  };
@@ -4751,6 +4926,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
4751
4926
  await Promise.all([bridge.connect(), stream.connect()]);
4752
4927
  void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
4753
4928
  });
4929
+ attaching.stop();
4754
4930
  tui.banner([
4755
4931
  `${c.bold}${c.magenta}Standard Code${c.reset} ${c.dim}\u2014 coding agent${c.reset}`,
4756
4932
  `${c.gray}project:${c.reset} ${projectDir}`,