codsh-bundle 0.3.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/LICENSE +21 -0
- package/README.md +10 -0
- package/agent-presets/code-cli/agent.cordis.yml +322 -0
- package/agent-presets/code-cli/preset.yml +3 -0
- package/cordis.patch.yml +165 -0
- package/lib/index.js +4911 -0
- package/lib/invariant.js +23 -0
- package/lib/startup.js +64 -0
- package/lib/types/approval.d.ts +57 -0
- package/lib/types/banner.d.ts +30 -0
- package/lib/types/completion.d.ts +45 -0
- package/lib/types/console.d.ts +240 -0
- package/lib/types/custom-commands.d.ts +54 -0
- package/lib/types/editor.d.ts +186 -0
- package/lib/types/index.d.ts +48 -0
- package/lib/types/inputbox.d.ts +45 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/keys.d.ts +123 -0
- package/lib/types/markdown.d.ts +73 -0
- package/lib/types/preset-install.d.ts +34 -0
- package/lib/types/prompt.d.ts +150 -0
- package/lib/types/questions.d.ts +65 -0
- package/lib/types/screen.d.ts +204 -0
- package/lib/types/selector.d.ts +98 -0
- package/lib/types/spinner.d.ts +63 -0
- package/lib/types/startup.d.ts +31 -0
- package/lib/types/status.d.ts +94 -0
- package/lib/types/streaming.d.ts +68 -0
- package/lib/types/theme.d.ts +75 -0
- package/lib/types/transcript.d.ts +129 -0
- package/lib/types/wrap.d.ts +27 -0
- package/package.json +117 -0
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/invariant.ts
|
|
2
|
+
const PACKAGE_NAME = "codsh-bundle";
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
const name = "coding-cli-invariant";
|
|
5
|
+
/** Service required before the companion can register. */
|
|
6
|
+
const inject = ["invariants"];
|
|
7
|
+
/**
|
|
8
|
+
* No runtime invariant: the surface is a renderer and input loop over the
|
|
9
|
+
* session log whose observable contract (rendered transcript, approval
|
|
10
|
+
* decisions, exit code) is process-level and owned by the launcher e2e. It
|
|
11
|
+
* holds no durable relation of its own — every fact it shows is derived from
|
|
12
|
+
* `session/event`, whose relations `dsh-session` already audits.
|
|
13
|
+
*/
|
|
14
|
+
const install = () => {};
|
|
15
|
+
/**
|
|
16
|
+
* Register this package's invariant companion.
|
|
17
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
18
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
19
|
+
*/
|
|
20
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
package/lib/startup.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
|
|
3
|
+
|
|
4
|
+
//#region src/startup.ts
|
|
5
|
+
/** Stable Cordis plugin name. */
|
|
6
|
+
const name = "coding-cli-startup";
|
|
7
|
+
/** Services required before the invocation can be resolved. */
|
|
8
|
+
const inject = ["cmdlineArgs"];
|
|
9
|
+
/** Service provided by this plugin and injected by the terminal runner. */
|
|
10
|
+
const CODING_CLI_STARTUP_SERVICE = "codingCliStartup";
|
|
11
|
+
/**
|
|
12
|
+
* This app's command: the optional task positional, its flags, and its help text.
|
|
13
|
+
* @returns a fresh program, so one process can parse more than once (tests).
|
|
14
|
+
*/
|
|
15
|
+
function codingCliCommand() {
|
|
16
|
+
return new Command().name("dsh code").description("Work through coding tasks in an interactive terminal session.").helpOption("-h, --help", "show this help").argument("[task...]", "opening task; multiple words are joined by spaces. Omit it to start at the prompt").option("--resume <session>", "reopen a session by id and continue its conversation").option("--continue", "reopen the most recent session in this working directory").option("--preset <id>", "compose the agent from this preset instead of the roster default").option("-p, --print", "render the answer to the opening task and exit without entering the prompt").addHelpText("after", `
|
|
17
|
+
Examples:
|
|
18
|
+
dsh code start an interactive session
|
|
19
|
+
dsh code "add a slugify helper" start with an opening task
|
|
20
|
+
dsh code -p "what does this repo do?" answer once and exit
|
|
21
|
+
dsh code --continue reopen the latest session here
|
|
22
|
+
dsh code --resume session-1234 reopen one session by id
|
|
23
|
+
dsh code --preset standard compose from a different preset
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the session to reopen from the two mutually exclusive continuation flags.
|
|
28
|
+
* @param program - the parsed command, used to report a usage error.
|
|
29
|
+
* @param options - the flags commander collected.
|
|
30
|
+
* @returns the session id, `'latest'`, or the empty string.
|
|
31
|
+
*/
|
|
32
|
+
function resolveResume(program, options) {
|
|
33
|
+
if (options.continue === true && options.resume !== void 0) program.error("error: --continue and --resume are mutually exclusive");
|
|
34
|
+
if (options.continue === true) return "latest";
|
|
35
|
+
if (options.resume === void 0) return "";
|
|
36
|
+
if (options.resume.trim() === "") program.error("error: --resume needs a session id");
|
|
37
|
+
return options.resume;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse and provide this invocation as an ordinary Cordis service. On `--help`
|
|
41
|
+
* and on a usage error nothing is provided, so the runner never mounts.
|
|
42
|
+
* @param ctx - plugin context carrying the command line.
|
|
43
|
+
*/
|
|
44
|
+
function apply(ctx) {
|
|
45
|
+
const program = codingCliCommand();
|
|
46
|
+
program.action(() => {
|
|
47
|
+
const options = program.opts();
|
|
48
|
+
const task = program.args.join(" ").trim();
|
|
49
|
+
const print = options.print === true;
|
|
50
|
+
if (print && task === "") program.error("error: --print needs a task, for example: dsh code -p \"run the tests\"");
|
|
51
|
+
const preset = options.preset ?? "";
|
|
52
|
+
if (options.preset !== void 0 && preset.trim() === "") program.error("error: --preset needs a preset id");
|
|
53
|
+
ctx.provide(CODING_CLI_STARTUP_SERVICE, {
|
|
54
|
+
task,
|
|
55
|
+
resume: resolveResume(program, options),
|
|
56
|
+
preset,
|
|
57
|
+
print
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
parseCmdline(ctx, program);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { CODING_CLI_STARTUP_SERVICE, apply, inject, name };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal's approval answerer: it puts one pending tool call to the
|
|
3
|
+
* keyboard and returns the decision.
|
|
4
|
+
*
|
|
5
|
+
* `ApprovalOutcome` has no remembered grant — the vocabulary is `allowed-once`,
|
|
6
|
+
* `rejected`, `cancelled`, and `unavailable` — so "allow every call to this
|
|
7
|
+
* tool" is this surface's own state, kept here and answered as `allowed-once`
|
|
8
|
+
* without asking again.
|
|
9
|
+
* @module codsh-bundle/src/approval
|
|
10
|
+
*/
|
|
11
|
+
import type { ApprovalOutcome, ApprovalRequest } from '@deepseek-ai/dsh-user-approval';
|
|
12
|
+
import type { Theme } from './theme.ts';
|
|
13
|
+
/** One keystroke the approval prompt accepts. */
|
|
14
|
+
export type ApprovalAnswer = 'once' | 'always' | 'reject';
|
|
15
|
+
/** Reads one approval keystroke from the terminal. */
|
|
16
|
+
export interface ApprovalPrompt {
|
|
17
|
+
/**
|
|
18
|
+
* Ask the person about one pending call.
|
|
19
|
+
* @param toolName - the tool awaiting a decision.
|
|
20
|
+
* @param reason - the asker's explanation, when it supplied one.
|
|
21
|
+
* @param signal - aborts the prompt when the call is cancelled.
|
|
22
|
+
* @returns the chosen answer, or undefined when the prompt was aborted.
|
|
23
|
+
*/
|
|
24
|
+
ask(toolName: string, reason: string | undefined, signal: AbortSignal | undefined): Promise<ApprovalAnswer | undefined>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Approval state for one terminal session.
|
|
28
|
+
*
|
|
29
|
+
* The remembered set is per-process and never written to disk. An
|
|
30
|
+
* {@link ApprovalRequest} carries the tool name, reason, and call id but NOT
|
|
31
|
+
* the call arguments, so a grant cannot be narrowed to "this command" — it
|
|
32
|
+
* covers every later call to that tool. Persisting a grant that broad across
|
|
33
|
+
* runs would outlive the intent that produced it.
|
|
34
|
+
*/
|
|
35
|
+
export declare class TerminalApproval {
|
|
36
|
+
private readonly prompt;
|
|
37
|
+
private readonly theme;
|
|
38
|
+
private readonly write;
|
|
39
|
+
private readonly allowed;
|
|
40
|
+
constructor(prompt: ApprovalPrompt, theme: Theme, write: (line: string) => void);
|
|
41
|
+
/** Tool names granted for the rest of this process, in grant order. */
|
|
42
|
+
get remembered(): readonly string[];
|
|
43
|
+
/** Forget every remembered grant, so the next call of each tool asks again. */
|
|
44
|
+
clear(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Decide one request, asking the keyboard unless the tool is already granted.
|
|
47
|
+
* @param req - the pending decision.
|
|
48
|
+
* @returns the outcome for this request.
|
|
49
|
+
*/
|
|
50
|
+
decide(req: ApprovalRequest): Promise<ApprovalOutcome>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Map one keystroke to an approval answer.
|
|
54
|
+
* @param key - the character typed at the prompt.
|
|
55
|
+
* @returns the answer, or undefined when the key means nothing here.
|
|
56
|
+
*/
|
|
57
|
+
export declare function answerForKey(key: string): ApprovalAnswer | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opening banner: what answered, where, and which keys matter.
|
|
3
|
+
* @module codsh-bundle/src/banner
|
|
4
|
+
*/
|
|
5
|
+
import type { Theme } from './theme.ts';
|
|
6
|
+
/** What the banner reports about the composed session. */
|
|
7
|
+
export interface BannerFacts {
|
|
8
|
+
/** Model route answering this session. */
|
|
9
|
+
model: string;
|
|
10
|
+
/** Composed preset, absent when the deployment composes no roster. */
|
|
11
|
+
preset?: string | undefined;
|
|
12
|
+
/** Session workspace. */
|
|
13
|
+
cwd: string;
|
|
14
|
+
/** Checked-out branch, absent outside a repository. */
|
|
15
|
+
branch?: string | undefined;
|
|
16
|
+
/** Session identity, so a person can resume this exact conversation later. */
|
|
17
|
+
session: string;
|
|
18
|
+
/** Whether Escape can reach the surface; decides which interrupt is named. */
|
|
19
|
+
readsKeys: boolean;
|
|
20
|
+
/** Whether the transcript replayed a resumed conversation above the banner. */
|
|
21
|
+
resumed: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render the opening banner.
|
|
25
|
+
* @param facts - what to report.
|
|
26
|
+
* @param theme - styling for the frame and the detail lines.
|
|
27
|
+
* @param columns - display columns available; a narrow terminal loses the frame.
|
|
28
|
+
* @returns the lines to print, ending with a blank separator.
|
|
29
|
+
*/
|
|
30
|
+
export declare function bannerLines(facts: BannerFacts, theme: Theme, columns: number): string[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion sources for the prompt: slash commands, and workspace files behind
|
|
3
|
+
* an `@` mention.
|
|
4
|
+
*
|
|
5
|
+
* Bare prose never completes. A sentence headed for the model would be rewritten
|
|
6
|
+
* by a completer guessing at it, so a path has to be asked for — `@` is that
|
|
7
|
+
* request, and it is also what makes the mention legible to the model, which
|
|
8
|
+
* reads the file with its own tools.
|
|
9
|
+
*
|
|
10
|
+
* Matching is fuzzy, not prefix: the person knows a fragment of the name, not
|
|
11
|
+
* where in the path it starts. A prefix match still ranks first, so the fuzzy
|
|
12
|
+
* fallback never steals an exact intention.
|
|
13
|
+
* @module codsh-bundle/src/completion
|
|
14
|
+
*/
|
|
15
|
+
/** One completable command: its name without the slash, and what it does. */
|
|
16
|
+
export interface CompletableCommand {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}
|
|
20
|
+
/** `readline`'s completer result: the candidates, and the substring they replace. */
|
|
21
|
+
export type CompletionResult = [completions: string[], substring: string];
|
|
22
|
+
/**
|
|
23
|
+
* Score `needle` against `hay` as a fuzzy subsequence.
|
|
24
|
+
*
|
|
25
|
+
* Consecutive hits and hits on a boundary (start, or after a separator) score
|
|
26
|
+
* higher, which is what ranks `src/idx` matches the way a person expects. A
|
|
27
|
+
* needle that is not a subsequence scores nothing at all.
|
|
28
|
+
* @param needle - what was typed, matched case-insensitively.
|
|
29
|
+
* @param hay - the candidate.
|
|
30
|
+
* @returns the score, or undefined when it does not match.
|
|
31
|
+
*/
|
|
32
|
+
export declare function fuzzyScore(needle: string, hay: string): number | undefined;
|
|
33
|
+
/** Drop the cached workspace walk, so a test controls what the next Tab sees. */
|
|
34
|
+
export declare function resetFileIndex(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Build the completer for one session.
|
|
37
|
+
*
|
|
38
|
+
* The command list is read on each use rather than captured, because a command
|
|
39
|
+
* registry is scoped and changes with the session's mode — plan mode alone adds
|
|
40
|
+
* and removes one.
|
|
41
|
+
* @param commands - reads the currently registered commands.
|
|
42
|
+
* @param cwd - the workspace `@` mentions resolve against.
|
|
43
|
+
* @returns a completer over the word under the cursor.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createCompleter(commands: () => readonly CompletableCommand[], cwd: string): (line: string) => CompletionResult;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The process-facing terminal, in two shapes.
|
|
3
|
+
*
|
|
4
|
+
* On a terminal this surface owns the keyboard AND the screen: raw mode, its own
|
|
5
|
+
* key decoding, and an alternate-screen viewport ({@link Screen}) that holds the
|
|
6
|
+
* transcript in a scrollback buffer of its own with the input box pinned below
|
|
7
|
+
* it. That is what an inline completion menu, a multi-line prompt, and a session
|
|
8
|
+
* that reads as its own space require — `readline` reports no lone Escape, draws
|
|
9
|
+
* no menu, and decides for itself what Enter means.
|
|
10
|
+
*
|
|
11
|
+
* Off a terminal it is a line reader over `readline`, because a pipe has no
|
|
12
|
+
* cursor to manage and every line in a script is a separate instruction. Both
|
|
13
|
+
* shapes answer the same reads, which is what keeps piped runs and tests on the
|
|
14
|
+
* same code path as a person typing.
|
|
15
|
+
* @module codsh-bundle/src/console
|
|
16
|
+
*/
|
|
17
|
+
import type { Key } from './keys.ts';
|
|
18
|
+
/** The output stream this surface writes to. */
|
|
19
|
+
export interface OutputStream extends NodeJS.WritableStream {
|
|
20
|
+
readonly columns?: number;
|
|
21
|
+
readonly rows?: number;
|
|
22
|
+
readonly isTTY?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The input stream this surface reads from. */
|
|
25
|
+
export interface InputStream extends NodeJS.ReadableStream {
|
|
26
|
+
readonly isTTY?: boolean;
|
|
27
|
+
setRawMode?(mode: boolean): unknown;
|
|
28
|
+
}
|
|
29
|
+
/** Where the cursor belongs among a region's rows. */
|
|
30
|
+
export interface RegionCursor {
|
|
31
|
+
row: number;
|
|
32
|
+
column: number;
|
|
33
|
+
}
|
|
34
|
+
/** Line input and output over one pair of process streams. */
|
|
35
|
+
export declare class TerminalConsole {
|
|
36
|
+
private readonly input;
|
|
37
|
+
private readonly output;
|
|
38
|
+
private readonly rl;
|
|
39
|
+
private readonly decoder;
|
|
40
|
+
private readonly pending;
|
|
41
|
+
private readonly waiters;
|
|
42
|
+
private keyHandler;
|
|
43
|
+
/** Keys decoded before any handler registered — type-ahead is never dropped. */
|
|
44
|
+
private readonly earlyKeys;
|
|
45
|
+
private escapeTimer;
|
|
46
|
+
private ended;
|
|
47
|
+
/** The viewport this surface owns on a terminal; absent off one. */
|
|
48
|
+
private readonly screen;
|
|
49
|
+
constructor(input: InputStream, output: OutputStream);
|
|
50
|
+
/** Display columns available for one line, never below {@link MIN_COLUMNS}. */
|
|
51
|
+
get columns(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Columns content may be laid out for: one less than the width, because the
|
|
54
|
+
* viewport wraps at that boundary. Markdown layout MUST use this figure — a
|
|
55
|
+
* table laid out one column wider is refolded by the viewport, and its rows
|
|
56
|
+
* shear apart.
|
|
57
|
+
*/
|
|
58
|
+
get contentColumns(): number;
|
|
59
|
+
/** Whether the output stream is a terminal. */
|
|
60
|
+
get isTty(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Whether this surface owns the keyboard.
|
|
63
|
+
*
|
|
64
|
+
* That needs both streams on a terminal: raw mode is what delivers a key
|
|
65
|
+
* before its line, and there is no point managing rows on a stream with no
|
|
66
|
+
* cursor.
|
|
67
|
+
*/
|
|
68
|
+
get readsKeys(): boolean;
|
|
69
|
+
/** Whether input has finished. */
|
|
70
|
+
get finished(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Register a handler for terminal window changes.
|
|
73
|
+
* @param handler - called after each resize while registered.
|
|
74
|
+
* @returns a disposer that removes it.
|
|
75
|
+
*/
|
|
76
|
+
onResize(handler: () => void): () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Take the viewport: the transcript and the prompt live on their own screen
|
|
79
|
+
* from here, and the terminal keeps the buffer the person had.
|
|
80
|
+
*/
|
|
81
|
+
enterScreen(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Give the terminal back. Idempotent: every exit path calls it.
|
|
84
|
+
*/
|
|
85
|
+
leaveScreen(): void;
|
|
86
|
+
/** Whether this surface currently holds its own screen. */
|
|
87
|
+
get owningScreen(): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Scroll the transcript inside the viewport.
|
|
90
|
+
* @param delta - rows to move; negative goes back into history.
|
|
91
|
+
*/
|
|
92
|
+
scrollBy(delta: number): void;
|
|
93
|
+
/**
|
|
94
|
+
* Set the notice shown while the transcript is scrolled back.
|
|
95
|
+
* @param text - the styled line, or the empty string for none.
|
|
96
|
+
*/
|
|
97
|
+
setScrollNotice(text: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* Scroll the transcript by a whole viewport.
|
|
100
|
+
* @param direction - -1 for back into history, 1 towards the tail.
|
|
101
|
+
*/
|
|
102
|
+
scrollPage(direction: -1 | 1): void;
|
|
103
|
+
/** Return to the tail of the transcript. */
|
|
104
|
+
scrollToBottom(): void;
|
|
105
|
+
/** Physical rows currently scrolled out of view; zero means at the tail. */
|
|
106
|
+
get scrolledBy(): number;
|
|
107
|
+
/**
|
|
108
|
+
* Clear the transcript this session accumulated.
|
|
109
|
+
*
|
|
110
|
+
* On its own screen there is no shell scrollback to preserve, so this empties
|
|
111
|
+
* the buffer the viewport shows rather than wiping a shared terminal.
|
|
112
|
+
*/
|
|
113
|
+
clearScreen(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Route decoded keys to a handler.
|
|
116
|
+
* @param handler - receives every key while registered.
|
|
117
|
+
* @returns a disposer that removes it.
|
|
118
|
+
*/
|
|
119
|
+
onKey(handler: (key: Key) => void): () => void;
|
|
120
|
+
/**
|
|
121
|
+
* Decode one read and dispatch its keys.
|
|
122
|
+
* @param chunk - the bytes the terminal delivered.
|
|
123
|
+
*/
|
|
124
|
+
private onBytes;
|
|
125
|
+
/** Hand one key to the handler, or hold it until one registers. */
|
|
126
|
+
private deliver;
|
|
127
|
+
/** Wait briefly for the rest of a held sequence, then resolve it as Escape. */
|
|
128
|
+
private armEscapeFlush;
|
|
129
|
+
/**
|
|
130
|
+
* Hand one input line to the longest-waiting read, or queue it.
|
|
131
|
+
* @param line - the line the reader produced.
|
|
132
|
+
*/
|
|
133
|
+
private offer;
|
|
134
|
+
/** Mark input finished and release every waiting read. */
|
|
135
|
+
private end;
|
|
136
|
+
/**
|
|
137
|
+
* Keep one finished transcript line.
|
|
138
|
+
*
|
|
139
|
+
* On its own screen the line goes into the viewport's scrollback, which is
|
|
140
|
+
* what lets the transcript scroll under a prompt that does not move. Off one
|
|
141
|
+
* it is written straight out, because a pipe's reader wants exactly that.
|
|
142
|
+
* @param line - the line, without its terminator.
|
|
143
|
+
*/
|
|
144
|
+
write(line: string): void;
|
|
145
|
+
/**
|
|
146
|
+
* Replace the rows pinned below the transcript.
|
|
147
|
+
*
|
|
148
|
+
* This is the whole live area: an input box, a completion menu, a working
|
|
149
|
+
* indicator, the status row. Off a terminal the call is ignored — a
|
|
150
|
+
* redirected transcript must not collect frames of a box nobody can see.
|
|
151
|
+
* @param rows - the rows to display, top to bottom.
|
|
152
|
+
* @param cursor - where to leave the terminal cursor among them.
|
|
153
|
+
* @param focus - whether the rows hold input focus, which is when the cursor
|
|
154
|
+
* shows. Parked anywhere else it reads as content colliding with it.
|
|
155
|
+
*/
|
|
156
|
+
setRegion(rows: readonly string[], cursor: RegionCursor, focus?: boolean): void;
|
|
157
|
+
/**
|
|
158
|
+
* Keep one collapsible block: summary now, full form behind the toggle.
|
|
159
|
+
*
|
|
160
|
+
* Off a terminal only the summary is written — a pipe has no keys to toggle
|
|
161
|
+
* with, and scripts want the digest.
|
|
162
|
+
* @param summary - the collapsed lines.
|
|
163
|
+
* @param full - the expanded lines.
|
|
164
|
+
*/
|
|
165
|
+
appendFold(summary: readonly string[], full: readonly string[]): void;
|
|
166
|
+
/**
|
|
167
|
+
* Swap every collapsible block between summary and full form.
|
|
168
|
+
* @returns false when there is nothing to toggle.
|
|
169
|
+
*/
|
|
170
|
+
/**
|
|
171
|
+
* Anchor a mouse selection at a terminal position.
|
|
172
|
+
* @param row - terminal row, 1-based.
|
|
173
|
+
* @param column - terminal column, 1-based.
|
|
174
|
+
*/
|
|
175
|
+
mouseDown(row: number, column: number): void;
|
|
176
|
+
/**
|
|
177
|
+
* Extend the mouse selection to a terminal position.
|
|
178
|
+
* @param row - terminal row, 1-based.
|
|
179
|
+
* @param column - terminal column, 1-based.
|
|
180
|
+
*/
|
|
181
|
+
mouseDrag(row: number, column: number): void;
|
|
182
|
+
/**
|
|
183
|
+
* Finish the mouse selection.
|
|
184
|
+
* @returns the selected text, or undefined for a bare click.
|
|
185
|
+
*/
|
|
186
|
+
mouseUp(): string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Put text on the clipboard.
|
|
189
|
+
*
|
|
190
|
+
* Two channels, because neither is universal: OSC 52 reaches through SSH and
|
|
191
|
+
* works wherever the terminal permits it, and the platform helper covers the
|
|
192
|
+
* terminals that refuse the escape. `CODSH_CLIPBOARD` narrows it to `osc52`,
|
|
193
|
+
* `system`, or `off` — tests use `osc52` so a run never touches the real
|
|
194
|
+
* clipboard.
|
|
195
|
+
* @param text - the plain text to copy.
|
|
196
|
+
* @returns whether a copy was attempted at all.
|
|
197
|
+
*/
|
|
198
|
+
copyText(text: string): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Make the last `count` written lines a collapsible block after the fact.
|
|
201
|
+
*
|
|
202
|
+
* Screen-only on purpose: a pipe already carries the full text, and a
|
|
203
|
+
* summary would subtract from it.
|
|
204
|
+
* @param count - how many trailing lines the block owns.
|
|
205
|
+
* @param summary - the collapsed lines, already styled.
|
|
206
|
+
*/
|
|
207
|
+
foldRecent(count: number, summary: readonly string[]): void;
|
|
208
|
+
toggleFolds(): boolean;
|
|
209
|
+
/** Return every block to its summary. */
|
|
210
|
+
collapseFolds(): void;
|
|
211
|
+
/** Take the pinned rows down, leaving the transcript alone. */
|
|
212
|
+
clearRegion(): void;
|
|
213
|
+
/** Ring the terminal bell; a pipe gets nothing to beep with. */
|
|
214
|
+
bell(): void;
|
|
215
|
+
/**
|
|
216
|
+
* Set the terminal window title.
|
|
217
|
+
* @param title - the title text; control bytes are the terminal's to reject.
|
|
218
|
+
*/
|
|
219
|
+
setTitle(title: string): void;
|
|
220
|
+
/**
|
|
221
|
+
* Read one line from a piped stream.
|
|
222
|
+
*
|
|
223
|
+
* Only the non-terminal shape reads this way; with the keyboard owned, input
|
|
224
|
+
* arrives as keys and the caller drives an editor instead.
|
|
225
|
+
* @param signal - aborts the pending read.
|
|
226
|
+
* @returns the line, or undefined when input ended or the read aborted.
|
|
227
|
+
*/
|
|
228
|
+
readLine(signal?: AbortSignal): Promise<string | undefined>;
|
|
229
|
+
/** Restore the terminal and stop reading. */
|
|
230
|
+
close(): void;
|
|
231
|
+
/**
|
|
232
|
+
* Write one line to the terminal the person keeps, not to our viewport.
|
|
233
|
+
*
|
|
234
|
+
* The exit summary is what needs this: the session's own screen disappears
|
|
235
|
+
* with it, so the few facts worth keeping — the session id, what it cost —
|
|
236
|
+
* have to land in the buffer that survives.
|
|
237
|
+
* @param line - the line, without its terminator.
|
|
238
|
+
*/
|
|
239
|
+
writeAfterScreen(line: string): void;
|
|
240
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom slash commands from Markdown files.
|
|
3
|
+
*
|
|
4
|
+
* A file named `review.md` under a commands root becomes `/review`: its
|
|
5
|
+
* frontmatter `description` labels it in the completion menu, and its body is
|
|
6
|
+
* the prompt submitted when the command runs — with `$ARGUMENTS` replaced by
|
|
7
|
+
* whatever followed the name. They are canned prompts, not handlers: execution
|
|
8
|
+
* goes through the same submission path as a typed message.
|
|
9
|
+
* @module codsh-bundle/src/custom-commands
|
|
10
|
+
*/
|
|
11
|
+
/** One loaded command. */
|
|
12
|
+
export interface CustomCommand {
|
|
13
|
+
/** The name typed after the slash. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Menu label, from frontmatter or a fallback naming the file. */
|
|
16
|
+
description: string;
|
|
17
|
+
/** The prompt body, with `$ARGUMENTS` placeholders intact. */
|
|
18
|
+
template: string;
|
|
19
|
+
}
|
|
20
|
+
/** What loading produced: the commands, and what was skipped and why. */
|
|
21
|
+
export interface CustomCommandLoad {
|
|
22
|
+
commands: CustomCommand[];
|
|
23
|
+
/** One line per skipped file, for the surface to show once at startup. */
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse one command file: optional `---` frontmatter with a `description`
|
|
28
|
+
* line, then the prompt body.
|
|
29
|
+
* @param source - the file content.
|
|
30
|
+
* @returns the description (empty when absent) and the body.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseCommandFile(source: string): {
|
|
33
|
+
description: string;
|
|
34
|
+
body: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Fill a template with the typed arguments.
|
|
38
|
+
* @param template - the prompt body.
|
|
39
|
+
* @param typed - what followed the command name, possibly empty.
|
|
40
|
+
* @returns the prompt to submit: placeholders replaced, or the arguments
|
|
41
|
+
* appended when the template never asked for them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function expandTemplate(template: string, typed: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Load every command under the given roots, later roots shadowing earlier.
|
|
46
|
+
*
|
|
47
|
+
* A missing root is normal (most setups define no custom commands); an
|
|
48
|
+
* unreadable or misnamed file is a warning, never a failed startup — a broken
|
|
49
|
+
* canned prompt should cost that prompt, not the session.
|
|
50
|
+
* @param roots - directories to scan, lowest precedence first.
|
|
51
|
+
* @param taken - names already registered, which a file cannot shadow.
|
|
52
|
+
* @returns the loaded commands and the warnings to show once.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loadCustomCommands(roots: readonly string[], taken: ReadonlySet<string>): Promise<CustomCommandLoad>;
|