tokengolf 0.4.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/cli.js CHANGED
@@ -575,28 +575,28 @@ var init_score = __esm({
575
575
  name: "Haiku",
576
576
  label: "Rogue",
577
577
  emoji: "\u{1F3F9}",
578
- difficulty: "Hard",
578
+ difficulty: "Nightmare",
579
579
  color: "red"
580
580
  },
581
581
  sonnet: {
582
582
  name: "Sonnet",
583
583
  label: "Fighter",
584
584
  emoji: "\u2694\uFE0F",
585
- difficulty: "Normal",
585
+ difficulty: "Standard",
586
586
  color: "cyan"
587
587
  },
588
588
  opusplan: {
589
589
  name: "Paladin",
590
590
  label: "Paladin",
591
591
  emoji: "\u269C\uFE0F",
592
- difficulty: "Calculated",
592
+ difficulty: "Tactical",
593
593
  color: "yellow"
594
594
  },
595
595
  opus: {
596
596
  name: "Opus",
597
597
  label: "Warlock",
598
598
  emoji: "\u{1F9D9}",
599
- difficulty: "Easy",
599
+ difficulty: "Casual",
600
600
  color: "magenta"
601
601
  }
602
602
  };
@@ -610,6 +610,24 @@ var init_score = __esm({
610
610
  }
611
611
  });
612
612
 
613
+ // src/lib/ui.js
614
+ var ACCENT_BORDER, ACCENT_PADDING;
615
+ var init_ui = __esm({
616
+ "src/lib/ui.js"() {
617
+ ACCENT_BORDER = {
618
+ topLeft: " ",
619
+ top: " ",
620
+ topRight: " ",
621
+ left: "\u2588\u2588",
622
+ right: " ",
623
+ bottomLeft: " ",
624
+ bottom: " ",
625
+ bottomRight: " "
626
+ };
627
+ ACCENT_PADDING = 3;
628
+ }
629
+ });
630
+
613
631
  // src/components/ActiveRun.js
614
632
  import React2, { useState as useState2, useEffect } from "react";
615
633
  import { Box as Box2, Text as Text2, useApp as useApp2, useInput } from "ink";
@@ -637,9 +655,12 @@ function ActiveRun({ run: initialRun }) {
637
655
  return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", gap: 1, paddingX: 1, paddingY: 1 }, /* @__PURE__ */ React2.createElement(Box2, { gap: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "yellow" }, "\u26F3 TokenGolf"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Active Run"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray", dimColor: true }, formatElapsed(run.startedAt))), /* @__PURE__ */ React2.createElement(
638
656
  Box2,
639
657
  {
640
- borderStyle: "round",
658
+ borderStyle: ACCENT_BORDER,
641
659
  borderColor: "yellow",
642
- paddingX: 1,
660
+ borderRight: false,
661
+ borderTop: false,
662
+ borderBottom: false,
663
+ paddingLeft: ACCENT_PADDING,
643
664
  paddingY: 1,
644
665
  flexDirection: "column",
645
666
  gap: 1
@@ -664,13 +685,14 @@ function ActiveRun({ run: initialRun }) {
664
685
  ));
665
686
  })),
666
687
  /* @__PURE__ */ React2.createElement(Box2, { gap: 3, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Prompts ", /* @__PURE__ */ React2.createElement(Text2, { color: "white" }, run.promptCount || 0)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Tools ", /* @__PURE__ */ React2.createElement(Text2, { color: "white" }, run.totalToolCalls || 0))),
667
- pct >= 80 && pct < 100 && /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red", bold: true }, "\u26A0\uFE0F BUDGET WARNING \u2014 ", formatCost(run.budget - run.spent), " left"))
688
+ pct >= 80 && pct < 100 && /* @__PURE__ */ React2.createElement(Box2, { paddingLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red", bold: true }, "\u26A0\uFE0F BUDGET WARNING \u2014 ", formatCost(run.budget - run.spent), " left"))
668
689
  ), /* @__PURE__ */ React2.createElement(Text2, { color: "gray", dimColor: true }, "tokengolf win [--spent 0.18] \xB7 tokengolf bust \xB7 q to close"));
669
690
  }
670
691
  var init_ActiveRun = __esm({
671
692
  "src/components/ActiveRun.js"() {
672
693
  init_state();
673
694
  init_score();
695
+ init_ui();
674
696
  }
675
697
  });
676
698
 
@@ -697,9 +719,12 @@ function ScoreCard({ run }) {
697
719
  return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", paddingX: 1, paddingY: 1, gap: 1 }, /* @__PURE__ */ React3.createElement(
698
720
  Box3,
699
721
  {
700
- borderStyle: "double",
722
+ borderStyle: ACCENT_BORDER,
701
723
  borderColor: won ? "yellow" : "red",
702
- paddingX: 2,
724
+ borderRight: false,
725
+ borderTop: false,
726
+ borderBottom: false,
727
+ paddingLeft: ACCENT_PADDING,
703
728
  paddingY: 1,
704
729
  flexDirection: "column",
705
730
  gap: 1
@@ -720,23 +745,13 @@ function ScoreCard({ run }) {
720
745
  return /* @__PURE__ */ React3.createElement(Text3, { key: model, color: "gray" }, short, " ", /* @__PURE__ */ React3.createElement(Text3, { color: "white" }, pctOfTotal, "%"), " ", /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, formatCost(cost)));
721
746
  }))),
722
747
  run.toolCalls && Object.keys(run.toolCalls).length > 0 && /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", gap: 0, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "Tool calls:"), /* @__PURE__ */ React3.createElement(Box3, { gap: 2, flexWrap: "wrap" }, Object.entries(run.toolCalls).map(([tool, count]) => /* @__PURE__ */ React3.createElement(Text3, { key: tool, color: "gray" }, /* @__PURE__ */ React3.createElement(Text3, { color: "white" }, tool), " \xD7", count)))),
723
- !won && run.budget && /* @__PURE__ */ React3.createElement(
724
- Box3,
725
- {
726
- borderStyle: "single",
727
- borderColor: "red",
728
- paddingX: 1,
729
- marginTop: 1,
730
- flexDirection: "column"
731
- },
732
- /* @__PURE__ */ React3.createElement(Text3, { color: "red", bold: true }, "Cause of death: Budget exceeded by ", formatCost(run.spent - run.budget)),
733
- /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "Tip: Use Read with line ranges instead of full file reads.")
734
- )
748
+ !won && run.budget && /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1, flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "red", bold: true }, "Cause of death: Budget exceeded by ", formatCost(run.spent - run.budget)), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "Tip: Use Read with line ranges instead of full file reads."))
735
749
  ), /* @__PURE__ */ React3.createElement(Box3, { gap: 2 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "tokengolf start \u2014 run again"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "\xB7"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "tokengolf stats \u2014 career stats"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "\xB7"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "q to exit")));
736
750
  }
737
751
  var init_ScoreCard = __esm({
738
752
  "src/components/ScoreCard.js"() {
739
753
  init_score();
754
+ init_ui();
740
755
  }
741
756
  });
742
757
 
@@ -751,15 +766,44 @@ function StatsView({ stats }) {
751
766
  if (stats.total === 0) {
752
767
  return /* @__PURE__ */ React4.createElement(Box4, { paddingX: 1, paddingY: 1, flexDirection: "column", gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "yellow" }, "\u26F3 TokenGolf Stats"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "No completed runs yet."), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "Start one: ", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, "tokengolf start")));
753
768
  }
754
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", paddingX: 1, paddingY: 1, gap: 1 }, /* @__PURE__ */ React4.createElement(Box4, { gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "yellow" }, "\u26F3 TokenGolf"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "Career Stats")), /* @__PURE__ */ React4.createElement(Box4, { borderStyle: "single", borderColor: "gray", paddingX: 1, paddingY: 1, gap: 4 }, /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "RUNS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "white" }, stats.total)), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "WINS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "green" }, stats.wins)), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "DEATHS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "red" }, stats.deaths)), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "WIN RATE"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: stats.winRate >= 70 ? "green" : stats.winRate >= 40 ? "yellow" : "red" }, stats.winRate, "%")), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "AVG SPEND"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, formatCost(stats.avgSpend)))), stats.bestRun && (() => {
769
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", paddingX: 1, paddingY: 1, gap: 1 }, /* @__PURE__ */ React4.createElement(Box4, { gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "yellow" }, "\u26F3 TokenGolf"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "Career Stats")), /* @__PURE__ */ React4.createElement(
770
+ Box4,
771
+ {
772
+ borderStyle: ACCENT_BORDER,
773
+ borderColor: "gray",
774
+ borderRight: false,
775
+ borderTop: false,
776
+ borderBottom: false,
777
+ paddingLeft: ACCENT_PADDING,
778
+ paddingY: 1,
779
+ gap: 4
780
+ },
781
+ /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "RUNS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "white" }, stats.total)),
782
+ /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "WINS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "green" }, stats.wins)),
783
+ /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "DEATHS"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "red" }, stats.deaths)),
784
+ /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "WIN RATE"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: stats.winRate >= 70 ? "green" : stats.winRate >= 40 ? "yellow" : "red" }, stats.winRate, "%")),
785
+ /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "AVG SPEND"), /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, formatCost(stats.avgSpend)))
786
+ ), stats.bestRun && (() => {
755
787
  const bestTier = getTier(stats.bestRun.spent);
756
788
  const bestMc = getModelClass(stats.bestRun.model);
757
789
  return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", gap: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, "\u{1F3C6} Personal Best"), /* @__PURE__ */ React4.createElement(
758
790
  Box4,
759
791
  {
760
- borderStyle: "round",
792
+ borderStyle: {
793
+ topLeft: " ",
794
+ top: " ",
795
+ topRight: " ",
796
+ left: "\u2588\u2588",
797
+ right: " ",
798
+ bottomLeft: " ",
799
+ bottom: " ",
800
+ bottomRight: " "
801
+ },
761
802
  borderColor: "yellow",
762
- paddingX: 1,
803
+ borderRight: false,
804
+ borderTop: false,
805
+ borderBottom: false,
806
+ paddingLeft: ACCENT_PADDING,
763
807
  paddingY: 1,
764
808
  flexDirection: "column"
765
809
  },
@@ -772,11 +816,12 @@ function StatsView({ stats }) {
772
816
  const mc = getModelClass(run.model);
773
817
  const pct = run.budget ? getBudgetPct(run.spent, run.budget) : null;
774
818
  return /* @__PURE__ */ React4.createElement(Box4, { key: i, gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, { color: won ? "green" : "red" }, won ? "\u2713" : "\u2717"), /* @__PURE__ */ React4.createElement(Text4, { color: "white" }, (run.quest || "Flow").slice(0, 34).padEnd(34)), /* @__PURE__ */ React4.createElement(Text4, { color: won ? "green" : "red" }, formatCost(run.spent)), run.budget ? /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "/", formatCost(run.budget)) : null, /* @__PURE__ */ React4.createElement(Text4, null, mc.emoji), /* @__PURE__ */ React4.createElement(Text4, { color: tier.color }, tier.emoji), pct !== null && /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, pct, "%"));
775
- })), stats.achievements.length > 0 && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", gap: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "Recent achievements:"), /* @__PURE__ */ React4.createElement(Box4, { flexWrap: "wrap", gap: 1 }, stats.achievements.slice(0, 12).map((a, i) => /* @__PURE__ */ React4.createElement(Box4, { key: i, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, a.emoji, " ", /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, a.label)))))), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "q to exit"));
819
+ })), stats.achievements.length > 0 && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", gap: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "Recent achievements:"), /* @__PURE__ */ React4.createElement(Box4, { flexWrap: "wrap", columnGap: 2 }, stats.achievements.slice(0, 12).map((a, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i }, a.emoji, " ", /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, a.label))))), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "q to exit"));
776
820
  }
777
821
  var init_StatsView = __esm({
778
822
  "src/components/StatsView.js"() {
779
823
  init_score();
824
+ init_ui();
780
825
  }
781
826
  });
782
827
 
@@ -2020,14 +2065,18 @@ function autoDetectCost(run) {
2020
2065
  // src/components/StartRun.js
2021
2066
  init_state();
2022
2067
  init_score();
2068
+ init_ui();
2023
2069
  import React, { useState } from "react";
2024
2070
  import { Box, Text, useApp } from "ink";
2025
2071
  import { TextInput, Select, ConfirmInput } from "@inkjs/ui";
2026
2072
  var MODEL_OPTIONS = [
2027
- { label: "\u2694\uFE0F Sonnet \u2014 Balanced. The default run. [Normal]", value: "claude-sonnet-4-6" },
2028
- { label: "\u{1F3F9} Haiku \u2014 Glass cannon. Hard mode. [Hard]", value: "claude-haiku-4-5-20251001" },
2029
- { label: "\u269C\uFE0F Paladin \u2014 Opus plans, Sonnet executes. [Calculated]", value: "opusplan" },
2030
- { label: "\u{1F9D9} Opus \u2014 Powerful but expensive. [Easy]", value: "claude-opus-4-6" }
2073
+ { label: "\u2694\uFE0F Sonnet \u2014 Balanced. The default run. [Standard]", value: "claude-sonnet-4-6" },
2074
+ {
2075
+ label: "\u{1F3F9} Haiku \u2014 Glass cannon. Hard mode. [Nightmare]",
2076
+ value: "claude-haiku-4-5-20251001"
2077
+ },
2078
+ { label: "\u269C\uFE0F Paladin \u2014 Opus plans, Sonnet executes. [Tactical]", value: "opusplan" },
2079
+ { label: "\u{1F9D9} Opus \u2014 Powerful but expensive. [Casual]", value: "claude-opus-4-6" }
2031
2080
  ];
2032
2081
  var EFFORT_OPTIONS_BASE = [
2033
2082
  { label: "\u2696\uFE0F Medium \u2014 Balanced (Anthropic recommended for Sonnet)", value: "medium" },
@@ -2066,9 +2115,12 @@ function StartRun() {
2066
2115
  {
2067
2116
  flexDirection: "column",
2068
2117
  gap: 1,
2069
- borderStyle: "single",
2118
+ borderStyle: ACCENT_BORDER,
2070
2119
  borderColor: "gray",
2071
- paddingX: 1,
2120
+ borderRight: false,
2121
+ borderTop: false,
2122
+ borderBottom: false,
2123
+ paddingLeft: ACCENT_PADDING,
2072
2124
  paddingY: 1
2073
2125
  },
2074
2126
  /* @__PURE__ */ React.createElement(Box, { gap: 2, alignItems: "flex-start" }, /* @__PURE__ */ React.createElement(Text, { color: step === "quest" ? "cyan" : "gray" }, "\u{1F4CB} Quest "), step === "quest" ? /* @__PURE__ */ React.createElement(
@@ -2135,9 +2187,21 @@ function StartRun() {
2135
2187
  step === "confirm" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(
2136
2188
  Box,
2137
2189
  {
2138
- borderStyle: "round",
2190
+ borderStyle: {
2191
+ topLeft: " ",
2192
+ top: " ",
2193
+ topRight: " ",
2194
+ left: "\u2588\u2588",
2195
+ right: " ",
2196
+ bottomLeft: " ",
2197
+ bottom: " ",
2198
+ bottomRight: " "
2199
+ },
2139
2200
  borderColor: "yellow",
2140
- paddingX: 1,
2201
+ borderRight: false,
2202
+ borderTop: false,
2203
+ borderBottom: false,
2204
+ paddingLeft: ACCENT_PADDING,
2141
2205
  paddingY: 1,
2142
2206
  flexDirection: "column"
2143
2207
  },
@@ -0,0 +1,45 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="300" viewBox="0 0 1200 300">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0%" stop-color="#1a1812"/>
5
+ <stop offset="100%" stop-color="#23211a"/>
6
+ </linearGradient>
7
+ <linearGradient id="gold" x1="0" y1="0" x2="1" y2="0">
8
+ <stop offset="0%" stop-color="#d4a840"/>
9
+ <stop offset="100%" stop-color="#e8c060"/>
10
+ </linearGradient>
11
+ <linearGradient id="rule" x1="0" y1="0" x2="1" y2="0">
12
+ <stop offset="0%" stop-color="#3d3828" stop-opacity="0"/>
13
+ <stop offset="15%" stop-color="#3d3828"/>
14
+ <stop offset="85%" stop-color="#3d3828"/>
15
+ <stop offset="100%" stop-color="#3d3828" stop-opacity="0"/>
16
+ </linearGradient>
17
+ </defs>
18
+
19
+ <!-- Background -->
20
+ <rect width="1200" height="300" fill="url(#bg)"/>
21
+
22
+ <!-- Top decorative rule -->
23
+ <rect x="100" y="40" width="1000" height="1" fill="url(#rule)"/>
24
+
25
+ <!-- Flag icon -->
26
+ <text x="600" y="115" text-anchor="middle" font-size="52" fill="#d4a840" opacity="0.9">&#9971;</text>
27
+
28
+ <!-- Title -->
29
+ <text x="600" y="175" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="64" font-weight="bold" letter-spacing="6">
30
+ <tspan fill="url(#gold)">Token</tspan><tspan fill="#e8dcc4">Golf</tspan>
31
+ </text>
32
+
33
+ <!-- Tagline -->
34
+ <text x="600" y="215" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="20" font-style="italic" fill="#9a9180" letter-spacing="1">
35
+ Every token matters.
36
+ </text>
37
+
38
+ <!-- Bottom decorative rule -->
39
+ <rect x="100" y="248" width="1000" height="1" fill="url(#rule)"/>
40
+
41
+ <!-- Subtle tagline detail -->
42
+ <text x="600" y="278" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="13" fill="#3d3828" letter-spacing="3">
43
+ 4 classes &#183; 60+ achievements &#183; 9 hooks
44
+ </text>
45
+ </svg>