codsh-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The input box: a framed, multi-line prompt that wraps long lines, grows with
3
+ * its content, and windows when it grows past its budget — with the completion
4
+ * menu under it.
5
+ *
6
+ * Pure layout. It turns an {@link EditorView} into the rows of the bottom region
7
+ * and says where the terminal cursor belongs, so the drawing code has no opinion
8
+ * about editing and this file has none about terminals.
9
+ *
10
+ * Wrapping is by display width, hard at the boundary: a wrap that respected word
11
+ * breaks would need the same word knowledge in the cursor mapping, and a cursor
12
+ * that disagrees with the wrap by one cell is worse than a word split across
13
+ * rows. Text is never truncated here — hiding typed text is how an input box
14
+ * loses a person's work.
15
+ * @module codsh-cli/src/inputbox
16
+ */
17
+ import type { EditorView } from './editor.ts';
18
+ import type { Theme } from './theme.ts';
19
+ /** What the box is asked to show besides the buffer. */
20
+ export interface BoxOptions {
21
+ /** Dim text shown inside an empty box, e.g. what `/` and `@` do. */
22
+ placeholder?: string | undefined;
23
+ /** Dim text shown under the box when the menu is closed. */
24
+ hint?: string | undefined;
25
+ /** Styles the frame; absent frames dim. A mode announces itself here. */
26
+ accent?: ((text: string) => string) | undefined;
27
+ }
28
+ /** The rows to draw and where the cursor goes among them. */
29
+ export interface BoxLayout {
30
+ /** Rows, top to bottom, each already fitted to the terminal. */
31
+ rows: string[];
32
+ /** Index into {@link rows} where the cursor belongs. */
33
+ cursorRow: number;
34
+ /** Display column of the cursor on that row, from zero. */
35
+ cursorColumn: number;
36
+ }
37
+ /**
38
+ * Lay out the input box.
39
+ * @param view - what the editor is showing.
40
+ * @param theme - styling for the frame, the marker, and the menu.
41
+ * @param columns - display columns available.
42
+ * @param options - placeholder, hint, and frame accent.
43
+ * @returns the rows and cursor position.
44
+ */
45
+ export declare function inputBox(view: EditorView, theme: Theme, columns: number, options?: BoxOptions): BoxLayout;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Package-owned invariant companion for `codsh-cli`.
3
+ * @module codsh-cli/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "coding-cli-invariant";
8
+ /** Service required before the companion can register. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Terminal bytes to key events.
3
+ *
4
+ * Owning the keyboard is what an input box costs: `readline` cannot report a
5
+ * lone Escape, cannot be asked to draw a completion menu, and decides for itself
6
+ * what Enter means. Decoding here is the price of deciding those things.
7
+ *
8
+ * The decoder is incremental because a terminal splits sequences across reads:
9
+ * an arrow key can arrive as `ESC`, then `[`, then `A`. Anything it cannot yet
10
+ * resolve is held until the next byte rather than guessed at.
11
+ * @module codsh-cli/src/keys
12
+ */
13
+ /** What one keystroke means to the editor. */
14
+ export type Key = {
15
+ kind: 'text';
16
+ text: string;
17
+ } | {
18
+ kind: 'enter';
19
+ } | {
20
+ kind: 'newline';
21
+ } | {
22
+ kind: 'tab';
23
+ } | {
24
+ kind: 'backspace';
25
+ } | {
26
+ kind: 'delete';
27
+ } | {
28
+ kind: 'up';
29
+ } | {
30
+ kind: 'down';
31
+ } | {
32
+ kind: 'left';
33
+ } | {
34
+ kind: 'right';
35
+ } | {
36
+ kind: 'home';
37
+ } | {
38
+ kind: 'end';
39
+ } | {
40
+ kind: 'escape';
41
+ } | {
42
+ kind: 'interrupt';
43
+ } | {
44
+ kind: 'eof';
45
+ } | {
46
+ kind: 'kill-line';
47
+ } | {
48
+ kind: 'kill-input';
49
+ } | {
50
+ kind: 'kill-word';
51
+ } | {
52
+ kind: 'word-left';
53
+ } | {
54
+ kind: 'word-right';
55
+ } | {
56
+ kind: 'shift-tab';
57
+ } | {
58
+ kind: 'clear-screen';
59
+ } | {
60
+ kind: 'expand-output';
61
+ } | {
62
+ kind: 'paste';
63
+ text: string;
64
+ };
65
+ /** Decodes terminal bytes into keys, holding partial sequences between reads. */
66
+ export declare class KeyDecoder {
67
+ private held;
68
+ private pasting;
69
+ private pasted;
70
+ /**
71
+ * Feed one read's worth of input.
72
+ * @param chunk - the bytes as text.
73
+ * @returns the keys this read completed, in order.
74
+ */
75
+ push(chunk: string): Key[];
76
+ /** Whether bytes are held back awaiting the rest of a sequence. */
77
+ get pending(): boolean;
78
+ /**
79
+ * Resolve a held Escape that no further byte arrived for.
80
+ *
81
+ * `ESC` alone and the first byte of `ESC [ A` are the same byte, so the two can
82
+ * only be told apart by what follows — or by nothing following. The caller arms
83
+ * a short timer after each read and calls this when it expires: an arrow key
84
+ * split across reads completes long before that, and a key pressed by itself
85
+ * never completes at all.
86
+ * @returns the Escape key, or nothing when the held bytes are a real prefix.
87
+ */
88
+ flush(): Key[];
89
+ /**
90
+ * Resolve the held bytes into one key, if they are enough.
91
+ * @returns the keys produced, or undefined when more bytes are needed.
92
+ */
93
+ private take;
94
+ /**
95
+ * Collect bracketed-paste content up to its end marker.
96
+ * @returns the paste key once complete, otherwise undefined.
97
+ */
98
+ private takePasted;
99
+ }
100
+ /** Ask the terminal to wrap pasted text in markers. */
101
+ export declare const ENABLE_PASTE_MARKERS = "\u001B[?2004h";
102
+ /** Stop the terminal wrapping pasted text, restoring what it did before. */
103
+ export declare const DISABLE_PASTE_MARKERS = "\u001B[?2004l";
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Markdown as terminal lines.
3
+ *
4
+ * A model answers in Markdown, and printing the source verbatim leaves the
5
+ * reader to parse `**` and fences by eye. This renders the constructs that
6
+ * actually change how an answer reads — headings, lists, quotes, emphasis,
7
+ * inline code, and fenced blocks — and leaves everything else exactly as it
8
+ * arrived. Anything unrecognised must survive unchanged: mangling prose to
9
+ * decorate it is worse than not decorating it.
10
+ * @module codsh-cli/src/markdown
11
+ */
12
+ import type { SyntaxTheme, Theme } from './theme.ts';
13
+ /**
14
+ * Colour one line of code by token class.
15
+ *
16
+ * A heuristic, not a parser: it has no state between lines, so a string or
17
+ * comment spanning several lines is coloured only on the line where it opens.
18
+ * Getting that wrong costs a colour, never the text — every branch reproduces
19
+ * its input exactly.
20
+ * @param line - the code line.
21
+ * @param syntax - styling per token class.
22
+ * @returns the coloured line.
23
+ */
24
+ export declare function highlightCode(line: string, syntax: SyntaxTheme): string;
25
+ /**
26
+ * Style the inline constructs of one line of prose.
27
+ * @param text - the line, with block syntax already stripped.
28
+ * @param theme - styling for emphasis, code spans, and link targets.
29
+ * @returns the styled line.
30
+ */
31
+ export declare function renderInline(text: string, theme: Theme): string;
32
+ /**
33
+ * A Markdown renderer that consumes one line at a time.
34
+ *
35
+ * Stateful because fencing is: whether a line is code depends on a fence seen
36
+ * earlier, so a renderer that forgot between lines would style the inside of a
37
+ * code block as prose. This is the form streaming needs — a line can be
38
+ * rendered the moment it completes, without waiting for the whole answer.
39
+ */
40
+ export interface MarkdownStream {
41
+ /**
42
+ * Render one input line.
43
+ * @param line - the line, without its terminator.
44
+ * @returns the output lines, which may be none (a fence delimiter, or a table
45
+ * row held until the table ends).
46
+ */
47
+ line(line: string): string[];
48
+ /**
49
+ * Close the stream, rendering anything still held back.
50
+ *
51
+ * A table is only recognisable once its delimiter row arrives, so its rows
52
+ * buffer; an answer that ends mid-table must still show them.
53
+ * @returns the remaining output lines.
54
+ */
55
+ flush(): string[];
56
+ /** Whether the renderer is currently inside a fenced block. */
57
+ readonly inCode: boolean;
58
+ }
59
+ /**
60
+ * Build a line-at-a-time Markdown renderer.
61
+ * @param theme - styling for every construct.
62
+ * @param columns - display columns available, read per table; absent means
63
+ * unconstrained. A table wider than this prints as its source lines.
64
+ * @returns the renderer, carrying its own fence and table state.
65
+ */
66
+ export declare function createMarkdownStream(theme: Theme, columns?: () => number): MarkdownStream;
67
+ /**
68
+ * Render a whole Markdown answer as terminal lines.
69
+ * @param text - the answer, as the model produced it.
70
+ * @param theme - styling for every construct.
71
+ * @returns the output lines, blocks included.
72
+ */
73
+ export declare function renderMarkdown(text: string, theme: Theme): string[];
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Installing this bundle's own preset into the Harness home.
3
+ *
4
+ * The launcher owns the roster's `roots`: `composeProfile` overwrites that key
5
+ * with the installed app's shipped directory, so a bundle cannot contribute a
6
+ * search root of its own. What it can reach is the writable user root the
7
+ * roster appends by default, which is why a packaged preset is copied there
8
+ * rather than pointed at in place.
9
+ *
10
+ * The copy is idempotent and never overwrites: a preset a person edited — or
11
+ * one a newer package version would change — stays as it is, because the user
12
+ * root is theirs. Removing the directory restores the packaged copy.
13
+ * @module codsh-cli/src/preset-install
14
+ */
15
+ /** The preset this bundle's patch names as the roster default. */
16
+ export declare const PACKAGED_PRESET = "code-cli";
17
+ /** What one install attempt did. */
18
+ export interface PresetInstallResult {
19
+ /** Absolute directory the preset occupies after the attempt. */
20
+ path: string;
21
+ /** Whether this call created it; false when it was already present. */
22
+ installed: boolean;
23
+ }
24
+ /**
25
+ * Where {@link installPackagedPreset} puts the preset by default.
26
+ * @returns the absolute preset directory under the Harness home's user root.
27
+ */
28
+ export declare function packagedPresetPath(): string;
29
+ /**
30
+ * Copy the packaged preset into the user root unless it is already there.
31
+ * @param home - the user preset root; defaults to the Harness home's.
32
+ * @returns where the preset lives and whether this call wrote it.
33
+ */
34
+ export declare function installPackagedPreset(home?: string): Promise<PresetInstallResult>;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The prompt as the person sees it: an input box, a completion menu, a working
3
+ * indicator, a status row, and — when a decision is being asked — a selection
4
+ * widget in the box's place.
5
+ *
6
+ * This is where the two input shapes meet. On a terminal it drives the editor
7
+ * from decoded keys and owns the bottom region; off one it reads lines from the
8
+ * pipe and draws nothing. Callers ask for the next submission either way.
9
+ * @module codsh-cli/src/prompt
10
+ */
11
+ import type { TerminalConsole } from './console.ts';
12
+ import type { EditorSources } from './editor.ts';
13
+ import type { SelectOutcome, SelectSpec } from './selector.ts';
14
+ import type { Theme } from './theme.ts';
15
+ /** What the prompt reports to its owner. */
16
+ export interface PromptHandlers {
17
+ /** Ctrl-C: stop the work, or leave. */
18
+ interrupt(): void;
19
+ /** Escape with nothing of the prompt's own to dismiss: stop the work. */
20
+ escape(): void;
21
+ /** Ctrl-D on an untouched prompt: leave. */
22
+ eof(): void;
23
+ /** Shift-Tab: cycle the session's mode. */
24
+ shiftTab?(): void;
25
+ /** Ctrl-O: show the last clipped tool output in full. */
26
+ expandOutput?(): void;
27
+ }
28
+ /** Drives the input box and answers reads and selections. */
29
+ export declare class Prompt {
30
+ private readonly console;
31
+ private readonly theme;
32
+ private readonly handlers;
33
+ /** Dim text shown inside an empty box. */
34
+ private readonly placeholder?;
35
+ private readonly editor;
36
+ private pending;
37
+ private select_;
38
+ /**
39
+ * Submissions made before anything asked for them.
40
+ *
41
+ * Typing while the agent works — or in the instant before a read begins — must
42
+ * not be lost; the queue is what a line reader provides for free.
43
+ */
44
+ private readonly queued;
45
+ /** The working indicator shown under the box. */
46
+ private hint;
47
+ /** The always-current session facts shown as the region's last row. */
48
+ private status;
49
+ /** The assistant line still arriving, shown above the box. */
50
+ private streaming;
51
+ /** Frame styling for the current mode, e.g. plan mode's accent. */
52
+ private accent;
53
+ /** Whether a read is outstanding, which decides where a submission goes. */
54
+ private reading;
55
+ /**
56
+ * Whether the interactive session is running, which is when the box is worth
57
+ * drawing. The box stays up while the agent works — typing ahead must be
58
+ * visible, and a prompt that vanishes for every turn reads as losing focus —
59
+ * so this is session-scoped, not read-scoped.
60
+ */
61
+ private engaged;
62
+ constructor(console: TerminalConsole, theme: Theme, sources: EditorSources, handlers: PromptHandlers,
63
+ /** Dim text shown inside an empty box. */
64
+ placeholder?: string | undefined);
65
+ /** The editor's submission history, for persistence. */
66
+ get history(): readonly string[];
67
+ /**
68
+ * Preload history from an earlier session.
69
+ * @param entries - past submissions, oldest first.
70
+ */
71
+ seedHistory(entries: readonly string[]): void;
72
+ /** Whether the box holds no typed text. */
73
+ get empty(): boolean;
74
+ /**
75
+ * Show the input box from now on, independent of an outstanding read.
76
+ * @param engaged - whether the interactive session is running.
77
+ */
78
+ setEngaged(engaged: boolean): void;
79
+ /**
80
+ * Put earlier text back into the box for editing.
81
+ * @param text - the text to edit.
82
+ */
83
+ prefill(text: string): void;
84
+ /**
85
+ * Set the working indicator under the box.
86
+ * @param text - the text, or undefined to drop the row.
87
+ */
88
+ setHint(text: string | undefined): void;
89
+ /**
90
+ * Set the status row, the region's always-current last line.
91
+ * @param text - the styled row, or undefined to drop it.
92
+ */
93
+ setStatus(text: string | undefined): void;
94
+ /**
95
+ * Set the frame accent, which is how a mode shows on the box itself.
96
+ * @param accent - the styling, or undefined for the default frame.
97
+ */
98
+ setAccent(accent: ((text: string) => string) | undefined): void;
99
+ /**
100
+ * Set the assistant line currently arriving, shown above the box.
101
+ * @param text - the partial line, or undefined when none is open.
102
+ */
103
+ setStreaming(text: string | undefined): void;
104
+ /**
105
+ * Write one finished transcript line above the region.
106
+ * @param line - the line to keep.
107
+ */
108
+ write(line: string): void;
109
+ /**
110
+ * Wait for the next submitted text.
111
+ * @param signal - abandons the read, which an aborted tool call does.
112
+ * @returns the text, or undefined when input ended or the read was abandoned.
113
+ */
114
+ read(signal?: AbortSignal): Promise<string | undefined>;
115
+ /**
116
+ * Put one decision to the keyboard as an arrow-key selection.
117
+ *
118
+ * Only the terminal shape can offer this; the caller keeps a line-based
119
+ * fallback for pipes, where the selection keys cannot arrive.
120
+ * @param spec - the question and its options.
121
+ * @param signal - cancels the selection, which an aborted tool call does.
122
+ * @returns how the person decided.
123
+ */
124
+ select(spec: SelectSpec, signal?: AbortSignal): Promise<SelectOutcome>;
125
+ /** Take the region down, so what follows lands at the bottom of the screen. */
126
+ clear(): void;
127
+ /**
128
+ * Apply one key: control keys to the owner, a selection's keys to the
129
+ * selector, everything else to the editor.
130
+ * @param key - the decoded keystroke.
131
+ */
132
+ private onKey;
133
+ /** Recompose and redraw the bottom region. */
134
+ private render;
135
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The terminal's user-questions provider: it renders each question as a
3
+ * numbered menu, reads one line per question, and encodes the answer.
4
+ *
5
+ * A question may offer options, free text, or both. Selecting by number
6
+ * answers with that option's label; typing anything else answers as `custom`,
7
+ * which is the encoding `ask_user_question` documents for an "Other" reply.
8
+ * @module codsh-cli/src/questions
9
+ */
10
+ import type { AskUserQuestionAnswer, AskUserQuestionAnswerItem, AskUserQuestionItem, AskUserQuestionRequest } from '@deepseek-ai/dsh-user-questions';
11
+ import type { SelectOutcome, SelectSpec } from './selector.ts';
12
+ import type { Theme } from './theme.ts';
13
+ /** Puts one selection to the keyboard; absent on a pipe, which types instead. */
14
+ export type SelectAsk = (spec: SelectSpec, signal?: AbortSignal) => Promise<SelectOutcome>;
15
+ /** Reads one answer from the person. */
16
+ export interface LineReader {
17
+ /**
18
+ * Read one submission.
19
+ * @param signal - aborts the read when the owning tool call is cancelled.
20
+ * @returns the answer, or undefined when input ended or the read aborted.
21
+ */
22
+ read(signal?: AbortSignal): Promise<string | undefined>;
23
+ }
24
+ /**
25
+ * Parse a selection line against one question's options.
26
+ *
27
+ * A comma-separated list of numbers selects those options; a multi-select
28
+ * question accepts several, a single-select takes the first. Anything that is
29
+ * not a valid index becomes the free-text answer.
30
+ * @param line - the line the person typed.
31
+ * @param question - the question being answered.
32
+ * @returns the encoded answer for this question.
33
+ */
34
+ export declare function encodeAnswer(line: string, question: AskUserQuestionItem): AskUserQuestionAnswerItem;
35
+ /**
36
+ * Render one question as the lines shown above its prompt.
37
+ * @param question - the question to render.
38
+ * @param theme - styling for the heading and option list.
39
+ * @returns the lines to print.
40
+ */
41
+ export declare function questionLines(question: AskUserQuestionItem, theme: Theme): string[];
42
+ /** Answers `ask_user_question` from the terminal. */
43
+ export declare class TerminalQuestions {
44
+ private readonly reader;
45
+ private readonly theme;
46
+ private readonly write;
47
+ /** The arrow-key selection, offered only where keys can arrive. */
48
+ private readonly select?;
49
+ constructor(reader: LineReader, theme: Theme, write: (line: string) => void,
50
+ /** The arrow-key selection, offered only where keys can arrive. */
51
+ select?: SelectAsk | undefined);
52
+ /**
53
+ * Put every question in one request to the person, in order.
54
+ * @param request - the questions, owner agent, and abort signal.
55
+ * @returns one answer per question, in request order.
56
+ */
57
+ ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>;
58
+ /**
59
+ * Put one question to the person.
60
+ * @param question - the question.
61
+ * @param signal - aborts with the owning tool call.
62
+ * @returns the encoded answer.
63
+ */
64
+ private one;
65
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The arrow-key selection widget: approvals, questions, and any other choice a
3
+ * person makes by moving a marker rather than by typing an answer.
4
+ *
5
+ * Pure state, like the editor: keys in, a verdict and rows out. One widget
6
+ * serves single-select, multi-select, and shortcut keys, because three slightly
7
+ * different pickers is how the same bug ships three times.
8
+ * @module codsh-cli/src/selector
9
+ */
10
+ import type { Key } from './keys.ts';
11
+ import type { Theme } from './theme.ts';
12
+ /** One choice on offer. */
13
+ export interface SelectOption {
14
+ /** What the choice is, shown as its row. */
15
+ label: string;
16
+ /** Extra context shown dimly beside the label. */
17
+ detail?: string;
18
+ /** Single key that picks this option outright (e.g. `y`). */
19
+ shortcut?: string;
20
+ }
21
+ /** What a selection asks. */
22
+ export interface SelectSpec {
23
+ /** The question, shown above the options. */
24
+ title: string;
25
+ options: readonly SelectOption[];
26
+ /** Whether several options may be chosen; Space toggles, Enter confirms. */
27
+ multi?: boolean;
28
+ /** Label for a trailing "type your own" row; absent offers none. */
29
+ custom?: string;
30
+ }
31
+ /** How one selection ended. */
32
+ export type SelectOutcome = {
33
+ kind: 'chosen';
34
+ indices: number[];
35
+ } | {
36
+ kind: 'custom';
37
+ } | {
38
+ kind: 'cancelled';
39
+ };
40
+ /** What a key did to the selection. */
41
+ export type SelectorStep = {
42
+ kind: 'pending';
43
+ } | {
44
+ kind: 'done';
45
+ outcome: SelectOutcome;
46
+ };
47
+ /** An in-progress selection. */
48
+ export declare class Selector {
49
+ private readonly spec;
50
+ private selected;
51
+ private readonly checked;
52
+ constructor(spec: SelectSpec);
53
+ /** How many rows the widget offers, the custom row included. */
54
+ private get count();
55
+ /** Whether a row index is the custom "type your own" row. */
56
+ private isCustom;
57
+ /**
58
+ * Apply one key.
59
+ * @param key - the decoded keystroke.
60
+ * @returns whether the selection settled, and how.
61
+ */
62
+ handle(key: Key): SelectorStep;
63
+ /**
64
+ * Resolve a typed character: a digit jumps, a shortcut picks, Space toggles.
65
+ * @param text - what was typed.
66
+ * @returns whether the selection settled.
67
+ */
68
+ private typed;
69
+ /**
70
+ * Settle on a row.
71
+ * @param index - the row accepted.
72
+ * @returns the settled step.
73
+ */
74
+ private accept;
75
+ /**
76
+ * Render the widget.
77
+ * @param theme - styling for the marker, shortcuts, and details.
78
+ * @param columns - display columns available per row.
79
+ * @returns the rows, title first.
80
+ */
81
+ view(theme: Theme, columns: number): string[];
82
+ /**
83
+ * One option's label with its number and shortcut.
84
+ * @param option - the option to label.
85
+ * @param theme - styling for the shortcut.
86
+ * @returns the label text.
87
+ */
88
+ private label;
89
+ /**
90
+ * One rendered row.
91
+ * @param index - the row's index.
92
+ * @param label - the row's label, already styled.
93
+ * @param detail - dim context beside it.
94
+ * @param theme - styling for the marker and detail.
95
+ * @param columns - display columns available.
96
+ * @returns the row text.
97
+ */
98
+ private row;
99
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The working indicator: one rewritable line saying the agent is busy, how long
3
+ * it has been, and which key stops it.
4
+ *
5
+ * It occupies the console's single live line, so the transcript above it stays
6
+ * append-only and the indicator never survives into a redirected transcript.
7
+ * @module codsh-cli/src/spinner
8
+ */
9
+ import type { Theme } from './theme.ts';
10
+ /** The console surface a spinner drives. */
11
+ export interface LiveSurface {
12
+ setLive(text: string | undefined): void;
13
+ readonly isTty: boolean;
14
+ }
15
+ /** Formats the indicator's text for one tick. */
16
+ export interface SpinnerLabel {
17
+ /** What the agent is doing, e.g. `working`. */
18
+ verb: string;
19
+ /** Key that cancels, named for the surface that can read it. */
20
+ interrupt: string;
21
+ /** Live extra segment, read each tick — e.g. tokens spent so far. */
22
+ detail?: () => string | undefined;
23
+ }
24
+ /**
25
+ * Render one tick of the indicator.
26
+ * @param frame - the frame index, taken modulo the frame count.
27
+ * @param elapsedMs - milliseconds since the work started.
28
+ * @param label - the verb and interrupt key to name.
29
+ * @param theme - styling for the frame and the hint.
30
+ * @returns the line to display.
31
+ */
32
+ export declare function spinnerText(frame: number, elapsedMs: number, label: SpinnerLabel, theme: Theme): string;
33
+ /** Drives the working indicator for as long as the agent is busy. */
34
+ export declare class Spinner {
35
+ private readonly surface;
36
+ private readonly theme;
37
+ private readonly label;
38
+ /** Injected so tests advance time without waiting for it. */
39
+ private readonly now;
40
+ private timer;
41
+ private frame;
42
+ private startedAt;
43
+ constructor(surface: LiveSurface, theme: Theme, label: SpinnerLabel,
44
+ /** Injected so tests advance time without waiting for it. */
45
+ now?: () => number);
46
+ /** Whether the indicator is running. */
47
+ get running(): boolean;
48
+ /**
49
+ * Start the indicator, or do nothing when it is already running or the
50
+ * surface has no cursor to rewrite.
51
+ */
52
+ start(): void;
53
+ /** Stop the indicator and clear its line. */
54
+ stop(): void;
55
+ /** Paint the current frame. */
56
+ private draw;
57
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The terminal app's command-line provider: it parses the optional task
3
+ * positional, the session-continuation flags, the preset override, and
4
+ * `--help`, then publishes {@link CODING_CLI_STARTUP_SERVICE}. The runner is an
5
+ * ordinary consumer whose lazy config waits for that service.
6
+ * @module codsh-cli/startup
7
+ */
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ /** Stable Cordis plugin name. */
10
+ export declare const name = "coding-cli-startup";
11
+ /** Services required before the invocation can be resolved. */
12
+ export declare const inject: string[];
13
+ /** Service provided by this plugin and injected by the terminal runner. */
14
+ export declare const CODING_CLI_STARTUP_SERVICE = "codingCliStartup";
15
+ /** What the runner row reads from {@link CODING_CLI_STARTUP_SERVICE}. */
16
+ export interface CodingCliStartupValues {
17
+ /** Opening task text, or the empty string when the session starts at the prompt. */
18
+ task: string;
19
+ /** Session to reopen: an explicit id, `'latest'` for `--continue`, or the empty string for a new session. */
20
+ resume: string;
21
+ /** Preset id overriding the roster default, or the empty string to accept it. */
22
+ preset: string;
23
+ /** Render the answer and exit rather than entering the interactive loop. */
24
+ print: boolean;
25
+ }
26
+ /**
27
+ * Parse and provide this invocation as an ordinary Cordis service. On `--help`
28
+ * and on a usage error nothing is provided, so the runner never mounts.
29
+ * @param ctx - plugin context carrying the command line.
30
+ */
31
+ export declare function apply(ctx: Context): void;