@hortus/cli 0.0.0-stage → 1.0.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/api.d.ts +53 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +15 -0
- package/dist/api.js.map +1 -0
- package/dist/app.d.ts +9 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +93 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +66 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +235 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-menu.d.ts +33 -0
- package/dist/command-menu.d.ts.map +1 -0
- package/dist/command-menu.js +135 -0
- package/dist/command-menu.js.map +1 -0
- package/dist/commands.d.ts +36 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +73 -0
- package/dist/commands.js.map +1 -0
- package/dist/entry.d.ts +35 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +96 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +20 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +233 -0
- package/dist/markdown.js.map +1 -0
- package/dist/prompt-input.d.ts +14 -0
- package/dist/prompt-input.d.ts.map +1 -0
- package/dist/prompt-input.js +139 -0
- package/dist/prompt-input.js.map +1 -0
- package/dist/session-picker.d.ts +9 -0
- package/dist/session-picker.d.ts.map +1 -0
- package/dist/session-picker.js +47 -0
- package/dist/session-picker.js.map +1 -0
- package/dist/status-bar.d.ts +3 -0
- package/dist/status-bar.d.ts.map +1 -0
- package/dist/status-bar.js +12 -0
- package/dist/status-bar.js.map +1 -0
- package/dist/transcript.d.ts +30 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +164 -0
- package/dist/transcript.js.map +1 -0
- package/package.json +37 -3
- package/README.md +0 -3
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import stringWidth from "string-width";
|
|
5
|
+
import { findCommand, parseCommand } from "./commands.js";
|
|
6
|
+
import { theme } from "./entry.js";
|
|
7
|
+
/** 0 exact, 1 prefix, 2 substring, 3 no match; ignoring case. */
|
|
8
|
+
function rank(text, query) {
|
|
9
|
+
const t = text.toLowerCase();
|
|
10
|
+
const q = query.toLowerCase();
|
|
11
|
+
return t === q ? 0 : t.startsWith(q) ? 1 : t.includes(q) ? 2 : 3;
|
|
12
|
+
}
|
|
13
|
+
/** The matching rows, exact first, then prefix, then substring; sort is stable, so each rank keeps its order. */
|
|
14
|
+
const byRank = (matches) => matches
|
|
15
|
+
.filter((m) => m.rank < 3)
|
|
16
|
+
.sort((a, b) => a.rank - b.rank)
|
|
17
|
+
.map((m) => m.row);
|
|
18
|
+
/**
|
|
19
|
+
* The `/` menu's rows for `input`: commands while the name is typed, then the command's candidates from `loaded` for
|
|
20
|
+
* its argument; undefined while those are still loading.
|
|
21
|
+
*/
|
|
22
|
+
export function suggest(commands, input, loaded) {
|
|
23
|
+
const parsed = parseCommand(input);
|
|
24
|
+
if (!parsed)
|
|
25
|
+
return [];
|
|
26
|
+
const { name, arg } = parsed;
|
|
27
|
+
if (arg === undefined)
|
|
28
|
+
return byRank(commands
|
|
29
|
+
.map((c) => {
|
|
30
|
+
// shown by the name that matches best, the canonical one on a tie; not by an alias that runs another command
|
|
31
|
+
const [shown, r] = [c.name, ...(c.aliases ?? []).filter((a) => findCommand(commands, a) === c)]
|
|
32
|
+
.map((n) => [n, rank(n, name)])
|
|
33
|
+
.reduce((a, b) => (b[1] < a[1] ? b : a));
|
|
34
|
+
const row = {
|
|
35
|
+
label: `/${shown}${c.args ? ` ${c.args}` : ""}`,
|
|
36
|
+
description: c.description,
|
|
37
|
+
// a command that takes an argument leaves room to type it
|
|
38
|
+
value: `/${shown}${c.args || c.complete ? " " : ""}`,
|
|
39
|
+
};
|
|
40
|
+
return { shown, rank: r, row };
|
|
41
|
+
})
|
|
42
|
+
.sort((a, b) => a.shown.localeCompare(b.shown)));
|
|
43
|
+
const cmd = findCommand(commands, name);
|
|
44
|
+
const candidates = cmd?.complete ? loaded.get(cmd) : [];
|
|
45
|
+
return (candidates &&
|
|
46
|
+
byRank(candidates.map((c) => ({
|
|
47
|
+
// by the label too, just below the same kind of value match: `m` puts model m0 above every model of a provider
|
|
48
|
+
// whose name has an m, while an exact value still beats a label equal to it (openai/gpt-5 on openrouter)
|
|
49
|
+
rank: Math.min(rank(c.value, arg), rank(c.label ?? c.value, arg) + 0.5),
|
|
50
|
+
row: { label: c.label ?? c.value, description: c.description, value: `/${name} ${c.value}` },
|
|
51
|
+
}))));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The `/` menu for the prompt's `input`. `onKey` takes the prompt's ↑/↓ (select), Tab (put the selected row into the
|
|
55
|
+
* input) and Enter (the same, unless the input already is it or nothing has loaded yet: then it submits) while the menu
|
|
56
|
+
* shows; `hide` is Esc.
|
|
57
|
+
*/
|
|
58
|
+
export function useCommandMenu(cli, input, setInput) {
|
|
59
|
+
// each command's last candidates, so coming back to its argument doesn't show Loading… again
|
|
60
|
+
const [loaded, setLoaded] = useState(() => new Map());
|
|
61
|
+
const parsed = parseCommand(input);
|
|
62
|
+
const cmd = parsed?.arg === undefined ? undefined : findCommand(cli.commands, parsed.name);
|
|
63
|
+
// the latest call per command, so an older, slower one can't overwrite its list (one still running when the argument
|
|
64
|
+
// is left keeps its list for next time)
|
|
65
|
+
const calls = useRef(new Map());
|
|
66
|
+
// once each time the argument starts being typed, not per keystroke
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const complete = cmd?.complete;
|
|
69
|
+
if (!cmd || !complete)
|
|
70
|
+
return;
|
|
71
|
+
const n = (calls.current.get(cmd) ?? 0) + 1;
|
|
72
|
+
calls.current.set(cmd, n);
|
|
73
|
+
const latest = () => calls.current.get(cmd) === n;
|
|
74
|
+
const keep = (list) => {
|
|
75
|
+
// plugin code: candidates that aren't all strings are left out, and so are repeats (rows are told apart by value)
|
|
76
|
+
const next = [
|
|
77
|
+
...new Map((Array.isArray(list) ? list : [])
|
|
78
|
+
.filter((c) => [c?.value, c?.label ?? "", c?.description ?? ""].every((s) => typeof s === "string"))
|
|
79
|
+
.map((c) => [c.value, c])).values(),
|
|
80
|
+
];
|
|
81
|
+
// an unchanged list leaves the rows, and so the selection, as they are
|
|
82
|
+
if (latest())
|
|
83
|
+
setLoaded((m) => (JSON.stringify(m.get(cmd)) === JSON.stringify(next) ? m : new Map(m).set(cmd, next)));
|
|
84
|
+
};
|
|
85
|
+
// the promise turns a sync throw into a rejection too
|
|
86
|
+
new Promise((resolve) => resolve(complete(cli))).then(keep, (e) => {
|
|
87
|
+
if (latest())
|
|
88
|
+
cli.report(`Completing /${cmd.name} failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
89
|
+
keep([]);
|
|
90
|
+
});
|
|
91
|
+
}, [cli, cmd]);
|
|
92
|
+
const rows = suggest(cli.commands, input, loaded);
|
|
93
|
+
// the selected row's value, so a refreshed list keeps that row selected; none, or one that's gone, is the first row
|
|
94
|
+
const [sel, setSel] = useState();
|
|
95
|
+
const [hidden, setHidden] = useState(false);
|
|
96
|
+
// any edit selects the first row again and brings back a menu hidden by Esc
|
|
97
|
+
const [seen, setSeen] = useState(input);
|
|
98
|
+
if (seen !== input) {
|
|
99
|
+
setSeen(input);
|
|
100
|
+
setSel(undefined);
|
|
101
|
+
setHidden(false);
|
|
102
|
+
}
|
|
103
|
+
const indexOf = (value) => Math.max(0, rows?.findIndex((r) => r.value === value) ?? 0);
|
|
104
|
+
const at = indexOf(sel);
|
|
105
|
+
const shown = !hidden && (!rows || rows.length > 0);
|
|
106
|
+
const onKey = (_, key) => {
|
|
107
|
+
const row = rows?.[at];
|
|
108
|
+
const dir = key.upArrow ? -1 : key.downArrow ? 1 : 0;
|
|
109
|
+
const enter = key.return && !key.shift && !key.meta;
|
|
110
|
+
if (!shown || !(dir || key.tab || enter) || (enter && (!row || row.value === input)))
|
|
111
|
+
return false;
|
|
112
|
+
// from the latest selection: keys can come faster than renders (a held ↓)
|
|
113
|
+
if (dir)
|
|
114
|
+
setSel((v) => rows?.[Math.max(0, Math.min(indexOf(v) + dir, (rows?.length ?? 0) - 1))]?.value);
|
|
115
|
+
// nothing to fill while loading
|
|
116
|
+
else if (row)
|
|
117
|
+
setInput(row.value);
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
120
|
+
return { rows, at, shown, hide: () => setHidden(true), onKey };
|
|
121
|
+
}
|
|
122
|
+
const MENU_ROWS = 5;
|
|
123
|
+
/** `rows` under the prompt, at most five, scrolled to keep the selected (accent) one in view; undefined = loading. */
|
|
124
|
+
export function CommandMenu({ rows, at }) {
|
|
125
|
+
// the session picker's window: the selection stays mid-way where it can
|
|
126
|
+
const start = Math.max(0, Math.min(at - Math.floor(MENU_ROWS / 2), (rows?.length ?? 0) - MENU_ROWS));
|
|
127
|
+
const shown = rows?.slice(start, start + MENU_ROWS) ?? [];
|
|
128
|
+
// descriptions line up in one column, which stays put while scrolling
|
|
129
|
+
const width = Math.max(0, ...(rows ?? []).map((r) => stringWidth(r.label)));
|
|
130
|
+
return (_jsxs(Box, { flexShrink: 0, flexDirection: "column", paddingLeft: 4, children: [!rows && _jsx(Text, { color: theme.dim, children: "Loading\u2026" }), shown.map((r, i) => {
|
|
131
|
+
const color = start + i === at ? theme.accent : undefined;
|
|
132
|
+
return (_jsxs(Text, { wrap: "truncate-end", color: color, children: [r.label, r.description && (_jsxs(Text, { color: color ?? theme.dim, children: [" ".repeat(width - stringWidth(r.label) + 2), r.description] }))] }, i));
|
|
133
|
+
})] }));
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=command-menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-menu.js","sourceRoot":"","sources":["../src/command-menu.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Cli } from "./cli.ts";
|
|
2
|
+
/** One choice for a command's argument. */
|
|
3
|
+
export interface Candidate {
|
|
4
|
+
/** the argument once chosen; the typed argument is matched against it and the label */
|
|
5
|
+
value: string;
|
|
6
|
+
/** shown in the menu instead of `value` */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** dim text after the label */
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
11
|
+
/** A `/name arg` command. The built-in ones use nothing a plugin's `cli.commands` can't. */
|
|
12
|
+
export interface Command {
|
|
13
|
+
name: string;
|
|
14
|
+
/** other names that run it, e.g. "clear" for "new" */
|
|
15
|
+
aliases?: string[];
|
|
16
|
+
description: string;
|
|
17
|
+
/** argument hint shown in the menu, e.g. "[provider/model]" */
|
|
18
|
+
args?: string;
|
|
19
|
+
/** every candidate for the argument; called when the argument starts being typed, then narrowed by what's typed */
|
|
20
|
+
complete?: (cli: Cli) => Candidate[] | Promise<Candidate[]>;
|
|
21
|
+
/** returned text is shown as an info line, a thrown error as an error line */
|
|
22
|
+
run: (arg: string, cli: Cli) => string | void | Promise<string | undefined> | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `/name arg` split up; `arg` is undefined while the name is still being typed (no space after it yet). One line only,
|
|
26
|
+
* and a name has no "/", so a message starting with a path (`/etc/hosts is ...`) isn't a command.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseCommand(input: string): {
|
|
29
|
+
name: string;
|
|
30
|
+
arg: string | undefined;
|
|
31
|
+
} | null;
|
|
32
|
+
/** The command `name` runs: the one called that, else one with it as an alias. */
|
|
33
|
+
export declare const findCommand: (commands: Command[], name: string) => Command | undefined;
|
|
34
|
+
/** /new, /model and /resume. */
|
|
35
|
+
export declare const builtinCommands: Command[];
|
|
36
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAGpC,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,4FAA4F;AAC5F,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mHAAmH;IACnH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC5D,8EAA8E;IAC9E,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM;;;SAGzC;AAED,kFAAkF;AAClF,eAAO,MAAM,WAAW,aAAc,OAAO,EAAE,QAAQ,MAAM,wBAC6B,CAAC;AAE3F,gCAAgC;AAChC,eAAO,MAAM,eAAe,EAAE,OAAO,EAyDpC,CAAC"}
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { listSessions, openSession } from "@hortus/core";
|
|
3
|
+
import { stamp } from "./entry.js";
|
|
4
|
+
/**
|
|
5
|
+
* `/name arg` split up; `arg` is undefined while the name is still being typed (no space after it yet). One line only,
|
|
6
|
+
* and a name has no "/", so a message starting with a path (`/etc/hosts is ...`) isn't a command.
|
|
7
|
+
*/
|
|
8
|
+
export function parseCommand(input) {
|
|
9
|
+
const m = input.match(/^\/([^\s/]*)(?: +(.*))?$/);
|
|
10
|
+
return m && { name: m[1] ?? "", arg: m[2] };
|
|
11
|
+
}
|
|
12
|
+
/** The command `name` runs: the one called that, else one with it as an alias. */
|
|
13
|
+
export const findCommand = (commands, name) => commands.find((c) => c.name === name) ?? commands.find((c) => c.aliases?.includes(name));
|
|
14
|
+
/** /new, /model and /resume. */
|
|
15
|
+
export const builtinCommands = [
|
|
16
|
+
{
|
|
17
|
+
name: "new",
|
|
18
|
+
aliases: ["clear"],
|
|
19
|
+
description: "Start a new session",
|
|
20
|
+
// next to the current one; model and provider start from the config again
|
|
21
|
+
run: async (_, cli) => cli.setSession(await openSession(null, dirname(cli.session.path))),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "model",
|
|
25
|
+
args: "[provider/model]",
|
|
26
|
+
description: "Switch the provider and model",
|
|
27
|
+
// listed afresh each time, so a provider that failed shows up once it works; the menu shows the last list meanwhile
|
|
28
|
+
complete: async (cli) => (await cli.agent.listModels()).map(({ provider, id }) => ({
|
|
29
|
+
value: `${provider}/${id}`,
|
|
30
|
+
label: id,
|
|
31
|
+
description: provider,
|
|
32
|
+
})),
|
|
33
|
+
run: async (arg, cli) => {
|
|
34
|
+
if (!arg) {
|
|
35
|
+
// resolved, so the config's defaults show too
|
|
36
|
+
const { status } = cli.ui.getState();
|
|
37
|
+
return status ? `Model: ${status.name}/${status.model}` : "No model selected";
|
|
38
|
+
}
|
|
39
|
+
// the provider ends at the first "/"; model ids may have more (openrouter/anthropic/claude-sonnet-4.5)
|
|
40
|
+
const [, provider, model] = arg.match(/^([^/\s]+)\/(\S+)$/) ?? [];
|
|
41
|
+
if (!provider || !model)
|
|
42
|
+
throw new Error("Usage: /model <provider>/<model>");
|
|
43
|
+
// the session shown now, even if another is shown while resolving
|
|
44
|
+
const { session } = cli;
|
|
45
|
+
// throws for a provider missing from secrets.json or of an unknown type
|
|
46
|
+
await cli.agent.resolve({ provider, model });
|
|
47
|
+
await session.setModel(model, provider);
|
|
48
|
+
return `Model set to ${provider}/${model}`;
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "resume",
|
|
53
|
+
args: "[session]",
|
|
54
|
+
description: "Resume a session",
|
|
55
|
+
// the sessions next to the shown one, newest first, without it; titles on one line, like the session picker's
|
|
56
|
+
complete: async (cli) => (await listSessions(dirname(cli.session.path)))
|
|
57
|
+
.filter((s) => s.path !== cli.session.path)
|
|
58
|
+
.map((s) => ({
|
|
59
|
+
value: s.id,
|
|
60
|
+
label: `${stamp(s.created)} ${s.title.replace(/\s+/g, " ") || "(no messages)"}`,
|
|
61
|
+
})),
|
|
62
|
+
run: async (arg, cli) => {
|
|
63
|
+
const { path } = cli.session;
|
|
64
|
+
// no id: the first candidate, so Enter while they're still loading picks what it would have
|
|
65
|
+
const s = (await listSessions(dirname(path))).find((s) => (arg ? s.id === arg : s.path !== path));
|
|
66
|
+
if (!s)
|
|
67
|
+
throw new Error(arg ? `Session not found: ${arg}` : "No sessions to resume");
|
|
68
|
+
// at its last entry, like `hortus -r`
|
|
69
|
+
cli.setSession(await openSession(s.leafId, dirname(path)));
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/entry.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Message, ToolCall, ToolResult } from "@hortus/core";
|
|
2
|
+
export interface Entry {
|
|
3
|
+
id: number;
|
|
4
|
+
kind: "info" | "user" | "thinking" | "assistant" | "tool" | "toolOk" | "toolError" | "error";
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function entry(kind: Entry["kind"], text: string): Entry;
|
|
8
|
+
interface Style {
|
|
9
|
+
color?: string;
|
|
10
|
+
backgroundColor?: string;
|
|
11
|
+
italic?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** pi's dark theme: the okhsl() colors of its dark.json, as hex. */
|
|
14
|
+
export declare const theme: {
|
|
15
|
+
text: string;
|
|
16
|
+
muted: string;
|
|
17
|
+
dim: string;
|
|
18
|
+
accent: string;
|
|
19
|
+
border: string;
|
|
20
|
+
borderIdle: string;
|
|
21
|
+
green: string;
|
|
22
|
+
red: string;
|
|
23
|
+
yellow: string;
|
|
24
|
+
blue: string;
|
|
25
|
+
thinking: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const styles: Record<Entry["kind"], Style>;
|
|
28
|
+
/** A tool call block like pi's: a one-line header, then a short preview of the input or result. */
|
|
29
|
+
export declare function formatTool(call: ToolCall, result?: ToolResult): string;
|
|
30
|
+
/** Local time as fixed-width `YYYY-MM-DD HH:MM`, so titles line up. */
|
|
31
|
+
export declare const stamp: (d: Date) => string;
|
|
32
|
+
/** Totals over `messages`, context fill from the last reply, and the cache hit rate of all input. */
|
|
33
|
+
export declare function usageLine(messages: Message[], window?: number): string;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;IAC7F,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAE9D;AAED,UAAU,KAAK;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,oEAAoE;AACpE,eAAO,MAAM,KAAK;IAChB,IAAI;IACJ,KAAK;IACL,GAAG;IACH,MAAM;IACN,MAAM;IACN,UAAU;IACV,KAAK;IACL,GAAG;IACH,MAAM;IACN,IAAI;IACJ,QAAQ;CACT,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAU/C,CAAC;AAUF,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAkCtE;AAGD,uEAAuE;AACvE,eAAO,MAAM,KAAK,MAAO,IAAI,WACkF,CAAC;AAKhH,qGAAqG;AACrG,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,UAW7D"}
|
package/dist/entry.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
let nextId = 0;
|
|
2
|
+
export function entry(kind, text) {
|
|
3
|
+
return { id: nextId++, kind, text };
|
|
4
|
+
}
|
|
5
|
+
/** pi's dark theme: the okhsl() colors of its dark.json, as hex. */
|
|
6
|
+
export const theme = {
|
|
7
|
+
text: "#dee0e1",
|
|
8
|
+
muted: "#9da5a9",
|
|
9
|
+
dim: "#7e888e",
|
|
10
|
+
accent: "#a798d7",
|
|
11
|
+
border: "#5fa8cc",
|
|
12
|
+
borderIdle: "#6c767b",
|
|
13
|
+
green: "#68b78d",
|
|
14
|
+
red: "#ea7f81",
|
|
15
|
+
yellow: "#cd9a22",
|
|
16
|
+
blue: "#69add0",
|
|
17
|
+
thinking: "#96a0a4",
|
|
18
|
+
};
|
|
19
|
+
export const styles = {
|
|
20
|
+
info: { color: theme.dim },
|
|
21
|
+
user: { color: theme.text, backgroundColor: "#213b49" },
|
|
22
|
+
thinking: { color: theme.thinking, italic: true },
|
|
23
|
+
assistant: {},
|
|
24
|
+
// pending / success / error tool blocks
|
|
25
|
+
tool: { backgroundColor: "#34383a" },
|
|
26
|
+
toolOk: { backgroundColor: "#254131" },
|
|
27
|
+
toolError: { backgroundColor: "#5b282a" },
|
|
28
|
+
error: { color: theme.red },
|
|
29
|
+
};
|
|
30
|
+
/** Keeps the first (or last) `n` lines, noting how many were cut. */
|
|
31
|
+
function preview(text, n, fromEnd = false) {
|
|
32
|
+
const lines = text.replace(/\n$/, "").split("\n");
|
|
33
|
+
if (lines.length <= n)
|
|
34
|
+
return lines;
|
|
35
|
+
const more = `... (${lines.length - n} ${fromEnd ? "earlier" : "more"} lines)`;
|
|
36
|
+
return fromEnd ? [more, ...lines.slice(-n)] : [...lines.slice(0, n), more];
|
|
37
|
+
}
|
|
38
|
+
/** A tool call block like pi's: a one-line header, then a short preview of the input or result. */
|
|
39
|
+
export function formatTool(call, result) {
|
|
40
|
+
const i = (call.input ?? {});
|
|
41
|
+
const out = result && (result.image ? "[image]" : result.output);
|
|
42
|
+
let head;
|
|
43
|
+
let body = [];
|
|
44
|
+
if (call.name === "bash") {
|
|
45
|
+
head = `$ ${i.command}`;
|
|
46
|
+
if (out)
|
|
47
|
+
body = preview(out, 5, true);
|
|
48
|
+
}
|
|
49
|
+
else if (call.name === "read") {
|
|
50
|
+
const from = Number(i.offset ?? 1);
|
|
51
|
+
const range = i.offset || i.limit ? `:${from}${i.limit ? `-${from + Number(i.limit) - 1}` : ""}` : "";
|
|
52
|
+
head = `read ${i.path}${range}`;
|
|
53
|
+
if (out)
|
|
54
|
+
body = preview(out, 10);
|
|
55
|
+
}
|
|
56
|
+
else if (call.name === "write") {
|
|
57
|
+
head = `write ${i.path}`;
|
|
58
|
+
body = result?.isError && out ? preview(out, 10) : preview(String(i.content ?? ""), 10);
|
|
59
|
+
}
|
|
60
|
+
else if (call.name === "edit") {
|
|
61
|
+
head = `edit ${i.path}`;
|
|
62
|
+
const edits = (Array.isArray(i.edits) ? i.edits : []);
|
|
63
|
+
const diff = edits.flatMap((e) => [
|
|
64
|
+
...String(e.oldText ?? "")
|
|
65
|
+
.split("\n")
|
|
66
|
+
.map((l) => `- ${l}`),
|
|
67
|
+
...String(e.newText ?? "")
|
|
68
|
+
.split("\n")
|
|
69
|
+
.map((l) => `+ ${l}`),
|
|
70
|
+
]);
|
|
71
|
+
body = result?.isError && out ? preview(out, 10) : preview(diff.join("\n"), 10);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
head = `${call.name} ${JSON.stringify(call.input)}`;
|
|
75
|
+
if (out)
|
|
76
|
+
body = preview(out, 10);
|
|
77
|
+
}
|
|
78
|
+
// blank rows pad the block's background above and below, as pi does
|
|
79
|
+
return ["", head, ...(body.length ? ["", ...body] : []), ""].join("\n");
|
|
80
|
+
}
|
|
81
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
82
|
+
/** Local time as fixed-width `YYYY-MM-DD HH:MM`, so titles line up. */
|
|
83
|
+
export const stamp = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
84
|
+
const k = (n) => `${(n / 1000).toFixed(1)}K`;
|
|
85
|
+
const pct = (n, of) => (of ? `${((n / of) * 100).toFixed(1)}%` : "-");
|
|
86
|
+
/** Totals over `messages`, context fill from the last reply, and the cache hit rate of all input. */
|
|
87
|
+
export function usageLine(messages, window) {
|
|
88
|
+
const usages = messages.flatMap((m) => (m.role === "assistant" && m.usage ? [m.usage] : []));
|
|
89
|
+
const sum = (f) => usages.reduce((n, u) => n + f(u), 0);
|
|
90
|
+
const input = sum((u) => u.input);
|
|
91
|
+
const last = usages.at(-1);
|
|
92
|
+
const ctx = window ? `${pct(last ? last.input + last.output : 0, window)}/${(window / 1e6).toFixed(1)}M` : "-/-";
|
|
93
|
+
const hit = pct(sum((u) => u.cached), input);
|
|
94
|
+
return `↑ ${k(input)} ↓ ${k(sum((u) => u.output))} ${ctx} CH ${hit}`;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { registerHooks } from "node:module";
|
|
5
|
+
import { stdout } from "node:process";
|
|
6
|
+
import { createAgent, listSessions, openSession } from "@hortus/core";
|
|
7
|
+
import { render } from "ink";
|
|
8
|
+
import { App } from "./app.js";
|
|
9
|
+
import { createCli } from "./cli.js";
|
|
10
|
+
import { SessionPicker } from "./session-picker.js";
|
|
11
|
+
// a plugin's react/ink/@hortus imports resolve from here, so its components run on our React and Ink (two Reacts break
|
|
12
|
+
// hooks). Every importer, not just ~/.hortus/plugins: a symlinked plugin's realpath lies outside it.
|
|
13
|
+
const shared = /^(react|ink|@hortus\/(core|cli))(\/|$)/;
|
|
14
|
+
registerHooks({ resolve: (s, c, next) => next(s, shared.test(s) ? { ...c, parentURL: import.meta.url } : c) });
|
|
15
|
+
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
16
|
+
// plugins can depend on "@hortus/cli" in their hortus.json; that's us
|
|
17
|
+
const load = { provided: { "@hortus/cli": version } };
|
|
18
|
+
// `hortus -r <id>` resumes that session, `hortus -r` picks one from a list
|
|
19
|
+
const [flag, id, ...rest] = process.argv.slice(2);
|
|
20
|
+
const fail = (message) => {
|
|
21
|
+
console.error(message);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
};
|
|
24
|
+
if ((flag && flag !== "-r") || rest.length)
|
|
25
|
+
fail("usage: hortus [-r [session_id]]");
|
|
26
|
+
let resume;
|
|
27
|
+
if (flag) {
|
|
28
|
+
const sessions = await listSessions();
|
|
29
|
+
if (id) {
|
|
30
|
+
resume = sessions.find((s) => s.id === id);
|
|
31
|
+
if (!resume)
|
|
32
|
+
fail(`Session not found: ${id}`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
if (!sessions.length)
|
|
36
|
+
fail("No sessions to resume");
|
|
37
|
+
await render(_jsx(SessionPicker, { sessions: sessions, onPick: (s) => (resume = s) }), {
|
|
38
|
+
exitOnCtrlC: false,
|
|
39
|
+
}).waitUntilExit();
|
|
40
|
+
if (!resume)
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const session = await openSession(resume?.leafId ?? null);
|
|
45
|
+
const cli = await createCli(session, (observer) => createAgent({ ...load, extra: [observer] }));
|
|
46
|
+
// report button presses, drags and wheel (1002) in SGR encoding (1006); always switched back off on exit
|
|
47
|
+
stdout.write("\x1b[?1002h\x1b[?1006h");
|
|
48
|
+
process.on("exit", () => stdout.write("\x1b[?1002l\x1b[?1006l"));
|
|
49
|
+
await render(_jsx(App, { cli: cli }), {
|
|
50
|
+
alternateScreen: true,
|
|
51
|
+
exitOnCtrlC: false,
|
|
52
|
+
// lets the terminal report Shift+Enter apart from Enter
|
|
53
|
+
kittyKeyboard: { mode: "auto" },
|
|
54
|
+
}).waitUntilExit();
|
|
55
|
+
// the alternate screen is gone on exit, so leave the conversation in normal scrollback
|
|
56
|
+
for (const e of cli.ui.getState().entries)
|
|
57
|
+
console.log(`${e.kind !== "info" && e.kind !== "error" ? "\n" : ""}${e.text}`);
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TextProps } from "ink";
|
|
2
|
+
/** A run of text with one style; a rendered line is a list of these. */
|
|
3
|
+
export interface Span extends Pick<TextProps, "color" | "bold" | "italic" | "dimColor" | "strikethrough" | "underline"> {
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
type Style = Omit<Span, "text">;
|
|
7
|
+
export declare function inline(text: string, style?: Style): Span[];
|
|
8
|
+
/** Word-wraps at `width` columns without dropping any character, so a row's text concatenates back to the line. */
|
|
9
|
+
export declare function wrap(line: string, width: number): string[];
|
|
10
|
+
/** Text between columns [from, to) of a row. */
|
|
11
|
+
export declare function sliceCols(text: string, from: number, to: number): string;
|
|
12
|
+
/** The parts of `spans` between columns [from, to). */
|
|
13
|
+
export declare function cutCols(spans: Span[], from: number, to: number): Span[];
|
|
14
|
+
/**
|
|
15
|
+
* Renders markdown into one list of styled spans per output line: fences, headings, rules, tables, quotes,
|
|
16
|
+
* bullets, and inline styles, which may span the lines of a paragraph, quote or list item.
|
|
17
|
+
*/
|
|
18
|
+
export declare function markdown(text: string, width: number): Span[][];
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAIrC,wEAAwE;AACxE,MAAM,WAAW,IACf,SAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,eAAe,GAAG,WAAW,CAAC;IACjG,IAAI,EAAE,MAAM,CAAC;CACd;AACD,KAAK,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAMhC,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,KAAU,GAAG,IAAI,EAAE,CAoB9D;AAID,mHAAmH;AACnH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB1D;AAED,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAQ/D;AAED,uDAAuD;AACvD,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAS9D;AA+DD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,CAkF9D"}
|