@0xtiby/toby 0.0.1 → 1.0.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.
Files changed (2) hide show
  1. package/dist/cli.js +338 -286
  2. package/package.json +8 -5
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.tsx
4
4
  import meow from "meow";
5
- import { render, Text as Text12 } from "ink";
5
+ import { render, Text as Text13 } from "ink";
6
6
 
7
7
  // src/commands/plan.tsx
8
8
  import { useState as useState3, useEffect as useEffect2, useMemo as useMemo2 } from "react";
@@ -1523,14 +1523,72 @@ function Build(flags2) {
1523
1523
  }
1524
1524
 
1525
1525
  // src/commands/init.tsx
1526
- import { useState as useState5, useEffect as useEffect4 } from "react";
1527
- import { Text as Text5, Box as Box5, useApp as useApp2 } from "ink";
1526
+ import { useState as useState6, useEffect as useEffect5 } from "react";
1527
+ import { Text as Text6, Box as Box6, useApp as useApp2 } from "ink";
1528
1528
  import SelectInput from "ink-select-input";
1529
1529
  import TextInput from "ink-text-input";
1530
1530
  import fs7 from "fs";
1531
1531
  import path7 from "path";
1532
- import { detectAll, getKnownModels } from "@0xtiby/spawner";
1532
+ import { detectAll } from "@0xtiby/spawner";
1533
+
1534
+ // src/hooks/useModels.ts
1535
+ import { useState as useState5, useEffect as useEffect4 } from "react";
1536
+ import { listModels } from "@0xtiby/spawner";
1537
+ var DEFAULT_ITEM = { label: "default", value: "default" };
1538
+ function useModels(cli2, options = {}) {
1539
+ const { enabled = true } = options;
1540
+ const [items, setItems] = useState5([DEFAULT_ITEM]);
1541
+ const [loading, setLoading] = useState5(enabled);
1542
+ const [error, setError] = useState5(null);
1543
+ useEffect4(() => {
1544
+ if (!enabled) {
1545
+ setLoading(false);
1546
+ return;
1547
+ }
1548
+ let cancelled = false;
1549
+ setLoading(true);
1550
+ setError(null);
1551
+ listModels({ cli: cli2, fallback: true }).then((models) => {
1552
+ if (!cancelled) {
1553
+ const mapped = models.map((m) => ({
1554
+ label: `${m.name} (${m.id})`,
1555
+ value: m.id
1556
+ }));
1557
+ setItems([DEFAULT_ITEM, ...mapped]);
1558
+ }
1559
+ }).catch((err) => {
1560
+ if (!cancelled) {
1561
+ const message = err instanceof Error ? err.message : String(err);
1562
+ console.warn(`Failed to load models for ${cli2}: ${message}`);
1563
+ setError(message);
1564
+ setItems([DEFAULT_ITEM]);
1565
+ }
1566
+ }).finally(() => {
1567
+ if (!cancelled) setLoading(false);
1568
+ });
1569
+ return () => {
1570
+ cancelled = true;
1571
+ };
1572
+ }, [cli2, enabled]);
1573
+ return { items, loading, error };
1574
+ }
1575
+
1576
+ // src/components/LoadingSpinner.tsx
1577
+ import { Text as Text5, Box as Box5 } from "ink";
1578
+ import Spinner from "ink-spinner";
1533
1579
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1580
+ function LoadingSpinner({ message = "Loading..." }) {
1581
+ return /* @__PURE__ */ jsxs4(Box5, { children: [
1582
+ /* @__PURE__ */ jsx5(Spinner, { type: "dots" }),
1583
+ /* @__PURE__ */ jsxs4(Text5, { children: [
1584
+ " ",
1585
+ message
1586
+ ] })
1587
+ ] });
1588
+ }
1589
+
1590
+ // src/commands/init.tsx
1591
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1534
1592
  function createProject(selections, cwd = process.cwd()) {
1535
1593
  const localDir = getLocalDir(cwd);
1536
1594
  const configPath = path7.join(localDir, CONFIG_FILE);
@@ -1589,15 +1647,15 @@ function getInstalledClis(result) {
1589
1647
  return Object.entries(result).filter(([, info]) => info.installed).map(([name]) => name);
1590
1648
  }
1591
1649
  function CliTable({ clis }) {
1592
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginBottom: 1, children: [
1593
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Detected CLIs:" }),
1650
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginBottom: 1, children: [
1651
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Detected CLIs:" }),
1594
1652
  Object.entries(clis).map(
1595
- ([name, info]) => /* @__PURE__ */ jsxs4(Text5, { children: [
1653
+ ([name, info]) => /* @__PURE__ */ jsxs5(Text6, { children: [
1596
1654
  " ",
1597
- info.installed ? /* @__PURE__ */ jsx5(Text5, { color: "green", children: "\u2713" }) : /* @__PURE__ */ jsx5(Text5, { color: "red", children: "\u2717" }),
1655
+ info.installed ? /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "red", children: "\u2717" }),
1598
1656
  " ",
1599
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: name }),
1600
- info.installed && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
1657
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: name }),
1658
+ info.installed && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
1601
1659
  " ",
1602
1660
  info.version,
1603
1661
  info.authenticated ? " (authenticated)" : " (not authenticated)"
@@ -1606,13 +1664,6 @@ function CliTable({ clis }) {
1606
1664
  )
1607
1665
  ] });
1608
1666
  }
1609
- function modelItems(cli2) {
1610
- const models = getKnownModels(cli2);
1611
- return [
1612
- { label: "default", value: "default" },
1613
- ...models.map((m) => ({ label: `${m.name} (${m.id})`, value: m.id }))
1614
- ];
1615
- }
1616
1667
  function hasAllInitFlags(flags2) {
1617
1668
  return flags2.planCli !== void 0 && flags2.planModel !== void 0 && flags2.buildCli !== void 0 && flags2.buildModel !== void 0 && flags2.specsDir !== void 0;
1618
1669
  }
@@ -1623,8 +1674,8 @@ function NonInteractiveInit({ flags: flags2 }) {
1623
1674
  const invalidCli = [planCli, buildCli].find(
1624
1675
  (cli2) => !CLI_NAMES.includes(cli2)
1625
1676
  );
1626
- const [status, setStatus] = useState5(invalidCli ? { type: "invalid_cli", cli: invalidCli } : { type: "detecting" });
1627
- useEffect4(() => {
1677
+ const [status, setStatus] = useState6(invalidCli ? { type: "invalid_cli", cli: invalidCli } : { type: "detecting" });
1678
+ useEffect5(() => {
1628
1679
  if (invalidCli) {
1629
1680
  process.exitCode = 1;
1630
1681
  exit();
@@ -1661,23 +1712,23 @@ function NonInteractiveInit({ flags: flags2 }) {
1661
1712
  });
1662
1713
  }, []);
1663
1714
  if (status.type === "invalid_cli") {
1664
- return /* @__PURE__ */ jsx5(Text5, { color: "red", children: `\u2717 Unknown CLI: ${status.cli}. Must be one of: ${CLI_NAMES.join(", ")}` });
1715
+ return /* @__PURE__ */ jsx6(Text6, { color: "red", children: `\u2717 Unknown CLI: ${status.cli}. Must be one of: ${CLI_NAMES.join(", ")}` });
1665
1716
  }
1666
1717
  if (status.type === "detecting") {
1667
- return /* @__PURE__ */ jsx5(Text5, { children: "Detecting installed CLIs..." });
1718
+ return /* @__PURE__ */ jsx6(Text6, { children: "Detecting installed CLIs..." });
1668
1719
  }
1669
1720
  if (status.type === "error") {
1670
- return /* @__PURE__ */ jsx5(Text5, { color: "red", children: `\u2717 ${status.message}` });
1721
+ return /* @__PURE__ */ jsx6(Text6, { color: "red", children: `\u2717 ${status.message}` });
1671
1722
  }
1672
1723
  const { result, selections } = status;
1673
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1674
- /* @__PURE__ */ jsx5(Text5, { color: "green", bold: true, children: "\u2713 Project initialized!" }),
1675
- /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
1724
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1725
+ /* @__PURE__ */ jsx6(Text6, { color: "green", bold: true, children: "\u2713 Project initialized!" }),
1726
+ /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
1676
1727
  " created ",
1677
1728
  path7.relative(process.cwd(), result.configPath)
1678
1729
  ] }),
1679
- result.statusCreated && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " created .toby/status.json" }),
1680
- result.specsDirCreated && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
1730
+ result.statusCreated && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " created .toby/status.json" }),
1731
+ result.specsDirCreated && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
1681
1732
  " created ",
1682
1733
  selections.specsDir,
1683
1734
  "/"
@@ -1686,16 +1737,16 @@ function NonInteractiveInit({ flags: flags2 }) {
1686
1737
  }
1687
1738
  function Init(flags2) {
1688
1739
  if (hasAllInitFlags(flags2)) {
1689
- return /* @__PURE__ */ jsx5(NonInteractiveInit, { flags: flags2 });
1740
+ return /* @__PURE__ */ jsx6(NonInteractiveInit, { flags: flags2 });
1690
1741
  }
1691
- return /* @__PURE__ */ jsx5(InteractiveInit, { version: flags2.version });
1742
+ return /* @__PURE__ */ jsx6(InteractiveInit, { version: flags2.version });
1692
1743
  }
1693
1744
  function InteractiveInit({ version: version2 }) {
1694
1745
  const { exit } = useApp2();
1695
- const [phase, setPhase] = useState5("detecting");
1696
- const [clis, setClis] = useState5(null);
1697
- const [installedClis, setInstalledClis] = useState5([]);
1698
- const [selections, setSelections] = useState5({
1746
+ const [phase, setPhase] = useState6("detecting");
1747
+ const [clis, setClis] = useState6(null);
1748
+ const [installedClis, setInstalledClis] = useState6([]);
1749
+ const [selections, setSelections] = useState6({
1699
1750
  planCli: "claude",
1700
1751
  planModel: "default",
1701
1752
  buildCli: "claude",
@@ -1703,10 +1754,12 @@ function InteractiveInit({ version: version2 }) {
1703
1754
  specsDir: DEFAULT_SPECS_DIR,
1704
1755
  verbose: false
1705
1756
  });
1706
- const [specsDirInput, setSpecsDirInput] = useState5(DEFAULT_SPECS_DIR);
1707
- const [result, setResult] = useState5(null);
1708
- const [error, setError] = useState5(null);
1709
- useEffect4(() => {
1757
+ const [specsDirInput, setSpecsDirInput] = useState6(DEFAULT_SPECS_DIR);
1758
+ const [result, setResult] = useState6(null);
1759
+ const [error, setError] = useState6(null);
1760
+ const planModels = useModels(selections.planCli, { enabled: phase === "plan_model" });
1761
+ const buildModels = useModels(selections.buildCli, { enabled: phase === "build_model" });
1762
+ useEffect5(() => {
1710
1763
  if (phase !== "detecting") return;
1711
1764
  detectAll().then((detectResult) => {
1712
1765
  setClis(detectResult);
@@ -1765,66 +1818,68 @@ function InteractiveInit({ version: version2 }) {
1765
1818
  });
1766
1819
  }
1767
1820
  if (phase === "detecting") {
1768
- return /* @__PURE__ */ jsx5(Text5, { children: "Detecting installed CLIs..." });
1821
+ return /* @__PURE__ */ jsx6(Text6, { children: "Detecting installed CLIs..." });
1769
1822
  }
1770
1823
  if (phase === "no_cli") {
1771
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1772
- clis && /* @__PURE__ */ jsx5(CliTable, { clis }),
1773
- /* @__PURE__ */ jsx5(Text5, { color: "red", bold: true, children: "No AI CLIs found. Install one of the following:" }),
1774
- /* @__PURE__ */ jsx5(Text5, { children: " \u2022 claude \u2014 npm install -g @anthropic-ai/claude-code" }),
1775
- /* @__PURE__ */ jsx5(Text5, { children: " \u2022 codex \u2014 npm install -g @openai/codex" }),
1776
- /* @__PURE__ */ jsx5(Text5, { children: " \u2022 opencode \u2014 go install github.com/opencode-ai/opencode@latest" })
1824
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1825
+ clis && /* @__PURE__ */ jsx6(CliTable, { clis }),
1826
+ /* @__PURE__ */ jsx6(Text6, { color: "red", bold: true, children: "No AI CLIs found. Install one of the following:" }),
1827
+ /* @__PURE__ */ jsx6(Text6, { children: " \u2022 claude \u2014 npm install -g @anthropic-ai/claude-code" }),
1828
+ /* @__PURE__ */ jsx6(Text6, { children: " \u2022 codex \u2014 npm install -g @openai/codex" }),
1829
+ /* @__PURE__ */ jsx6(Text6, { children: " \u2022 opencode \u2014 go install github.com/opencode-ai/opencode@latest" })
1777
1830
  ] });
1778
1831
  }
1779
1832
  const cliItems = installedClis.map((name) => ({
1780
1833
  label: `${name} \u2014 ${clis?.[name]?.version ?? "unknown"}`,
1781
1834
  value: name
1782
1835
  }));
1783
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1784
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: `toby v${version2} \u2014 project setup
1836
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1837
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: `toby v${version2} \u2014 project setup
1785
1838
  ` }),
1786
- clis && /* @__PURE__ */ jsx5(CliTable, { clis }),
1787
- phase === "plan_cli" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1788
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Select CLI for planning:" }),
1789
- /* @__PURE__ */ jsx5(SelectInput, { items: cliItems, onSelect: handlePlanCliSelect })
1839
+ clis && /* @__PURE__ */ jsx6(CliTable, { clis }),
1840
+ phase === "plan_cli" && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1841
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Select CLI for planning:" }),
1842
+ /* @__PURE__ */ jsx6(SelectInput, { items: cliItems, onSelect: handlePlanCliSelect })
1790
1843
  ] }),
1791
- phase === "plan_model" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1792
- /* @__PURE__ */ jsxs4(Text5, { bold: true, children: [
1844
+ phase === "plan_model" && planModels.loading && /* @__PURE__ */ jsx6(LoadingSpinner, { message: "Loading models..." }),
1845
+ phase === "plan_model" && !planModels.loading && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1846
+ /* @__PURE__ */ jsxs5(Text6, { bold: true, children: [
1793
1847
  "Select model for planning (",
1794
1848
  selections.planCli,
1795
1849
  "):"
1796
1850
  ] }),
1797
- /* @__PURE__ */ jsx5(
1851
+ /* @__PURE__ */ jsx6(
1798
1852
  SelectInput,
1799
1853
  {
1800
- items: modelItems(selections.planCli),
1854
+ items: planModels.items,
1801
1855
  onSelect: handlePlanModelSelect
1802
1856
  }
1803
1857
  )
1804
1858
  ] }),
1805
- phase === "build_cli" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1806
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Select CLI for building:" }),
1807
- /* @__PURE__ */ jsx5(SelectInput, { items: cliItems, onSelect: handleBuildCliSelect })
1859
+ phase === "build_cli" && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1860
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Select CLI for building:" }),
1861
+ /* @__PURE__ */ jsx6(SelectInput, { items: cliItems, onSelect: handleBuildCliSelect })
1808
1862
  ] }),
1809
- phase === "build_model" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1810
- /* @__PURE__ */ jsxs4(Text5, { bold: true, children: [
1863
+ phase === "build_model" && buildModels.loading && /* @__PURE__ */ jsx6(LoadingSpinner, { message: "Loading models..." }),
1864
+ phase === "build_model" && !buildModels.loading && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1865
+ /* @__PURE__ */ jsxs5(Text6, { bold: true, children: [
1811
1866
  "Select model for building (",
1812
1867
  selections.buildCli,
1813
1868
  "):"
1814
1869
  ] }),
1815
- /* @__PURE__ */ jsx5(
1870
+ /* @__PURE__ */ jsx6(
1816
1871
  SelectInput,
1817
1872
  {
1818
- items: modelItems(selections.buildCli),
1873
+ items: buildModels.items,
1819
1874
  onSelect: handleBuildModelSelect
1820
1875
  }
1821
1876
  )
1822
1877
  ] }),
1823
- phase === "specs_dir" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1824
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Specs directory:" }),
1825
- /* @__PURE__ */ jsxs4(Box5, { children: [
1826
- /* @__PURE__ */ jsx5(Text5, { children: " > " }),
1827
- /* @__PURE__ */ jsx5(
1878
+ phase === "specs_dir" && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1879
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Specs directory:" }),
1880
+ /* @__PURE__ */ jsxs5(Box6, { children: [
1881
+ /* @__PURE__ */ jsx6(Text6, { children: " > " }),
1882
+ /* @__PURE__ */ jsx6(
1828
1883
  TextInput,
1829
1884
  {
1830
1885
  value: specsDirInput,
@@ -1834,10 +1889,10 @@ function InteractiveInit({ version: version2 }) {
1834
1889
  )
1835
1890
  ] })
1836
1891
  ] }),
1837
- phase === "verbose" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
1838
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Verbose output:" }),
1839
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " Show full CLI output including tool use and system events" }),
1840
- /* @__PURE__ */ jsx5(
1892
+ phase === "verbose" && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1893
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Verbose output:" }),
1894
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " Show full CLI output including tool use and system events" }),
1895
+ /* @__PURE__ */ jsx6(
1841
1896
  SelectInput,
1842
1897
  {
1843
1898
  items: [
@@ -1848,38 +1903,38 @@ function InteractiveInit({ version: version2 }) {
1848
1903
  }
1849
1904
  )
1850
1905
  ] }),
1851
- phase === "done" && error && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
1852
- /* @__PURE__ */ jsx5(Text5, { color: "red", bold: true, children: "\u2717 Initialization failed" }),
1853
- /* @__PURE__ */ jsx5(Text5, { color: "red", children: ` ${error}` })
1906
+ phase === "done" && error && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
1907
+ /* @__PURE__ */ jsx6(Text6, { color: "red", bold: true, children: "\u2717 Initialization failed" }),
1908
+ /* @__PURE__ */ jsx6(Text6, { color: "red", children: ` ${error}` })
1854
1909
  ] }),
1855
- phase === "done" && result && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
1856
- /* @__PURE__ */ jsx5(Text5, { color: "green", bold: true, children: "\u2713 Project initialized!" }),
1857
- /* @__PURE__ */ jsx5(Text5, { children: "" }),
1858
- /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
1910
+ phase === "done" && result && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
1911
+ /* @__PURE__ */ jsx6(Text6, { color: "green", bold: true, children: "\u2713 Project initialized!" }),
1912
+ /* @__PURE__ */ jsx6(Text6, { children: "" }),
1913
+ /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
1859
1914
  " created ",
1860
1915
  path7.relative(process.cwd(), result.configPath)
1861
1916
  ] }),
1862
- result.statusCreated && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " created .toby/status.json" }),
1863
- result.specsDirCreated && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
1917
+ result.statusCreated && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " created .toby/status.json" }),
1918
+ result.specsDirCreated && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
1864
1919
  " created ",
1865
1920
  selections.specsDir,
1866
1921
  "/"
1867
1922
  ] }),
1868
- /* @__PURE__ */ jsx5(Text5, { children: "" }),
1869
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Next steps:" }),
1870
- /* @__PURE__ */ jsxs4(Text5, { children: [
1923
+ /* @__PURE__ */ jsx6(Text6, { children: "" }),
1924
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Next steps:" }),
1925
+ /* @__PURE__ */ jsxs5(Text6, { children: [
1871
1926
  " 1. Add spec files to ",
1872
1927
  selections.specsDir,
1873
1928
  "/"
1874
1929
  ] }),
1875
- /* @__PURE__ */ jsxs4(Text5, { children: [
1930
+ /* @__PURE__ */ jsxs5(Text6, { children: [
1876
1931
  " 2. Run ",
1877
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "toby plan" }),
1932
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "toby plan" }),
1878
1933
  " to plan a spec"
1879
1934
  ] }),
1880
- /* @__PURE__ */ jsxs4(Text5, { children: [
1935
+ /* @__PURE__ */ jsxs5(Text6, { children: [
1881
1936
  " 3. Run ",
1882
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "toby build" }),
1937
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "toby build" }),
1883
1938
  " to build tasks"
1884
1939
  ] })
1885
1940
  ] })
@@ -1887,9 +1942,9 @@ function InteractiveInit({ version: version2 }) {
1887
1942
  }
1888
1943
 
1889
1944
  // src/commands/status.tsx
1890
- import { Text as Text6, Box as Box6 } from "ink";
1945
+ import { Text as Text7, Box as Box7 } from "ink";
1891
1946
  import fs8 from "fs";
1892
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1947
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1893
1948
  function formatDuration(startedAt, completedAt) {
1894
1949
  if (!completedAt) return "\u2014";
1895
1950
  const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
@@ -1937,15 +1992,15 @@ function StatusTable({ rows }) {
1937
1992
  };
1938
1993
  const separator = `${"\u2500".repeat(colWidths.name + 2)}\u253C${"\u2500".repeat(colWidths.status + 2)}\u253C${"\u2500".repeat(colWidths.iterations + 2)}\u253C${"\u2500".repeat(colWidths.tokens + 2)}`;
1939
1994
  const headerLine = ` ${pad(headers.name, colWidths.name)} \u2502 ${pad(headers.status, colWidths.status)} \u2502 ${pad(headers.iterations, colWidths.iterations)} \u2502 ${pad(headers.tokens, colWidths.tokens)} `;
1940
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1941
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: headerLine }),
1942
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: separator }),
1943
- rows.map((row) => /* @__PURE__ */ jsx6(Text6, { children: ` ${pad(row.name, colWidths.name)} \u2502 ${pad(row.status, colWidths.status)} \u2502 ${pad(String(row.iterations), colWidths.iterations)} \u2502 ${pad(String(row.tokens), colWidths.tokens)} ` }, row.name))
1995
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
1996
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: headerLine }),
1997
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: separator }),
1998
+ rows.map((row) => /* @__PURE__ */ jsx7(Text7, { children: ` ${pad(row.name, colWidths.name)} \u2502 ${pad(row.status, colWidths.status)} \u2502 ${pad(String(row.iterations), colWidths.iterations)} \u2502 ${pad(String(row.tokens), colWidths.tokens)} ` }, row.name))
1944
1999
  ] });
1945
2000
  }
1946
2001
  function IterationTable({ rows }) {
1947
2002
  if (rows.length === 0) {
1948
- return /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "No iterations yet" });
2003
+ return /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No iterations yet" });
1949
2004
  }
1950
2005
  const headers = { index: "#", type: "Type", cli: "CLI", tokens: "Tokens", duration: "Duration", exitCode: "Exit" };
1951
2006
  const w = {
@@ -1958,10 +2013,10 @@ function IterationTable({ rows }) {
1958
2013
  };
1959
2014
  const separator = `${"\u2500".repeat(w.index + 2)}\u253C${"\u2500".repeat(w.type + 2)}\u253C${"\u2500".repeat(w.cli + 2)}\u253C${"\u2500".repeat(w.tokens + 2)}\u253C${"\u2500".repeat(w.duration + 2)}\u253C${"\u2500".repeat(w.exitCode + 2)}`;
1960
2015
  const headerLine = ` ${pad(headers.index, w.index)} \u2502 ${pad(headers.type, w.type)} \u2502 ${pad(headers.cli, w.cli)} \u2502 ${pad(headers.tokens, w.tokens)} \u2502 ${pad(headers.duration, w.duration)} \u2502 ${pad(headers.exitCode, w.exitCode)} `;
1961
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1962
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: headerLine }),
1963
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: separator }),
1964
- rows.map((row) => /* @__PURE__ */ jsx6(Text6, { children: ` ${pad(row.index, w.index)} \u2502 ${pad(row.type, w.type)} \u2502 ${pad(row.cli, w.cli)} \u2502 ${pad(row.tokens, w.tokens)} \u2502 ${pad(row.duration, w.duration)} \u2502 ${pad(row.exitCode, w.exitCode)} ` }, row.index))
2016
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2017
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: headerLine }),
2018
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: separator }),
2019
+ rows.map((row) => /* @__PURE__ */ jsx7(Text7, { children: ` ${pad(row.index, w.index)} \u2502 ${pad(row.type, w.type)} \u2502 ${pad(row.cli, w.cli)} \u2502 ${pad(row.tokens, w.tokens)} \u2502 ${pad(row.duration, w.duration)} \u2502 ${pad(row.exitCode, w.exitCode)} ` }, row.index))
1965
2020
  ] });
1966
2021
  }
1967
2022
  function DetailedView({ specName, cwd }) {
@@ -1969,7 +2024,7 @@ function DetailedView({ specName, cwd }) {
1969
2024
  const specs = discoverSpecs(cwd, config);
1970
2025
  const spec = findSpec(specs, specName);
1971
2026
  if (!spec) {
1972
- return /* @__PURE__ */ jsxs5(Text6, { color: "red", children: [
2027
+ return /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
1973
2028
  "Spec not found: ",
1974
2029
  specName
1975
2030
  ] });
@@ -1995,21 +2050,21 @@ function DetailedView({ specName, cwd }) {
1995
2050
  (sum, iter) => sum + (iter.tokensUsed ?? 0),
1996
2051
  0
1997
2052
  );
1998
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
1999
- statusWarning && /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: statusWarning }),
2000
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: spec.name }),
2001
- /* @__PURE__ */ jsxs5(Text6, { children: [
2053
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2054
+ statusWarning && /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: statusWarning }),
2055
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: spec.name }),
2056
+ /* @__PURE__ */ jsxs6(Text7, { children: [
2002
2057
  "Status: ",
2003
2058
  entry.status
2004
2059
  ] }),
2005
- /* @__PURE__ */ jsx6(Text6, { children: "" }),
2006
- /* @__PURE__ */ jsx6(IterationTable, { rows: iterationRows }),
2007
- /* @__PURE__ */ jsx6(Text6, { children: "" }),
2008
- /* @__PURE__ */ jsxs5(Text6, { children: [
2060
+ /* @__PURE__ */ jsx7(Text7, { children: "" }),
2061
+ /* @__PURE__ */ jsx7(IterationTable, { rows: iterationRows }),
2062
+ /* @__PURE__ */ jsx7(Text7, { children: "" }),
2063
+ /* @__PURE__ */ jsxs6(Text7, { children: [
2009
2064
  "Iterations: ",
2010
2065
  entry.iterations.length
2011
2066
  ] }),
2012
- /* @__PURE__ */ jsxs5(Text6, { children: [
2067
+ /* @__PURE__ */ jsxs6(Text7, { children: [
2013
2068
  "Tokens used: ",
2014
2069
  totalTokens
2015
2070
  ] })
@@ -2019,43 +2074,43 @@ function Status({ spec, version: version2 }) {
2019
2074
  const cwd = process.cwd();
2020
2075
  const localDir = getLocalDir(cwd);
2021
2076
  if (!fs8.existsSync(localDir)) {
2022
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
2023
- /* @__PURE__ */ jsx6(Text6, { color: "red", bold: true, children: "Toby not initialized" }),
2024
- /* @__PURE__ */ jsxs5(Text6, { children: [
2077
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2078
+ /* @__PURE__ */ jsx7(Text7, { color: "red", bold: true, children: "Toby not initialized" }),
2079
+ /* @__PURE__ */ jsxs6(Text7, { children: [
2025
2080
  "Run ",
2026
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "toby init" }),
2081
+ /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "toby init" }),
2027
2082
  " to set up your project."
2028
2083
  ] })
2029
2084
  ] });
2030
2085
  }
2031
2086
  if (spec) {
2032
- return /* @__PURE__ */ jsx6(DetailedView, { specName: spec, cwd });
2087
+ return /* @__PURE__ */ jsx7(DetailedView, { specName: spec, cwd });
2033
2088
  }
2034
2089
  const { rows, warnings } = buildRows(cwd);
2035
2090
  if (rows.length === 0) {
2036
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
2037
- /* @__PURE__ */ jsx6(Text6, { children: `toby v${version2}` }),
2038
- warnings.map((w) => /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: w }, w)),
2039
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "No specs found. Add .md files to your specs directory." })
2091
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2092
+ /* @__PURE__ */ jsx7(Text7, { children: `toby v${version2}` }),
2093
+ warnings.map((w) => /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: w }, w)),
2094
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No specs found. Add .md files to your specs directory." })
2040
2095
  ] });
2041
2096
  }
2042
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
2043
- /* @__PURE__ */ jsx6(Text6, { children: `toby v${version2}` }),
2044
- warnings.map((w) => /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: w }, w)),
2045
- /* @__PURE__ */ jsx6(Text6, { children: "" }),
2046
- /* @__PURE__ */ jsx6(StatusTable, { rows })
2097
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2098
+ /* @__PURE__ */ jsx7(Text7, { children: `toby v${version2}` }),
2099
+ warnings.map((w) => /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: w }, w)),
2100
+ /* @__PURE__ */ jsx7(Text7, { children: "" }),
2101
+ /* @__PURE__ */ jsx7(StatusTable, { rows })
2047
2102
  ] });
2048
2103
  }
2049
2104
 
2050
2105
  // src/commands/config.tsx
2051
- import { useState as useState6, useEffect as useEffect5 } from "react";
2052
- import { Text as Text7, Box as Box7, useApp as useApp3 } from "ink";
2106
+ import { useState as useState7, useEffect as useEffect6 } from "react";
2107
+ import { Text as Text8, Box as Box8, useApp as useApp3 } from "ink";
2053
2108
  import SelectInput2 from "ink-select-input";
2054
2109
  import TextInput2 from "ink-text-input";
2055
2110
  import fs9 from "fs";
2056
2111
  import path8 from "path";
2057
- import { detectAll as detectAll2, getKnownModels as getKnownModels2 } from "@0xtiby/spawner";
2058
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2112
+ import { detectAll as detectAll2 } from "@0xtiby/spawner";
2113
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2059
2114
  var VALID_KEYS = {
2060
2115
  "plan.cli": "string",
2061
2116
  "plan.model": "string",
@@ -2122,28 +2177,28 @@ function readMergeWriteConfig(mutations) {
2122
2177
  }
2123
2178
  function ConfigGet({ configKey }) {
2124
2179
  if (!(configKey in VALID_KEYS)) {
2125
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Unknown config key: ${configKey}
2180
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Unknown config key: ${configKey}
2126
2181
  Valid keys: ${Object.keys(VALID_KEYS).join(", ")}` });
2127
2182
  }
2128
2183
  const cwd = process.cwd();
2129
2184
  const localDir = getLocalDir(cwd);
2130
2185
  if (!fs9.existsSync(localDir)) {
2131
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2132
- /* @__PURE__ */ jsx7(Text7, { color: "red", bold: true, children: "No config found" }),
2133
- /* @__PURE__ */ jsxs6(Text7, { children: [
2186
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2187
+ /* @__PURE__ */ jsx8(Text8, { color: "red", bold: true, children: "No config found" }),
2188
+ /* @__PURE__ */ jsxs7(Text8, { children: [
2134
2189
  "Run ",
2135
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "toby init" }),
2190
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "toby init" }),
2136
2191
  " to set up your project."
2137
2192
  ] })
2138
2193
  ] });
2139
2194
  }
2140
2195
  const config = loadConfig();
2141
2196
  const value = getNestedValue(config, configKey);
2142
- return /* @__PURE__ */ jsx7(Text7, { children: String(value) });
2197
+ return /* @__PURE__ */ jsx8(Text8, { children: String(value) });
2143
2198
  }
2144
2199
  function ConfigSet({ configKey, value }) {
2145
2200
  if (!(configKey in VALID_KEYS)) {
2146
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Unknown config key: ${configKey}
2201
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Unknown config key: ${configKey}
2147
2202
  Valid keys: ${Object.keys(VALID_KEYS).join(", ")}` });
2148
2203
  }
2149
2204
  const type = VALID_KEYS[configKey];
@@ -2151,7 +2206,7 @@ Valid keys: ${Object.keys(VALID_KEYS).join(", ")}` });
2151
2206
  try {
2152
2207
  parsed = parseValue(value, type);
2153
2208
  } catch (err) {
2154
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Invalid value for ${configKey}: ${err.message}` });
2209
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Invalid value for ${configKey}: ${err.message}` });
2155
2210
  }
2156
2211
  const partial = {};
2157
2212
  setNestedValue(partial, configKey, parsed);
@@ -2159,23 +2214,16 @@ Valid keys: ${Object.keys(VALID_KEYS).join(", ")}` });
2159
2214
  ConfigSchema.parse({ ...partial });
2160
2215
  } catch (err) {
2161
2216
  const msg = err instanceof Error ? err.message : String(err);
2162
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Validation error for ${configKey}: ${msg}` });
2217
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Validation error for ${configKey}: ${msg}` });
2163
2218
  }
2164
2219
  try {
2165
2220
  readMergeWriteConfig([{ key: configKey, value: parsed }]);
2166
2221
  } catch (err) {
2167
2222
  const code = err.code;
2168
2223
  const msg = code === "EACCES" ? `Permission denied writing to ${path8.join(getLocalDir(process.cwd()), CONFIG_FILE)}` : `Failed to write config: ${err.message}`;
2169
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: msg });
2224
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: msg });
2170
2225
  }
2171
- return /* @__PURE__ */ jsx7(Text7, { color: "green", children: `Set ${configKey} = ${String(parsed)}` });
2172
- }
2173
- function modelItems2(cli2) {
2174
- const models = getKnownModels2(cli2);
2175
- return [
2176
- { label: "default", value: "default" },
2177
- ...models.map((m) => ({ label: `${m.name} (${m.id})`, value: m.id }))
2178
- ];
2226
+ return /* @__PURE__ */ jsx8(Text8, { color: "green", children: `Set ${configKey} = ${String(parsed)}` });
2179
2227
  }
2180
2228
  function configToEditorValues(config) {
2181
2229
  return {
@@ -2222,9 +2270,9 @@ function pastPhase(current, target) {
2222
2270
  return PHASE_ORDER[current] > PHASE_ORDER[target];
2223
2271
  }
2224
2272
  function CompletedField({ label, value }) {
2225
- return /* @__PURE__ */ jsxs6(Text7, { children: [
2273
+ return /* @__PURE__ */ jsxs7(Text8, { children: [
2226
2274
  " ",
2227
- /* @__PURE__ */ jsxs6(Text7, { dimColor: true, children: [
2275
+ /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
2228
2276
  label,
2229
2277
  ":"
2230
2278
  ] }),
@@ -2234,9 +2282,9 @@ function CompletedField({ label, value }) {
2234
2282
  }
2235
2283
  function ConfigEditor({ version: version2 }) {
2236
2284
  const { exit } = useApp3();
2237
- const [phase, setPhase] = useState6("loading");
2238
- const [installedClis, setInstalledClis] = useState6([]);
2239
- const [values, setValues] = useState6({
2285
+ const [phase, setPhase] = useState7("loading");
2286
+ const [installedClis, setInstalledClis] = useState7([]);
2287
+ const [values, setValues] = useState7({
2240
2288
  planCli: "claude",
2241
2289
  planModel: "default",
2242
2290
  planIterations: 2,
@@ -2246,10 +2294,12 @@ function ConfigEditor({ version: version2 }) {
2246
2294
  specsDir: "specs",
2247
2295
  verbose: false
2248
2296
  });
2249
- const [iterInput, setIterInput] = useState6("");
2250
- const [specsDirInput, setSpecsDirInput] = useState6("");
2251
- const [saveError, setSaveError] = useState6(null);
2252
- useEffect5(() => {
2297
+ const [iterInput, setIterInput] = useState7("");
2298
+ const [specsDirInput, setSpecsDirInput] = useState7("");
2299
+ const [saveError, setSaveError] = useState7(null);
2300
+ const planModels = useModels(values.planCli, { enabled: phase === "plan_model" });
2301
+ const buildModels = useModels(values.buildCli, { enabled: phase === "build_model" });
2302
+ useEffect6(() => {
2253
2303
  if (phase !== "loading") return;
2254
2304
  const config = loadConfig();
2255
2305
  const initial = configToEditorValues(config);
@@ -2288,17 +2338,17 @@ function ConfigEditor({ version: version2 }) {
2288
2338
  exit();
2289
2339
  }
2290
2340
  if (phase === "loading") {
2291
- return /* @__PURE__ */ jsx7(Text7, { children: "Loading configuration..." });
2341
+ return /* @__PURE__ */ jsx8(Text8, { children: "Loading configuration..." });
2292
2342
  }
2293
2343
  const planCliItems = cliItems();
2294
2344
  const buildCliItems = cliItems();
2295
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2296
- /* @__PURE__ */ jsx7(Text7, { bold: true, children: `toby v${version2} \u2014 config editor
2345
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2346
+ /* @__PURE__ */ jsx8(Text8, { bold: true, children: `toby v${version2} \u2014 config editor
2297
2347
  ` }),
2298
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "Plan" }),
2299
- phase === "plan_cli" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2300
- /* @__PURE__ */ jsx7(Text7, { children: " cli:" }),
2301
- /* @__PURE__ */ jsx7(
2348
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: "cyan", children: "Plan" }),
2349
+ phase === "plan_cli" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2350
+ /* @__PURE__ */ jsx8(Text8, { children: " cli:" }),
2351
+ /* @__PURE__ */ jsx8(
2302
2352
  SelectInput2,
2303
2353
  {
2304
2354
  items: planCliItems,
@@ -2310,14 +2360,15 @@ function ConfigEditor({ version: version2 }) {
2310
2360
  }
2311
2361
  )
2312
2362
  ] }),
2313
- phase !== "plan_cli" && /* @__PURE__ */ jsx7(CompletedField, { label: "cli", value: values.planCli }),
2314
- phase === "plan_model" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2315
- /* @__PURE__ */ jsx7(Text7, { children: " model:" }),
2316
- /* @__PURE__ */ jsx7(
2363
+ phase !== "plan_cli" && /* @__PURE__ */ jsx8(CompletedField, { label: "cli", value: values.planCli }),
2364
+ phase === "plan_model" && planModels.loading && /* @__PURE__ */ jsx8(LoadingSpinner, { message: "Loading models..." }),
2365
+ phase === "plan_model" && !planModels.loading && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2366
+ /* @__PURE__ */ jsx8(Text8, { children: " model:" }),
2367
+ /* @__PURE__ */ jsx8(
2317
2368
  SelectInput2,
2318
2369
  {
2319
- items: modelItems2(values.planCli),
2320
- initialIndex: initialIndex(modelItems2(values.planCli), values.planModel),
2370
+ items: planModels.items,
2371
+ initialIndex: initialIndex(planModels.items, values.planModel),
2321
2372
  onSelect: (item) => {
2322
2373
  setValues((v) => ({ ...v, planModel: item.value }));
2323
2374
  setIterInput(String(values.planIterations));
@@ -2326,10 +2377,10 @@ function ConfigEditor({ version: version2 }) {
2326
2377
  }
2327
2378
  )
2328
2379
  ] }),
2329
- pastPhase(phase, "plan_model") && /* @__PURE__ */ jsx7(CompletedField, { label: "model", value: values.planModel }),
2330
- phase === "plan_iterations" && /* @__PURE__ */ jsxs6(Box7, { children: [
2331
- /* @__PURE__ */ jsx7(Text7, { children: " iterations: " }),
2332
- /* @__PURE__ */ jsx7(
2380
+ pastPhase(phase, "plan_model") && /* @__PURE__ */ jsx8(CompletedField, { label: "model", value: values.planModel }),
2381
+ phase === "plan_iterations" && /* @__PURE__ */ jsxs7(Box8, { children: [
2382
+ /* @__PURE__ */ jsx8(Text8, { children: " iterations: " }),
2383
+ /* @__PURE__ */ jsx8(
2333
2384
  TextInput2,
2334
2385
  {
2335
2386
  value: iterInput,
@@ -2344,14 +2395,14 @@ function ConfigEditor({ version: version2 }) {
2344
2395
  }
2345
2396
  )
2346
2397
  ] }),
2347
- pastPhase(phase, "plan_iterations") && /* @__PURE__ */ jsx7(CompletedField, { label: "iterations", value: String(values.planIterations) }),
2348
- pastPhase(phase, "plan_iterations") && /* @__PURE__ */ jsxs6(Fragment2, { children: [
2349
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2350
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "Build" })
2398
+ pastPhase(phase, "plan_iterations") && /* @__PURE__ */ jsx8(CompletedField, { label: "iterations", value: String(values.planIterations) }),
2399
+ pastPhase(phase, "plan_iterations") && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2400
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2401
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: "cyan", children: "Build" })
2351
2402
  ] }),
2352
- phase === "build_cli" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2353
- /* @__PURE__ */ jsx7(Text7, { children: " cli:" }),
2354
- /* @__PURE__ */ jsx7(
2403
+ phase === "build_cli" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2404
+ /* @__PURE__ */ jsx8(Text8, { children: " cli:" }),
2405
+ /* @__PURE__ */ jsx8(
2355
2406
  SelectInput2,
2356
2407
  {
2357
2408
  items: buildCliItems,
@@ -2363,14 +2414,15 @@ function ConfigEditor({ version: version2 }) {
2363
2414
  }
2364
2415
  )
2365
2416
  ] }),
2366
- pastPhase(phase, "build_cli") && /* @__PURE__ */ jsx7(CompletedField, { label: "cli", value: values.buildCli }),
2367
- phase === "build_model" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2368
- /* @__PURE__ */ jsx7(Text7, { children: " model:" }),
2369
- /* @__PURE__ */ jsx7(
2417
+ pastPhase(phase, "build_cli") && /* @__PURE__ */ jsx8(CompletedField, { label: "cli", value: values.buildCli }),
2418
+ phase === "build_model" && buildModels.loading && /* @__PURE__ */ jsx8(LoadingSpinner, { message: "Loading models..." }),
2419
+ phase === "build_model" && !buildModels.loading && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2420
+ /* @__PURE__ */ jsx8(Text8, { children: " model:" }),
2421
+ /* @__PURE__ */ jsx8(
2370
2422
  SelectInput2,
2371
2423
  {
2372
- items: modelItems2(values.buildCli),
2373
- initialIndex: initialIndex(modelItems2(values.buildCli), values.buildModel),
2424
+ items: buildModels.items,
2425
+ initialIndex: initialIndex(buildModels.items, values.buildModel),
2374
2426
  onSelect: (item) => {
2375
2427
  setValues((v) => ({ ...v, buildModel: item.value }));
2376
2428
  setIterInput(String(values.buildIterations));
@@ -2379,10 +2431,10 @@ function ConfigEditor({ version: version2 }) {
2379
2431
  }
2380
2432
  )
2381
2433
  ] }),
2382
- pastPhase(phase, "build_model") && /* @__PURE__ */ jsx7(CompletedField, { label: "model", value: values.buildModel }),
2383
- phase === "build_iterations" && /* @__PURE__ */ jsxs6(Box7, { children: [
2384
- /* @__PURE__ */ jsx7(Text7, { children: " iterations: " }),
2385
- /* @__PURE__ */ jsx7(
2434
+ pastPhase(phase, "build_model") && /* @__PURE__ */ jsx8(CompletedField, { label: "model", value: values.buildModel }),
2435
+ phase === "build_iterations" && /* @__PURE__ */ jsxs7(Box8, { children: [
2436
+ /* @__PURE__ */ jsx8(Text8, { children: " iterations: " }),
2437
+ /* @__PURE__ */ jsx8(
2386
2438
  TextInput2,
2387
2439
  {
2388
2440
  value: iterInput,
@@ -2398,14 +2450,14 @@ function ConfigEditor({ version: version2 }) {
2398
2450
  }
2399
2451
  )
2400
2452
  ] }),
2401
- pastPhase(phase, "build_iterations") && /* @__PURE__ */ jsx7(CompletedField, { label: "iterations", value: String(values.buildIterations) }),
2402
- pastPhase(phase, "build_iterations") && /* @__PURE__ */ jsxs6(Fragment2, { children: [
2403
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2404
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "General" })
2453
+ pastPhase(phase, "build_iterations") && /* @__PURE__ */ jsx8(CompletedField, { label: "iterations", value: String(values.buildIterations) }),
2454
+ pastPhase(phase, "build_iterations") && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2455
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2456
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: "cyan", children: "General" })
2405
2457
  ] }),
2406
- phase === "specs_dir" && /* @__PURE__ */ jsxs6(Box7, { children: [
2407
- /* @__PURE__ */ jsx7(Text7, { children: " specsDir: " }),
2408
- /* @__PURE__ */ jsx7(
2458
+ phase === "specs_dir" && /* @__PURE__ */ jsxs7(Box8, { children: [
2459
+ /* @__PURE__ */ jsx8(Text8, { children: " specsDir: " }),
2460
+ /* @__PURE__ */ jsx8(
2409
2461
  TextInput2,
2410
2462
  {
2411
2463
  value: specsDirInput,
@@ -2418,10 +2470,10 @@ function ConfigEditor({ version: version2 }) {
2418
2470
  }
2419
2471
  )
2420
2472
  ] }),
2421
- pastPhase(phase, "specs_dir") && /* @__PURE__ */ jsx7(CompletedField, { label: "specsDir", value: values.specsDir }),
2422
- phase === "verbose" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2423
- /* @__PURE__ */ jsx7(Text7, { children: " verbose:" }),
2424
- /* @__PURE__ */ jsx7(
2473
+ pastPhase(phase, "specs_dir") && /* @__PURE__ */ jsx8(CompletedField, { label: "specsDir", value: values.specsDir }),
2474
+ phase === "verbose" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2475
+ /* @__PURE__ */ jsx8(Text8, { children: " verbose:" }),
2476
+ /* @__PURE__ */ jsx8(
2425
2477
  SelectInput2,
2426
2478
  {
2427
2479
  items: [
@@ -2436,13 +2488,13 @@ function ConfigEditor({ version: version2 }) {
2436
2488
  }
2437
2489
  )
2438
2490
  ] }),
2439
- phase === "done" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
2440
- /* @__PURE__ */ jsx7(CompletedField, { label: "verbose", value: String(values.verbose) }),
2441
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2442
- saveError ? /* @__PURE__ */ jsxs6(Text7, { color: "red", bold: true, children: [
2491
+ phase === "done" && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2492
+ /* @__PURE__ */ jsx8(CompletedField, { label: "verbose", value: String(values.verbose) }),
2493
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2494
+ saveError ? /* @__PURE__ */ jsxs7(Text8, { color: "red", bold: true, children: [
2443
2495
  "\u2717 ",
2444
2496
  saveError
2445
- ] }) : /* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u2713 Config saved" })
2497
+ ] }) : /* @__PURE__ */ jsx8(Text8, { color: "green", bold: true, children: "\u2713 Config saved" })
2446
2498
  ] })
2447
2499
  ] });
2448
2500
  }
@@ -2470,7 +2522,7 @@ function ConfigSetBatch({ pairs }) {
2470
2522
  }
2471
2523
  if (errors.length > 0) {
2472
2524
  process.exitCode = 1;
2473
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: errors.map((e, i) => /* @__PURE__ */ jsx7(Text7, { color: "red", children: e }, i)) });
2525
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: errors.map((e, i) => /* @__PURE__ */ jsx8(Text8, { color: "red", children: e }, i)) });
2474
2526
  }
2475
2527
  const partial = {};
2476
2528
  for (const { key, value } of parsed) {
@@ -2481,7 +2533,7 @@ function ConfigSetBatch({ pairs }) {
2481
2533
  } catch (err) {
2482
2534
  process.exitCode = 1;
2483
2535
  const msg = err instanceof Error ? err.message : String(err);
2484
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Validation error: ${msg}` });
2536
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Validation error: ${msg}` });
2485
2537
  }
2486
2538
  try {
2487
2539
  readMergeWriteConfig(parsed);
@@ -2489,12 +2541,12 @@ function ConfigSetBatch({ pairs }) {
2489
2541
  process.exitCode = 1;
2490
2542
  const code = err.code;
2491
2543
  const msg = code === "EACCES" ? `Permission denied writing to ${path8.join(getLocalDir(process.cwd()), CONFIG_FILE)}` : `Failed to write config: ${err.message}`;
2492
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: msg });
2544
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: msg });
2493
2545
  }
2494
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: parsed.map(({ key, value }) => /* @__PURE__ */ jsx7(Text7, { color: "green", children: `Set ${key} = ${String(value)}` }, key)) });
2546
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: parsed.map(({ key, value }) => /* @__PURE__ */ jsx8(Text8, { color: "green", children: `Set ${key} = ${String(value)}` }, key)) });
2495
2547
  }
2496
2548
  function UnknownSubcommand({ subcommand }) {
2497
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Unknown config subcommand: ${subcommand}
2549
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Unknown config subcommand: ${subcommand}
2498
2550
  Usage: toby config [get <key> | set <key> <value>]` });
2499
2551
  }
2500
2552
  function Config({
@@ -2504,39 +2556,39 @@ function Config({
2504
2556
  version: version2
2505
2557
  }) {
2506
2558
  if (subcommand && subcommand !== "get" && subcommand !== "set") {
2507
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2508
- /* @__PURE__ */ jsx7(Text7, { children: `toby v${version2}` }),
2509
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2510
- /* @__PURE__ */ jsx7(UnknownSubcommand, { subcommand })
2559
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2560
+ /* @__PURE__ */ jsx8(Text8, { children: `toby v${version2}` }),
2561
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2562
+ /* @__PURE__ */ jsx8(UnknownSubcommand, { subcommand })
2511
2563
  ] });
2512
2564
  }
2513
2565
  if (subcommand === "get" && configKey) {
2514
- return /* @__PURE__ */ jsx7(ConfigGet, { configKey });
2566
+ return /* @__PURE__ */ jsx8(ConfigGet, { configKey });
2515
2567
  }
2516
2568
  if (subcommand === "set" && configKey && value) {
2517
- return /* @__PURE__ */ jsx7(ConfigSet, { configKey, value });
2569
+ return /* @__PURE__ */ jsx8(ConfigSet, { configKey, value });
2518
2570
  }
2519
2571
  if (subcommand === "set" && configKey && !value) {
2520
- return /* @__PURE__ */ jsx7(Text7, { color: "red", children: `Missing value for config set.
2572
+ return /* @__PURE__ */ jsx8(Text8, { color: "red", children: `Missing value for config set.
2521
2573
  Usage: toby config set <key> <value>` });
2522
2574
  }
2523
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
2524
- /* @__PURE__ */ jsx7(Text7, { children: `toby v${version2}` }),
2525
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2526
- /* @__PURE__ */ jsx7(Text7, { children: "Usage: toby config [get <key> | set <key> <value>]" }),
2527
- /* @__PURE__ */ jsx7(Text7, { children: "" }),
2528
- /* @__PURE__ */ jsx7(Text7, { bold: true, children: "Available keys:" }),
2529
- Object.entries(VALID_KEYS).map(([key, type]) => /* @__PURE__ */ jsx7(Text7, { children: ` ${key} (${type})` }, key))
2575
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
2576
+ /* @__PURE__ */ jsx8(Text8, { children: `toby v${version2}` }),
2577
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2578
+ /* @__PURE__ */ jsx8(Text8, { children: "Usage: toby config [get <key> | set <key> <value>]" }),
2579
+ /* @__PURE__ */ jsx8(Text8, { children: "" }),
2580
+ /* @__PURE__ */ jsx8(Text8, { bold: true, children: "Available keys:" }),
2581
+ Object.entries(VALID_KEYS).map(([key, type]) => /* @__PURE__ */ jsx8(Text8, { children: ` ${key} (${type})` }, key))
2530
2582
  ] });
2531
2583
  }
2532
2584
 
2533
2585
  // src/components/Welcome.tsx
2534
- import { useState as useState8, useEffect as useEffect7, useMemo as useMemo5 } from "react";
2535
- import { Box as Box11, Text as Text11, useApp as useApp4, useStdout as useStdout2 } from "ink";
2586
+ import { useState as useState9, useEffect as useEffect8, useMemo as useMemo5 } from "react";
2587
+ import { Box as Box12, Text as Text12, useApp as useApp4, useStdout as useStdout2 } from "ink";
2536
2588
 
2537
2589
  // src/components/hamster/HamsterWheel.tsx
2538
- import { useState as useState7, useEffect as useEffect6, useMemo as useMemo4 } from "react";
2539
- import { Box as Box8, Text as Text8, useStdout } from "ink";
2590
+ import { useState as useState8, useEffect as useEffect7, useMemo as useMemo4 } from "react";
2591
+ import { Box as Box9, Text as Text9, useStdout } from "ink";
2540
2592
 
2541
2593
  // src/components/hamster/palette.ts
2542
2594
  var PALETTE = {
@@ -2695,7 +2747,7 @@ function generateWheelPixels(cx, cy, outerRadius, innerRadius, spokeAngle, aspec
2695
2747
  }
2696
2748
 
2697
2749
  // src/components/hamster/HamsterWheel.tsx
2698
- import { jsx as jsx8 } from "react/jsx-runtime";
2750
+ import { jsx as jsx9 } from "react/jsx-runtime";
2699
2751
  function buildGrid(width, height) {
2700
2752
  return Array.from(
2701
2753
  { length: height },
@@ -2778,9 +2830,9 @@ function HamsterWheel({
2778
2830
  heightProp,
2779
2831
  columns
2780
2832
  );
2781
- const [frame, setFrame] = useState7(0);
2782
- const [spokeAngle, setSpokeAngle] = useState7(0);
2783
- useEffect6(() => {
2833
+ const [frame, setFrame] = useState8(0);
2834
+ const [spokeAngle, setSpokeAngle] = useState8(0);
2835
+ useEffect7(() => {
2784
2836
  if (speed === 0 || isStatic) return;
2785
2837
  const interval = computeInterval(HAMSTER_BASE_INTERVAL, speed);
2786
2838
  const id = setInterval(() => {
@@ -2788,7 +2840,7 @@ function HamsterWheel({
2788
2840
  }, interval);
2789
2841
  return () => clearInterval(id);
2790
2842
  }, [speed, isStatic]);
2791
- useEffect6(() => {
2843
+ useEffect7(() => {
2792
2844
  if (speed === 0 || isStatic) return;
2793
2845
  const interval = computeInterval(WHEEL_BASE_INTERVAL, speed);
2794
2846
  const id = setInterval(() => {
@@ -2828,43 +2880,43 @@ function HamsterWheel({
2828
2880
  return buildColorRuns(grid, resolvedWidth, resolvedHeight);
2829
2881
  }, [resolvedWidth, resolvedHeight, frame, spokeAngle, isStatic]);
2830
2882
  if (isStatic) {
2831
- return /* @__PURE__ */ jsx8(Text8, { children: " \u{1F439} toby" });
2883
+ return /* @__PURE__ */ jsx9(Text9, { children: " \u{1F439} toby" });
2832
2884
  }
2833
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: renderedRows.map((runs, y) => /* @__PURE__ */ jsx8(Text8, { children: runs.map((run, i) => /* @__PURE__ */ jsx8(Text8, { color: run.fg, backgroundColor: run.bg, children: run.char.repeat(run.length) }, i)) }, y)) });
2885
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: renderedRows.map((runs, y) => /* @__PURE__ */ jsx9(Text9, { children: runs.map((run, i) => /* @__PURE__ */ jsx9(Text9, { color: run.fg, backgroundColor: run.bg, children: run.char.repeat(run.length) }, i)) }, y)) });
2834
2886
  }
2835
2887
 
2836
2888
  // src/components/InfoPanel.tsx
2837
- import { Box as Box9, Text as Text9 } from "ink";
2838
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2889
+ import { Box as Box10, Text as Text10 } from "ink";
2890
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2839
2891
  var formatTokens = (n) => new Intl.NumberFormat().format(n);
2840
2892
  function StatRow({ label, value }) {
2841
- return /* @__PURE__ */ jsxs7(Box9, { children: [
2842
- /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
2893
+ return /* @__PURE__ */ jsxs8(Box10, { children: [
2894
+ /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
2843
2895
  String(label).padStart(9),
2844
2896
  " "
2845
2897
  ] }),
2846
- /* @__PURE__ */ jsx9(Text9, { children: value })
2898
+ /* @__PURE__ */ jsx10(Text10, { children: value })
2847
2899
  ] });
2848
2900
  }
2849
2901
  function InfoPanel({ version: version2, stats }) {
2850
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
2851
- /* @__PURE__ */ jsxs7(Text9, { bold: true, color: "#f0a030", children: [
2902
+ return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", children: [
2903
+ /* @__PURE__ */ jsxs8(Text10, { bold: true, color: "#f0a030", children: [
2852
2904
  "toby v",
2853
2905
  version2
2854
2906
  ] }),
2855
- stats !== null && /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", marginTop: 1, children: [
2856
- /* @__PURE__ */ jsx9(StatRow, { label: "Specs", value: stats.totalSpecs }),
2857
- /* @__PURE__ */ jsx9(StatRow, { label: "Planned", value: stats.planned }),
2858
- /* @__PURE__ */ jsx9(StatRow, { label: "Done", value: stats.done }),
2859
- /* @__PURE__ */ jsx9(StatRow, { label: "Tokens", value: formatTokens(stats.totalTokens) })
2907
+ stats !== null && /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", marginTop: 1, children: [
2908
+ /* @__PURE__ */ jsx10(StatRow, { label: "Specs", value: stats.totalSpecs }),
2909
+ /* @__PURE__ */ jsx10(StatRow, { label: "Planned", value: stats.planned }),
2910
+ /* @__PURE__ */ jsx10(StatRow, { label: "Done", value: stats.done }),
2911
+ /* @__PURE__ */ jsx10(StatRow, { label: "Tokens", value: formatTokens(stats.totalTokens) })
2860
2912
  ] })
2861
2913
  ] });
2862
2914
  }
2863
2915
 
2864
2916
  // src/components/MainMenu.tsx
2865
- import { Text as Text10, Box as Box10 } from "ink";
2917
+ import { Text as Text11, Box as Box11 } from "ink";
2866
2918
  import SelectInput3 from "ink-select-input";
2867
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2919
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2868
2920
  var MENU_ITEMS = [
2869
2921
  { label: "plan", value: "plan", description: "Plan specs with AI loop engine" },
2870
2922
  { label: "build", value: "build", description: "Build tasks one-per-spawn with AI" },
@@ -2872,16 +2924,16 @@ var MENU_ITEMS = [
2872
2924
  { label: "config", value: "config", description: "Manage configuration" }
2873
2925
  ];
2874
2926
  function MenuItem({ isSelected = false, label, description }) {
2875
- return /* @__PURE__ */ jsxs8(Box10, { children: [
2876
- /* @__PURE__ */ jsx10(Text10, { color: isSelected ? "blue" : void 0, children: label.padEnd(10) }),
2877
- description && /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
2927
+ return /* @__PURE__ */ jsxs9(Box11, { children: [
2928
+ /* @__PURE__ */ jsx11(Text11, { color: isSelected ? "blue" : void 0, children: label.padEnd(10) }),
2929
+ description && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
2878
2930
  "\u2014 ",
2879
2931
  description
2880
2932
  ] })
2881
2933
  ] });
2882
2934
  }
2883
2935
  function MainMenu({ onSelect }) {
2884
- return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsx10(
2936
+ return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(
2885
2937
  SelectInput3,
2886
2938
  {
2887
2939
  items: MENU_ITEMS,
@@ -2936,60 +2988,60 @@ function computeProjectStats(cwd) {
2936
2988
  }
2937
2989
 
2938
2990
  // src/components/Welcome.tsx
2939
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2991
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2940
2992
  var NARROW_THRESHOLD = 60;
2941
2993
  function Welcome({ version: version2 }) {
2942
2994
  const { exit } = useApp4();
2943
2995
  const { stdout } = useStdout2();
2944
- const [selectedCommand, setSelectedCommand] = useState8(null);
2996
+ const [selectedCommand, setSelectedCommand] = useState9(null);
2945
2997
  const stats = useMemo5(() => computeProjectStats(), []);
2946
2998
  const isNarrow = (stdout.columns ?? 80) < NARROW_THRESHOLD;
2947
- useEffect7(() => {
2999
+ useEffect8(() => {
2948
3000
  if (selectedCommand === "status") {
2949
3001
  const timer = setTimeout(() => exit(), 0);
2950
3002
  return () => clearTimeout(timer);
2951
3003
  }
2952
3004
  }, [selectedCommand, exit]);
2953
3005
  if (selectedCommand === "plan") {
2954
- return /* @__PURE__ */ jsx11(Plan, {});
3006
+ return /* @__PURE__ */ jsx12(Plan, {});
2955
3007
  }
2956
3008
  if (selectedCommand === "build") {
2957
- return /* @__PURE__ */ jsx11(Build, {});
3009
+ return /* @__PURE__ */ jsx12(Build, {});
2958
3010
  }
2959
3011
  if (selectedCommand === "status") {
2960
- return /* @__PURE__ */ jsx11(Status, { version: version2 });
3012
+ return /* @__PURE__ */ jsx12(Status, { version: version2 });
2961
3013
  }
2962
3014
  if (selectedCommand === "config") {
2963
- return /* @__PURE__ */ jsx11(ConfigEditor, { version: version2 });
3015
+ return /* @__PURE__ */ jsx12(ConfigEditor, { version: version2 });
2964
3016
  }
2965
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", gap: 1, children: [
2966
- isNarrow ? /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
2967
- /* @__PURE__ */ jsxs9(Text11, { bold: true, color: "#f0a030", children: [
3017
+ return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", gap: 1, children: [
3018
+ isNarrow ? /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
3019
+ /* @__PURE__ */ jsxs10(Text12, { bold: true, color: "#f0a030", children: [
2968
3020
  "\u{1F439} toby v",
2969
3021
  version2
2970
3022
  ] }),
2971
- stats !== null && /* @__PURE__ */ jsxs9(Text11, { children: [
2972
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "Specs: " }),
2973
- /* @__PURE__ */ jsx11(Text11, { children: stats.totalSpecs }),
2974
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " \xB7 Planned: " }),
2975
- /* @__PURE__ */ jsx11(Text11, { children: stats.planned }),
2976
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " \xB7 Done: " }),
2977
- /* @__PURE__ */ jsx11(Text11, { children: stats.done }),
2978
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " \xB7 Tokens: " }),
2979
- /* @__PURE__ */ jsx11(Text11, { children: formatTokens(stats.totalTokens) })
3023
+ stats !== null && /* @__PURE__ */ jsxs10(Text12, { children: [
3024
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Specs: " }),
3025
+ /* @__PURE__ */ jsx12(Text12, { children: stats.totalSpecs }),
3026
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " \xB7 Planned: " }),
3027
+ /* @__PURE__ */ jsx12(Text12, { children: stats.planned }),
3028
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " \xB7 Done: " }),
3029
+ /* @__PURE__ */ jsx12(Text12, { children: stats.done }),
3030
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " \xB7 Tokens: " }),
3031
+ /* @__PURE__ */ jsx12(Text12, { children: formatTokens(stats.totalTokens) })
2980
3032
  ] })
2981
- ] }) : /* @__PURE__ */ jsxs9(Box11, { flexDirection: "row", gap: 2, children: [
2982
- /* @__PURE__ */ jsx11(HamsterWheel, {}),
2983
- /* @__PURE__ */ jsx11(InfoPanel, { version: version2, stats })
3033
+ ] }) : /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", gap: 2, children: [
3034
+ /* @__PURE__ */ jsx12(HamsterWheel, {}),
3035
+ /* @__PURE__ */ jsx12(InfoPanel, { version: version2, stats })
2984
3036
  ] }),
2985
- /* @__PURE__ */ jsx11(MainMenu, { onSelect: setSelectedCommand })
3037
+ /* @__PURE__ */ jsx12(MainMenu, { onSelect: setSelectedCommand })
2986
3038
  ] });
2987
3039
  }
2988
3040
 
2989
3041
  // src/cli.tsx
2990
- import { jsx as jsx12 } from "react/jsx-runtime";
3042
+ import { jsx as jsx13 } from "react/jsx-runtime";
2991
3043
  function Help({ version: version2 }) {
2992
- return /* @__PURE__ */ jsx12(Text12, { children: `toby v${version2} \u2014 AI-assisted development loop engine
3044
+ return /* @__PURE__ */ jsx13(Text13, { children: `toby v${version2} \u2014 AI-assisted development loop engine
2993
3045
 
2994
3046
  Usage
2995
3047
  $ toby <command> [options]
@@ -3010,7 +3062,7 @@ Spec Selection
3010
3062
  --specs=<names> Alias for --spec` });
3011
3063
  }
3012
3064
  function UnknownCommand({ command: command2 }) {
3013
- return /* @__PURE__ */ jsx12(Text12, { color: "red", children: `Unknown command: ${command2}
3065
+ return /* @__PURE__ */ jsx13(Text13, { color: "red", children: `Unknown command: ${command2}
3014
3066
  Run "toby --help" for available commands.` });
3015
3067
  }
3016
3068
  var cli = meow(
@@ -3086,7 +3138,7 @@ var resolvedSpec = cli.flags.specs ?? cli.flags.spec;
3086
3138
  var flags = { ...cli.flags, spec: resolvedSpec };
3087
3139
  var commands = {
3088
3140
  plan: {
3089
- render: (flags2) => /* @__PURE__ */ jsx12(
3141
+ render: (flags2) => /* @__PURE__ */ jsx13(
3090
3142
  Plan,
3091
3143
  {
3092
3144
  spec: flags2.spec,
@@ -3101,7 +3153,7 @@ var commands = {
3101
3153
  waitForExit: true
3102
3154
  },
3103
3155
  build: {
3104
- render: (flags2) => /* @__PURE__ */ jsx12(
3156
+ render: (flags2) => /* @__PURE__ */ jsx13(
3105
3157
  Build,
3106
3158
  {
3107
3159
  spec: flags2.spec,
@@ -3116,7 +3168,7 @@ var commands = {
3116
3168
  waitForExit: true
3117
3169
  },
3118
3170
  init: {
3119
- render: (flags2, _input, version2) => /* @__PURE__ */ jsx12(
3171
+ render: (flags2, _input, version2) => /* @__PURE__ */ jsx13(
3120
3172
  Init,
3121
3173
  {
3122
3174
  version: version2,
@@ -3131,17 +3183,17 @@ var commands = {
3131
3183
  waitForExit: true
3132
3184
  },
3133
3185
  status: {
3134
- render: (flags2, _input, version2) => /* @__PURE__ */ jsx12(Status, { spec: flags2.spec, version: version2 })
3186
+ render: (flags2, _input, version2) => /* @__PURE__ */ jsx13(Status, { spec: flags2.spec, version: version2 })
3135
3187
  },
3136
3188
  config: {
3137
3189
  render: (_flags, input, version2) => {
3138
3190
  const [, subcommand, ...rest] = input;
3139
- if (!subcommand) return /* @__PURE__ */ jsx12(ConfigEditor, { version: version2 });
3191
+ if (!subcommand) return /* @__PURE__ */ jsx13(ConfigEditor, { version: version2 });
3140
3192
  if (subcommand === "set" && rest.some((arg) => arg.includes("="))) {
3141
- return /* @__PURE__ */ jsx12(ConfigSetBatch, { pairs: rest.filter((arg) => arg.includes("=")) });
3193
+ return /* @__PURE__ */ jsx13(ConfigSetBatch, { pairs: rest.filter((arg) => arg.includes("=")) });
3142
3194
  }
3143
3195
  const [configKey, value] = rest;
3144
- return /* @__PURE__ */ jsx12(
3196
+ return /* @__PURE__ */ jsx13(
3145
3197
  Config,
3146
3198
  {
3147
3199
  subcommand,
@@ -3158,10 +3210,10 @@ var version = cli.pkg.version ?? "0.0.0";
3158
3210
  var [command] = cli.input;
3159
3211
  if (!command) {
3160
3212
  if (process.stdin.isTTY) {
3161
- const app = render(/* @__PURE__ */ jsx12(Welcome, { version }));
3213
+ const app = render(/* @__PURE__ */ jsx13(Welcome, { version }));
3162
3214
  await app.waitUntilExit();
3163
3215
  } else {
3164
- render(/* @__PURE__ */ jsx12(Help, { version })).unmount();
3216
+ render(/* @__PURE__ */ jsx13(Help, { version })).unmount();
3165
3217
  }
3166
3218
  } else if (command in commands) {
3167
3219
  const entry = commands[command];
@@ -3172,6 +3224,6 @@ if (!command) {
3172
3224
  app.unmount();
3173
3225
  }
3174
3226
  } else {
3175
- render(/* @__PURE__ */ jsx12(UnknownCommand, { command })).unmount();
3227
+ render(/* @__PURE__ */ jsx13(UnknownCommand, { command })).unmount();
3176
3228
  process.exitCode = 1;
3177
3229
  }
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@0xtiby/toby",
3
- "version": "0.0.1",
3
+ "version": "1.0.1",
4
4
  "description": "AI-assisted development loop engine CLI",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/0xtiby/toby.git"
8
+ },
5
9
  "type": "module",
10
+ "packageManager": "pnpm@10.28.1",
6
11
  "bin": {
7
12
  "toby": "./dist/cli.js"
8
13
  },
@@ -16,12 +21,10 @@
16
21
  "scripts": {
17
22
  "build": "tsup",
18
23
  "dev": "tsup --watch",
19
- "test": "vitest run",
20
- "bootstrap": "bash scripts/bootstrap.sh",
21
- "setup:branch-protection": "bash scripts/setup-branch-protection.sh"
24
+ "test": "vitest run"
22
25
  },
23
26
  "dependencies": {
24
- "@0xtiby/spawner": "latest",
27
+ "@0xtiby/spawner": "^1.1.0",
25
28
  "ink": "^5.0.1",
26
29
  "ink-select-input": "^6.2.0",
27
30
  "ink-spinner": "^5.0.0",