@butlerw/vellum 0.2.0 → 0.2.2

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/dist/index.mjs +201 -33
  2. package/package.json +4 -4
package/dist/index.mjs CHANGED
@@ -195908,7 +195908,7 @@ init_esm_shims();
195908
195908
 
195909
195909
  // src/version.ts
195910
195910
  init_esm_shims();
195911
- var version = "0.2.0" ;
195911
+ var version = "0.2.2" ;
195912
195912
 
195913
195913
  // src/tui/components/Banner/ShimmerText.tsx
195914
195914
  init_esm_shims();
@@ -195950,16 +195950,13 @@ function useShimmer(config = {}) {
195950
195950
  const cycleCountRef = useRef(0);
195951
195951
  const lastTickRef = useRef(null);
195952
195952
  const lastDebugRef = useRef(0);
195953
+ const hasAnimationTickRef = useRef(false);
195954
+ const { timestamp: timestamp2, isPaused } = useAnimation();
195953
195955
  const onCompleteRef = useRef(onComplete);
195954
195956
  onCompleteRef.current = onComplete;
195955
- useEffect(() => {
195956
- if (!isActive || isComplete) return;
195957
- lastTickRef.current = Date.now();
195958
- const timer = setInterval(() => {
195959
- const now = Date.now();
195960
- const lastTick = lastTickRef.current ?? now;
195961
- lastTickRef.current = now;
195962
- const deltaMs = now - lastTick;
195957
+ const advancePosition = useCallback(
195958
+ (deltaMs, nowMs) => {
195959
+ if (deltaMs <= 0) return;
195963
195960
  const step = deltaMs / cycleDuration;
195964
195961
  let nextPosition = positionRef.current + step;
195965
195962
  if (nextPosition >= 1) {
@@ -195968,7 +195965,6 @@ function useShimmer(config = {}) {
195968
195965
  cycleCountRef.current = updatedCycleCount;
195969
195966
  setCycleCount(updatedCycleCount);
195970
195967
  if (maxCycles !== void 0 && updatedCycleCount >= maxCycles) {
195971
- clearInterval(timer);
195972
195968
  setIsActive(false);
195973
195969
  setIsComplete(true);
195974
195970
  positionRef.current = 0.618;
@@ -195982,16 +195978,44 @@ function useShimmer(config = {}) {
195982
195978
  setPosition(nextPosition);
195983
195979
  if (SHIMMER_DEBUG_ENABLED) {
195984
195980
  const lastDebug = lastDebugRef.current;
195985
- if (now - lastDebug >= 1e3) {
195986
- lastDebugRef.current = now;
195981
+ if (nowMs - lastDebug >= 1e3) {
195982
+ lastDebugRef.current = nowMs;
195987
195983
  debugShimmer(
195988
195984
  `deltaMs=${deltaMs.toFixed(1)} position=${nextPosition.toFixed(3)} cycle=${cycleCountRef.current}`
195989
195985
  );
195990
195986
  }
195991
195987
  }
195988
+ },
195989
+ [cycleDuration, maxCycles]
195990
+ );
195991
+ useEffect(() => {
195992
+ if (timestamp2 !== 0) {
195993
+ hasAnimationTickRef.current = true;
195994
+ }
195995
+ }, [timestamp2]);
195996
+ const useGlobalClock = hasAnimationTickRef.current && !isPaused;
195997
+ useEffect(() => {
195998
+ lastTickRef.current = null;
195999
+ }, []);
196000
+ useEffect(() => {
196001
+ if (!useGlobalClock || !isActive || isComplete) return;
196002
+ const lastTick = lastTickRef.current ?? timestamp2;
196003
+ lastTickRef.current = timestamp2;
196004
+ const deltaMs = timestamp2 - lastTick;
196005
+ advancePosition(deltaMs, timestamp2);
196006
+ }, [useGlobalClock, isActive, isComplete, timestamp2, advancePosition]);
196007
+ useEffect(() => {
196008
+ if (useGlobalClock || !isActive || isComplete) return;
196009
+ lastTickRef.current = Date.now();
196010
+ const timer = setInterval(() => {
196011
+ const now = Date.now();
196012
+ const lastTick = lastTickRef.current ?? now;
196013
+ lastTickRef.current = now;
196014
+ const deltaMs = now - lastTick;
196015
+ advancePosition(deltaMs, now);
195992
196016
  }, updateInterval);
195993
196017
  return () => clearInterval(timer);
195994
- }, [isActive, cycleDuration, updateInterval, maxCycles, isComplete]);
196018
+ }, [useGlobalClock, isActive, isComplete, updateInterval, advancePosition]);
195995
196019
  useEffect(() => {
195996
196020
  if (enabled && !isComplete) {
195997
196021
  setIsActive(true);
@@ -196026,22 +196050,144 @@ function calculateShimmerIntensity(charIndex, totalChars, shimmerPosition, shimm
196026
196050
  const intensity = Math.cos(normalizedDistance * Math.PI * 0.5);
196027
196051
  return Math.max(0, intensity);
196028
196052
  }
196029
- function hexToRgb(hex) {
196030
- const cleaned = hex.replace("#", "");
196031
- const bigint = parseInt(cleaned, 16);
196032
- return {
196033
- r: bigint >> 16 & 255,
196034
- g: bigint >> 8 & 255,
196035
- b: bigint & 255
196036
- };
196053
+ var NAMED_COLORS = {
196054
+ black: "#000000",
196055
+ red: "#FF0000",
196056
+ green: "#008000",
196057
+ yellow: "#FFFF00",
196058
+ blue: "#0000FF",
196059
+ magenta: "#FF00FF",
196060
+ cyan: "#00FFFF",
196061
+ white: "#FFFFFF",
196062
+ gray: "#808080",
196063
+ grey: "#808080",
196064
+ orange: "#FFA500",
196065
+ pink: "#FFC0CB",
196066
+ purple: "#800080",
196067
+ brown: "#A52A2A",
196068
+ lime: "#00FF00",
196069
+ navy: "#000080",
196070
+ teal: "#008080",
196071
+ olive: "#808000",
196072
+ maroon: "#800000",
196073
+ aqua: "#00FFFF",
196074
+ fuchsia: "#FF00FF",
196075
+ silver: "#C0C0C0"
196076
+ };
196077
+ function clampRgb(value) {
196078
+ return Math.max(0, Math.min(255, Math.round(value)));
196079
+ }
196080
+ function parseHexColor(hex) {
196081
+ const cleaned = hex.replace("#", "").trim();
196082
+ if (cleaned.length === 3) {
196083
+ const rChar = cleaned[0];
196084
+ const gChar = cleaned[1];
196085
+ const bChar = cleaned[2];
196086
+ if (!rChar || !gChar || !bChar) return null;
196087
+ const r = parseInt(rChar + rChar, 16);
196088
+ const g = parseInt(gChar + gChar, 16);
196089
+ const b = parseInt(bChar + bChar, 16);
196090
+ if ([r, g, b].some((value) => Number.isNaN(value))) return null;
196091
+ return { r, g, b };
196092
+ }
196093
+ if (cleaned.length === 6) {
196094
+ const bigint = parseInt(cleaned, 16);
196095
+ if (Number.isNaN(bigint)) return null;
196096
+ return {
196097
+ r: bigint >> 16 & 255,
196098
+ g: bigint >> 8 & 255,
196099
+ b: bigint & 255
196100
+ };
196101
+ }
196102
+ return null;
196103
+ }
196104
+ function ansi256ToRgb(code) {
196105
+ if (!Number.isFinite(code)) return null;
196106
+ if (code < 0 || code > 255) return null;
196107
+ const ansi16 = [
196108
+ { r: 0, g: 0, b: 0 },
196109
+ // 0 black
196110
+ { r: 128, g: 0, b: 0 },
196111
+ // 1 red
196112
+ { r: 0, g: 128, b: 0 },
196113
+ // 2 green
196114
+ { r: 128, g: 128, b: 0 },
196115
+ // 3 yellow
196116
+ { r: 0, g: 0, b: 128 },
196117
+ // 4 blue
196118
+ { r: 128, g: 0, b: 128 },
196119
+ // 5 magenta
196120
+ { r: 0, g: 128, b: 128 },
196121
+ // 6 cyan
196122
+ { r: 192, g: 192, b: 192 },
196123
+ // 7 white (light gray)
196124
+ { r: 128, g: 128, b: 128 },
196125
+ // 8 bright black (dark gray)
196126
+ { r: 255, g: 0, b: 0 },
196127
+ // 9 bright red
196128
+ { r: 0, g: 255, b: 0 },
196129
+ // 10 bright green
196130
+ { r: 255, g: 255, b: 0 },
196131
+ // 11 bright yellow
196132
+ { r: 0, g: 0, b: 255 },
196133
+ // 12 bright blue
196134
+ { r: 255, g: 0, b: 255 },
196135
+ // 13 bright magenta
196136
+ { r: 0, g: 255, b: 255 },
196137
+ // 14 bright cyan
196138
+ { r: 255, g: 255, b: 255 }
196139
+ // 15 bright white
196140
+ ];
196141
+ if (code < 16) {
196142
+ return ansi16[code] ?? null;
196143
+ }
196144
+ if (code >= 232) {
196145
+ const gray = (code - 232) * 10 + 8;
196146
+ return { r: gray, g: gray, b: gray };
196147
+ }
196148
+ const index = code - 16;
196149
+ const r = Math.floor(index / 36);
196150
+ const g = Math.floor(index % 36 / 6);
196151
+ const b = index % 6;
196152
+ const toChannel = (value) => value === 0 ? 0 : value * 40 + 55;
196153
+ return { r: toChannel(r), g: toChannel(g), b: toChannel(b) };
196154
+ }
196155
+ function parseColorToRgb(color) {
196156
+ const trimmed2 = color.trim().toLowerCase();
196157
+ if (!trimmed2) {
196158
+ return { r: 128, g: 128, b: 128 };
196159
+ }
196160
+ const named = NAMED_COLORS[trimmed2];
196161
+ if (named) {
196162
+ return parseHexColor(named) ?? { r: 128, g: 128, b: 128 };
196163
+ }
196164
+ const hexMatch = trimmed2.match(/^#?[0-9a-f]{3}$|^#?[0-9a-f]{6}$/i);
196165
+ if (hexMatch) {
196166
+ return parseHexColor(trimmed2) ?? { r: 128, g: 128, b: 128 };
196167
+ }
196168
+ const rgbMatch = trimmed2.match(
196169
+ /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([\d.]+))?\s*\)$/
196170
+ );
196171
+ if (rgbMatch) {
196172
+ const r = clampRgb(Number.parseInt(rgbMatch[1] ?? "0", 10));
196173
+ const g = clampRgb(Number.parseInt(rgbMatch[2] ?? "0", 10));
196174
+ const b = clampRgb(Number.parseInt(rgbMatch[3] ?? "0", 10));
196175
+ return { r, g, b };
196176
+ }
196177
+ const ansiMatch = trimmed2.match(/^ansi256\(\s*(\d{1,3})\s*\)$/i);
196178
+ if (ansiMatch) {
196179
+ const code = Number.parseInt(ansiMatch[1] ?? "", 10);
196180
+ return ansi256ToRgb(code) ?? { r: 128, g: 128, b: 128 };
196181
+ }
196182
+ return { r: 128, g: 128, b: 128 };
196037
196183
  }
196038
196184
  function rgbToHex(r, g, b) {
196039
196185
  const toHex = (n) => Math.round(n).toString(16).padStart(2, "0");
196040
196186
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
196041
196187
  }
196042
196188
  function interpolateColor(baseColor, highlightColor, intensity) {
196043
- const base = hexToRgb(baseColor);
196044
- const highlight = hexToRgb(highlightColor);
196189
+ const base = parseColorToRgb(baseColor);
196190
+ const highlight = parseColorToRgb(highlightColor);
196045
196191
  const r = base.r + (highlight.r - base.r) * intensity;
196046
196192
  const g = base.g + (highlight.g - base.g) * intensity;
196047
196193
  const b = base.b + (highlight.b - base.b) * intensity;
@@ -199969,6 +200115,27 @@ function StreamingIndicatorImpl({
199969
200115
  }
199970
200116
  };
199971
200117
  const phaseColor = getPhaseColor();
200118
+ const highlightColor = theme.brand.highlight ?? "#FFD700";
200119
+ const shouldShimmerLabel = phase === "thinking" || phase === "generating" || phase === "tool_call" || phase === "tool_executing";
200120
+ const renderLabel = () => {
200121
+ if (!shouldShimmerLabel) {
200122
+ return /* @__PURE__ */ jsxs(Text, { color: phaseColor, children: [
200123
+ " ",
200124
+ displayLabel
200125
+ ] });
200126
+ }
200127
+ return /* @__PURE__ */ jsx(
200128
+ BannerShimmerText,
200129
+ {
200130
+ baseColor: phaseColor,
200131
+ highlightColor,
200132
+ shimmerConfig: { cycleDuration: 2e3 },
200133
+ shimmerWidth: 0.2,
200134
+ bold: true,
200135
+ children: ` ${displayLabel}`
200136
+ }
200137
+ );
200138
+ };
199972
200139
  if (narrow) {
199973
200140
  const icon = PHASE_ICONS[phase];
199974
200141
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
@@ -199978,10 +200145,7 @@ function StreamingIndicatorImpl({
199978
200145
  " ",
199979
200146
  icon
199980
200147
  ] }),
199981
- /* @__PURE__ */ jsxs(Text, { color: phaseColor, children: [
199982
- " ",
199983
- displayLabel
199984
- ] })
200148
+ renderLabel()
199985
200149
  ] }),
199986
200150
  meta && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: meta }) }),
199987
200151
  rightContent && /* @__PURE__ */ jsx(Box, { children: rightContent })
@@ -199989,10 +200153,7 @@ function StreamingIndicatorImpl({
199989
200153
  }
199990
200154
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "center", children: [
199991
200155
  /* @__PURE__ */ jsx(Spinner, { color: phaseColor, frames: spinnerFrames }),
199992
- /* @__PURE__ */ jsxs(Text, { color: phaseColor, children: [
199993
- " ",
199994
- displayLabel
199995
- ] }),
200156
+ renderLabel(),
199996
200157
  meta && /* @__PURE__ */ jsxs(Fragment, { children: [
199997
200158
  /* @__PURE__ */ jsx(Text, { children: " " }),
199998
200159
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: meta })
@@ -229095,7 +229256,8 @@ Respond in JSON: { "score": 0.X, "reasoning": "..." }`;
229095
229256
  try {
229096
229257
  const fullPath = join(env2.workingDir, file);
229097
229258
  const content = await readFile(fullPath, "utf-8");
229098
- const truncated = content.length > MAX_FILE_SIZE2 ? content.slice(0, MAX_FILE_SIZE2) + "\n... (truncated)" : content;
229259
+ const truncated = content.length > MAX_FILE_SIZE2 ? `${content.slice(0, MAX_FILE_SIZE2)}
229260
+ ... (truncated)` : content;
229099
229261
  contents.push(`### ${file}
229100
229262
  \`\`\`
229101
229263
  ${truncated}
@@ -229313,7 +229475,9 @@ var TaskLoader = class {
229313
229475
  */
229314
229476
  async loadTask(taskId) {
229315
229477
  if (this.cache.has(taskId)) {
229316
- return this.cache.get(taskId);
229478
+ const cached = this.cache.get(taskId);
229479
+ if (cached)
229480
+ return cached;
229317
229481
  }
229318
229482
  const taskPath = await this.findTaskFile(taskId);
229319
229483
  if (!taskPath) {
@@ -229648,6 +229812,10 @@ ${"-".repeat(60)}`);
229648
229812
  console.log(`\u26A0\uFE0F REGRESSION DETECTED in ${report.regression.regressedTasks.length} task(s):`);
229649
229813
  for (const taskId of report.regression.regressedTasks) {
229650
229814
  const comp = report.regression.comparisons.find((c) => c.taskId === taskId);
229815
+ if (!comp) {
229816
+ console.log(` - ${taskId}: regression details unavailable`);
229817
+ continue;
229818
+ }
229651
229819
  console.log(` - ${taskId}: ${comp.regressionReason}`);
229652
229820
  }
229653
229821
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@butlerw/vellum",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Next-generation AI coding assistant CLI",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -57,13 +57,13 @@
57
57
  "tsx": "^4.21.0",
58
58
  "typescript": "^5.9.3",
59
59
  "@vellum/core": "0.1.15",
60
+ "@vellum/eval": "0.1.0",
60
61
  "@vellum/lsp": "0.1.0",
61
62
  "@vellum/mcp": "0.1.3",
62
- "@vellum/eval": "0.1.0",
63
63
  "@vellum/plugin": "0.1.3",
64
+ "@vellum/provider": "0.2.0",
64
65
  "@vellum/sandbox": "0.1.0",
65
- "@vellum/shared": "0.1.0",
66
- "@vellum/provider": "0.2.0"
66
+ "@vellum/shared": "0.1.0"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsup",