@aragon-agent/cli 0.6.25 → 0.6.27
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/CHANGELOG.md +104 -0
- package/README.md +194 -13
- package/dist/agent/reducer.d.ts +22 -0
- package/dist/agent/reducer.js +4 -1
- package/dist/cli.js +214 -2
- package/dist/commands/branch-command.d.ts +61 -0
- package/dist/commands/branch-command.js +117 -0
- package/dist/commands/builtins.js +48 -6
- package/dist/commands/registry.d.ts +28 -0
- package/dist/commands/session-command.d.ts +54 -0
- package/dist/commands/session-command.js +109 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/exec/index.js +10 -1
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/terminal-plan.d.ts +173 -0
- package/dist/proc/terminal-plan.js +393 -0
- package/dist/session/branch-invocation.d.ts +127 -0
- package/dist/session/branch-invocation.js +153 -0
- package/dist/session/branch-launch.d.ts +79 -0
- package/dist/session/branch-launch.js +265 -0
- package/dist/session/checkpoint.d.ts +257 -0
- package/dist/session/checkpoint.js +512 -0
- package/dist/session/cli-commands.d.ts +2 -0
- package/dist/session/cli-commands.js +36 -5
- package/dist/session/identity.d.ts +72 -0
- package/dist/session/identity.js +148 -0
- package/dist/session/limits.d.ts +66 -0
- package/dist/session/limits.js +66 -0
- package/dist/session/persist.d.ts +30 -5
- package/dist/session/startup-session.d.ts +107 -0
- package/dist/session/startup-session.js +315 -0
- package/dist/session/store.d.ts +60 -1
- package/dist/session/store.js +95 -6
- package/dist/ui/App.d.ts +54 -2
- package/dist/ui/App.js +115 -21
- package/dist/ui/Header.js +8 -16
- package/dist/ui/StatusBar.d.ts +8 -0
- package/dist/ui/StatusBar.js +12 -3
- package/dist/ui/layout/header-hints.d.ts +68 -0
- package/dist/ui/layout/header-hints.js +138 -0
- package/dist/ui/layout/session-chip.d.ts +52 -0
- package/dist/ui/layout/session-chip.js +64 -0
- package/dist/ui/layout/status-layout.d.ts +10 -0
- package/dist/ui/layout/status-layout.js +8 -1
- package/dist/ui/overlays/HelpOverlay.js +9 -3
- package/dist/ui/session-bridge.d.ts +26 -0
- package/dist/ui/session-bridge.js +32 -0
- package/package.json +2 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/session` - report this session's identity, and the two things a user does
|
|
3
|
+
* with it (tui-session-id-resume §3.11 / D-13 / D-14).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY - `src/commands/**` is inside `glyphs.test.ts::inScope`.
|
|
6
|
+
*
|
|
7
|
+
* WHY THE BODY LIVES HERE AND THE REGISTRATION STAYS IN `builtins.ts`: the same
|
|
8
|
+
* reason `/clear` and `/compact` do. `builtins.ts` is already past the
|
|
9
|
+
* 1000-line guideline, and this command is a report, not a one-liner.
|
|
10
|
+
*
|
|
11
|
+
* COPYING THE RESUME COMMAND IS THE POINT, NOT A CONVENIENCE (D-14). Typing 6
|
|
12
|
+
* characters off a status bar is the only step of this whole feature that can
|
|
13
|
+
* fail for a human, and `/session copy` removes it: one keystroke, the exact
|
|
14
|
+
* command in the clipboard, no transcription.
|
|
15
|
+
*/
|
|
16
|
+
import { sessionPathFor } from '../session/store.js';
|
|
17
|
+
/**
|
|
18
|
+
* The one path an id maps to.
|
|
19
|
+
*
|
|
20
|
+
* THROUGH `sessionPathFor` RATHER THAN A SECOND `join` (§2.2): that function is
|
|
21
|
+
* the single choke point where an id becomes a file name, and a report that
|
|
22
|
+
* built its own path would be the first thing to disagree with the writer about
|
|
23
|
+
* where a session lives.
|
|
24
|
+
*/
|
|
25
|
+
export function sessionFilePath(id) {
|
|
26
|
+
return sessionPathFor(id);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run `/session`, `/session copy` or `/session pin`.
|
|
30
|
+
*
|
|
31
|
+
* ONE NOTICE FOR THE WHOLE REPORT (§3.11 / R-20): six separate notices would put
|
|
32
|
+
* six cards in the transcript and replay them as six fragments on exit. The
|
|
33
|
+
* `file` line is NOT truncated even when it overflows: it is a path the user
|
|
34
|
+
* needs to be able to copy, and it is in the same family as `/save`'s
|
|
35
|
+
* `Saved session to <path>`.
|
|
36
|
+
*/
|
|
37
|
+
export function runSessionCommand(ctx) {
|
|
38
|
+
const port = ctx.session;
|
|
39
|
+
if (!port) {
|
|
40
|
+
// HONEST RATHER THAN SILENT: a build without the feature should say so,
|
|
41
|
+
// because "nothing happened" reads as a bug.
|
|
42
|
+
ctx.notify('warn', 'This session has no id (session recording is off for this build).');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const verb = ctx.args.trim().toLowerCase();
|
|
46
|
+
const identity = port.identity();
|
|
47
|
+
if (!identity) {
|
|
48
|
+
ctx.notify('warn', 'This session has no id (session recording is off for this build).');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (verb === 'copy') {
|
|
52
|
+
copyCommand(ctx, identity);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (verb === 'pin') {
|
|
56
|
+
pinSession(ctx, port, identity);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (verb !== '') {
|
|
60
|
+
// Rejected rather than ignored: a command that silently discards an argument
|
|
61
|
+
// is a surface that lies about what it understood.
|
|
62
|
+
ctx.notify('warn', `Unknown argument "${ctx.args.trim()}" - use /session [copy|pin].`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
report(ctx, identity);
|
|
66
|
+
}
|
|
67
|
+
/** The six-line report. Pure read: no checkpoint, no toast, no dispatch. */
|
|
68
|
+
function report(ctx, identity) {
|
|
69
|
+
const resume = `aragon --resume ${identity.id}`;
|
|
70
|
+
const lines = [
|
|
71
|
+
`id ${identity.id}`,
|
|
72
|
+
`file ${sessionFilePath(identity.id)}`,
|
|
73
|
+
identity.parentId
|
|
74
|
+
? `branch ${identity.parentId} (this session does not write to it)`
|
|
75
|
+
: 'branch (none - this session started here)',
|
|
76
|
+
`pinned ${identity.pinned ? 'yes' : 'no'}`,
|
|
77
|
+
`resume ${resume}`,
|
|
78
|
+
'save as aragon --save <new-id>',
|
|
79
|
+
];
|
|
80
|
+
ctx.notify('info', lines.join('\n'));
|
|
81
|
+
}
|
|
82
|
+
/** `/session copy` - the exact command, through the SAME door `/copy` uses. */
|
|
83
|
+
function copyCommand(ctx, identity) {
|
|
84
|
+
const text = `aragon --resume ${identity.id}`;
|
|
85
|
+
if (!ctx.requestCopy) {
|
|
86
|
+
ctx.notify('warn', 'Clipboard coordinator unavailable.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
void ctx.requestCopy({ text, lines: 1 }).then((result) => {
|
|
90
|
+
if (result.status === 'confirmed' || result.status === 'sent') {
|
|
91
|
+
ctx.toast('info', `Copied ${text}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// A failed or refused copy must not look like a successful one: the command
|
|
95
|
+
// is short enough to read off the report above, so naming the failure is
|
|
96
|
+
// enough to recover.
|
|
97
|
+
ctx.notify('warn', result.status === 'busy' ? 'Clipboard is busy - try /session copy again.' :
|
|
98
|
+
`Copy failed: ${result.reason}`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** `/session pin` - write once and stop `sessions prune` from ever touching it. */
|
|
102
|
+
function pinSession(ctx, port, identity) {
|
|
103
|
+
const result = port.checkpoint({ pin: true });
|
|
104
|
+
if (!result.ok) {
|
|
105
|
+
ctx.notify('warn', `Could not pin this session: ${result.message}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
ctx.toast('info', `Session ${identity.id} pinned: aragon sessions prune will not delete it.`);
|
|
109
|
+
}
|
package/dist/config/load.d.ts
CHANGED
|
@@ -228,6 +228,21 @@ export interface CliFlags {
|
|
|
228
228
|
compaction?: boolean;
|
|
229
229
|
/** `--compaction-threshold <0..1|N%>` — session override for `threshold`. */
|
|
230
230
|
compactionThreshold?: string;
|
|
231
|
+
/**
|
|
232
|
+
* `--resume <id|path>` - continue a saved session under a NEW id
|
|
233
|
+
* (tui-session-id-resume 4).
|
|
234
|
+
*
|
|
235
|
+
* A FLAG FIELD AND NOT A CONFIG KEY (D-19). There is no persisted "always
|
|
236
|
+
* resume this session", and no switch that shows an id without writing it:
|
|
237
|
+
* that state - an id a user can copy and cannot use - is worse than having no
|
|
238
|
+
* id at all.
|
|
239
|
+
*
|
|
240
|
+
* Carried through `toFlags` like every other flag; a field that reaches only
|
|
241
|
+
* `CliFlags` is inert with no compile error and no runtime error.
|
|
242
|
+
*/
|
|
243
|
+
resumeSession?: string;
|
|
244
|
+
/** `--save <id>` - name THIS run's persistent session and pin it. */
|
|
245
|
+
saveSessionAs?: string;
|
|
231
246
|
}
|
|
232
247
|
/**
|
|
233
248
|
* Resolve the effective config by merging all layers, then derive the runtime
|
package/dist/exec/index.js
CHANGED
|
@@ -418,7 +418,16 @@ function planSession(options, cwd, err) {
|
|
|
418
418
|
if (options.sessionId !== undefined) {
|
|
419
419
|
// THE UPSERT. A wrapper that mints its own id must not have to ask whether
|
|
420
420
|
// this is the first turn; that is the whole reason this form exists.
|
|
421
|
-
|
|
421
|
+
//
|
|
422
|
+
// AND IT IS A RESUME FORM, so the same write-back rule applies (tui-session-
|
|
423
|
+
// id-resume D-20 / I-3). When a file already exists under that id,
|
|
424
|
+
// `resumedSessionId` decides which id this run writes - and for a TUI
|
|
425
|
+
// checkpoint that answer is a NEW uuid. Without this line the wrapper-friendly
|
|
426
|
+
// upsert was the one remaining way to reach a TUI checkpoint and replace it,
|
|
427
|
+
// which is silent, unrecoverable loss of the user's live conversation.
|
|
428
|
+
const path = sessionPathFor(options.sessionId);
|
|
429
|
+
const saved = readSessionFile(path);
|
|
430
|
+
return { id: saved ? resumedSessionId(path, saved) : options.sessionId, saved, saving, cwd };
|
|
422
431
|
}
|
|
423
432
|
return { id: randomUUID(), saved: null, saving, cwd };
|
|
424
433
|
}
|
package/dist/logging/logger.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import type { LogConfig } from '../config/schema.js';
|
|
24
24
|
import { type LogLevelName } from './levels.js';
|
|
25
25
|
/** Closed set, not free text: a typo'd scope is a record nobody can filter on. */
|
|
26
|
-
export type LogScope = 'cli' | 'config' | 'agent' | 'tool' | 'skills' | 'llm' | 'migrate' | 'log' | 'fast' | 'history' | 'update' | 'compaction';
|
|
26
|
+
export type LogScope = 'cli' | 'config' | 'agent' | 'tool' | 'skills' | 'llm' | 'migrate' | 'log' | 'fast' | 'history' | 'update' | 'compaction' | 'session' | 'branch';
|
|
27
27
|
export interface LogRecord {
|
|
28
28
|
ts: string;
|
|
29
29
|
lv: LogLevelName;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a new terminal window looks like from the outside (tui-branch-terminal-shortcut
|
|
3
|
+
* 3.7).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY - `src/proc/**` is inside `glyphs.test.ts::inScope`.
|
|
6
|
+
*
|
|
7
|
+
* PURE, AND THAT IS THE POINT. Three platforms, at least ten terminal emulators,
|
|
8
|
+
* four exec-argument conventions and Windows' three layers of parsing (we -> `cmd`
|
|
9
|
+
* -> `PowerShell`) all collapse into "what should we spawn, with which argv". A
|
|
10
|
+
* command body that called `spawn('cmd', ['/c','start', ...])` inline would bury
|
|
11
|
+
* the only testable part of this feature - the command line itself - inside
|
|
12
|
+
* untestable IO. `proc/readiness.ts` and `proc/classify.ts` set the same precedent
|
|
13
|
+
* inside this directory.
|
|
14
|
+
*
|
|
15
|
+
* WHY SESSION CODE MAY DEPEND ON THIS MODULE: it imports nothing from
|
|
16
|
+
* `node:child_process`, reads no configuration and creates no process. The single
|
|
17
|
+
* place a process is created stays in `session/branch-launch.ts`, so `proc/` keeps
|
|
18
|
+
* meaning "processes" and `session/` keeps meaning "sessions".
|
|
19
|
+
*
|
|
20
|
+
* EVERY RUNG CARRIES ITS OWN WORKING DIRECTORY (D-16 / D-23 / I-9). This is the
|
|
21
|
+
* one thing a reviewer will assume is free and is not: `spawn({ cwd })` decides the
|
|
22
|
+
* cwd of the process WE start, and for `wt -w 0 nt` (a new tab in an already
|
|
23
|
+
* running Windows Terminal) or `osascript` (a new Terminal.app window) that
|
|
24
|
+
* process merely forwards the request - the window opens in the terminal's own
|
|
25
|
+
* default directory. So `wt` passes `-d`, and both script rungs start with
|
|
26
|
+
* `cd <cwd> &&`. `--resume` does not restore a cwd, so a window that opens in the
|
|
27
|
+
* wrong place can never be corrected from inside the branch.
|
|
28
|
+
*/
|
|
29
|
+
/** The three ladders, collapsed from `NodeJS.Platform` (win32 / darwin / everything else). */
|
|
30
|
+
export type TerminalPlatform = 'win32' | 'darwin' | 'posix';
|
|
31
|
+
export interface ShellInvocation {
|
|
32
|
+
/** Executable: an absolute path or a name resolved through PATH. */
|
|
33
|
+
path: string;
|
|
34
|
+
/** Which command syntax the aragon command has to be encoded for. */
|
|
35
|
+
kind: 'powershell' | 'posix';
|
|
36
|
+
}
|
|
37
|
+
export interface TerminalLaunchPlan {
|
|
38
|
+
/** The program we spawn. */
|
|
39
|
+
file: string;
|
|
40
|
+
args: string[];
|
|
41
|
+
/** True only for the `cmd` rung, whose command line `cmd.exe` must see verbatim. */
|
|
42
|
+
windowsVerbatimArguments: boolean;
|
|
43
|
+
/** Ladder name, for the log and for the success toast's debug detail. */
|
|
44
|
+
rung: string;
|
|
45
|
+
}
|
|
46
|
+
export type TerminalPlanResult = {
|
|
47
|
+
ok: true;
|
|
48
|
+
plans: TerminalLaunchPlan[];
|
|
49
|
+
shell: ShellInvocation;
|
|
50
|
+
manual: string;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
reason: 'no-launcher';
|
|
54
|
+
shell: ShellInvocation;
|
|
55
|
+
manual: string;
|
|
56
|
+
};
|
|
57
|
+
export interface TerminalPlanInput {
|
|
58
|
+
platform: NodeJS.Platform;
|
|
59
|
+
env: Record<string, string | undefined>;
|
|
60
|
+
pathDirs: readonly string[];
|
|
61
|
+
isExecutable?: (name: string) => boolean;
|
|
62
|
+
shell: ShellInvocation;
|
|
63
|
+
/** The aragon command's argv pieces (`session/branch-invocation.ts`'s product). */
|
|
64
|
+
command: readonly string[];
|
|
65
|
+
/** New window title, ASCII, already truncated to the cell budget. */
|
|
66
|
+
title: string;
|
|
67
|
+
cwd: string;
|
|
68
|
+
/**
|
|
69
|
+
* `renderManualCommand(command, platform)`'s product: two lines on win32
|
|
70
|
+
* (`PowerShell:` / `cmd.exe :`), one line everywhere else.
|
|
71
|
+
*/
|
|
72
|
+
manual: string;
|
|
73
|
+
}
|
|
74
|
+
/** The env var that overrides the shell the new window runs (D-7). */
|
|
75
|
+
export declare const ARAGON_BRANCH_SHELL_ENV = "ARAGON_BRANCH_SHELL";
|
|
76
|
+
/**
|
|
77
|
+
* Build the ladder for this platform, most-preferred rung first.
|
|
78
|
+
*
|
|
79
|
+
* The return value is a LIST because the failures this feature can actually hit
|
|
80
|
+
* are synchronous argument rejections (ENOENT on `wt.exe`, no X display), and a
|
|
81
|
+
* list lets `branch-launch.ts` walk down it without knowing anything about
|
|
82
|
+
* terminals. Rung COUNT is bounded by the caller (`BRANCH_TERMINAL_LIMITS.maxRungs`),
|
|
83
|
+
* so there is exactly one place that number lives.
|
|
84
|
+
*/
|
|
85
|
+
export declare function planTerminalLaunch(input: TerminalPlanInput): TerminalPlanResult;
|
|
86
|
+
/**
|
|
87
|
+
* The PowerShell statement that runs the aragon command (D-1).
|
|
88
|
+
*
|
|
89
|
+
* THE LEADING `&` IS NOT DECORATION. In command position PowerShell reads a quoted
|
|
90
|
+
* string as an EXPRESSION, so `'C:\Program Files\nodejs\node.exe' <script>` is a
|
|
91
|
+
* parse error - "unexpected token ..." at column 36 - and the child is never
|
|
92
|
+
* created, while the parent still believes the window opened (K-1: `wt`/`start`
|
|
93
|
+
* forward and exit). That is exactly the report this branch exists to fix.
|
|
94
|
+
* `&` is the call operator: it takes the following word as a command name whatever
|
|
95
|
+
* its shape, and it does not change name resolution (`& 'npm.cmd'` still finds
|
|
96
|
+
* `npm` through PATH/PATHEXT, appendix B-8).
|
|
97
|
+
*
|
|
98
|
+
* `posixScript` MUST NOT be given the same treatment: in sh `&` means run in the
|
|
99
|
+
* background, so the window would reach the trailing `exec <shell> -i` immediately
|
|
100
|
+
* and the branch would start behind a prompt that looks idle (I-3 / K-1).
|
|
101
|
+
*/
|
|
102
|
+
export declare function powerShellScript(parts: readonly string[]): string;
|
|
103
|
+
/** Bare words stay bare; everything else becomes a single-quoted literal. */
|
|
104
|
+
export declare function powerShellQuote(value: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* `-EncodedCommand` takes base64 of UTF-16LE, which needs no shell quoting at all.
|
|
107
|
+
*
|
|
108
|
+
* The alphabet is `A-Za-z0-9+/=` - no spaces, no quotes, no `cmd` metacharacters -
|
|
109
|
+
* so the blob survives all three of Windows' parsers as an ordinary word. This is
|
|
110
|
+
* the same trick `ui/win-vt-force.ts` uses, for the same reason: encode the fragile
|
|
111
|
+
* thing into a form nothing can argue with, at construction time. Our command line
|
|
112
|
+
* necessarily contains spaced paths (`C:\Program Files\nodejs\node.exe`), so the
|
|
113
|
+
* fragile thing is not hypothetical.
|
|
114
|
+
*/
|
|
115
|
+
export declare function encodePowerShellCommand(script: string): string;
|
|
116
|
+
/** POSIX single-quote escaping: `'` becomes `'\''`. */
|
|
117
|
+
export declare function posixQuote(value: string): string;
|
|
118
|
+
export declare function posixScript(parts: readonly string[]): string;
|
|
119
|
+
/** AppleScript string literals only need `\` and `"` escaped. */
|
|
120
|
+
export declare function escapeAppleScriptText(value: string): string;
|
|
121
|
+
/**
|
|
122
|
+
* Win32 argv quoting for the `cmd` rung's title and shell path: quote when it
|
|
123
|
+
* contains whitespace, otherwise leave it alone.
|
|
124
|
+
*
|
|
125
|
+
* THE INPUT DOMAIN EXCLUDES `"` BY CONSTRUCTION (R-17/T-48): Windows paths cannot
|
|
126
|
+
* contain it, and the title is `aragon <id>` where an id is `[A-Za-z0-9._-]`. If a
|
|
127
|
+
* future source can contain a quote, this must switch to `cmd`'s rule (`""`) and
|
|
128
|
+
* not to the POSIX `\"` written here - the two are not interchangeable.
|
|
129
|
+
*/
|
|
130
|
+
export declare function quoteWin32Arg(value: string): string;
|
|
131
|
+
/** PATH entries for `name` probing: both spellings on Windows, `:` on POSIX. */
|
|
132
|
+
export declare function pathDirsOf(env: Record<string, string | undefined>, platform: NodeJS.Platform): string[];
|
|
133
|
+
/**
|
|
134
|
+
* The default probe: is this a file we could execute?
|
|
135
|
+
*
|
|
136
|
+
* An absolute or separator-bearing value is stat'd directly (that is the
|
|
137
|
+
* `ARAGON_BRANCH_SHELL=/usr/local/bin/fish` case); a bare name is looked up in the
|
|
138
|
+
* PATH directories, trying `name` and - on Windows - `name + '.exe'`, because a
|
|
139
|
+
* user typing `pwsh` and a probe looking for `pwsh.exe` are the same request.
|
|
140
|
+
*
|
|
141
|
+
* EVERY EXCEPTION RETURNS FALSE: one failed stat means "this candidate is not
|
|
142
|
+
* here", not "the feature is broken", and a missing terminal must degrade to the
|
|
143
|
+
* manual command rather than throw out of a key handler.
|
|
144
|
+
*/
|
|
145
|
+
export declare function defaultIsExecutable(platform: NodeJS.Platform, pathDirs?: readonly string[]): (name: string) => boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Which shell the new window runs (D-7), first match wins.
|
|
148
|
+
*
|
|
149
|
+
* The requirement says "PowerShell / bash", and both platforms have two answers
|
|
150
|
+
* worth distinguishing: PowerShell 7 (`pwsh.exe`) when the user installed it,
|
|
151
|
+
* Windows PowerShell 5.1 otherwise; `$SHELL` when the user's login shell is not
|
|
152
|
+
* bash, then bash, then `/bin/sh` as the one path that exists everywhere.
|
|
153
|
+
* `ARAGON_BRANCH_SHELL` is the escape hatch - an ENV VALUE, not a config key
|
|
154
|
+
* (D-17): a "Ctrl+L opens no terminal" switch would create the one state this
|
|
155
|
+
* feature must never have.
|
|
156
|
+
*/
|
|
157
|
+
export declare function resolveShell(input: {
|
|
158
|
+
platform: NodeJS.Platform;
|
|
159
|
+
env: Record<string, string | undefined>;
|
|
160
|
+
isExecutable?: (name: string) => boolean;
|
|
161
|
+
pathDirs: readonly string[];
|
|
162
|
+
}): ShellInvocation;
|
|
163
|
+
/**
|
|
164
|
+
* Fit a window title into `maxCells` columns (R-7 / T-47).
|
|
165
|
+
*
|
|
166
|
+
* THE TITLE IS THE ONLY THING THIS TOUCHES. An id can be 64 characters long, so
|
|
167
|
+
* `aragon <id>` exceeds the 60-cell budget by construction for the longest ids -
|
|
168
|
+
* and truncating the id anywhere else (the command line, `--resume`) would produce
|
|
169
|
+
* a branch that cannot find its own parent. Code points, not bytes: the title is
|
|
170
|
+
* ASCII today, and a future non-ASCII title must not be cut through a surrogate
|
|
171
|
+
* pair.
|
|
172
|
+
*/
|
|
173
|
+
export declare function truncateTitle(title: string, maxCells: number): string;
|