@hortus/cli 0.0.0-stage → 0.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.
Files changed (50) hide show
  1. package/dist/api.d.ts +53 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +15 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/app.d.ts +9 -0
  6. package/dist/app.d.ts.map +1 -0
  7. package/dist/app.js +93 -0
  8. package/dist/app.js.map +1 -0
  9. package/dist/cli.d.ts +66 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +235 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/command-menu.d.ts +33 -0
  14. package/dist/command-menu.d.ts.map +1 -0
  15. package/dist/command-menu.js +135 -0
  16. package/dist/command-menu.js.map +1 -0
  17. package/dist/commands.d.ts +36 -0
  18. package/dist/commands.d.ts.map +1 -0
  19. package/dist/commands.js +73 -0
  20. package/dist/commands.js.map +1 -0
  21. package/dist/entry.d.ts +35 -0
  22. package/dist/entry.d.ts.map +1 -0
  23. package/dist/entry.js +96 -0
  24. package/dist/entry.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +58 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/markdown.d.ts +20 -0
  30. package/dist/markdown.d.ts.map +1 -0
  31. package/dist/markdown.js +233 -0
  32. package/dist/markdown.js.map +1 -0
  33. package/dist/prompt-input.d.ts +14 -0
  34. package/dist/prompt-input.d.ts.map +1 -0
  35. package/dist/prompt-input.js +139 -0
  36. package/dist/prompt-input.js.map +1 -0
  37. package/dist/session-picker.d.ts +9 -0
  38. package/dist/session-picker.d.ts.map +1 -0
  39. package/dist/session-picker.js +47 -0
  40. package/dist/session-picker.js.map +1 -0
  41. package/dist/status-bar.d.ts +3 -0
  42. package/dist/status-bar.d.ts.map +1 -0
  43. package/dist/status-bar.js +12 -0
  44. package/dist/status-bar.js.map +1 -0
  45. package/dist/transcript.d.ts +30 -0
  46. package/dist/transcript.d.ts.map +1 -0
  47. package/dist/transcript.js +164 -0
  48. package/dist/transcript.js.map +1 -0
  49. package/package.json +37 -3
  50. 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":";AAAA,OAAO,EAAE,GAAG,EAAY,IAAI,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAgC,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAUnC,iEAAiE;AACjE,SAAS,IAAI,CAAC,IAAY,EAAE,KAAa;IACvC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,iHAAiH;AACjH,MAAM,MAAM,GAAG,CAAC,OAA4C,EAAE,EAAE,CAC9D,OAAO;KACJ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;KACzB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;KAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAEvB;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,QAAmB,EAAE,KAAa,EAAE,MAAiC;IAC3F,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAC7B,IAAI,GAAG,KAAK,SAAS;QACnB,OAAO,MAAM,CACX,QAAQ;aACL,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,6GAA6G;YAC7G,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;iBAC5F,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAU,CAAC;iBACvC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG;gBACV,KAAK,EAAE,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC/C,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,0DAA0D;gBAC1D,KAAK,EAAE,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;aACrD,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;QACjC,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAClD,CAAC;IACJ,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,OAAO,CACL,UAAU;QACV,MAAM,CACJ,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrB,+GAA+G;YAC/G,yGAAyG;YACzG,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;YACvE,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;SAC7F,CAAC,CAAC,CACJ,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAQ,EAAE,KAAa,EAAE,QAAiC;IACvF,6FAA6F;IAC7F,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,EAAwB,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3F,qHAAqH;IACrH,wCAAwC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,GAAG,EAAmB,CAAC,CAAC;IACjD,oEAAoE;IACpE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,GAAG,EAAE,QAAQ,CAAC;QAC/B,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC9B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5C,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,EAAE;YAC7B,kHAAkH;YAClH,MAAM,IAAI,GAAG;gBACX,GAAG,IAAI,GAAG,CACR,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;qBACnG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAU,CAAC,CACrC,CAAC,MAAM,EAAE;aACX,CAAC;YACF,uEAAuE;YACvE,IAAI,MAAM,EAAE;gBACV,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5G,CAAC,CAAC;QACF,sDAAsD;QACtD,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;YAC7E,IAAI,MAAM,EAAE;gBAAE,GAAG,CAAC,MAAM,CAAC,eAAe,GAAG,CAAC,IAAI,YAAY,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1G,IAAI,CAAC,EAAE,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAElD,oHAAoH;IACpH,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,EAAU,CAAC;IACzC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,4EAA4E;IAC5E,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,MAAM,CAAC,SAAS,CAAC,CAAC;QAClB,SAAS,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,KAAyB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3G,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,GAAQ,EAAE,EAAE;QACpC,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACpD,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACnG,0EAA0E;QAC1E,IAAI,GAAG;YAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxG,gCAAgC;aAC3B,IAAI,GAAG;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,sHAAsH;AACtH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,EAAkD;IACtF,wEAAwE;IACxE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IACrG,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IAC1D,sEAAsE;IACtE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5E,OAAO,CAEL,MAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAE,CAAC,aACtD,CAAC,IAAI,IAAI,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,CAAC,GAAG,8BAAiB,EAChD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAClB,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC1D,OAAO,CAEL,MAAC,IAAI,IAAS,IAAI,EAAC,cAAc,EAAC,KAAK,EAAE,KAAK,aAC3C,CAAC,CAAC,KAAK,EACP,CAAC,CAAC,WAAW,IAAI,CAChB,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,IAAI,KAAK,CAAC,GAAG,aAC5B,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAC5C,CAAC,CAAC,WAAW,IACT,CACR,KAPQ,CAAC,CAQL,CACR,CAAC;YACJ,CAAC,CAAC,IACE,CACP,CAAC;AACJ,CAAC"}
@@ -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"}
@@ -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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"}
@@ -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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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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"}