@tuned-tensor/cli 0.5.0 → 0.6.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/README.md +6 -3
- package/dist/index.js +146 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,9 +46,12 @@ tt local support-agent › run --dry-run
|
|
|
46
46
|
|
|
47
47
|
Commands are routed to the mode shown in the prompt. Prefix a single command
|
|
48
48
|
with `cloud` or `local` to override the mode without switching it. Useful
|
|
49
|
-
session commands include `/help`, `/status`, `/context`, `/mode`, `/
|
|
50
|
-
`/clear`, and `/exit`.
|
|
51
|
-
|
|
49
|
+
session commands include `/help`, `/status`, `/context`, `/mode`, `/model`,
|
|
50
|
+
`/cd`, `/clear`, and `/exit`. `/model` shows the model in play — the active
|
|
51
|
+
local model, or the cloud spec's base model — and `/model <id>` activates a
|
|
52
|
+
verified local model. Mistyped session commands suggest the closest match.
|
|
53
|
+
The shell keeps normal terminal scrollback and command history only for the
|
|
54
|
+
current process.
|
|
52
55
|
|
|
53
56
|
Explicit commands remain non-interactive, including in CI. `tt --help` shows
|
|
54
57
|
the complete command surface, `tt status` inspects both targets without a
|
package/dist/index.js
CHANGED
|
@@ -1290,6 +1290,7 @@ var SLASH_COMMANDS = [
|
|
|
1290
1290
|
{ path: "/context", description: "Show the current project and backend context." },
|
|
1291
1291
|
{ path: "/mode cloud", description: "Use the managed cloud workflow." },
|
|
1292
1292
|
{ path: "/mode local", description: "Use the local GPU workflow." },
|
|
1293
|
+
{ path: "/model", description: "Show the active model; /model <id> activates a local model." },
|
|
1293
1294
|
{ path: "/cd", description: "Change the shell's working directory." },
|
|
1294
1295
|
{ path: "/clear", description: "Clear the terminal." },
|
|
1295
1296
|
{ path: "/exit", description: "Exit the TT shell." }
|
|
@@ -1711,10 +1712,49 @@ var SLASH_NAMES = /* @__PURE__ */ new Set([
|
|
|
1711
1712
|
"status",
|
|
1712
1713
|
"context",
|
|
1713
1714
|
"mode",
|
|
1715
|
+
"model",
|
|
1714
1716
|
"clear",
|
|
1715
1717
|
"cd",
|
|
1716
1718
|
"exit"
|
|
1717
1719
|
]);
|
|
1720
|
+
var WORKFLOW_ROOT_COMMANDS = new Set(
|
|
1721
|
+
COMMAND_CATALOG.map((command) => command.path.split(" ", 1)[0])
|
|
1722
|
+
);
|
|
1723
|
+
function editDistance(left, right) {
|
|
1724
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
1725
|
+
for (let i = 1; i <= left.length; i += 1) {
|
|
1726
|
+
let diagonal = previous[0];
|
|
1727
|
+
previous[0] = i;
|
|
1728
|
+
for (let j = 1; j <= right.length; j += 1) {
|
|
1729
|
+
const above = previous[j];
|
|
1730
|
+
previous[j] = Math.min(
|
|
1731
|
+
previous[j] + 1,
|
|
1732
|
+
previous[j - 1] + 1,
|
|
1733
|
+
diagonal + (left[i - 1] === right[j - 1] ? 0 : 1)
|
|
1734
|
+
);
|
|
1735
|
+
diagonal = above;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
return previous[right.length];
|
|
1739
|
+
}
|
|
1740
|
+
function suggestSlashCommands(rawName) {
|
|
1741
|
+
const names = [...SLASH_NAMES].sort();
|
|
1742
|
+
const prefixMatches = names.filter((name) => name.startsWith(rawName));
|
|
1743
|
+
if (prefixMatches.length > 0) return prefixMatches;
|
|
1744
|
+
return names.filter((name) => editDistance(name, rawName) <= 2);
|
|
1745
|
+
}
|
|
1746
|
+
function unknownSlashCommandError(rawName) {
|
|
1747
|
+
if (WORKFLOW_ROOT_COMMANDS.has(rawName)) {
|
|
1748
|
+
return new ShellParseError(
|
|
1749
|
+
`Unknown session command: /${rawName}. Workflow commands need no slash \u2014 try "${rawName} --help".`
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
const suggestions = suggestSlashCommands(rawName);
|
|
1753
|
+
const hint = suggestions.length > 0 ? ` Did you mean ${suggestions.map((name) => `/${name}`).join(" or ")}?` : "";
|
|
1754
|
+
return new ShellParseError(
|
|
1755
|
+
`Unknown session command: /${rawName}.${hint} Use /help to see available commands.`
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1718
1758
|
function parseSlashCommand(input) {
|
|
1719
1759
|
const trimmed = input.trim();
|
|
1720
1760
|
if (!trimmed) return null;
|
|
@@ -1730,7 +1770,7 @@ function parseSlashCommand(input) {
|
|
|
1730
1770
|
const tokens = tokenizeShellInput(trimmed);
|
|
1731
1771
|
const rawName = tokens[0].slice(1).toLowerCase();
|
|
1732
1772
|
if (!SLASH_NAMES.has(rawName)) {
|
|
1733
|
-
throw
|
|
1773
|
+
throw unknownSlashCommandError(rawName);
|
|
1734
1774
|
}
|
|
1735
1775
|
return {
|
|
1736
1776
|
name: rawName,
|
|
@@ -1740,6 +1780,22 @@ function parseSlashCommand(input) {
|
|
|
1740
1780
|
function errorMessage(error) {
|
|
1741
1781
|
return error instanceof Error ? error.message : String(error);
|
|
1742
1782
|
}
|
|
1783
|
+
var successMark = () => chalk2.green("\u2713");
|
|
1784
|
+
var errorMark = () => chalk2.red("\u2717");
|
|
1785
|
+
var accent = chalk2.hex("#8B5CF6");
|
|
1786
|
+
var DETAIL_LABEL_WIDTH = 15;
|
|
1787
|
+
function styleDetailLines(lines) {
|
|
1788
|
+
return lines.map((line) => {
|
|
1789
|
+
if (line.length <= DETAIL_LABEL_WIDTH) return line;
|
|
1790
|
+
const label = line.slice(0, DETAIL_LABEL_WIDTH);
|
|
1791
|
+
if (!label.trim()) return line;
|
|
1792
|
+
const color = label.startsWith("Warning") ? chalk2.yellow : accent.bold;
|
|
1793
|
+
return `${color(label)}${line.slice(DETAIL_LABEL_WIDTH)}`;
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
function detailLine(label, value) {
|
|
1797
|
+
return `${label.padEnd(DETAIL_LABEL_WIDTH)}${value}`;
|
|
1798
|
+
}
|
|
1743
1799
|
function assertNoArgs(command, args) {
|
|
1744
1800
|
if (args.length > 0) {
|
|
1745
1801
|
throw new ShellParseError(`/${command} does not accept arguments.`);
|
|
@@ -1754,47 +1810,67 @@ function expandDirectory(value, cwd, env) {
|
|
|
1754
1810
|
function helpText(mode, query, palette = false) {
|
|
1755
1811
|
const groups = groupedCatalog(mode, query);
|
|
1756
1812
|
const lines = [];
|
|
1757
|
-
lines.push(palette ? `Commands for ${mode} \u2014 type a command or use cloud/local as a one-shot prefix` : `TT ${mode} commands${query ? ` matching ${JSON.stringify(query)}` : ""}`);
|
|
1813
|
+
lines.push(accent.bold(palette ? `Commands for ${mode} \u2014 type a command or use cloud/local as a one-shot prefix` : `TT ${mode} commands${query ? ` matching ${JSON.stringify(query)}` : ""}`));
|
|
1758
1814
|
if (groups.size === 0) {
|
|
1759
|
-
lines.push(" No matching commands.");
|
|
1815
|
+
lines.push(chalk2.dim(" No matching commands."));
|
|
1760
1816
|
} else {
|
|
1761
1817
|
for (const [group, commands] of groups) {
|
|
1762
|
-
lines.push(
|
|
1763
|
-
|
|
1818
|
+
lines.push("");
|
|
1819
|
+
lines.push(chalk2.bold(group));
|
|
1764
1820
|
for (const command of commands) {
|
|
1765
|
-
lines.push(` ${command.path.padEnd(22)} ${command.description}`);
|
|
1821
|
+
lines.push(` ${accent(command.path.padEnd(22))} ${command.description}`);
|
|
1766
1822
|
}
|
|
1767
1823
|
}
|
|
1768
1824
|
}
|
|
1769
1825
|
if (!query) {
|
|
1770
|
-
lines.push("
|
|
1826
|
+
lines.push("");
|
|
1827
|
+
lines.push(chalk2.bold("Session"));
|
|
1771
1828
|
for (const command of SLASH_COMMANDS) {
|
|
1772
|
-
lines.push(` ${command.path.padEnd(22)} ${command.description}`);
|
|
1829
|
+
lines.push(` ${accent(command.path.padEnd(22))} ${command.description}`);
|
|
1773
1830
|
}
|
|
1774
|
-
lines.push("
|
|
1775
|
-
lines.push(
|
|
1831
|
+
lines.push(` ${accent("?".padEnd(22))} Alias for /help.`);
|
|
1832
|
+
lines.push(chalk2.dim(
|
|
1833
|
+
"\nTab completes commands. Shell operators and shell escapes are disabled."
|
|
1834
|
+
));
|
|
1776
1835
|
}
|
|
1777
1836
|
return `${lines.join("\n")}
|
|
1778
1837
|
`;
|
|
1779
1838
|
}
|
|
1839
|
+
function activeModelLabel(snapshot) {
|
|
1840
|
+
return snapshot.mode === "local" ? snapshot.context.local.activeModelId ?? "base" : snapshot.context.spec?.baseModel ?? "\u2014";
|
|
1841
|
+
}
|
|
1842
|
+
function logoRows() {
|
|
1843
|
+
const muted = chalk2.dim("\u2588\u2588");
|
|
1844
|
+
return [
|
|
1845
|
+
`${chalk2.hex("#A78BFA")("\u2588\u2588")} ${muted} ${muted}`,
|
|
1846
|
+
`${muted} ${accent("\u2588\u2588")} ${muted}`,
|
|
1847
|
+
`${muted} ${muted} ${chalk2.hex("#7C3AED")("\u2588\u2588")}`
|
|
1848
|
+
];
|
|
1849
|
+
}
|
|
1780
1850
|
function renderShellBanner(snapshot) {
|
|
1781
1851
|
const spec = snapshot.context.spec?.name ?? snapshot.context.spec?.path ?? "no spec";
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
chalk2.
|
|
1786
|
-
""
|
|
1787
|
-
]
|
|
1852
|
+
const heading = snapshot.version ? `${accent.bold("Tuned Tensor")} ${chalk2.dim(`v${snapshot.version}`)}` : accent.bold("Tuned Tensor");
|
|
1853
|
+
const textRows = [
|
|
1854
|
+
heading,
|
|
1855
|
+
`${chalk2.bold(snapshot.mode)} \xB7 ${snapshot.context.projectName} \xB7 ${spec} \xB7 model ${activeModelLabel(snapshot)}`,
|
|
1856
|
+
chalk2.dim("/help for commands \xB7 Tab completes \xB7 /mode cloud|local switches")
|
|
1857
|
+
];
|
|
1858
|
+
const logo = logoRows();
|
|
1859
|
+
const lines = textRows.map((row, index) => `${logo[index]} ${row}`);
|
|
1860
|
+
return `${lines.join("\n")}
|
|
1861
|
+
|
|
1862
|
+
`;
|
|
1788
1863
|
}
|
|
1789
1864
|
function renderShellPrompt(snapshot) {
|
|
1790
|
-
return `${
|
|
1865
|
+
return `${accent("tt")} ${chalk2.bold(snapshot.mode)} ${chalk2.dim(snapshot.context.projectName)} \u203A `;
|
|
1791
1866
|
}
|
|
1792
1867
|
var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
1793
|
-
constructor(runner, io, env, contextProvider, initialContext) {
|
|
1868
|
+
constructor(runner, io, env, contextProvider, version, initialContext) {
|
|
1794
1869
|
this.runner = runner;
|
|
1795
1870
|
this.io = io;
|
|
1796
1871
|
this.env = env;
|
|
1797
1872
|
this.contextProvider = contextProvider;
|
|
1873
|
+
this.version = version;
|
|
1798
1874
|
this.mode = initialContext.inferredTarget;
|
|
1799
1875
|
this.modeSource = initialContext.targetSource;
|
|
1800
1876
|
this.cwd = initialContext.cwd;
|
|
@@ -1814,6 +1890,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1814
1890
|
options.io,
|
|
1815
1891
|
env,
|
|
1816
1892
|
contextProvider,
|
|
1893
|
+
options.version,
|
|
1817
1894
|
initialContext
|
|
1818
1895
|
);
|
|
1819
1896
|
}
|
|
@@ -1822,7 +1899,8 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1822
1899
|
mode: this.mode,
|
|
1823
1900
|
modeSource: this.modeSource,
|
|
1824
1901
|
cwd: this.cwd,
|
|
1825
|
-
context: this.context
|
|
1902
|
+
context: this.context,
|
|
1903
|
+
version: this.version
|
|
1826
1904
|
};
|
|
1827
1905
|
}
|
|
1828
1906
|
prompt() {
|
|
@@ -1843,6 +1921,18 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1843
1921
|
this.io.write(`${lines.join("\n")}
|
|
1844
1922
|
`);
|
|
1845
1923
|
}
|
|
1924
|
+
modelLines() {
|
|
1925
|
+
if (this.mode === "local") {
|
|
1926
|
+
return styleDetailLines([
|
|
1927
|
+
detailLine("Active model", this.context.local.activeModelId ?? "base"),
|
|
1928
|
+
detailLine("Change", "/model <id> to activate a verified local model")
|
|
1929
|
+
]);
|
|
1930
|
+
}
|
|
1931
|
+
return styleDetailLines([
|
|
1932
|
+
detailLine("Base model", this.context.spec?.baseModel ?? "\u2014"),
|
|
1933
|
+
detailLine("Change", "edit base_model in tunedtensor.json, then run push")
|
|
1934
|
+
]);
|
|
1935
|
+
}
|
|
1846
1936
|
async handleSlash(command) {
|
|
1847
1937
|
switch (command.name) {
|
|
1848
1938
|
case "palette":
|
|
@@ -1854,12 +1944,14 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1854
1944
|
case "status":
|
|
1855
1945
|
assertNoArgs("status", command.args);
|
|
1856
1946
|
await this.refreshContext();
|
|
1857
|
-
this.writeLines(formatShellStatus(this.context, this.mode));
|
|
1947
|
+
this.writeLines(styleDetailLines(formatShellStatus(this.context, this.mode)));
|
|
1858
1948
|
return "continue";
|
|
1859
1949
|
case "context":
|
|
1860
1950
|
assertNoArgs("context", command.args);
|
|
1861
1951
|
await this.refreshContext();
|
|
1862
|
-
this.writeLines(
|
|
1952
|
+
this.writeLines(styleDetailLines(
|
|
1953
|
+
formatShellContext(this.context, this.mode, this.modeSource)
|
|
1954
|
+
));
|
|
1863
1955
|
return "continue";
|
|
1864
1956
|
case "mode": {
|
|
1865
1957
|
if (command.args.length === 0) {
|
|
@@ -1872,10 +1964,32 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1872
1964
|
}
|
|
1873
1965
|
this.mode = command.args[0];
|
|
1874
1966
|
this.modeSource = "session";
|
|
1875
|
-
this.io.write(
|
|
1967
|
+
this.io.write(`${successMark()} Workflow switched to ${this.mode}.
|
|
1876
1968
|
`);
|
|
1877
1969
|
return "continue";
|
|
1878
1970
|
}
|
|
1971
|
+
case "model": {
|
|
1972
|
+
if (command.args.length === 0) {
|
|
1973
|
+
await this.refreshContext();
|
|
1974
|
+
this.writeLines(this.modelLines());
|
|
1975
|
+
return "continue";
|
|
1976
|
+
}
|
|
1977
|
+
if (command.args.length !== 1) {
|
|
1978
|
+
throw new ShellParseError("Usage: /model [model-id]");
|
|
1979
|
+
}
|
|
1980
|
+
if (this.mode !== "local") {
|
|
1981
|
+
throw new ShellParseError(
|
|
1982
|
+
"Activating models is a local workflow action. Use /mode local first; cloud specs change base_model in tunedtensor.json."
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
await this.runner({
|
|
1986
|
+
target: "local",
|
|
1987
|
+
args: ["models", "activate", command.args[0]],
|
|
1988
|
+
cwd: this.cwd
|
|
1989
|
+
});
|
|
1990
|
+
await this.refreshContext();
|
|
1991
|
+
return "continue";
|
|
1992
|
+
}
|
|
1879
1993
|
case "clear":
|
|
1880
1994
|
assertNoArgs("clear", command.args);
|
|
1881
1995
|
this.io.clear();
|
|
@@ -1896,7 +2010,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1896
2010
|
}
|
|
1897
2011
|
this.cwd = nextDirectory;
|
|
1898
2012
|
await this.refreshContext();
|
|
1899
|
-
this.io.write(
|
|
2013
|
+
this.io.write(`${successMark()} Directory: ${this.cwd}
|
|
1900
2014
|
`);
|
|
1901
2015
|
return "continue";
|
|
1902
2016
|
}
|
|
@@ -1907,6 +2021,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1907
2021
|
}
|
|
1908
2022
|
async handleLine(input) {
|
|
1909
2023
|
try {
|
|
2024
|
+
if (/^(exit|quit)$/i.test(input.trim())) return "exit";
|
|
1910
2025
|
const slash = parseSlashCommand(input);
|
|
1911
2026
|
if (slash) return await this.handleSlash(slash);
|
|
1912
2027
|
const routed = routeShellCommand(input, this.mode);
|
|
@@ -1919,7 +2034,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
|
|
|
1919
2034
|
await this.refreshContext();
|
|
1920
2035
|
return "continue";
|
|
1921
2036
|
} catch (error) {
|
|
1922
|
-
this.io.writeError(`${
|
|
2037
|
+
this.io.writeError(`${errorMark()} ${errorMessage(error)}
|
|
1923
2038
|
`);
|
|
1924
2039
|
return "continue";
|
|
1925
2040
|
}
|
|
@@ -1981,7 +2096,8 @@ async function startInteractiveShell(options) {
|
|
|
1981
2096
|
runner: foregroundRunner,
|
|
1982
2097
|
io: streamIO(output, error),
|
|
1983
2098
|
cwd: options.cwd,
|
|
1984
|
-
env: options.env
|
|
2099
|
+
env: options.env,
|
|
2100
|
+
version: options.version
|
|
1985
2101
|
});
|
|
1986
2102
|
const completer = createCommandCompleter(() => session.snapshot().mode);
|
|
1987
2103
|
readline = createInterface({
|
|
@@ -6042,7 +6158,8 @@ function createProgram(version, runtime = {}) {
|
|
|
6042
6158
|
output: runtime.stdout ?? process.stdout,
|
|
6043
6159
|
error: runtime.stderr ?? process.stderr,
|
|
6044
6160
|
cwd,
|
|
6045
|
-
env: shellEnvironment
|
|
6161
|
+
env: shellEnvironment,
|
|
6162
|
+
version
|
|
6046
6163
|
});
|
|
6047
6164
|
};
|
|
6048
6165
|
program.name("tt").description("Tuned Tensor \u2014 one terminal for cloud and local fine-tuning").version(version).option("-k, --api-key <key>", "API key (overrides stored key)").option(
|
|
@@ -6182,7 +6299,8 @@ async function runCli(version, runtime = {}) {
|
|
|
6182
6299
|
output: runtime.stdout ?? process.stdout,
|
|
6183
6300
|
error: runtime.stderr ?? process.stderr,
|
|
6184
6301
|
cwd: runtime.cwd ?? process.cwd(),
|
|
6185
|
-
env
|
|
6302
|
+
env,
|
|
6303
|
+
version
|
|
6186
6304
|
});
|
|
6187
6305
|
return;
|
|
6188
6306
|
}
|
|
@@ -6191,7 +6309,7 @@ async function runCli(version, runtime = {}) {
|
|
|
6191
6309
|
}
|
|
6192
6310
|
|
|
6193
6311
|
// src/index.ts
|
|
6194
|
-
runCli("0.
|
|
6312
|
+
runCli("0.6.0").catch((err) => {
|
|
6195
6313
|
reportError(err);
|
|
6196
6314
|
process.exit(1);
|
|
6197
6315
|
});
|