@deepseek-harness-tui/dsh-tui 0.8.6 → 0.8.8
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/README.md +15 -5
- package/bin/dsh-tui.js +269 -218
- package/cordis.patch.yml +48 -22
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +14 -13
- package/lib/types/cc/syntaxRuns.d.ts +16 -0
- package/lib/types/cc/syntaxRuns.d.ts.map +1 -0
- package/lib/types/cc/syntaxRuns.js +79 -0
- package/lib/types/commands.d.ts +14 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +20 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +19 -0
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -0
- package/lib/types/components/Chat/SubagentMessage.js +75 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
- package/lib/types/components/EffortInputBorder.js +2 -2
- package/lib/types/components/EffortTierBadge.js +1 -1
- package/lib/types/components/FileSuggestions.d.ts +8 -4
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +22 -12
- package/lib/types/components/HelpMenu.d.ts +8 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +15 -4
- package/lib/types/components/LogoV2.d.ts +7 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +13 -4
- package/lib/types/components/MessageList.d.ts +6 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +17 -6
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +100 -36
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SplitDiffView.d.ts +1 -13
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +6 -105
- package/lib/types/components/SubagentCard.d.ts +9 -0
- package/lib/types/components/SubagentCard.d.ts.map +1 -0
- package/lib/types/components/SubagentCard.js +26 -0
- package/lib/types/components/SubagentDashboard.d.ts +13 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -0
- package/lib/types/components/SubagentDashboard.js +50 -0
- package/lib/types/components/SubagentDetailScene.d.ts +16 -0
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -0
- package/lib/types/components/SubagentDetailScene.js +141 -0
- package/lib/types/components/SyntaxText.d.ts +15 -0
- package/lib/types/components/SyntaxText.d.ts.map +1 -0
- package/lib/types/components/SyntaxText.js +34 -0
- package/lib/types/components/ThinkingToggle.d.ts +3 -10
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +4 -9
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +18 -7
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +42 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +15 -2
- package/lib/types/dsh-adapter/channel.d.ts +64 -3
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +387 -37
- package/lib/types/dsh-adapter/contract.d.ts +67 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +149 -15
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +6 -6
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +38 -0
- package/lib/types/dsh-adapter/shortcuts.js +3 -3
- package/lib/types/dsh-adapter/subagents.d.ts +87 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/subagents.js +204 -0
- package/lib/types/herdr.d.ts +28 -0
- package/lib/types/herdr.d.ts.map +1 -0
- package/lib/types/herdr.js +133 -0
- package/lib/types/i18n.d.ts +233 -32
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +87 -16
- package/lib/types/ink/ink.d.ts +4 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +84 -38
- package/lib/types/ink/render-node-to-output.d.ts +6 -3
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +33 -7
- package/lib/types/ink/selection.d.ts +29 -4
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +41 -5
- package/lib/types/minimalMode.d.ts +13 -0
- package/lib/types/minimalMode.d.ts.map +1 -0
- package/lib/types/minimalMode.js +17 -0
- package/lib/types/screens/Chat.d.ts +1 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +94 -43
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +6 -2
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +4 -2
- package/lib/types/theme.d.ts +8 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +38 -14
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +25 -7
- package/lib/types/update.d.ts +18 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +73 -2
- package/lib/types/utils/execFileNoThrow.d.ts +3 -20
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +53 -32
- package/lib/types/utils/externalEditor.d.ts +12 -11
- package/lib/types/utils/externalEditor.d.ts.map +1 -1
- package/lib/types/utils/externalEditor.js +11 -11
- package/lib/types/utils/fileSuggestions.d.ts +19 -0
- package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
- package/lib/types/utils/fileSuggestions.js +40 -0
- package/lib/types/utils/mentions.d.ts.map +1 -1
- package/lib/types/utils/mentions.js +3 -1
- package/package.json +81 -74
package/lib/types/update.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { gte, gt, lt, valid } from 'semver';
|
|
7
7
|
import { shellQuote } from './utils/shellQuote.js';
|
|
@@ -225,6 +225,70 @@ export function tuiUpdatePluginArgs(profile, targetVersion) {
|
|
|
225
225
|
export function isTransientUpdateFailure(stderr) {
|
|
226
226
|
return /ENOENT|EPERM|EBUSY/i.test(stderr) && /_tmp_\d+/i.test(stderr);
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Best-effort migrate the GLOBAL launcher to the delegating shim (0.8.7):
|
|
230
|
+
* after a successful profile update, copy this package's `bin/dsh-tui.js`
|
|
231
|
+
* and `package.json` over the global install so the launcher can never lag
|
|
232
|
+
* the profile again — the shim delegates all logic to the profile copy it
|
|
233
|
+
* just updated. Single-file-safe by contract: the new bin imports nothing
|
|
234
|
+
* from lib/ (see its header), so overwriting it inside an older global
|
|
235
|
+
* install cannot dangle a missing helper.
|
|
236
|
+
*
|
|
237
|
+
* Locating the global dir relies on argv[1] being the global `dsh-tui.js`
|
|
238
|
+
* (true when booted through the `dsh-tui` command). Source checkouts and
|
|
239
|
+
* direct `dsh --profile` boots resolve nothing — the migration is a silent
|
|
240
|
+
* no-op there. Write failures (permissions, locked files) are equally
|
|
241
|
+
* silent: the launcher-alignment warning remains the fallback diagnosis.
|
|
242
|
+
*
|
|
243
|
+
* @returns true when the global launcher files were replaced.
|
|
244
|
+
*/
|
|
245
|
+
export function migrateGlobalLauncher() {
|
|
246
|
+
const launcherBin = process.argv[1];
|
|
247
|
+
if (launcherBin === undefined || !launcherBin.endsWith('dsh-tui.js'))
|
|
248
|
+
return false;
|
|
249
|
+
// Walk up from the bin to the containing package; accept it only when it
|
|
250
|
+
// is OUR package and not the profile copy we are running from (junction
|
|
251
|
+
// layouts collapse both onto the same real path — copying onto ourselves
|
|
252
|
+
// would be a no-op at best).
|
|
253
|
+
let dir = dirname(resolve(launcherBin));
|
|
254
|
+
const ownDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
255
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
256
|
+
const manifest = join(dir, 'package.json');
|
|
257
|
+
try {
|
|
258
|
+
const parsed = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
259
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
260
|
+
&& parsed.name === PACKAGE_NAME) {
|
|
261
|
+
let same = false;
|
|
262
|
+
try {
|
|
263
|
+
same = realpathSync(dir) === realpathSync(ownDir);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
same = resolve(dir) === resolve(ownDir);
|
|
267
|
+
}
|
|
268
|
+
if (same)
|
|
269
|
+
return false;
|
|
270
|
+
// tmp + rename keeps each file atomic; a crash mid-migration leaves
|
|
271
|
+
// either the old or the new file, never a truncated one.
|
|
272
|
+
const replace = (target, source) => {
|
|
273
|
+
const staged = `${target}.dsh-tui-migrate`;
|
|
274
|
+
writeFileSync(staged, readFileSync(source));
|
|
275
|
+
renameSync(staged, target);
|
|
276
|
+
};
|
|
277
|
+
replace(join(dir, 'bin', 'dsh-tui.js'), join(ownDir, 'bin', 'dsh-tui.js'));
|
|
278
|
+
replace(manifest, join(ownDir, 'package.json'));
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Unreadable manifest at this level — keep walking up.
|
|
284
|
+
}
|
|
285
|
+
const parent = dirname(dir);
|
|
286
|
+
if (parent === dir)
|
|
287
|
+
return false;
|
|
288
|
+
dir = parent;
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
228
292
|
/**
|
|
229
293
|
* Update the installed dsh-tui package and restart the same launcher while
|
|
230
294
|
* preserving the active session. The TUI must already be unmounted before
|
|
@@ -297,6 +361,13 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
|
|
|
297
361
|
return { updateCode: 1, restartCode: 1 };
|
|
298
362
|
}
|
|
299
363
|
}
|
|
364
|
+
// Launcher migration (0.8.7): the freshly installed profile carries the
|
|
365
|
+
// delegating shim — stamp it over the global launcher so this is the LAST
|
|
366
|
+
// time the outer copy can lag. Best-effort; the alignment warning stays as
|
|
367
|
+
// the fallback when the copy is impossible.
|
|
368
|
+
if (migrateGlobalLauncher()) {
|
|
369
|
+
process.stderr.write('dsh-tui: global launcher aligned to the delegating shim (no manual npm i -g needed anymore).\n');
|
|
370
|
+
}
|
|
300
371
|
const restartCode = await runProcess(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
301
372
|
env: {
|
|
302
373
|
...process.env,
|
|
@@ -4,31 +4,14 @@ export interface ExecFileNoThrowResult {
|
|
|
4
4
|
stdout: string;
|
|
5
5
|
stderr: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
* working directory.
|
|
10
|
-
*/
|
|
7
|
+
/** Options for {@link execFileNoThrow}: stdin payload, timeout, and child
|
|
8
|
+
* working directory. */
|
|
11
9
|
export interface ExecFileNoThrowOptions {
|
|
12
|
-
/** Stdin payload (OSC 52 clipboard helpers and tmux load-buffer). */
|
|
13
10
|
input?: string;
|
|
14
|
-
/** Kill the child after this many milliseconds. */
|
|
15
11
|
timeout?: number;
|
|
16
|
-
/**
|
|
17
|
-
* Accepted for call-site compatibility with the original execa-based
|
|
18
|
-
* implementation; `false` (the only value the Ink core passes) matches this
|
|
19
|
-
* shim's behavior of running in the current working directory.
|
|
20
|
-
*/
|
|
21
12
|
useCwd?: boolean;
|
|
22
|
-
/** Working directory for the child; defaults to the parent's cwd. */
|
|
23
13
|
cwd?: string;
|
|
24
14
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Run a command without throwing: resolves with `{ code, stdout, stderr }`
|
|
27
|
-
* even when the process exits non-zero or cannot spawn.
|
|
28
|
-
* @param file - The executable path to spawn.
|
|
29
|
-
* @param args - Command-line arguments; defaults to none.
|
|
30
|
-
* @param options - Spawn options (input, timeout, cwd).
|
|
31
|
-
* @returns The process outcome: exit code, captured stdout, and captured stderr.
|
|
32
|
-
*/
|
|
15
|
+
/** Run a command without throwing; child output is bounded for safety. */
|
|
33
16
|
export declare function execFileNoThrow(file: string, args?: readonly string[], options?: ExecFileNoThrowOptions): Promise<ExecFileNoThrowResult>;
|
|
34
17
|
//# sourceMappingURL=execFileNoThrow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execFileNoThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/execFileNoThrow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"execFileNoThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/execFileNoThrow.ts"],"names":[],"mappings":"AAKA,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;wBACwB;AACxB,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA6BD,0EAA0E;AAC1E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CA+BhC"}
|
|
@@ -1,44 +1,65 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
const MAX_CAPTURE_BYTES = 64 * 1024;
|
|
4
|
+
function appendBounded(chunks, chunk, total) {
|
|
5
|
+
if (total.value >= MAX_CAPTURE_BYTES)
|
|
6
|
+
return;
|
|
7
|
+
const remaining = MAX_CAPTURE_BYTES - total.value;
|
|
8
|
+
const part = chunk.subarray(0, remaining);
|
|
9
|
+
chunks.push(part);
|
|
10
|
+
total.value += part.length;
|
|
11
|
+
}
|
|
12
|
+
/** Quote one argument for the Windows command interpreter without flattening
|
|
13
|
+
* the caller's argv into an unquoted shell command. */
|
|
14
|
+
function quoteWindowsArg(value) {
|
|
15
|
+
const escaped = value.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1');
|
|
16
|
+
return `"${escaped.replace(/%/g, '%%')}"`;
|
|
17
|
+
}
|
|
18
|
+
function spawnCommand(file, args, options) {
|
|
19
|
+
const isWindowsScript = process.platform === 'win32' && /\.(?:cmd|bat)$/iu.test(extname(file));
|
|
20
|
+
if (!isWindowsScript)
|
|
21
|
+
return spawn(file, args, { timeout: options?.timeout, cwd: options?.cwd });
|
|
22
|
+
const command = `"${[file, ...args].map(quoteWindowsArg).join(' ')}"`;
|
|
23
|
+
return spawn(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', command], {
|
|
24
|
+
timeout: options?.timeout,
|
|
25
|
+
cwd: options?.cwd,
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
windowsVerbatimArguments: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Run a command without throwing; child output is bounded for safety. */
|
|
31
|
+
export function execFileNoThrow(file, args = [], options) {
|
|
11
32
|
return new Promise(resolve => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
33
|
+
let child;
|
|
34
|
+
try {
|
|
35
|
+
child = spawnCommand(file, args, options);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
resolve({ code: 1, stdout: '', stderr: '' });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
15
41
|
const stdoutChunks = [];
|
|
16
42
|
const stderrChunks = [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// EPIPE/EOF belongs to that child outcome and must not crash the caller.
|
|
25
|
-
child.stdin.on('error', () => { });
|
|
26
|
-
child.on('error', () => {
|
|
27
|
-
resolve({
|
|
28
|
-
code: 1,
|
|
29
|
-
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
30
|
-
stderr: Buffer.concat(stderrChunks).toString('utf8'),
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
child.on('close', code => {
|
|
43
|
+
const stdoutTotal = { value: 0 };
|
|
44
|
+
const stderrTotal = { value: 0 };
|
|
45
|
+
let settled = false;
|
|
46
|
+
const finish = (code) => {
|
|
47
|
+
if (settled)
|
|
48
|
+
return;
|
|
49
|
+
settled = true;
|
|
34
50
|
resolve({
|
|
35
51
|
code,
|
|
36
52
|
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
37
53
|
stderr: Buffer.concat(stderrChunks).toString('utf8'),
|
|
38
54
|
});
|
|
39
|
-
}
|
|
55
|
+
};
|
|
56
|
+
child.stdout?.on('data', (chunk) => appendBounded(stdoutChunks, chunk, stdoutTotal));
|
|
57
|
+
child.stderr?.on('data', (chunk) => appendBounded(stderrChunks, chunk, stderrTotal));
|
|
58
|
+
child.stdin?.on('error', () => { });
|
|
59
|
+
child.on('error', () => finish(1));
|
|
60
|
+
child.on('close', code => finish(code));
|
|
40
61
|
if (options?.input !== undefined)
|
|
41
|
-
child.stdin
|
|
42
|
-
child.stdin
|
|
62
|
+
child.stdin?.write(options.input);
|
|
63
|
+
child.stdin?.end();
|
|
43
64
|
});
|
|
44
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* External editor round-trip for the prompt input (issue #123): Ctrl+
|
|
2
|
+
* External editor round-trip for the prompt input (issue #123): Ctrl+G dumps
|
|
3
3
|
* the current draft into a temp file, hands the terminal to `$VISUAL` /
|
|
4
4
|
* `$EDITOR` (nvim, vim, nano, `code --wait`, …), and returns the saved text
|
|
5
5
|
* for the input to adopt.
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* failing restore never overrides the outcome.
|
|
17
17
|
*
|
|
18
18
|
* Editor resolution order mirrors readline's edit-and-execute-command:
|
|
19
|
-
* `$VISUAL` → `$EDITOR
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the command line is split
|
|
19
|
+
* `$VISUAL` → `$EDITOR`. There is deliberately NO fallback editor: an
|
|
20
|
+
* unresolved editor reports `unavailable` and the UI asks the user to set
|
|
21
|
+
* `$VISUAL` or `$EDITOR` (dropping someone into an unconfigured `vi` is a
|
|
22
|
+
* trap for users who don't know how to quit it). The variable may carry
|
|
23
|
+
* arguments (`EDITOR="code --wait"`), so the command line is split
|
|
24
|
+
* quote-aware before spawning.
|
|
24
25
|
*
|
|
25
26
|
* Windows launch: libuv resolves bare names to `.exe` on PATH but will NOT
|
|
26
27
|
* execute `.cmd`/`.bat` shims (VS Code's `code` on PATH is `code.cmd`), and
|
|
@@ -37,7 +38,8 @@
|
|
|
37
38
|
* - `edited`: the saved content differs from the draft — adopt `text`
|
|
38
39
|
* - `unchanged`: the file matches the draft (modulo newline convention), or
|
|
39
40
|
* the editor exited non-zero (`:cq` abort semantics) — keep the draft
|
|
40
|
-
* - `unavailable`: no editor could be resolved (
|
|
41
|
+
* - `unavailable`: no editor could be resolved (neither `$VISUAL` nor
|
|
42
|
+
* `$EDITOR` is set)
|
|
41
43
|
* - `failed`: the editor process or the temp-file round-trip errored
|
|
42
44
|
* (`message` names the failed command or carries the fs error)
|
|
43
45
|
*/
|
|
@@ -59,11 +61,10 @@ export type EditorOutcome = {
|
|
|
59
61
|
export declare function splitEditorCommand(commandLine: string): string[];
|
|
60
62
|
/**
|
|
61
63
|
* Resolve the editor argv from the environment. `$VISUAL` wins over
|
|
62
|
-
* `$EDITOR` (readline convention);
|
|
63
|
-
*
|
|
64
|
-
* parameter so the Windows branch is unit-testable from CI's Linux runners.
|
|
64
|
+
* `$EDITOR` (readline convention); when neither is set there is no fallback
|
|
65
|
+
* editor — returns undefined and the caller reports `unavailable`.
|
|
65
66
|
*/
|
|
66
|
-
export declare function resolveEditorCommand(env?: NodeJS.ProcessEnv
|
|
67
|
+
export declare function resolveEditorCommand(env?: NodeJS.ProcessEnv): string[] | undefined;
|
|
67
68
|
/**
|
|
68
69
|
* Windows shim resolution: a bare command like `code` usually lives on PATH
|
|
69
70
|
* as `code.cmd`, which libuv refuses to execute directly. Walk PATH with
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"externalEditor.d.ts","sourceRoot":"","sources":["../../../src/utils/externalEditor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"externalEditor.d.ts","sourceRoot":"","sources":["../../../src/utils/externalEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAUH;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BhE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,GAAG,SAAS,CAOtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAqBtC;AAKD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,CAalD;AA8CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAsDhF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* External editor round-trip for the prompt input (issue #123): Ctrl+
|
|
2
|
+
* External editor round-trip for the prompt input (issue #123): Ctrl+G dumps
|
|
3
3
|
* the current draft into a temp file, hands the terminal to `$VISUAL` /
|
|
4
4
|
* `$EDITOR` (nvim, vim, nano, `code --wait`, …), and returns the saved text
|
|
5
5
|
* for the input to adopt.
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* failing restore never overrides the outcome.
|
|
17
17
|
*
|
|
18
18
|
* Editor resolution order mirrors readline's edit-and-execute-command:
|
|
19
|
-
* `$VISUAL` → `$EDITOR
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the command line is split
|
|
19
|
+
* `$VISUAL` → `$EDITOR`. There is deliberately NO fallback editor: an
|
|
20
|
+
* unresolved editor reports `unavailable` and the UI asks the user to set
|
|
21
|
+
* `$VISUAL` or `$EDITOR` (dropping someone into an unconfigured `vi` is a
|
|
22
|
+
* trap for users who don't know how to quit it). The variable may carry
|
|
23
|
+
* arguments (`EDITOR="code --wait"`), so the command line is split
|
|
24
|
+
* quote-aware before spawning.
|
|
24
25
|
*
|
|
25
26
|
* Windows launch: libuv resolves bare names to `.exe` on PATH but will NOT
|
|
26
27
|
* execute `.cmd`/`.bat` shims (VS Code's `code` on PATH is `code.cmd`), and
|
|
@@ -76,17 +77,16 @@ export function splitEditorCommand(commandLine) {
|
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
79
|
* Resolve the editor argv from the environment. `$VISUAL` wins over
|
|
79
|
-
* `$EDITOR` (readline convention);
|
|
80
|
-
*
|
|
81
|
-
* parameter so the Windows branch is unit-testable from CI's Linux runners.
|
|
80
|
+
* `$EDITOR` (readline convention); when neither is set there is no fallback
|
|
81
|
+
* editor — returns undefined and the caller reports `unavailable`.
|
|
82
82
|
*/
|
|
83
|
-
export function resolveEditorCommand(env = process.env
|
|
83
|
+
export function resolveEditorCommand(env = process.env) {
|
|
84
84
|
const raw = (env.VISUAL ?? '').trim() || (env.EDITOR ?? '').trim();
|
|
85
85
|
if (raw !== '') {
|
|
86
86
|
const args = splitEditorCommand(raw);
|
|
87
87
|
return args.length > 0 ? args : undefined;
|
|
88
88
|
}
|
|
89
|
-
return
|
|
89
|
+
return undefined;
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* Windows shim resolution: a bare command like `code` usually lives on PATH
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type FileCandidateKind = 'file' | 'directory';
|
|
2
|
+
export interface FileCandidate {
|
|
3
|
+
id: string;
|
|
4
|
+
path: string;
|
|
5
|
+
displayPath: string;
|
|
6
|
+
name: string;
|
|
7
|
+
kind: FileCandidateKind;
|
|
8
|
+
score: number;
|
|
9
|
+
}
|
|
10
|
+
export interface FileSuggestionOptions {
|
|
11
|
+
topK?: number;
|
|
12
|
+
maxFiles?: number;
|
|
13
|
+
maxDirectories?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function isPathLikeQuery(query: string): boolean;
|
|
16
|
+
export declare function fuzzySubsequenceScore(query: string, candidate: string): number | undefined;
|
|
17
|
+
export declare function rankFileCandidates(candidates: readonly FileCandidate[], query: string, topK?: number): FileCandidate[];
|
|
18
|
+
export declare function preserveSelection(previous: FileCandidate | undefined, next: readonly FileCandidate[], fallback?: number): number;
|
|
19
|
+
//# sourceMappingURL=fileSuggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileSuggestions.d.ts","sourceRoot":"","sources":["../../../src/utils/fileSuggestions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,CAAA;AAEpD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,iBAAiB,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAID,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiB1F;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,aAAa,EAAE,CAMlH;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,aAAa,EAAE,EAAE,QAAQ,SAAI,GAAG,MAAM,CAM3H"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const pathSeparators = /[\\/]/;
|
|
2
|
+
export function isPathLikeQuery(query) {
|
|
3
|
+
return query.startsWith('.') || query.startsWith('~') || query.startsWith('/') || /^[A-Za-z]:[\\/]/.test(query) || pathSeparators.test(query);
|
|
4
|
+
}
|
|
5
|
+
export function fuzzySubsequenceScore(query, candidate) {
|
|
6
|
+
const needle = query.toLocaleLowerCase();
|
|
7
|
+
const haystack = candidate.toLocaleLowerCase();
|
|
8
|
+
if (!needle)
|
|
9
|
+
return 0;
|
|
10
|
+
let cursor = 0;
|
|
11
|
+
let first = -1;
|
|
12
|
+
let gaps = 0;
|
|
13
|
+
for (const char of needle) {
|
|
14
|
+
const found = haystack.indexOf(char, cursor);
|
|
15
|
+
if (found === -1)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (first === -1)
|
|
18
|
+
first = found;
|
|
19
|
+
gaps += found - cursor;
|
|
20
|
+
cursor = found + 1;
|
|
21
|
+
}
|
|
22
|
+
const prefixBonus = first === 0 ? 20 : 0;
|
|
23
|
+
const boundaryBonus = first > 0 && /[\\/_. -]/.test(haystack[first - 1] ?? '') ? 8 : 0;
|
|
24
|
+
return needle.length * 10 + prefixBonus + boundaryBonus - gaps - haystack.length / 100;
|
|
25
|
+
}
|
|
26
|
+
export function rankFileCandidates(candidates, query, topK = 50) {
|
|
27
|
+
const ranked = candidates
|
|
28
|
+
.map(candidate => ({ ...candidate, score: fuzzySubsequenceScore(query, `${candidate.path} ${candidate.name}`) ?? -Infinity }))
|
|
29
|
+
.filter(candidate => Number.isFinite(candidate.score))
|
|
30
|
+
.sort((a, b) => b.score - a.score || a.kind.localeCompare(b.kind) || a.path.localeCompare(b.path));
|
|
31
|
+
return ranked.slice(0, Math.max(0, topK));
|
|
32
|
+
}
|
|
33
|
+
export function preserveSelection(previous, next, fallback = 0) {
|
|
34
|
+
if (previous) {
|
|
35
|
+
const index = next.findIndex(candidate => candidate.id === previous.id);
|
|
36
|
+
if (index >= 0)
|
|
37
|
+
return index;
|
|
38
|
+
}
|
|
39
|
+
return next.length === 0 ? 0 : Math.min(Math.max(0, fallback), next.length - 1);
|
|
40
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mentions.d.ts","sourceRoot":"","sources":["../../../src/utils/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;CACb;
|
|
1
|
+
{"version":3,"file":"mentions.d.ts","sourceRoot":"","sources":["../../../src/utils/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;CACb;AAQD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CA8B5D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAe3D"}
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* either a run of non-whitespace characters (`@src/a.ts`) or a double-quoted
|
|
7
7
|
* path (`@"my dir/a.ts"`, for paths containing spaces).
|
|
8
8
|
*/
|
|
9
|
-
/** True when `ch` delimits a token (whitespace).
|
|
9
|
+
/** True when `ch` delimits a token (whitespace). Path separators and the
|
|
10
|
+
* quote char are ordinary token characters on every platform — Windows
|
|
11
|
+
* backslash paths must survive both caret tracking and submission. */
|
|
10
12
|
const isBoundary = (ch) => ch === undefined || /\s/.test(ch);
|
|
11
13
|
/** Extract every `@` mention in `text` (typed order, duplicates kept out). */
|
|
12
14
|
export function extractMentions(text) {
|