@0xtiby/toby 0.0.1 → 1.1.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 +469 -293
- 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
|
|
5
|
+
import { render, Text as Text14 } from "ink";
|
|
6
6
|
|
|
7
7
|
// src/commands/plan.tsx
|
|
8
8
|
import { useState as useState3, useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
@@ -80,6 +80,7 @@ var LOCAL_TOBY_DIR = ".toby";
|
|
|
80
80
|
var DEFAULT_SPECS_DIR = "specs";
|
|
81
81
|
var STATUS_FILE = "status.json";
|
|
82
82
|
var CONFIG_FILE = "config.json";
|
|
83
|
+
var TRANSCRIPTS_DIR = "transcripts";
|
|
83
84
|
function getGlobalDir() {
|
|
84
85
|
return path.join(os.homedir(), GLOBAL_TOBY_DIR);
|
|
85
86
|
}
|
|
@@ -567,7 +568,6 @@ var AbortError = class extends Error {
|
|
|
567
568
|
// src/lib/transcript.ts
|
|
568
569
|
import fs6 from "fs";
|
|
569
570
|
import path6 from "path";
|
|
570
|
-
var TRANSCRIPTS_DIR = "transcripts";
|
|
571
571
|
function formatTimestamp() {
|
|
572
572
|
const now = /* @__PURE__ */ new Date();
|
|
573
573
|
const pad2 = (n, len = 2) => String(n).padStart(len, "0");
|
|
@@ -1523,14 +1523,72 @@ function Build(flags2) {
|
|
|
1523
1523
|
}
|
|
1524
1524
|
|
|
1525
1525
|
// src/commands/init.tsx
|
|
1526
|
-
import { useState as
|
|
1527
|
-
import { Text as
|
|
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
|
|
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__ */
|
|
1593
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1653
|
+
([name, info]) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
1596
1654
|
" ",
|
|
1597
|
-
info.installed ? /* @__PURE__ */
|
|
1655
|
+
info.installed ? /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "red", children: "\u2717" }),
|
|
1598
1656
|
" ",
|
|
1599
|
-
/* @__PURE__ */
|
|
1600
|
-
info.installed && /* @__PURE__ */
|
|
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] =
|
|
1627
|
-
|
|
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__ */
|
|
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__ */
|
|
1718
|
+
return /* @__PURE__ */ jsx6(Text6, { children: "Detecting installed CLIs..." });
|
|
1668
1719
|
}
|
|
1669
1720
|
if (status.type === "error") {
|
|
1670
|
-
return /* @__PURE__ */
|
|
1721
|
+
return /* @__PURE__ */ jsx6(Text6, { color: "red", children: `\u2717 ${status.message}` });
|
|
1671
1722
|
}
|
|
1672
1723
|
const { result, selections } = status;
|
|
1673
|
-
return /* @__PURE__ */
|
|
1674
|
-
/* @__PURE__ */
|
|
1675
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1680
|
-
result.specsDirCreated && /* @__PURE__ */
|
|
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__ */
|
|
1740
|
+
return /* @__PURE__ */ jsx6(NonInteractiveInit, { flags: flags2 });
|
|
1690
1741
|
}
|
|
1691
|
-
return /* @__PURE__ */
|
|
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] =
|
|
1696
|
-
const [clis, setClis] =
|
|
1697
|
-
const [installedClis, setInstalledClis] =
|
|
1698
|
-
const [selections, setSelections] =
|
|
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] =
|
|
1707
|
-
const [result, setResult] =
|
|
1708
|
-
const [error, setError] =
|
|
1709
|
-
|
|
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__ */
|
|
1821
|
+
return /* @__PURE__ */ jsx6(Text6, { children: "Detecting installed CLIs..." });
|
|
1769
1822
|
}
|
|
1770
1823
|
if (phase === "no_cli") {
|
|
1771
|
-
return /* @__PURE__ */
|
|
1772
|
-
clis && /* @__PURE__ */
|
|
1773
|
-
/* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1784
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1787
|
-
phase === "plan_cli" && /* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1792
|
-
|
|
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__ */
|
|
1851
|
+
/* @__PURE__ */ jsx6(
|
|
1798
1852
|
SelectInput,
|
|
1799
1853
|
{
|
|
1800
|
-
items:
|
|
1854
|
+
items: planModels.items,
|
|
1801
1855
|
onSelect: handlePlanModelSelect
|
|
1802
1856
|
}
|
|
1803
1857
|
)
|
|
1804
1858
|
] }),
|
|
1805
|
-
phase === "build_cli" && /* @__PURE__ */
|
|
1806
|
-
/* @__PURE__ */
|
|
1807
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1810
|
-
|
|
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__ */
|
|
1870
|
+
/* @__PURE__ */ jsx6(
|
|
1816
1871
|
SelectInput,
|
|
1817
1872
|
{
|
|
1818
|
-
items:
|
|
1873
|
+
items: buildModels.items,
|
|
1819
1874
|
onSelect: handleBuildModelSelect
|
|
1820
1875
|
}
|
|
1821
1876
|
)
|
|
1822
1877
|
] }),
|
|
1823
|
-
phase === "specs_dir" && /* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
/* @__PURE__ */
|
|
1826
|
-
/* @__PURE__ */
|
|
1827
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1852
|
-
/* @__PURE__ */
|
|
1853
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1863
|
-
result.specsDirCreated && /* @__PURE__ */
|
|
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__ */
|
|
1869
|
-
/* @__PURE__ */
|
|
1870
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1930
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
1876
1931
|
" 2. Run ",
|
|
1877
|
-
/* @__PURE__ */
|
|
1932
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "toby plan" }),
|
|
1878
1933
|
" to plan a spec"
|
|
1879
1934
|
] }),
|
|
1880
|
-
/* @__PURE__ */
|
|
1935
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
1881
1936
|
" 3. Run ",
|
|
1882
|
-
/* @__PURE__ */
|
|
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
|
|
1945
|
+
import { Text as Text7, Box as Box7 } from "ink";
|
|
1891
1946
|
import fs8 from "fs";
|
|
1892
|
-
import { jsx as
|
|
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__ */
|
|
1941
|
-
/* @__PURE__ */
|
|
1942
|
-
/* @__PURE__ */
|
|
1943
|
-
rows.map((row) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
rows.map((row) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1999
|
-
statusWarning && /* @__PURE__ */
|
|
2000
|
-
/* @__PURE__ */
|
|
2001
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2006
|
-
/* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2023
|
-
/* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
warnings.map((w) => /* @__PURE__ */
|
|
2039
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2043
|
-
/* @__PURE__ */
|
|
2044
|
-
warnings.map((w) => /* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
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
|
|
2052
|
-
import { Text as
|
|
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
|
|
2058
|
-
import { Fragment as Fragment2, jsx as
|
|
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__ */
|
|
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__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2133
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2224
|
+
return /* @__PURE__ */ jsx8(Text8, { color: "red", children: msg });
|
|
2170
2225
|
}
|
|
2171
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
2273
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
2226
2274
|
" ",
|
|
2227
|
-
/* @__PURE__ */
|
|
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] =
|
|
2238
|
-
const [installedClis, setInstalledClis] =
|
|
2239
|
-
const [values, setValues] =
|
|
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] =
|
|
2250
|
-
const [specsDirInput, setSpecsDirInput] =
|
|
2251
|
-
const [saveError, setSaveError] =
|
|
2252
|
-
|
|
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__ */
|
|
2341
|
+
return /* @__PURE__ */ jsx8(Text8, { children: "Loading configuration..." });
|
|
2292
2342
|
}
|
|
2293
2343
|
const planCliItems = cliItems();
|
|
2294
2344
|
const buildCliItems = cliItems();
|
|
2295
|
-
return /* @__PURE__ */
|
|
2296
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2299
|
-
phase === "plan_cli" && /* @__PURE__ */
|
|
2300
|
-
/* @__PURE__ */
|
|
2301
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2314
|
-
phase === "plan_model" && /* @__PURE__ */
|
|
2315
|
-
|
|
2316
|
-
/* @__PURE__ */
|
|
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:
|
|
2320
|
-
initialIndex: initialIndex(
|
|
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__ */
|
|
2330
|
-
phase === "plan_iterations" && /* @__PURE__ */
|
|
2331
|
-
/* @__PURE__ */
|
|
2332
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2348
|
-
pastPhase(phase, "plan_iterations") && /* @__PURE__ */
|
|
2349
|
-
/* @__PURE__ */
|
|
2350
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2367
|
-
phase === "build_model" && /* @__PURE__ */
|
|
2368
|
-
|
|
2369
|
-
/* @__PURE__ */
|
|
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:
|
|
2373
|
-
initialIndex: initialIndex(
|
|
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__ */
|
|
2383
|
-
phase === "build_iterations" && /* @__PURE__ */
|
|
2384
|
-
/* @__PURE__ */
|
|
2385
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2402
|
-
pastPhase(phase, "build_iterations") && /* @__PURE__ */
|
|
2403
|
-
/* @__PURE__ */
|
|
2404
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2407
|
-
/* @__PURE__ */
|
|
2408
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2422
|
-
phase === "verbose" && /* @__PURE__ */
|
|
2423
|
-
/* @__PURE__ */
|
|
2424
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2440
|
-
/* @__PURE__ */
|
|
2441
|
-
/* @__PURE__ */
|
|
2442
|
-
saveError ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2544
|
+
return /* @__PURE__ */ jsx8(Text8, { color: "red", children: msg });
|
|
2493
2545
|
}
|
|
2494
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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,153 @@ function Config({
|
|
|
2504
2556
|
version: version2
|
|
2505
2557
|
}) {
|
|
2506
2558
|
if (subcommand && subcommand !== "get" && subcommand !== "set") {
|
|
2507
|
-
return /* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2510
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2566
|
+
return /* @__PURE__ */ jsx8(ConfigGet, { configKey });
|
|
2515
2567
|
}
|
|
2516
2568
|
if (subcommand === "set" && configKey && value) {
|
|
2517
|
-
return /* @__PURE__ */
|
|
2569
|
+
return /* @__PURE__ */ jsx8(ConfigSet, { configKey, value });
|
|
2518
2570
|
}
|
|
2519
2571
|
if (subcommand === "set" && configKey && !value) {
|
|
2520
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
2524
|
-
/* @__PURE__ */
|
|
2525
|
-
/* @__PURE__ */
|
|
2526
|
-
/* @__PURE__ */
|
|
2527
|
-
/* @__PURE__ */
|
|
2528
|
-
/* @__PURE__ */
|
|
2529
|
-
Object.entries(VALID_KEYS).map(([key, type]) => /* @__PURE__ */
|
|
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
|
|
|
2585
|
+
// src/commands/clean.tsx
|
|
2586
|
+
import { useState as useState8, useEffect as useEffect7, useMemo as useMemo4 } from "react";
|
|
2587
|
+
import { Text as Text9, useApp as useApp4, useInput as useInput2 } from "ink";
|
|
2588
|
+
|
|
2589
|
+
// src/lib/clean.ts
|
|
2590
|
+
import path9 from "path";
|
|
2591
|
+
import fs10 from "fs";
|
|
2592
|
+
function listTranscripts(cwd) {
|
|
2593
|
+
const dir = path9.join(getLocalDir(cwd), TRANSCRIPTS_DIR);
|
|
2594
|
+
let entries;
|
|
2595
|
+
try {
|
|
2596
|
+
entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
2597
|
+
} catch {
|
|
2598
|
+
return [];
|
|
2599
|
+
}
|
|
2600
|
+
return entries.filter((e) => e.isFile()).map((e) => path9.join(dir, e.name));
|
|
2601
|
+
}
|
|
2602
|
+
function deleteTranscripts(files) {
|
|
2603
|
+
let deleted = 0;
|
|
2604
|
+
for (const file of files) {
|
|
2605
|
+
try {
|
|
2606
|
+
fs10.unlinkSync(file);
|
|
2607
|
+
deleted++;
|
|
2608
|
+
} catch {
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
return deleted;
|
|
2612
|
+
}
|
|
2613
|
+
function executeClean(cwd) {
|
|
2614
|
+
const files = listTranscripts(cwd);
|
|
2615
|
+
if (files.length === 0) {
|
|
2616
|
+
return { deleted: 0, failed: 0, total: 0 };
|
|
2617
|
+
}
|
|
2618
|
+
const deleted = deleteTranscripts(files);
|
|
2619
|
+
return { deleted, failed: files.length - deleted, total: files.length };
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// src/commands/clean.tsx
|
|
2623
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2624
|
+
function computeInitial(force) {
|
|
2625
|
+
const files = listTranscripts();
|
|
2626
|
+
if (files.length === 0) {
|
|
2627
|
+
return { phase: "empty", fileCount: 0, result: null };
|
|
2628
|
+
}
|
|
2629
|
+
if (!process.stdin.isTTY && !force) {
|
|
2630
|
+
return { phase: "error", fileCount: files.length, result: null };
|
|
2631
|
+
}
|
|
2632
|
+
if (force) {
|
|
2633
|
+
const r = executeClean();
|
|
2634
|
+
return { phase: "done", fileCount: files.length, result: r };
|
|
2635
|
+
}
|
|
2636
|
+
return { phase: "confirming", fileCount: files.length, result: null };
|
|
2637
|
+
}
|
|
2638
|
+
function Clean({ force }) {
|
|
2639
|
+
const { exit } = useApp4();
|
|
2640
|
+
const initial = useMemo4(() => computeInitial(force), [force]);
|
|
2641
|
+
useEffect7(() => {
|
|
2642
|
+
if (initial.phase === "error") {
|
|
2643
|
+
process.exitCode = 1;
|
|
2644
|
+
}
|
|
2645
|
+
}, [initial.phase]);
|
|
2646
|
+
const [phase, setPhase] = useState8(initial.phase);
|
|
2647
|
+
const [result, setResult] = useState8(initial.result);
|
|
2648
|
+
useEffect7(() => {
|
|
2649
|
+
if (phase === "empty" || phase === "done" || phase === "cancelled" || phase === "error") {
|
|
2650
|
+
const timer = setTimeout(() => exit(), 100);
|
|
2651
|
+
return () => clearTimeout(timer);
|
|
2652
|
+
}
|
|
2653
|
+
}, [phase, exit]);
|
|
2654
|
+
useInput2((input, key) => {
|
|
2655
|
+
if (phase !== "confirming") return;
|
|
2656
|
+
if (input === "y" || key.return) {
|
|
2657
|
+
const r = executeClean();
|
|
2658
|
+
setResult(r);
|
|
2659
|
+
setPhase("done");
|
|
2660
|
+
} else if (input === "n" || key.escape) {
|
|
2661
|
+
setPhase("cancelled");
|
|
2662
|
+
}
|
|
2663
|
+
});
|
|
2664
|
+
if (phase === "error") {
|
|
2665
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "red", children: "Error: Use --force to clean transcripts in non-interactive mode." });
|
|
2666
|
+
}
|
|
2667
|
+
if (phase === "empty") {
|
|
2668
|
+
return /* @__PURE__ */ jsx9(Text9, { children: "No transcripts to clean." });
|
|
2669
|
+
}
|
|
2670
|
+
if (phase === "confirming") {
|
|
2671
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
2672
|
+
"Found ",
|
|
2673
|
+
initial.fileCount,
|
|
2674
|
+
" transcript files. Delete all? [Y/n]"
|
|
2675
|
+
] });
|
|
2676
|
+
}
|
|
2677
|
+
if (phase === "done" && result) {
|
|
2678
|
+
if (result.failed > 0) {
|
|
2679
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
2680
|
+
"Deleted ",
|
|
2681
|
+
result.deleted,
|
|
2682
|
+
" transcript files. Failed to delete ",
|
|
2683
|
+
result.failed,
|
|
2684
|
+
" files."
|
|
2685
|
+
] });
|
|
2686
|
+
}
|
|
2687
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
2688
|
+
"Deleted ",
|
|
2689
|
+
result.deleted,
|
|
2690
|
+
" transcript files."
|
|
2691
|
+
] });
|
|
2692
|
+
}
|
|
2693
|
+
if (phase === "cancelled") {
|
|
2694
|
+
return /* @__PURE__ */ jsx9(Text9, { children: "Clean cancelled." });
|
|
2695
|
+
}
|
|
2696
|
+
return null;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2533
2699
|
// src/components/Welcome.tsx
|
|
2534
|
-
import { useState as
|
|
2535
|
-
import { Box as
|
|
2700
|
+
import { useState as useState10, useEffect as useEffect9, useMemo as useMemo6 } from "react";
|
|
2701
|
+
import { Box as Box12, Text as Text13, useApp as useApp5, useStdout as useStdout2 } from "ink";
|
|
2536
2702
|
|
|
2537
2703
|
// src/components/hamster/HamsterWheel.tsx
|
|
2538
|
-
import { useState as
|
|
2539
|
-
import { Box as
|
|
2704
|
+
import { useState as useState9, useEffect as useEffect8, useMemo as useMemo5 } from "react";
|
|
2705
|
+
import { Box as Box9, Text as Text10, useStdout } from "ink";
|
|
2540
2706
|
|
|
2541
2707
|
// src/components/hamster/palette.ts
|
|
2542
2708
|
var PALETTE = {
|
|
@@ -2695,7 +2861,7 @@ function generateWheelPixels(cx, cy, outerRadius, innerRadius, spokeAngle, aspec
|
|
|
2695
2861
|
}
|
|
2696
2862
|
|
|
2697
2863
|
// src/components/hamster/HamsterWheel.tsx
|
|
2698
|
-
import { jsx as
|
|
2864
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2699
2865
|
function buildGrid(width, height) {
|
|
2700
2866
|
return Array.from(
|
|
2701
2867
|
{ length: height },
|
|
@@ -2778,9 +2944,9 @@ function HamsterWheel({
|
|
|
2778
2944
|
heightProp,
|
|
2779
2945
|
columns
|
|
2780
2946
|
);
|
|
2781
|
-
const [frame, setFrame] =
|
|
2782
|
-
const [spokeAngle, setSpokeAngle] =
|
|
2783
|
-
|
|
2947
|
+
const [frame, setFrame] = useState9(0);
|
|
2948
|
+
const [spokeAngle, setSpokeAngle] = useState9(0);
|
|
2949
|
+
useEffect8(() => {
|
|
2784
2950
|
if (speed === 0 || isStatic) return;
|
|
2785
2951
|
const interval = computeInterval(HAMSTER_BASE_INTERVAL, speed);
|
|
2786
2952
|
const id = setInterval(() => {
|
|
@@ -2788,7 +2954,7 @@ function HamsterWheel({
|
|
|
2788
2954
|
}, interval);
|
|
2789
2955
|
return () => clearInterval(id);
|
|
2790
2956
|
}, [speed, isStatic]);
|
|
2791
|
-
|
|
2957
|
+
useEffect8(() => {
|
|
2792
2958
|
if (speed === 0 || isStatic) return;
|
|
2793
2959
|
const interval = computeInterval(WHEEL_BASE_INTERVAL, speed);
|
|
2794
2960
|
const id = setInterval(() => {
|
|
@@ -2796,7 +2962,7 @@ function HamsterWheel({
|
|
|
2796
2962
|
}, interval);
|
|
2797
2963
|
return () => clearInterval(id);
|
|
2798
2964
|
}, [speed, isStatic]);
|
|
2799
|
-
const renderedRows =
|
|
2965
|
+
const renderedRows = useMemo5(() => {
|
|
2800
2966
|
if (isStatic) return [];
|
|
2801
2967
|
const grid = buildGrid(resolvedWidth, resolvedHeight);
|
|
2802
2968
|
const { cx, cy, outerRadius, innerRadius } = computeWheelGeometry(
|
|
@@ -2828,43 +2994,43 @@ function HamsterWheel({
|
|
|
2828
2994
|
return buildColorRuns(grid, resolvedWidth, resolvedHeight);
|
|
2829
2995
|
}, [resolvedWidth, resolvedHeight, frame, spokeAngle, isStatic]);
|
|
2830
2996
|
if (isStatic) {
|
|
2831
|
-
return /* @__PURE__ */
|
|
2997
|
+
return /* @__PURE__ */ jsx10(Text10, { children: " \u{1F439} toby" });
|
|
2832
2998
|
}
|
|
2833
|
-
return /* @__PURE__ */
|
|
2999
|
+
return /* @__PURE__ */ jsx10(Box9, { flexDirection: "column", children: renderedRows.map((runs, y) => /* @__PURE__ */ jsx10(Text10, { children: runs.map((run, i) => /* @__PURE__ */ jsx10(Text10, { color: run.fg, backgroundColor: run.bg, children: run.char.repeat(run.length) }, i)) }, y)) });
|
|
2834
3000
|
}
|
|
2835
3001
|
|
|
2836
3002
|
// src/components/InfoPanel.tsx
|
|
2837
|
-
import { Box as
|
|
2838
|
-
import { jsx as
|
|
3003
|
+
import { Box as Box10, Text as Text11 } from "ink";
|
|
3004
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2839
3005
|
var formatTokens = (n) => new Intl.NumberFormat().format(n);
|
|
2840
3006
|
function StatRow({ label, value }) {
|
|
2841
|
-
return /* @__PURE__ */
|
|
2842
|
-
/* @__PURE__ */
|
|
3007
|
+
return /* @__PURE__ */ jsxs9(Box10, { children: [
|
|
3008
|
+
/* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
|
|
2843
3009
|
String(label).padStart(9),
|
|
2844
3010
|
" "
|
|
2845
3011
|
] }),
|
|
2846
|
-
/* @__PURE__ */
|
|
3012
|
+
/* @__PURE__ */ jsx11(Text11, { children: value })
|
|
2847
3013
|
] });
|
|
2848
3014
|
}
|
|
2849
3015
|
function InfoPanel({ version: version2, stats }) {
|
|
2850
|
-
return /* @__PURE__ */
|
|
2851
|
-
/* @__PURE__ */
|
|
3016
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
3017
|
+
/* @__PURE__ */ jsxs9(Text11, { bold: true, color: "#f0a030", children: [
|
|
2852
3018
|
"toby v",
|
|
2853
3019
|
version2
|
|
2854
3020
|
] }),
|
|
2855
|
-
stats !== null && /* @__PURE__ */
|
|
2856
|
-
/* @__PURE__ */
|
|
2857
|
-
/* @__PURE__ */
|
|
2858
|
-
/* @__PURE__ */
|
|
2859
|
-
/* @__PURE__ */
|
|
3021
|
+
stats !== null && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
3022
|
+
/* @__PURE__ */ jsx11(StatRow, { label: "Specs", value: stats.totalSpecs }),
|
|
3023
|
+
/* @__PURE__ */ jsx11(StatRow, { label: "Planned", value: stats.planned }),
|
|
3024
|
+
/* @__PURE__ */ jsx11(StatRow, { label: "Done", value: stats.done }),
|
|
3025
|
+
/* @__PURE__ */ jsx11(StatRow, { label: "Tokens", value: formatTokens(stats.totalTokens) })
|
|
2860
3026
|
] })
|
|
2861
3027
|
] });
|
|
2862
3028
|
}
|
|
2863
3029
|
|
|
2864
3030
|
// src/components/MainMenu.tsx
|
|
2865
|
-
import { Text as
|
|
3031
|
+
import { Text as Text12, Box as Box11 } from "ink";
|
|
2866
3032
|
import SelectInput3 from "ink-select-input";
|
|
2867
|
-
import { jsx as
|
|
3033
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2868
3034
|
var MENU_ITEMS = [
|
|
2869
3035
|
{ label: "plan", value: "plan", description: "Plan specs with AI loop engine" },
|
|
2870
3036
|
{ label: "build", value: "build", description: "Build tasks one-per-spawn with AI" },
|
|
@@ -2872,16 +3038,16 @@ var MENU_ITEMS = [
|
|
|
2872
3038
|
{ label: "config", value: "config", description: "Manage configuration" }
|
|
2873
3039
|
];
|
|
2874
3040
|
function MenuItem({ isSelected = false, label, description }) {
|
|
2875
|
-
return /* @__PURE__ */
|
|
2876
|
-
/* @__PURE__ */
|
|
2877
|
-
description && /* @__PURE__ */
|
|
3041
|
+
return /* @__PURE__ */ jsxs10(Box11, { children: [
|
|
3042
|
+
/* @__PURE__ */ jsx12(Text12, { color: isSelected ? "blue" : void 0, children: label.padEnd(10) }),
|
|
3043
|
+
description && /* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
|
|
2878
3044
|
"\u2014 ",
|
|
2879
3045
|
description
|
|
2880
3046
|
] })
|
|
2881
3047
|
] });
|
|
2882
3048
|
}
|
|
2883
3049
|
function MainMenu({ onSelect }) {
|
|
2884
|
-
return /* @__PURE__ */
|
|
3050
|
+
return /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx12(
|
|
2885
3051
|
SelectInput3,
|
|
2886
3052
|
{
|
|
2887
3053
|
items: MENU_ITEMS,
|
|
@@ -2892,9 +3058,9 @@ function MainMenu({ onSelect }) {
|
|
|
2892
3058
|
}
|
|
2893
3059
|
|
|
2894
3060
|
// src/lib/stats.ts
|
|
2895
|
-
import
|
|
3061
|
+
import fs11 from "fs";
|
|
2896
3062
|
function computeProjectStats(cwd) {
|
|
2897
|
-
if (!
|
|
3063
|
+
if (!fs11.existsSync(getLocalDir(cwd))) {
|
|
2898
3064
|
return null;
|
|
2899
3065
|
}
|
|
2900
3066
|
let statusData;
|
|
@@ -2936,60 +3102,60 @@ function computeProjectStats(cwd) {
|
|
|
2936
3102
|
}
|
|
2937
3103
|
|
|
2938
3104
|
// src/components/Welcome.tsx
|
|
2939
|
-
import { jsx as
|
|
3105
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2940
3106
|
var NARROW_THRESHOLD = 60;
|
|
2941
3107
|
function Welcome({ version: version2 }) {
|
|
2942
|
-
const { exit } =
|
|
3108
|
+
const { exit } = useApp5();
|
|
2943
3109
|
const { stdout } = useStdout2();
|
|
2944
|
-
const [selectedCommand, setSelectedCommand] =
|
|
2945
|
-
const stats =
|
|
3110
|
+
const [selectedCommand, setSelectedCommand] = useState10(null);
|
|
3111
|
+
const stats = useMemo6(() => computeProjectStats(), []);
|
|
2946
3112
|
const isNarrow = (stdout.columns ?? 80) < NARROW_THRESHOLD;
|
|
2947
|
-
|
|
3113
|
+
useEffect9(() => {
|
|
2948
3114
|
if (selectedCommand === "status") {
|
|
2949
3115
|
const timer = setTimeout(() => exit(), 0);
|
|
2950
3116
|
return () => clearTimeout(timer);
|
|
2951
3117
|
}
|
|
2952
3118
|
}, [selectedCommand, exit]);
|
|
2953
3119
|
if (selectedCommand === "plan") {
|
|
2954
|
-
return /* @__PURE__ */
|
|
3120
|
+
return /* @__PURE__ */ jsx13(Plan, {});
|
|
2955
3121
|
}
|
|
2956
3122
|
if (selectedCommand === "build") {
|
|
2957
|
-
return /* @__PURE__ */
|
|
3123
|
+
return /* @__PURE__ */ jsx13(Build, {});
|
|
2958
3124
|
}
|
|
2959
3125
|
if (selectedCommand === "status") {
|
|
2960
|
-
return /* @__PURE__ */
|
|
3126
|
+
return /* @__PURE__ */ jsx13(Status, { version: version2 });
|
|
2961
3127
|
}
|
|
2962
3128
|
if (selectedCommand === "config") {
|
|
2963
|
-
return /* @__PURE__ */
|
|
3129
|
+
return /* @__PURE__ */ jsx13(ConfigEditor, { version: version2 });
|
|
2964
3130
|
}
|
|
2965
|
-
return /* @__PURE__ */
|
|
2966
|
-
isNarrow ? /* @__PURE__ */
|
|
2967
|
-
/* @__PURE__ */
|
|
3131
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
|
|
3132
|
+
isNarrow ? /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
3133
|
+
/* @__PURE__ */ jsxs11(Text13, { bold: true, color: "#f0a030", children: [
|
|
2968
3134
|
"\u{1F439} toby v",
|
|
2969
3135
|
version2
|
|
2970
3136
|
] }),
|
|
2971
|
-
stats !== null && /* @__PURE__ */
|
|
2972
|
-
/* @__PURE__ */
|
|
2973
|
-
/* @__PURE__ */
|
|
2974
|
-
/* @__PURE__ */
|
|
2975
|
-
/* @__PURE__ */
|
|
2976
|
-
/* @__PURE__ */
|
|
2977
|
-
/* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
2979
|
-
/* @__PURE__ */
|
|
3137
|
+
stats !== null && /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
3138
|
+
/* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Specs: " }),
|
|
3139
|
+
/* @__PURE__ */ jsx13(Text13, { children: stats.totalSpecs }),
|
|
3140
|
+
/* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " \xB7 Planned: " }),
|
|
3141
|
+
/* @__PURE__ */ jsx13(Text13, { children: stats.planned }),
|
|
3142
|
+
/* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " \xB7 Done: " }),
|
|
3143
|
+
/* @__PURE__ */ jsx13(Text13, { children: stats.done }),
|
|
3144
|
+
/* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " \xB7 Tokens: " }),
|
|
3145
|
+
/* @__PURE__ */ jsx13(Text13, { children: formatTokens(stats.totalTokens) })
|
|
2980
3146
|
] })
|
|
2981
|
-
] }) : /* @__PURE__ */
|
|
2982
|
-
/* @__PURE__ */
|
|
2983
|
-
/* @__PURE__ */
|
|
3147
|
+
] }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 2, children: [
|
|
3148
|
+
/* @__PURE__ */ jsx13(HamsterWheel, {}),
|
|
3149
|
+
/* @__PURE__ */ jsx13(InfoPanel, { version: version2, stats })
|
|
2984
3150
|
] }),
|
|
2985
|
-
/* @__PURE__ */
|
|
3151
|
+
/* @__PURE__ */ jsx13(MainMenu, { onSelect: setSelectedCommand })
|
|
2986
3152
|
] });
|
|
2987
3153
|
}
|
|
2988
3154
|
|
|
2989
3155
|
// src/cli.tsx
|
|
2990
|
-
import { jsx as
|
|
3156
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
2991
3157
|
function Help({ version: version2 }) {
|
|
2992
|
-
return /* @__PURE__ */
|
|
3158
|
+
return /* @__PURE__ */ jsx14(Text14, { children: `toby v${version2} \u2014 AI-assisted development loop engine
|
|
2993
3159
|
|
|
2994
3160
|
Usage
|
|
2995
3161
|
$ toby <command> [options]
|
|
@@ -3000,6 +3166,7 @@ Commands
|
|
|
3000
3166
|
init Initialize toby in current project
|
|
3001
3167
|
status Show project status
|
|
3002
3168
|
config Manage configuration
|
|
3169
|
+
clean Delete all transcript files
|
|
3003
3170
|
|
|
3004
3171
|
Options
|
|
3005
3172
|
--help Show this help
|
|
@@ -3010,7 +3177,7 @@ Spec Selection
|
|
|
3010
3177
|
--specs=<names> Alias for --spec` });
|
|
3011
3178
|
}
|
|
3012
3179
|
function UnknownCommand({ command: command2 }) {
|
|
3013
|
-
return /* @__PURE__ */
|
|
3180
|
+
return /* @__PURE__ */ jsx14(Text14, { color: "red", children: `Unknown command: ${command2}
|
|
3014
3181
|
Run "toby --help" for available commands.` });
|
|
3015
3182
|
}
|
|
3016
3183
|
var cli = meow(
|
|
@@ -3024,6 +3191,7 @@ Commands
|
|
|
3024
3191
|
init Initialize toby in current project
|
|
3025
3192
|
status Show project status
|
|
3026
3193
|
config Manage configuration
|
|
3194
|
+
clean Delete all transcript files
|
|
3027
3195
|
|
|
3028
3196
|
Plan Options
|
|
3029
3197
|
--spec=<query> Target spec(s) by name, slug, number, or comma-separated list
|
|
@@ -3061,6 +3229,9 @@ Config Subcommands
|
|
|
3061
3229
|
config get <key> Show a config value (dot-notation)
|
|
3062
3230
|
config set <key> <value> Set a config value
|
|
3063
3231
|
config set <k>=<v> [<k>=<v>...] Batch set config values
|
|
3232
|
+
|
|
3233
|
+
Clean Options
|
|
3234
|
+
--force Skip confirmation prompt
|
|
3064
3235
|
`,
|
|
3065
3236
|
{
|
|
3066
3237
|
importMeta: import.meta,
|
|
@@ -3077,7 +3248,8 @@ Config Subcommands
|
|
|
3077
3248
|
buildCli: { type: "string" },
|
|
3078
3249
|
buildModel: { type: "string" },
|
|
3079
3250
|
specsDir: { type: "string" },
|
|
3080
|
-
session: { type: "string" }
|
|
3251
|
+
session: { type: "string" },
|
|
3252
|
+
force: { type: "boolean", default: false }
|
|
3081
3253
|
}
|
|
3082
3254
|
}
|
|
3083
3255
|
);
|
|
@@ -3086,7 +3258,7 @@ var resolvedSpec = cli.flags.specs ?? cli.flags.spec;
|
|
|
3086
3258
|
var flags = { ...cli.flags, spec: resolvedSpec };
|
|
3087
3259
|
var commands = {
|
|
3088
3260
|
plan: {
|
|
3089
|
-
render: (flags2) => /* @__PURE__ */
|
|
3261
|
+
render: (flags2) => /* @__PURE__ */ jsx14(
|
|
3090
3262
|
Plan,
|
|
3091
3263
|
{
|
|
3092
3264
|
spec: flags2.spec,
|
|
@@ -3101,7 +3273,7 @@ var commands = {
|
|
|
3101
3273
|
waitForExit: true
|
|
3102
3274
|
},
|
|
3103
3275
|
build: {
|
|
3104
|
-
render: (flags2) => /* @__PURE__ */
|
|
3276
|
+
render: (flags2) => /* @__PURE__ */ jsx14(
|
|
3105
3277
|
Build,
|
|
3106
3278
|
{
|
|
3107
3279
|
spec: flags2.spec,
|
|
@@ -3116,7 +3288,7 @@ var commands = {
|
|
|
3116
3288
|
waitForExit: true
|
|
3117
3289
|
},
|
|
3118
3290
|
init: {
|
|
3119
|
-
render: (flags2, _input, version2) => /* @__PURE__ */
|
|
3291
|
+
render: (flags2, _input, version2) => /* @__PURE__ */ jsx14(
|
|
3120
3292
|
Init,
|
|
3121
3293
|
{
|
|
3122
3294
|
version: version2,
|
|
@@ -3131,17 +3303,21 @@ var commands = {
|
|
|
3131
3303
|
waitForExit: true
|
|
3132
3304
|
},
|
|
3133
3305
|
status: {
|
|
3134
|
-
render: (flags2, _input, version2) => /* @__PURE__ */
|
|
3306
|
+
render: (flags2, _input, version2) => /* @__PURE__ */ jsx14(Status, { spec: flags2.spec, version: version2 })
|
|
3307
|
+
},
|
|
3308
|
+
clean: {
|
|
3309
|
+
render: (flags2) => /* @__PURE__ */ jsx14(Clean, { force: flags2.force }),
|
|
3310
|
+
waitForExit: true
|
|
3135
3311
|
},
|
|
3136
3312
|
config: {
|
|
3137
3313
|
render: (_flags, input, version2) => {
|
|
3138
3314
|
const [, subcommand, ...rest] = input;
|
|
3139
|
-
if (!subcommand) return /* @__PURE__ */
|
|
3315
|
+
if (!subcommand) return /* @__PURE__ */ jsx14(ConfigEditor, { version: version2 });
|
|
3140
3316
|
if (subcommand === "set" && rest.some((arg) => arg.includes("="))) {
|
|
3141
|
-
return /* @__PURE__ */
|
|
3317
|
+
return /* @__PURE__ */ jsx14(ConfigSetBatch, { pairs: rest.filter((arg) => arg.includes("=")) });
|
|
3142
3318
|
}
|
|
3143
3319
|
const [configKey, value] = rest;
|
|
3144
|
-
return /* @__PURE__ */
|
|
3320
|
+
return /* @__PURE__ */ jsx14(
|
|
3145
3321
|
Config,
|
|
3146
3322
|
{
|
|
3147
3323
|
subcommand,
|
|
@@ -3158,10 +3334,10 @@ var version = cli.pkg.version ?? "0.0.0";
|
|
|
3158
3334
|
var [command] = cli.input;
|
|
3159
3335
|
if (!command) {
|
|
3160
3336
|
if (process.stdin.isTTY) {
|
|
3161
|
-
const app = render(/* @__PURE__ */
|
|
3337
|
+
const app = render(/* @__PURE__ */ jsx14(Welcome, { version }));
|
|
3162
3338
|
await app.waitUntilExit();
|
|
3163
3339
|
} else {
|
|
3164
|
-
render(/* @__PURE__ */
|
|
3340
|
+
render(/* @__PURE__ */ jsx14(Help, { version })).unmount();
|
|
3165
3341
|
}
|
|
3166
3342
|
} else if (command in commands) {
|
|
3167
3343
|
const entry = commands[command];
|
|
@@ -3172,6 +3348,6 @@ if (!command) {
|
|
|
3172
3348
|
app.unmount();
|
|
3173
3349
|
}
|
|
3174
3350
|
} else {
|
|
3175
|
-
render(/* @__PURE__ */
|
|
3351
|
+
render(/* @__PURE__ */ jsx14(UnknownCommand, { command })).unmount();
|
|
3176
3352
|
process.exitCode = 1;
|
|
3177
3353
|
}
|