@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.
- 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
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Plugin, SessionState } from "@hortus/core";
|
|
2
|
+
import { type ComponentType } from "react";
|
|
3
|
+
import type { UiState } from "./cli.ts";
|
|
4
|
+
import type { Command } from "./commands.ts";
|
|
5
|
+
export type { SessionState } from "@hortus/core";
|
|
6
|
+
export { useShallow } from "zustand/react/shallow";
|
|
7
|
+
export type { Cli, UiState } from "./cli.ts";
|
|
8
|
+
export type { Candidate, Command } from "./commands.ts";
|
|
9
|
+
export { theme, usageLine } from "./entry.ts";
|
|
10
|
+
/** UI parts a plugin can replace. */
|
|
11
|
+
export interface Slots {
|
|
12
|
+
statusBar: ComponentType;
|
|
13
|
+
}
|
|
14
|
+
/** Default export of a plugin whose hortus.json depends on "@hortus/cli". */
|
|
15
|
+
export interface CliPlugin extends Plugin {
|
|
16
|
+
/** each slot override gets the implementation it replaces as `Default`: the built-in one or an earlier plugin's */
|
|
17
|
+
cli?: {
|
|
18
|
+
[K in keyof Slots]?: ComponentType<{
|
|
19
|
+
Default: Slots[K];
|
|
20
|
+
}>;
|
|
21
|
+
} & {
|
|
22
|
+
/** added to the `/` menu; one named like an earlier command (built-in or plugin) replaces it */
|
|
23
|
+
commands?: Command[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare const CliContext: import("react").Context<{
|
|
27
|
+
ui: import("zustand").StoreApi<UiState>;
|
|
28
|
+
agent: {
|
|
29
|
+
providers: Record<string, import("@hortus/core").ProviderDef>;
|
|
30
|
+
plugins: Plugin[];
|
|
31
|
+
resolve: (opts?: Pick<import("@hortus/core").RunOptions, "model" | "provider">) => Promise<{
|
|
32
|
+
name: string;
|
|
33
|
+
model: string;
|
|
34
|
+
provider: import("@hortus/core").Provider;
|
|
35
|
+
}>;
|
|
36
|
+
listModels: () => Promise<{
|
|
37
|
+
provider: string;
|
|
38
|
+
id: string;
|
|
39
|
+
}[]>;
|
|
40
|
+
run: (history: import("@hortus/core").Message[], opts?: import("@hortus/core").RunOptions) => Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
readonly session: import("@hortus/core").Session;
|
|
43
|
+
commands: Command[];
|
|
44
|
+
submit: (value: string) => boolean;
|
|
45
|
+
abort: () => void | undefined;
|
|
46
|
+
setCopied: (chars: number) => void;
|
|
47
|
+
report: (text: string) => void;
|
|
48
|
+
setSession: (session: import("@hortus/core").Session) => void;
|
|
49
|
+
} | undefined>;
|
|
50
|
+
/** Selectors must return primitives or existing references, or go through useShallow (zustand loops on a fresh object). */
|
|
51
|
+
export declare const useSession: <T>(selector: (s: SessionState) => T) => T;
|
|
52
|
+
export declare const useUi: <T>(selector: (s: UiState) => T) => T;
|
|
53
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,KAAK,aAAa,EAA6B,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAO,OAAO,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE9C,qCAAqC;AACrC,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,aAAa,CAAC;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,SAAU,SAAQ,MAAM;IACvC,mHAAmH;IACnH,GAAG,CAAC,EAAE;SAAG,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC;YAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;SAAE,CAAC;KAAE,GAAG;QACpE,gGAAgG;QAChG,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;KACtB,CAAC;CACH;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;cAA4C,CAAC;AAQpE,2HAA2H;AAC3H,eAAO,MAAM,UAAU,GAAI,CAAC,YAAY,CAAC,CAAC,EAAE,YAAY,KAAK,CAAC,KAAG,CAI9D,CAAC;AACJ,eAAO,MAAM,KAAK,GAAI,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,KAAG,CAAoC,CAAC"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import { useStore } from "zustand";
|
|
3
|
+
export { useShallow } from "zustand/react/shallow";
|
|
4
|
+
export { theme, usageLine } from "./entry.js";
|
|
5
|
+
export const CliContext = createContext(undefined);
|
|
6
|
+
function useCli() {
|
|
7
|
+
const cli = useContext(CliContext);
|
|
8
|
+
if (!cli)
|
|
9
|
+
throw new Error("hortus's hooks only work inside its TUI");
|
|
10
|
+
return cli;
|
|
11
|
+
}
|
|
12
|
+
/** Selectors must return primitives or existing references, or go through useShallow (zustand loops on a fresh object). */
|
|
13
|
+
export const useSession = (selector) => useStore(useUi((s) => s.session), selector);
|
|
14
|
+
export const useUi = (selector) => useStore(useCli().ui, selector);
|
|
15
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKnC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAgB9C,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAkB,SAAS,CAAC,CAAC;AAEpE,SAAS,MAAM;IACb,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACrE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2HAA2H;AAC3H,MAAM,CAAC,MAAM,UAAU,GAAG,CAAI,QAAgC,EAAK,EAAE,CACnE,QAAQ,CACN,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EACvB,QAAQ,CACT,CAAC;AACJ,MAAM,CAAC,MAAM,KAAK,GAAG,CAAI,QAA2B,EAAK,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC"}
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CliPlugin, type Slots } from "./api.ts";
|
|
2
|
+
import type { Cli } from "./cli.ts";
|
|
3
|
+
/** Built-in slots, each wrapped by every plugin's override in load order (later plugins end up outermost). */
|
|
4
|
+
export declare function composeSlots(plugins: CliPlugin[], onError: (text: string) => void): Slots;
|
|
5
|
+
/** The TUI; plugins' `cli` overrides are composed once into the slots it renders. */
|
|
6
|
+
export declare function App({ cli }: {
|
|
7
|
+
cli: Cli;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,KAAK,EAAS,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAgDpC,8GAA8G;AAC9G,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,KAAK,CAgBzF;AAED,qFAAqF;AACrF,wBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,+BAOxC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useApp, useBoxMetrics, useInput, useWindowSize } from "ink";
|
|
3
|
+
import { Component, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { CliContext, useUi } from "./api.js";
|
|
5
|
+
import { CommandMenu, useCommandMenu } from "./command-menu.js";
|
|
6
|
+
import { theme } from "./entry.js";
|
|
7
|
+
import { PromptInput } from "./prompt-input.js";
|
|
8
|
+
import { StatusBar } from "./status-bar.js";
|
|
9
|
+
import { Transcript } from "./transcript.js";
|
|
10
|
+
/** Clips children to its own height. `fromBottom` = rows scrolled up from the end, so 0 follows new content. */
|
|
11
|
+
function ScrollBox({ fromBottom, children, ...props }) {
|
|
12
|
+
const view = useRef(null);
|
|
13
|
+
const content = useRef(null);
|
|
14
|
+
const max = Math.max(0, useBoxMetrics(content).height - useBoxMetrics(view).height);
|
|
15
|
+
return (_jsx(Box, { ref: view, overflow: "hidden", flexDirection: "column",
|
|
16
|
+
// size from the parent, not from the (taller/wider) content
|
|
17
|
+
flexBasis: 0, contentOffsetY: max - Math.min(fromBottom, max), ...props, children: _jsx(Box, { ref: content, flexShrink: 0, flexDirection: "column", width: "100%", children: children }) }));
|
|
18
|
+
}
|
|
19
|
+
const clamp = (n) => Math.max(0, Math.min(n, 10_000));
|
|
20
|
+
const INPUT_ROWS = 8;
|
|
21
|
+
/** A throwing plugin component falls back to the one it replaced instead of taking the whole TUI down. */
|
|
22
|
+
class Guard extends Component {
|
|
23
|
+
state = { failed: false };
|
|
24
|
+
static getDerivedStateFromError = () => ({ failed: true });
|
|
25
|
+
componentDidCatch(e) {
|
|
26
|
+
this.props.onError(e);
|
|
27
|
+
}
|
|
28
|
+
render() {
|
|
29
|
+
const Fallback = this.props.fallback;
|
|
30
|
+
return this.state.failed ? _jsx(Fallback, {}) : this.props.children;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Built-in slots, each wrapped by every plugin's override in load order (later plugins end up outermost). */
|
|
34
|
+
export function composeSlots(plugins, onError) {
|
|
35
|
+
const slots = { statusBar: StatusBar };
|
|
36
|
+
for (const { cli } of plugins)
|
|
37
|
+
for (const k of Object.keys(slots)) {
|
|
38
|
+
const Override = cli?.[k];
|
|
39
|
+
if (!Override)
|
|
40
|
+
continue;
|
|
41
|
+
const Default = slots[k];
|
|
42
|
+
const fail = (e) => onError(`${k} plugin failed, showing the one it replaced: ${e instanceof Error ? e.message : String(e)}`);
|
|
43
|
+
slots[k] = () => (_jsx(Guard, { fallback: Default, onError: fail, children: _jsx(Override, { Default: Default }) }));
|
|
44
|
+
}
|
|
45
|
+
return slots;
|
|
46
|
+
}
|
|
47
|
+
/** The TUI; plugins' `cli` overrides are composed once into the slots it renders. */
|
|
48
|
+
export function App({ cli }) {
|
|
49
|
+
const slots = useMemo(() => composeSlots(cli.agent.plugins, cli.report), [cli]);
|
|
50
|
+
return (_jsx(CliContext, { value: cli, children: _jsx(Layout, { cli: cli, slots: slots }) }));
|
|
51
|
+
}
|
|
52
|
+
function Layout({ cli, slots }) {
|
|
53
|
+
const entries = useUi((s) => s.entries);
|
|
54
|
+
const live = useUi((s) => s.live);
|
|
55
|
+
const busy = useUi((s) => s.busy);
|
|
56
|
+
const path = useUi((s) => s.session.path);
|
|
57
|
+
const [input, setInput] = useState("");
|
|
58
|
+
const menu = useCommandMenu(cli, input, setInput);
|
|
59
|
+
// rows the input is scrolled up from its end; clamped on read by ScrollBox, so overshoot just sticks at the top
|
|
60
|
+
const [inScroll, setInScroll] = useState(0);
|
|
61
|
+
const { rows } = useWindowSize();
|
|
62
|
+
const { exit } = useApp();
|
|
63
|
+
useInput((input, key) => {
|
|
64
|
+
if (key.ctrl && input === "d")
|
|
65
|
+
exit();
|
|
66
|
+
else if (key.ctrl && input === "c") {
|
|
67
|
+
setInput("");
|
|
68
|
+
setInScroll(0);
|
|
69
|
+
}
|
|
70
|
+
else if (key.escape && menu.shown)
|
|
71
|
+
menu.hide();
|
|
72
|
+
else if (key.escape)
|
|
73
|
+
cli.abort();
|
|
74
|
+
});
|
|
75
|
+
const submit = (value) => {
|
|
76
|
+
if (!cli.submit(value))
|
|
77
|
+
return;
|
|
78
|
+
setInput("");
|
|
79
|
+
setInScroll(0);
|
|
80
|
+
};
|
|
81
|
+
// stable across keystrokes, so Transcript doesn't re-wrap the whole history on every key
|
|
82
|
+
const shown = useMemo(() => [
|
|
83
|
+
...entries,
|
|
84
|
+
...(live.thinking ? [{ id: -2, kind: "thinking", text: live.thinking }] : []),
|
|
85
|
+
...(live.text ? [{ id: -1, kind: "assistant", text: live.text }] : []),
|
|
86
|
+
], [entries, live]);
|
|
87
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(Transcript
|
|
88
|
+
// another session (/new) starts unscrolled, with nothing selected
|
|
89
|
+
, { entries: shown, onWheelBelow: (delta) => setInScroll((n) => clamp(n + delta)), onCopy: cli.setCopied }, path), _jsxs(Box, { flexShrink: 0, marginTop: 1, borderStyle: "round", borderColor: busy ? theme.borderIdle : theme.border, paddingX: 1, children: [_jsx(Text, { color: theme.accent, children: "> " }), _jsx(ScrollBox, { flexGrow: 1, maxHeight: INPUT_ROWS, fromBottom: inScroll, children: _jsx(PromptInput, { value: input, onChange: setInput, onSubmit: submit, onKey: menu.onKey,
|
|
90
|
+
// scroll just enough to keep the caret's row in view
|
|
91
|
+
onCaretRow: (row) => setInScroll((n) => Math.min(row, Math.max(n, row - INPUT_ROWS + 1))) }) })] }), menu.shown && _jsx(CommandMenu, { rows: menu.rows, at: menu.at }), _jsx(slots.statusBar, {})] }));
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Agent, Message, Plugin, Session } from "@hortus/core";
|
|
2
|
+
import { type Command } from "./commands.ts";
|
|
3
|
+
import { type Entry } from "./entry.ts";
|
|
4
|
+
/** What the TUI shows; written only by `createCli`, read through `useUi`. */
|
|
5
|
+
export interface UiState {
|
|
6
|
+
/** the session shown and run on; swapped by `setSession` (e.g. /new) */
|
|
7
|
+
session: Session;
|
|
8
|
+
entries: Entry[];
|
|
9
|
+
/** streamed thinking and text not yet turned into entries */
|
|
10
|
+
live: {
|
|
11
|
+
thinking: string;
|
|
12
|
+
text: string;
|
|
13
|
+
};
|
|
14
|
+
/** messages of the running turn; appended to the session when the run ends */
|
|
15
|
+
turn: Message[];
|
|
16
|
+
busy: boolean;
|
|
17
|
+
/** provider name and model the next run would use, plus the context window when the API reports it */
|
|
18
|
+
status?: {
|
|
19
|
+
name: string;
|
|
20
|
+
model: string;
|
|
21
|
+
window?: number;
|
|
22
|
+
};
|
|
23
|
+
/** chars of the last copy, shown until the next submit */
|
|
24
|
+
copied?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Transcript entries for messages already in the session, so a resumed one shows as it was left. */
|
|
27
|
+
export declare function replay(messages: Message[]): Entry[];
|
|
28
|
+
/**
|
|
29
|
+
* The TUI's logic, without React: runs the agent on `session` or a `/name arg` command, turns its events into
|
|
30
|
+
* transcript entries and keeps `status` in step with the session's model and provider. `makeAgent` creates the agent
|
|
31
|
+
* with the run observer it's given.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createCli(session: Session, makeAgent: (observer: Plugin) => Promise<Agent>): Promise<{
|
|
34
|
+
ui: import("zustand/vanilla").StoreApi<UiState>;
|
|
35
|
+
agent: {
|
|
36
|
+
providers: Record<string, import("@hortus/core").ProviderDef>;
|
|
37
|
+
plugins: Plugin[];
|
|
38
|
+
resolve: (opts?: Pick<import("@hortus/core").RunOptions, "model" | "provider">) => Promise<{
|
|
39
|
+
name: string;
|
|
40
|
+
model: string;
|
|
41
|
+
provider: import("@hortus/core").Provider;
|
|
42
|
+
}>;
|
|
43
|
+
listModels: () => Promise<{
|
|
44
|
+
provider: string;
|
|
45
|
+
id: string;
|
|
46
|
+
}[]>;
|
|
47
|
+
run: (history: Message[], opts?: import("@hortus/core").RunOptions) => Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
/** the session shown, `ui`'s `session` */
|
|
50
|
+
readonly session: Session;
|
|
51
|
+
/** the built-ins and every plugin's `cli.commands`, one per name */
|
|
52
|
+
commands: Command[];
|
|
53
|
+
/**
|
|
54
|
+
* starts `value` as a command or the next turn; false (the prompt keeps its input) when blank, still running or an
|
|
55
|
+
* unknown command
|
|
56
|
+
*/
|
|
57
|
+
submit: (value: string) => boolean;
|
|
58
|
+
abort: () => void | undefined;
|
|
59
|
+
setCopied: (chars: number) => void;
|
|
60
|
+
/** adds an error entry to the transcript */
|
|
61
|
+
report: (text: string) => void;
|
|
62
|
+
/** shows `session` in place of the current one: the transcript restarts from its messages, and runs go to it */
|
|
63
|
+
setSession: (session: Session) => void;
|
|
64
|
+
}>;
|
|
65
|
+
export type Cli = Awaited<ReturnType<typeof createCli>>;
|
|
66
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AAG9E,OAAO,EAAmB,KAAK,OAAO,EAA6B,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,KAAK,KAAK,EAAqB,MAAM,YAAY,CAAC;AAE3D,6EAA6E;AAC7E,MAAM,WAAW,OAAO;IACtB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,6DAA6D;IAC7D,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,8EAA8E;IAC9E,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,sGAAsG;IACtG,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qGAAqG;AACrG,wBAAgB,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAenD;AAsBD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;IAsJ7F,0CAA0C;;IAI1C,oEAAoE;;IAEpE;;;OAGG;oBACa,MAAM;;uBAaH,MAAM;IACzB,4CAA4C;mBAC7B,MAAM;IACrB,gHAAgH;0BAC1F,OAAO;GAOhC;AAED,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { createStore } from "zustand/vanilla";
|
|
2
|
+
import { builtinCommands, findCommand, parseCommand } from "./commands.js";
|
|
3
|
+
import { entry, formatTool } from "./entry.js";
|
|
4
|
+
/** Transcript entries for messages already in the session, so a resumed one shows as it was left. */
|
|
5
|
+
export function replay(messages) {
|
|
6
|
+
const out = [];
|
|
7
|
+
const calls = new Map();
|
|
8
|
+
for (const m of messages) {
|
|
9
|
+
if (m.role === "user")
|
|
10
|
+
out.push(entry("user", `\n${m.content}\n`));
|
|
11
|
+
else if (m.role === "assistant") {
|
|
12
|
+
if (m.text)
|
|
13
|
+
out.push(entry("assistant", m.text));
|
|
14
|
+
for (const c of m.toolCalls)
|
|
15
|
+
calls.set(c.id, c);
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
for (const r of m.results) {
|
|
19
|
+
const c = calls.get(r.id);
|
|
20
|
+
if (c)
|
|
21
|
+
out.push(entry(r.isError ? "toolError" : "toolOk", formatTool(c, r)));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/** UI state for showing `session` from its start: its file and settings, then its messages so far. */
|
|
27
|
+
function show(session) {
|
|
28
|
+
const { messages, model, provider } = session.getState();
|
|
29
|
+
return {
|
|
30
|
+
session,
|
|
31
|
+
entries: [
|
|
32
|
+
entry("info", `Session: ${session.path} (${messages.length} messages)${provider ? ` · provider: ${provider}` : ""}${model ? ` · model: ${model}` : ""}`),
|
|
33
|
+
...replay(messages),
|
|
34
|
+
],
|
|
35
|
+
live: { thinking: "", text: "" },
|
|
36
|
+
turn: [],
|
|
37
|
+
copied: undefined,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const errorText = (e) => (e instanceof Error ? e.message : String(e));
|
|
41
|
+
/**
|
|
42
|
+
* The TUI's logic, without React: runs the agent on `session` or a `/name arg` command, turns its events into
|
|
43
|
+
* transcript entries and keeps `status` in step with the session's model and provider. `makeAgent` creates the agent
|
|
44
|
+
* with the run observer it's given.
|
|
45
|
+
*/
|
|
46
|
+
export async function createCli(session, makeAgent) {
|
|
47
|
+
const ui = createStore()(() => ({ ...show(session), busy: false }));
|
|
48
|
+
const push = (...e) => ui.setState((s) => ({ entries: [...s.entries, ...e] }));
|
|
49
|
+
// Deltas reach `live` at most once a frame: every store change renders synchronously (useSyncExternalStore), and a
|
|
50
|
+
// fast stream of back-to-back sync renders trips React's nested-update limit while Ink's useBoxMetrics has updates
|
|
51
|
+
// pending.
|
|
52
|
+
let pending = { thinking: "", text: "" };
|
|
53
|
+
let timer;
|
|
54
|
+
const take = () => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
timer = undefined;
|
|
57
|
+
const p = pending;
|
|
58
|
+
pending = { thinking: "", text: "" };
|
|
59
|
+
return p;
|
|
60
|
+
};
|
|
61
|
+
const stream = (key, delta) => {
|
|
62
|
+
pending[key] += delta;
|
|
63
|
+
timer ??= setTimeout(() => {
|
|
64
|
+
const p = take();
|
|
65
|
+
ui.setState(({ live }) => ({ live: { thinking: live.thinking + p.thinking, text: live.text + p.text } }));
|
|
66
|
+
}, 1000 / 30);
|
|
67
|
+
};
|
|
68
|
+
// streamed-but-unfinished thinking and text become entries, then the live view resets
|
|
69
|
+
const flush = () => {
|
|
70
|
+
const p = take();
|
|
71
|
+
ui.setState(({ entries, live }) => {
|
|
72
|
+
const thinking = live.thinking + p.thinking;
|
|
73
|
+
const text = live.text + p.text;
|
|
74
|
+
return {
|
|
75
|
+
entries: [
|
|
76
|
+
...entries,
|
|
77
|
+
...(thinking ? [entry("thinking", thinking)] : []),
|
|
78
|
+
...(text ? [entry("assistant", text)] : []),
|
|
79
|
+
],
|
|
80
|
+
live: { thinking: "", text: "" },
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
// pending tool blocks by call id, replaced by their result block when it arrives
|
|
85
|
+
const calls = new Map();
|
|
86
|
+
const agent = await makeAgent({
|
|
87
|
+
on: {
|
|
88
|
+
thinking: (delta) => stream("thinking", delta),
|
|
89
|
+
text: (delta) => stream("text", delta),
|
|
90
|
+
tool_call: (call) => {
|
|
91
|
+
const e = entry("tool", formatTool(call));
|
|
92
|
+
calls.set(call.id, { call, entry: e });
|
|
93
|
+
push(e);
|
|
94
|
+
},
|
|
95
|
+
message: (message) => {
|
|
96
|
+
if (message.role === "tool") {
|
|
97
|
+
const done = new Map();
|
|
98
|
+
for (const r of message.results) {
|
|
99
|
+
const c = calls.get(r.id);
|
|
100
|
+
if (!c)
|
|
101
|
+
continue;
|
|
102
|
+
done.set(c.entry, { ...c.entry, kind: r.isError ? "toolError" : "toolOk", text: formatTool(c.call, r) });
|
|
103
|
+
calls.delete(r.id);
|
|
104
|
+
}
|
|
105
|
+
ui.setState((s) => ({ entries: s.entries.map((e) => done.get(e) ?? e) }));
|
|
106
|
+
}
|
|
107
|
+
// an assistant message closes the streamed thinking and text
|
|
108
|
+
flush();
|
|
109
|
+
ui.setState((s) => ({ turn: [...s.turn, message] }));
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// later plugins win, so a command can be replaced by name (like tools)
|
|
114
|
+
const commands = [
|
|
115
|
+
...new Map([...builtinCommands, ...agent.plugins.flatMap((p) => p.cli?.commands ?? [])].map((c) => [
|
|
116
|
+
c.name,
|
|
117
|
+
c,
|
|
118
|
+
])).values(),
|
|
119
|
+
];
|
|
120
|
+
// re-resolved whenever the session, or its model or provider, changes
|
|
121
|
+
let seq = 0;
|
|
122
|
+
async function refreshStatus() {
|
|
123
|
+
const n = ++seq;
|
|
124
|
+
const { model, provider } = ui.getState().session.getState();
|
|
125
|
+
try {
|
|
126
|
+
const r = await agent.resolve({ model, provider });
|
|
127
|
+
// a newer change won; don't overwrite it with a stale result
|
|
128
|
+
if (n !== seq)
|
|
129
|
+
return;
|
|
130
|
+
ui.setState({ status: { name: r.name, model: r.model } });
|
|
131
|
+
const window = await r.provider.contextWindow?.(r.model).catch(() => undefined);
|
|
132
|
+
if (n === seq)
|
|
133
|
+
ui.setState({ status: { name: r.name, model: r.model, window } });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
if (n === seq)
|
|
137
|
+
ui.setState({ status: undefined });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const watch = (session) => session.subscribe((s, prev) => {
|
|
141
|
+
if (s.model !== prev.model || s.provider !== prev.provider)
|
|
142
|
+
void refreshStatus();
|
|
143
|
+
});
|
|
144
|
+
let unwatch = watch(session);
|
|
145
|
+
ui.subscribe((s, prev) => {
|
|
146
|
+
if (s.session === prev.session)
|
|
147
|
+
return;
|
|
148
|
+
unwatch();
|
|
149
|
+
unwatch = watch(s.session);
|
|
150
|
+
void refreshStatus();
|
|
151
|
+
});
|
|
152
|
+
void refreshStatus();
|
|
153
|
+
let aborter;
|
|
154
|
+
async function run(value) {
|
|
155
|
+
const { session } = ui.getState();
|
|
156
|
+
const turn = [{ role: "user", content: value }];
|
|
157
|
+
// blank rows pad the message's background, as pi does
|
|
158
|
+
ui.setState((s) => ({ busy: true, turn, entries: [...s.entries, entry("user", `\n${value}\n`)] }));
|
|
159
|
+
const abort = new AbortController();
|
|
160
|
+
aborter = abort;
|
|
161
|
+
try {
|
|
162
|
+
const { messages, model, provider } = session.getState();
|
|
163
|
+
await agent.run([...messages, ...turn], { model, provider, signal: abort.signal }).catch((e) => {
|
|
164
|
+
if (!abort.signal.aborted)
|
|
165
|
+
throw e;
|
|
166
|
+
});
|
|
167
|
+
if (abort.signal.aborted) {
|
|
168
|
+
// the partial reply stays on screen but not in history; completed messages are kept
|
|
169
|
+
flush();
|
|
170
|
+
// error entries get no separator row, so the leading newline adds one; the next turn adds the one below
|
|
171
|
+
push(entry("error", "\nInterrupted"));
|
|
172
|
+
}
|
|
173
|
+
// the turn moves into session.messages in one tick, so the usage line never counts it twice
|
|
174
|
+
const saved = session.append(ui.getState().turn);
|
|
175
|
+
ui.setState({ turn: [] });
|
|
176
|
+
await saved;
|
|
177
|
+
}
|
|
178
|
+
catch (e) {
|
|
179
|
+
flush();
|
|
180
|
+
push(entry("error", errorText(e)));
|
|
181
|
+
}
|
|
182
|
+
ui.setState({ busy: false });
|
|
183
|
+
}
|
|
184
|
+
// not busy meanwhile, so a command can start a turn with `submit`
|
|
185
|
+
async function command(c, arg) {
|
|
186
|
+
try {
|
|
187
|
+
const text = await c.run(arg, cli);
|
|
188
|
+
if (text)
|
|
189
|
+
push(entry("info", text));
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
push(entry("error", errorText(e)));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const cli = {
|
|
196
|
+
ui,
|
|
197
|
+
agent,
|
|
198
|
+
/** the session shown, `ui`'s `session` */
|
|
199
|
+
get session() {
|
|
200
|
+
return ui.getState().session;
|
|
201
|
+
},
|
|
202
|
+
/** the built-ins and every plugin's `cli.commands`, one per name */
|
|
203
|
+
commands,
|
|
204
|
+
/**
|
|
205
|
+
* starts `value` as a command or the next turn; false (the prompt keeps its input) when blank, still running or an
|
|
206
|
+
* unknown command
|
|
207
|
+
*/
|
|
208
|
+
submit: (value) => {
|
|
209
|
+
if (!value.trim() || ui.getState().busy)
|
|
210
|
+
return false;
|
|
211
|
+
const cmd = parseCommand(value.trim());
|
|
212
|
+
const c = cmd && findCommand(commands, cmd.name);
|
|
213
|
+
if (cmd && !c) {
|
|
214
|
+
push(entry("error", `Unknown command: /${cmd.name}`));
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
ui.setState({ copied: undefined });
|
|
218
|
+
(c ? command(c, cmd?.arg ?? "") : run(value)).catch((e) => push(entry("error", errorText(e))));
|
|
219
|
+
return true;
|
|
220
|
+
},
|
|
221
|
+
abort: () => aborter?.abort(),
|
|
222
|
+
setCopied: (chars) => ui.setState({ copied: chars }),
|
|
223
|
+
/** adds an error entry to the transcript */
|
|
224
|
+
report: (text) => push(entry("error", text)),
|
|
225
|
+
/** shows `session` in place of the current one: the transcript restarts from its messages, and runs go to it */
|
|
226
|
+
setSession: (session) => {
|
|
227
|
+
// the running turn would end up split between the two
|
|
228
|
+
if (ui.getState().busy)
|
|
229
|
+
throw new Error("Can't switch sessions while a turn is running");
|
|
230
|
+
ui.setState(show(session));
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
return cli;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Key } from "ink";
|
|
2
|
+
import type { Cli } from "./cli.ts";
|
|
3
|
+
import { type Candidate, type Command } from "./commands.ts";
|
|
4
|
+
/** One row of the `/` menu, and the input once it's chosen. */
|
|
5
|
+
export interface Suggestion {
|
|
6
|
+
label: string;
|
|
7
|
+
/** dim text after the label */
|
|
8
|
+
description?: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The `/` menu's rows for `input`: commands while the name is typed, then the command's candidates from `loaded` for
|
|
13
|
+
* its argument; undefined while those are still loading.
|
|
14
|
+
*/
|
|
15
|
+
export declare function suggest(commands: Command[], input: string, loaded: Map<Command, Candidate[]>): Suggestion[] | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* The `/` menu for the prompt's `input`. `onKey` takes the prompt's ↑/↓ (select), Tab (put the selected row into the
|
|
18
|
+
* input) and Enter (the same, unless the input already is it or nothing has loaded yet: then it submits) while the menu
|
|
19
|
+
* shows; `hide` is Esc.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useCommandMenu(cli: Cli, input: string, setInput: (value: string) => void): {
|
|
22
|
+
rows: Suggestion[] | undefined;
|
|
23
|
+
at: number;
|
|
24
|
+
shown: boolean;
|
|
25
|
+
hide: () => void;
|
|
26
|
+
onKey: (_: string, key: Key) => boolean;
|
|
27
|
+
};
|
|
28
|
+
/** `rows` under the prompt, at most five, scrolled to keep the selected (accent) one in view; undefined = loading. */
|
|
29
|
+
export declare function CommandMenu({ rows, at }: {
|
|
30
|
+
rows: Suggestion[] | undefined;
|
|
31
|
+
at: number;
|
|
32
|
+
}): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=command-menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-menu.d.ts","sourceRoot":"","sources":["../src/command-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,GAAG,EAAQ,MAAM,KAAK,CAAC;AAG1C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAA6B,MAAM,eAAe,CAAC;AAGxF,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,4BAmC5F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;;;;;eAkDrE,MAAM,OAAO,GAAG;EAYnC;AAID,sHAAsH;AACtH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,+BA2BvF"}
|