@duetso/agent 0.1.80 → 0.1.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +1 -1
- package/dist/src/memory/observational.d.ts +8 -1
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +9 -3
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/session/session.d.ts +31 -13
- package/dist/src/session/session.d.ts.map +1 -1
- package/dist/src/session/session.js +63 -23
- package/dist/src/session/session.js.map +1 -1
- package/dist/src/tui/app.d.ts +25 -35
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +195 -2308
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/autocomplete-controller.d.ts +83 -0
- package/dist/src/tui/autocomplete-controller.d.ts.map +1 -0
- package/dist/src/tui/autocomplete-controller.js +308 -0
- package/dist/src/tui/autocomplete-controller.js.map +1 -0
- package/dist/src/tui/boot-screen.d.ts +64 -0
- package/dist/src/tui/boot-screen.d.ts.map +1 -0
- package/dist/src/tui/boot-screen.js +137 -0
- package/dist/src/tui/boot-screen.js.map +1 -0
- package/dist/src/tui/controllers.d.ts +39 -0
- package/dist/src/tui/controllers.d.ts.map +1 -0
- package/dist/src/tui/controllers.js +53 -0
- package/dist/src/tui/controllers.js.map +1 -0
- package/dist/src/tui/copy-controller.d.ts +75 -0
- package/dist/src/tui/copy-controller.d.ts.map +1 -0
- package/dist/src/tui/copy-controller.js +140 -0
- package/dist/src/tui/copy-controller.js.map +1 -0
- package/dist/src/tui/history-replay.d.ts +33 -0
- package/dist/src/tui/history-replay.d.ts.map +1 -0
- package/dist/src/tui/history-replay.js +47 -0
- package/dist/src/tui/history-replay.js.map +1 -0
- package/dist/src/tui/initial-prompt.d.ts +24 -0
- package/dist/src/tui/initial-prompt.d.ts.map +1 -0
- package/dist/src/tui/initial-prompt.js +29 -0
- package/dist/src/tui/initial-prompt.js.map +1 -0
- package/dist/src/tui/key-handlers.d.ts +49 -0
- package/dist/src/tui/key-handlers.d.ts.map +1 -0
- package/dist/src/tui/key-handlers.js +192 -0
- package/dist/src/tui/key-handlers.js.map +1 -0
- package/dist/src/tui/layout.d.ts +71 -0
- package/dist/src/tui/layout.d.ts.map +1 -0
- package/dist/src/tui/layout.js +245 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/paste-controller.d.ts +90 -0
- package/dist/src/tui/paste-controller.d.ts.map +1 -0
- package/dist/src/tui/paste-controller.js +234 -0
- package/dist/src/tui/paste-controller.js.map +1 -0
- package/dist/src/tui/question-picker.d.ts +111 -0
- package/dist/src/tui/question-picker.d.ts.map +1 -0
- package/dist/src/tui/question-picker.js +308 -0
- package/dist/src/tui/question-picker.js.map +1 -0
- package/dist/src/tui/renderer-lifecycle.d.ts +20 -0
- package/dist/src/tui/renderer-lifecycle.d.ts.map +1 -0
- package/dist/src/tui/renderer-lifecycle.js +50 -0
- package/dist/src/tui/renderer-lifecycle.js.map +1 -0
- package/dist/src/tui/session-subscription.d.ts +30 -0
- package/dist/src/tui/session-subscription.d.ts.map +1 -0
- package/dist/src/tui/session-subscription.js +78 -0
- package/dist/src/tui/session-subscription.js.map +1 -0
- package/dist/src/tui/sidebar.d.ts +2 -2
- package/dist/src/tui/sidebar.d.ts.map +1 -1
- package/dist/src/tui/sidebar.js +1 -1
- package/dist/src/tui/sidebar.js.map +1 -1
- package/dist/src/tui/slash-commands.d.ts +23 -0
- package/dist/src/tui/slash-commands.d.ts.map +1 -0
- package/dist/src/tui/slash-commands.js +79 -0
- package/dist/src/tui/slash-commands.js.map +1 -0
- package/dist/src/tui/starter-section.d.ts +99 -0
- package/dist/src/tui/starter-section.d.ts.map +1 -0
- package/dist/src/tui/starter-section.js +254 -0
- package/dist/src/tui/starter-section.js.map +1 -0
- package/dist/src/tui/status-controller.d.ts +94 -0
- package/dist/src/tui/status-controller.d.ts.map +1 -0
- package/dist/src/tui/status-controller.js +186 -0
- package/dist/src/tui/status-controller.js.map +1 -0
- package/dist/src/tui/step-renderer.d.ts +55 -0
- package/dist/src/tui/step-renderer.d.ts.map +1 -0
- package/dist/src/tui/step-renderer.js +246 -0
- package/dist/src/tui/step-renderer.js.map +1 -0
- package/dist/src/tui/transcript-writer.d.ts +63 -0
- package/dist/src/tui/transcript-writer.d.ts.map +1 -0
- package/dist/src/tui/transcript-writer.js +130 -0
- package/dist/src/tui/transcript-writer.js.map +1 -0
- package/dist/src/turn-runner/turn-runner.d.ts +77 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +150 -12
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/dist/src/turn-runner/usage-accounting.d.ts +18 -1
- package/dist/src/turn-runner/usage-accounting.d.ts.map +1 -1
- package/dist/src/turn-runner/usage-accounting.js +48 -27
- package/dist/src/turn-runner/usage-accounting.js.map +1 -1
- package/dist/src/types/protocol.d.ts +48 -17
- package/dist/src/types/protocol.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type CliRenderer, type ScrollBoxRenderable } from "@opentui/core";
|
|
2
|
+
import { type UpgradeStatusStream } from "../cli/auto-upgrade.js";
|
|
3
|
+
import type { Session } from "../session/session.js";
|
|
4
|
+
import type { TurnAgentFile } from "../types/protocol.js";
|
|
5
|
+
import type { AutocompleteController } from "./autocomplete-controller.js";
|
|
6
|
+
import type { Sidebar } from "./sidebar.js";
|
|
7
|
+
import type { StarterSection } from "./starter-section.js";
|
|
8
|
+
/**
|
|
9
|
+
* Inputs to the boot intro renderer. Everything is read-only — the boot
|
|
10
|
+
* screen is a one-shot painter that runs after chrome is constructed and
|
|
11
|
+
* before the first turn dispatches.
|
|
12
|
+
*/
|
|
13
|
+
export interface BootScreenDeps {
|
|
14
|
+
renderer: CliRenderer;
|
|
15
|
+
transcript: ScrollBoxRenderable;
|
|
16
|
+
appendLine(content: string, fg: string): void;
|
|
17
|
+
packageName: string;
|
|
18
|
+
packageVersion: string;
|
|
19
|
+
workDir: string;
|
|
20
|
+
modelName: string;
|
|
21
|
+
memoryModelName: string;
|
|
22
|
+
upgradeStatus$?: UpgradeStatusStream;
|
|
23
|
+
/**
|
|
24
|
+
* Present only on fresh (non-resume) mounts. `runTui` creates the
|
|
25
|
+
* StarterSection lazily and leaves it undefined on resume mounts, so the
|
|
26
|
+
* starter chrome is skipped without needing a separate resume flag.
|
|
27
|
+
*/
|
|
28
|
+
starters?: StarterSection;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Paints the boot banner, header, optional upgrade-status line, agent-file
|
|
32
|
+
* summary, and (on fresh boots) mounts the starter chrome.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderSetupIntro(deps: BootScreenDeps, skills: ReadonlyArray<{
|
|
35
|
+
name: string;
|
|
36
|
+
}>, agentFiles: readonly TurnAgentFile[]): void;
|
|
37
|
+
export declare function formatBootHeader(deps: {
|
|
38
|
+
packageVersion: string;
|
|
39
|
+
workDir: string;
|
|
40
|
+
modelName: string;
|
|
41
|
+
memoryModelName: string;
|
|
42
|
+
}): string;
|
|
43
|
+
/**
|
|
44
|
+
* One-shot boot intro: loads skills + agent files from the session, seeds
|
|
45
|
+
* the autocomplete index, paints the banner/header/agent-file line, and
|
|
46
|
+
* refreshes the sidebar from the session's current state snapshot.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderBootScreen(deps: {
|
|
49
|
+
renderer: CliRenderer;
|
|
50
|
+
transcript: ScrollBoxRenderable;
|
|
51
|
+
appendLine(content: string, fg: string): void;
|
|
52
|
+
session: Session;
|
|
53
|
+
sidebar: Sidebar;
|
|
54
|
+
autocomplete: AutocompleteController;
|
|
55
|
+
starters?: StarterSection;
|
|
56
|
+
packageName: string;
|
|
57
|
+
packageVersion: string;
|
|
58
|
+
workDir: string;
|
|
59
|
+
modelName: string;
|
|
60
|
+
memoryModelName: string;
|
|
61
|
+
upgradeStatus$?: UpgradeStatusStream;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
export declare function shortenCwd(cwd: string): string;
|
|
64
|
+
//# sourceMappingURL=boot-screen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-screen.d.ts","sourceRoot":"","sources":["../../../src/tui/boot-screen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAkB,MAAM,eAAe,CAAC;AAE3F,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAG3E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,mBAAmB,CAAC;IAChC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACvC,UAAU,EAAE,SAAS,aAAa,EAAE,GACnC,IAAI,CA6BN;AAmDD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAGT;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,mBAAmB,CAAC;IAChC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,sBAAsB,CAAC;IACrC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { TextRenderable } from "@opentui/core";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { describeUpgradeStatus, } from "../cli/auto-upgrade.js";
|
|
4
|
+
import { BUILT_IN_SLASH_COMMANDS } from "./autocomplete.js";
|
|
5
|
+
import { refreshSidebarFromSession } from "./session-subscription.js";
|
|
6
|
+
import { DUET_BANNER_LINES_COMPACT } from "./history.js";
|
|
7
|
+
import { COLORS } from "./theme.js";
|
|
8
|
+
/**
|
|
9
|
+
* Paints the boot banner, header, optional upgrade-status line, agent-file
|
|
10
|
+
* summary, and (on fresh boots) mounts the starter chrome.
|
|
11
|
+
*/
|
|
12
|
+
export function renderSetupIntro(deps, skills, agentFiles) {
|
|
13
|
+
// Compact 3-row wordmark. The full DUET_BANNER_LINES is ~6 rows tall and
|
|
14
|
+
// pushed the starter list off-screen on small terminals; this one keeps
|
|
15
|
+
// the brand mark visible while leaving room for the starter prompts to
|
|
16
|
+
// land above the fold.
|
|
17
|
+
for (const line of DUET_BANNER_LINES_COMPACT)
|
|
18
|
+
deps.appendLine(line, COLORS.status);
|
|
19
|
+
deps.appendLine(" ", COLORS.hint);
|
|
20
|
+
deps.appendLine(" ", COLORS.hint);
|
|
21
|
+
// One-line header. Keeps cwd/model context visible without burning another
|
|
22
|
+
// five rows. Provenance (env/file source) is intentionally dropped here —
|
|
23
|
+
// surface it via /whoami later.
|
|
24
|
+
deps.appendLine(formatBootHeader(deps), COLORS.status);
|
|
25
|
+
if (deps.upgradeStatus$) {
|
|
26
|
+
subscribeUpgradeStatus(deps);
|
|
27
|
+
}
|
|
28
|
+
// Only mention agent files when one is actually loaded; "[agent file]
|
|
29
|
+
// none" is noise on every empty boot.
|
|
30
|
+
if (agentFiles.length > 0) {
|
|
31
|
+
deps.appendLine(`[agent file] ${agentFiles.map((file) => file.name).join(", ")}`, COLORS.hint);
|
|
32
|
+
}
|
|
33
|
+
// Resume mounts leave `deps.starters` undefined so we skip the "what
|
|
34
|
+
// should we work on today?" menu — the user explicitly asked to drop
|
|
35
|
+
// back into a known conversation.
|
|
36
|
+
if (deps.starters) {
|
|
37
|
+
deps.starters.mount(skills);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function subscribeUpgradeStatus(deps) {
|
|
41
|
+
const stream = deps.upgradeStatus$;
|
|
42
|
+
if (!stream)
|
|
43
|
+
return;
|
|
44
|
+
// Lazy construction on the first status that has human-readable text.
|
|
45
|
+
// Statuses without text (current/locked/skipped) skip the constructor
|
|
46
|
+
// entirely; constructing eagerly would allocate a native text buffer
|
|
47
|
+
// against the renderer that we would never `destroy()` on the silent path.
|
|
48
|
+
//
|
|
49
|
+
// `subscribe()` replays the latest status synchronously, so the handler
|
|
50
|
+
// runs before `subscribe()` returns its unsubscribe handle. We set a
|
|
51
|
+
// `done` flag from inside the handler and tear down after `subscribe()`
|
|
52
|
+
// returns; subsequent (async) terminal statuses unsubscribe inline via
|
|
53
|
+
// the real handle.
|
|
54
|
+
let upgradeLine;
|
|
55
|
+
let done = false;
|
|
56
|
+
let unsubscribe = () => { };
|
|
57
|
+
const handle = (status) => {
|
|
58
|
+
const text = describeUpgradeStatus(deps.packageName, status);
|
|
59
|
+
if (!text) {
|
|
60
|
+
if (upgradeLine) {
|
|
61
|
+
deps.transcript.remove(upgradeLine.id);
|
|
62
|
+
upgradeLine.destroy();
|
|
63
|
+
upgradeLine = undefined;
|
|
64
|
+
}
|
|
65
|
+
// Terminal statuses with no human-readable form (current, locked,
|
|
66
|
+
// skipped) close the subscription so we stop reacting.
|
|
67
|
+
if (status.kind !== "checking") {
|
|
68
|
+
done = true;
|
|
69
|
+
unsubscribe();
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const fg = status.kind === "failed" ? COLORS.error : COLORS.system;
|
|
74
|
+
if (!upgradeLine) {
|
|
75
|
+
upgradeLine = new TextRenderable(deps.renderer, { content: `[update] ${text}`, fg });
|
|
76
|
+
deps.transcript.add(upgradeLine);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
upgradeLine.content = `[update] ${text}`;
|
|
80
|
+
upgradeLine.fg = fg;
|
|
81
|
+
}
|
|
82
|
+
if (status.kind === "upgraded" || status.kind === "failed") {
|
|
83
|
+
done = true;
|
|
84
|
+
unsubscribe();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
unsubscribe = stream.subscribe(handle);
|
|
88
|
+
if (done)
|
|
89
|
+
unsubscribe();
|
|
90
|
+
}
|
|
91
|
+
export function formatBootHeader(deps) {
|
|
92
|
+
const cwdLabel = shortenCwd(deps.workDir);
|
|
93
|
+
return `DUET AGENT v${deps.packageVersion} · ${cwdLabel} · ${deps.modelName} + ${deps.memoryModelName}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* One-shot boot intro: loads skills + agent files from the session, seeds
|
|
97
|
+
* the autocomplete index, paints the banner/header/agent-file line, and
|
|
98
|
+
* refreshes the sidebar from the session's current state snapshot.
|
|
99
|
+
*/
|
|
100
|
+
export async function renderBootScreen(deps) {
|
|
101
|
+
const [skills, agentFiles] = await Promise.all([
|
|
102
|
+
deps.session.getSkills(),
|
|
103
|
+
deps.session.getResolvedAgentFiles(),
|
|
104
|
+
]);
|
|
105
|
+
deps.autocomplete.setSkillItems([
|
|
106
|
+
...BUILT_IN_SLASH_COMMANDS,
|
|
107
|
+
...skills.map((skill) => ({
|
|
108
|
+
name: skill.name,
|
|
109
|
+
description: skill.description,
|
|
110
|
+
path: skill.baseDir,
|
|
111
|
+
group: "skills",
|
|
112
|
+
})),
|
|
113
|
+
]);
|
|
114
|
+
deps.autocomplete.refresh();
|
|
115
|
+
renderSetupIntro({
|
|
116
|
+
renderer: deps.renderer,
|
|
117
|
+
transcript: deps.transcript,
|
|
118
|
+
appendLine: deps.appendLine,
|
|
119
|
+
packageName: deps.packageName,
|
|
120
|
+
packageVersion: deps.packageVersion,
|
|
121
|
+
workDir: deps.workDir,
|
|
122
|
+
modelName: deps.modelName,
|
|
123
|
+
memoryModelName: deps.memoryModelName,
|
|
124
|
+
upgradeStatus$: deps.upgradeStatus$,
|
|
125
|
+
starters: deps.starters,
|
|
126
|
+
}, skills, agentFiles);
|
|
127
|
+
refreshSidebarFromSession({ session: deps.session, sidebar: deps.sidebar });
|
|
128
|
+
}
|
|
129
|
+
export function shortenCwd(cwd) {
|
|
130
|
+
const home = homedir();
|
|
131
|
+
if (cwd === home)
|
|
132
|
+
return "~";
|
|
133
|
+
if (cwd.startsWith(`${home}/`))
|
|
134
|
+
return `~${cwd.slice(home.length)}`;
|
|
135
|
+
return cwd;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=boot-screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-screen.js","sourceRoot":"","sources":["../../../src/tui/boot-screen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CliRenderer } from "@opentui/core";
|
|
2
|
+
import { AutocompleteController } from "./autocomplete-controller.js";
|
|
3
|
+
import { CopyController } from "./copy-controller.js";
|
|
4
|
+
import type { LayoutRefs } from "./layout.js";
|
|
5
|
+
import { PasteController } from "./paste-controller.js";
|
|
6
|
+
import { QuestionPicker } from "./question-picker.js";
|
|
7
|
+
import type { StatusController } from "./status-controller.js";
|
|
8
|
+
import type { TranscriptEntry } from "./transcript-log.js";
|
|
9
|
+
import type { TranscriptWriter } from "./transcript-writer.js";
|
|
10
|
+
import type { Session } from "../session/session.js";
|
|
11
|
+
export interface TuiControllers {
|
|
12
|
+
autocomplete: AutocompleteController;
|
|
13
|
+
questionPicker: QuestionPicker;
|
|
14
|
+
pasteController: PasteController;
|
|
15
|
+
copyController: CopyController;
|
|
16
|
+
}
|
|
17
|
+
export interface TuiControllerDeps {
|
|
18
|
+
renderer: CliRenderer;
|
|
19
|
+
ui: LayoutRefs;
|
|
20
|
+
session: Session;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
workDir: string;
|
|
23
|
+
transcriptWriter: TranscriptWriter;
|
|
24
|
+
statusController: StatusController;
|
|
25
|
+
appendBlock(label: string | null, body: string, fg: string): void;
|
|
26
|
+
recordTranscriptEntry(kind: TranscriptEntry["kind"], text: string): void;
|
|
27
|
+
reportError(error: unknown): void;
|
|
28
|
+
onPickerEscapeClose(): void;
|
|
29
|
+
getLastSelectionText(): string;
|
|
30
|
+
clearLastSelectionText(): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Bundles the four secondary input controllers that `runTui` wires onto the
|
|
34
|
+
* composer. Kept together because they all share the same layout handles and
|
|
35
|
+
* status/writer pair; building them in one place keeps `runTui` short and
|
|
36
|
+
* makes the controller surface easy to mock in isolation.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createTuiControllers(deps: TuiControllerDeps): TuiControllers;
|
|
39
|
+
//# sourceMappingURL=controllers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../../src/tui/controllers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,WAAW,CAAC;IACtB,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzE,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,mBAAmB,IAAI,IAAI,CAAC;IAC5B,oBAAoB,IAAI,MAAM,CAAC;IAC/B,sBAAsB,IAAI,IAAI,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CA6C5E"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AutocompleteController } from "./autocomplete-controller.js";
|
|
2
|
+
import { CopyController } from "./copy-controller.js";
|
|
3
|
+
import { PasteController } from "./paste-controller.js";
|
|
4
|
+
import { QuestionPicker } from "./question-picker.js";
|
|
5
|
+
/**
|
|
6
|
+
* Bundles the four secondary input controllers that `runTui` wires onto the
|
|
7
|
+
* composer. Kept together because they all share the same layout handles and
|
|
8
|
+
* status/writer pair; building them in one place keeps `runTui` short and
|
|
9
|
+
* makes the controller surface easy to mock in isolation.
|
|
10
|
+
*/
|
|
11
|
+
export function createTuiControllers(deps) {
|
|
12
|
+
const autocomplete = new AutocompleteController({
|
|
13
|
+
inputField: deps.ui.inputField,
|
|
14
|
+
skillAutocompletePanel: deps.ui.skillAutocompletePanel,
|
|
15
|
+
commandRows: deps.ui.commandRows,
|
|
16
|
+
commandHeader: deps.ui.commandHeader,
|
|
17
|
+
skillRows: deps.ui.skillRows,
|
|
18
|
+
skillHeader: deps.ui.skillHeader,
|
|
19
|
+
fileAutocompletePanel: deps.ui.fileAutocompletePanel,
|
|
20
|
+
fileAutocompleteRows: deps.ui.fileAutocompleteRows,
|
|
21
|
+
workDir: deps.workDir,
|
|
22
|
+
onEscapeClose: deps.onPickerEscapeClose,
|
|
23
|
+
});
|
|
24
|
+
const questionPicker = new QuestionPicker({
|
|
25
|
+
questionPanel: deps.ui.questionPanel,
|
|
26
|
+
questionTitle: deps.ui.questionTitle,
|
|
27
|
+
questionRows: deps.ui.questionRows,
|
|
28
|
+
inputField: deps.ui.inputField,
|
|
29
|
+
session: deps.session,
|
|
30
|
+
onEscapeClose: deps.onPickerEscapeClose,
|
|
31
|
+
appendBlock: deps.appendBlock,
|
|
32
|
+
recordTranscriptEntry: deps.recordTranscriptEntry,
|
|
33
|
+
reportError: deps.reportError,
|
|
34
|
+
markRunning: () => deps.statusController.markRunning(),
|
|
35
|
+
isRunning: () => deps.statusController.isRunning(),
|
|
36
|
+
});
|
|
37
|
+
const pasteController = new PasteController({
|
|
38
|
+
inputField: deps.ui.inputField,
|
|
39
|
+
sessionId: deps.sessionId,
|
|
40
|
+
workDir: deps.workDir,
|
|
41
|
+
appendBlock: deps.appendBlock,
|
|
42
|
+
statusController: deps.statusController,
|
|
43
|
+
});
|
|
44
|
+
const copyController = new CopyController({
|
|
45
|
+
renderer: deps.renderer,
|
|
46
|
+
transcriptWriter: deps.transcriptWriter,
|
|
47
|
+
statusController: deps.statusController,
|
|
48
|
+
getLastSelectionText: deps.getLastSelectionText,
|
|
49
|
+
clearLastSelectionText: deps.clearLastSelectionText,
|
|
50
|
+
});
|
|
51
|
+
return { autocomplete, questionPicker, pasteController, copyController };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=controllers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controllers.js","sourceRoot":"","sources":["../../../src/tui/controllers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6BtD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAuB;IAC1D,MAAM,YAAY,GAAG,IAAI,sBAAsB,CAAC;QAC9C,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU;QAC9B,sBAAsB,EAAE,IAAI,CAAC,EAAE,CAAC,sBAAsB;QACtD,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW;QAChC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa;QACpC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,SAAS;QAC5B,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW;QAChC,qBAAqB,EAAE,IAAI,CAAC,EAAE,CAAC,qBAAqB;QACpD,oBAAoB,EAAE,IAAI,CAAC,EAAE,CAAC,oBAAoB;QAClD,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa,EAAE,IAAI,CAAC,mBAAmB;KACxC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;QACxC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa;QACpC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa;QACpC,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC,YAAY;QAClC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU;QAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa,EAAE,IAAI,CAAC,mBAAmB;QACvC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,qBAAqB,EAAE,IAAI,CAAC,qBAAqB;QACjD,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE;QACtD,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;KACnD,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC;QAC1C,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU;QAC9B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;QACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;QAC/C,sBAAsB,EAAE,IAAI,CAAC,sBAAsB;KACpD,CAAC,CAAC;IAEH,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type CliRenderer, type KeyEvent } from "@opentui/core";
|
|
2
|
+
import { type ClipboardWriteResult } from "./clipboard.js";
|
|
3
|
+
import type { StatusController } from "./status-controller.js";
|
|
4
|
+
import type { TranscriptWriter } from "./transcript-writer.js";
|
|
5
|
+
export interface CopyControllerOptions {
|
|
6
|
+
/** Renderer that exposes the OSC 52 fallback writer and the
|
|
7
|
+
* drag-selection clear hook invoked after a successful copy. */
|
|
8
|
+
renderer: CliRenderer;
|
|
9
|
+
/** Transcript writer used to surface copy outcomes (success summary,
|
|
10
|
+
* clipboard write failure) and to source `/copy last|all|<N>` payloads
|
|
11
|
+
* from the message log. */
|
|
12
|
+
transcriptWriter: TranscriptWriter;
|
|
13
|
+
/** Status surface owns the selection-aware hint segment; cleared after a
|
|
14
|
+
* successful keystroke/slash-command copy so the hint drops the
|
|
15
|
+
* copy-keystroke advertisement. */
|
|
16
|
+
statusController: Pick<StatusController, "setSelectionText">;
|
|
17
|
+
/** Reads the most recent drag-selected text. The selection listener still
|
|
18
|
+
* lives in `runTui` so transcript-writer diagnostics see it on the same
|
|
19
|
+
* tick; the controller pulls it through this getter at copy time. */
|
|
20
|
+
getLastSelectionText: () => string;
|
|
21
|
+
/** Clears the cached drag-selection after a successful keystroke copy so
|
|
22
|
+
* subsequent keystrokes do not re-copy stale text. */
|
|
23
|
+
clearLastSelectionText: () => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Owns every path that moves text onto the OS clipboard: the platform copy
|
|
27
|
+
* keystroke (Cmd+C / Cmd+Shift+C / Ctrl+Shift+C), the `/copy` slash command,
|
|
28
|
+
* and the underlying two-stage writer (CLI first, OSC 52 fallback).
|
|
29
|
+
*
|
|
30
|
+
* Two-stage clipboard write rationale: the platform-native CLI (pbcopy /
|
|
31
|
+
* wl-copy / xclip / xsel / clip.exe) goes first because `writeClipboardText`
|
|
32
|
+
* reads the clipboard back to confirm the bytes landed — exit-code-only
|
|
33
|
+
* success is not enough (pbcopy from inside a raw-mode TUI on Warp/macOS
|
|
34
|
+
* exits 0 without updating NSPasteboard). OSC 52 is only the fallback when
|
|
35
|
+
* no local CLI exists (SSH session with no clipboard tool installed
|
|
36
|
+
* remotely); when a CLI ran but failed verification we surface that error
|
|
37
|
+
* directly instead of falling through to OSC 52, because OSC 52 would also
|
|
38
|
+
* silently "succeed" on the same broken terminals and hide the real failure.
|
|
39
|
+
*/
|
|
40
|
+
export declare class CopyController {
|
|
41
|
+
private readonly opts;
|
|
42
|
+
constructor(opts: CopyControllerOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Detect the platform copy keystroke and, when a non-empty drag-selection
|
|
45
|
+
* exists, route to `copyActiveSelection`. Returns true when the keystroke
|
|
46
|
+
* was consumed (caller must not fall through to Esc/other handlers) and
|
|
47
|
+
* false otherwise so the caller can keep processing.
|
|
48
|
+
*
|
|
49
|
+
* Accepts both "c" and "C" as the key name because some kitty parsers
|
|
50
|
+
* report the shifted letter while others report the base letter with
|
|
51
|
+
* `shift: true`.
|
|
52
|
+
*/
|
|
53
|
+
handleCopyKeystroke(key: KeyEvent): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Copy the active drag-selection to the clipboard and clear the highlight
|
|
56
|
+
* so the user gets visual confirmation the action happened. Used by the
|
|
57
|
+
* platform copy keystroke; the slash command path goes through
|
|
58
|
+
* `handleCopySlashCommand` so it can also serve `/copy last|all|<N>`.
|
|
59
|
+
*/
|
|
60
|
+
copyActiveSelection(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a `/copy ...` invocation to clipboard text and pipe it to the
|
|
63
|
+
* OS clipboard. When the user has an active drag-selection and ran a bare
|
|
64
|
+
* `/copy`, copy that highlight verbatim — it matches what they actually
|
|
65
|
+
* have on screen. Otherwise fall back to the transcript-log heuristic
|
|
66
|
+
* (`last` / `all` / `<N>`).
|
|
67
|
+
*
|
|
68
|
+
* Failures are surfaced in the transcript so users on minimal Linux
|
|
69
|
+
* installs see exactly which writer is missing.
|
|
70
|
+
*/
|
|
71
|
+
handleCopySlashCommand(raw: string): Promise<void>;
|
|
72
|
+
copyTextToClipboard(text: string): Promise<ClipboardWriteResult>;
|
|
73
|
+
describeCopySelection(argument: "last" | "all" | number, length: number): string;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=copy-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-controller.d.ts","sourceRoot":"","sources":["../../../src/tui/copy-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,KAAK,oBAAoB,EAAsB,MAAM,gBAAgB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC;qEACiE;IACjE,QAAQ,EAAE,WAAW,CAAC;IACtB;;gCAE4B;IAC5B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;wCAEoC;IACpC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;IAC7D;;0EAEsE;IACtE,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC;2DACuD;IACvD,sBAAsB,EAAE,MAAM,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAExD;;;;;;;;;OASG;IACH,mBAAmB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO;IAc3C;;;;;OAKG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB1C;;;;;;;;;OASG;IACG,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+ClD,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiBtE,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;CAMjF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { writeClipboardText } from "./clipboard.js";
|
|
2
|
+
import { COLORS } from "./theme.js";
|
|
3
|
+
import { parseCopyArgument, selectCopyText } from "./transcript-log.js";
|
|
4
|
+
/**
|
|
5
|
+
* Owns every path that moves text onto the OS clipboard: the platform copy
|
|
6
|
+
* keystroke (Cmd+C / Cmd+Shift+C / Ctrl+Shift+C), the `/copy` slash command,
|
|
7
|
+
* and the underlying two-stage writer (CLI first, OSC 52 fallback).
|
|
8
|
+
*
|
|
9
|
+
* Two-stage clipboard write rationale: the platform-native CLI (pbcopy /
|
|
10
|
+
* wl-copy / xclip / xsel / clip.exe) goes first because `writeClipboardText`
|
|
11
|
+
* reads the clipboard back to confirm the bytes landed — exit-code-only
|
|
12
|
+
* success is not enough (pbcopy from inside a raw-mode TUI on Warp/macOS
|
|
13
|
+
* exits 0 without updating NSPasteboard). OSC 52 is only the fallback when
|
|
14
|
+
* no local CLI exists (SSH session with no clipboard tool installed
|
|
15
|
+
* remotely); when a CLI ran but failed verification we surface that error
|
|
16
|
+
* directly instead of falling through to OSC 52, because OSC 52 would also
|
|
17
|
+
* silently "succeed" on the same broken terminals and hide the real failure.
|
|
18
|
+
*/
|
|
19
|
+
export class CopyController {
|
|
20
|
+
opts;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
this.opts = opts;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Detect the platform copy keystroke and, when a non-empty drag-selection
|
|
26
|
+
* exists, route to `copyActiveSelection`. Returns true when the keystroke
|
|
27
|
+
* was consumed (caller must not fall through to Esc/other handlers) and
|
|
28
|
+
* false otherwise so the caller can keep processing.
|
|
29
|
+
*
|
|
30
|
+
* Accepts both "c" and "C" as the key name because some kitty parsers
|
|
31
|
+
* report the shifted letter while others report the base letter with
|
|
32
|
+
* `shift: true`.
|
|
33
|
+
*/
|
|
34
|
+
handleCopyKeystroke(key) {
|
|
35
|
+
const isCopyLetter = key.name === "c" || key.name === "C";
|
|
36
|
+
if (!isCopyLetter)
|
|
37
|
+
return false;
|
|
38
|
+
const cmdHeld = key.super || key.meta;
|
|
39
|
+
const isCmdC = cmdHeld && !key.shift && !key.ctrl;
|
|
40
|
+
const isCmdShiftC = cmdHeld && key.shift && !key.ctrl;
|
|
41
|
+
const isCtrlShiftC = key.ctrl && key.shift && !cmdHeld;
|
|
42
|
+
if (!(isCmdC || isCmdShiftC || isCtrlShiftC))
|
|
43
|
+
return false;
|
|
44
|
+
if (this.opts.getLastSelectionText().trim().length === 0)
|
|
45
|
+
return false;
|
|
46
|
+
key.preventDefault();
|
|
47
|
+
void this.copyActiveSelection();
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Copy the active drag-selection to the clipboard and clear the highlight
|
|
52
|
+
* so the user gets visual confirmation the action happened. Used by the
|
|
53
|
+
* platform copy keystroke; the slash command path goes through
|
|
54
|
+
* `handleCopySlashCommand` so it can also serve `/copy last|all|<N>`.
|
|
55
|
+
*/
|
|
56
|
+
async copyActiveSelection() {
|
|
57
|
+
const text = this.opts.getLastSelectionText();
|
|
58
|
+
if (text.trim().length === 0)
|
|
59
|
+
return;
|
|
60
|
+
const result = await this.copyTextToClipboard(text);
|
|
61
|
+
this.opts.renderer.clearSelection();
|
|
62
|
+
this.opts.clearLastSelectionText();
|
|
63
|
+
this.opts.statusController.setSelectionText("");
|
|
64
|
+
if (result.ok) {
|
|
65
|
+
this.opts.transcriptWriter.appendBlock("[copy]", `copied selection (${text.length} char${text.length === 1 ? "" : "s"}) to clipboard via ${result.via}`, COLORS.system);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.opts.transcriptWriter.appendBlock("[copy]", `clipboard write failed: ${result.error ?? "unknown error"}` +
|
|
69
|
+
(process.platform === "linux" ? "\nInstall one of: wl-clipboard, xclip, xsel" : ""), COLORS.error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve a `/copy ...` invocation to clipboard text and pipe it to the
|
|
74
|
+
* OS clipboard. When the user has an active drag-selection and ran a bare
|
|
75
|
+
* `/copy`, copy that highlight verbatim — it matches what they actually
|
|
76
|
+
* have on screen. Otherwise fall back to the transcript-log heuristic
|
|
77
|
+
* (`last` / `all` / `<N>`).
|
|
78
|
+
*
|
|
79
|
+
* Failures are surfaced in the transcript so users on minimal Linux
|
|
80
|
+
* installs see exactly which writer is missing.
|
|
81
|
+
*/
|
|
82
|
+
async handleCopySlashCommand(raw) {
|
|
83
|
+
const argumentRaw = raw === "/copy" ? "" : raw.slice("/copy ".length);
|
|
84
|
+
const argument = parseCopyArgument(argumentRaw);
|
|
85
|
+
if (argument === undefined) {
|
|
86
|
+
this.opts.transcriptWriter.appendBlock("[copy]", "Usage: /copy [last|all|<N>] — last (default) copies the most recent agent reply, " +
|
|
87
|
+
"or copies the active drag-selection when one is present", COLORS.system);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// A bare `/copy` (or the copy keystroke while a selection is active)
|
|
91
|
+
// prefers the drag-selection so the clipboard matches what the user
|
|
92
|
+
// has highlighted on screen; an explicit `/copy last|all|<N>` always
|
|
93
|
+
// uses the transcript log instead.
|
|
94
|
+
const explicitArgument = argumentRaw.trim().length > 0;
|
|
95
|
+
const selection = this.opts.getLastSelectionText();
|
|
96
|
+
const useSelection = !explicitArgument && selection.trim().length > 0;
|
|
97
|
+
const text = useSelection
|
|
98
|
+
? selection
|
|
99
|
+
: selectCopyText(this.opts.transcriptWriter.entries(), argument);
|
|
100
|
+
if (!text) {
|
|
101
|
+
this.opts.transcriptWriter.appendBlock("[copy]", "nothing to copy yet", COLORS.system);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const result = await this.copyTextToClipboard(text);
|
|
105
|
+
if (result.ok) {
|
|
106
|
+
const summary = useSelection
|
|
107
|
+
? `selection (${text.length} char${text.length === 1 ? "" : "s"})`
|
|
108
|
+
: this.describeCopySelection(argument, text.length);
|
|
109
|
+
this.opts.transcriptWriter.appendBlock("[copy]", `copied ${summary} to clipboard via ${result.via}`, COLORS.system);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.opts.transcriptWriter.appendBlock("[copy]", `clipboard write failed: ${result.error ?? "unknown error"}` +
|
|
113
|
+
(process.platform === "linux" ? "\nInstall one of: wl-clipboard, xclip, xsel" : ""), COLORS.error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async copyTextToClipboard(text) {
|
|
117
|
+
const cli = await writeClipboardText(text);
|
|
118
|
+
if (cli.ok)
|
|
119
|
+
return cli;
|
|
120
|
+
// Only fall back to OSC 52 when the CLI was simply unavailable. If a
|
|
121
|
+
// CLI ran but the readback did not match (cli.kind ===
|
|
122
|
+
// "verification-failed"), OSC 52 is on the same broken pipe and would
|
|
123
|
+
// silently "succeed" the same way — surface the real error.
|
|
124
|
+
if (cli.kind === "no-writer" &&
|
|
125
|
+
this.opts.renderer.isOsc52Supported() &&
|
|
126
|
+
this.opts.renderer.copyToClipboardOSC52(text)) {
|
|
127
|
+
return { ok: true, via: "OSC 52" };
|
|
128
|
+
}
|
|
129
|
+
return cli;
|
|
130
|
+
}
|
|
131
|
+
describeCopySelection(argument, length) {
|
|
132
|
+
const chars = `${length} char${length === 1 ? "" : "s"}`;
|
|
133
|
+
if (argument === "last")
|
|
134
|
+
return `last message (${chars})`;
|
|
135
|
+
if (argument === "all")
|
|
136
|
+
return `full transcript (${chars})`;
|
|
137
|
+
return `last ${argument} messages (${chars})`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=copy-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-controller.js","sourceRoot":"","sources":["../../../src/tui/copy-controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { TranscriptEntry } from "./transcript-log.js";
|
|
3
|
+
import { type HistoryDisplayBlock } from "./history.js";
|
|
4
|
+
/**
|
|
5
|
+
* Inputs to the resume replay step. Pulled out so the closures inside `runTui`
|
|
6
|
+
* stay short and the (stateless) coordinator is testable in isolation.
|
|
7
|
+
*/
|
|
8
|
+
export interface HistoryReplayDeps {
|
|
9
|
+
appendLine(content: string, fg: string): void;
|
|
10
|
+
appendBlock(label: string | null, body: string, fg: string): void;
|
|
11
|
+
recordTranscriptEntry(kind: TranscriptEntry["kind"], text: string): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Subset of `RunTuiInput` consumed by the replay coordinator. Kept structural
|
|
15
|
+
* so callers can pass `RunTuiInput` directly without an adapter.
|
|
16
|
+
*/
|
|
17
|
+
export interface HistoryReplayInput {
|
|
18
|
+
history?: AgentMessage[];
|
|
19
|
+
/**
|
|
20
|
+
* Trailing user-turn exchanges to surface in the visible transcript.
|
|
21
|
+
* Undefined means "render everything"; `0` disables visible replay but
|
|
22
|
+
* still seeds the copy-out log below.
|
|
23
|
+
*/
|
|
24
|
+
resumeHistoryMessages?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Paints the trimmed display slice into the transcript ScrollBox and seeds
|
|
28
|
+
* the copy-out log from the full history so `/copy all` and `/copy <N>` can
|
|
29
|
+
* reach back further than what is actually rendered on resume.
|
|
30
|
+
*/
|
|
31
|
+
export declare function replayResumeHistory(deps: HistoryReplayDeps, input: HistoryReplayInput): void;
|
|
32
|
+
export type { HistoryDisplayBlock };
|
|
33
|
+
//# sourceMappingURL=history-replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-replay.d.ts","sourceRoot":"","sources":["../../../src/tui/history-replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAEL,KAAK,mBAAmB,EAGzB,MAAM,cAAc,CAAC;AAGtB;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1E;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI,CA8B5F;AAUD,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { historyDisplayBlocks, limitHistoryDisplayMessages, } from "./history.js";
|
|
2
|
+
import { COLORS } from "./theme.js";
|
|
3
|
+
/**
|
|
4
|
+
* Paints the trimmed display slice into the transcript ScrollBox and seeds
|
|
5
|
+
* the copy-out log from the full history so `/copy all` and `/copy <N>` can
|
|
6
|
+
* reach back further than what is actually rendered on resume.
|
|
7
|
+
*/
|
|
8
|
+
export function replayResumeHistory(deps, input) {
|
|
9
|
+
const messages = input.resumeHistoryMessages ?? Number.POSITIVE_INFINITY;
|
|
10
|
+
if (messages > 0 && input.history && input.history.length > 0) {
|
|
11
|
+
const limited = limitHistoryDisplayMessages(historyDisplayBlocks(input.history), messages);
|
|
12
|
+
if (limited.omittedBlocks > 0) {
|
|
13
|
+
deps.appendLine(`[resume] showing last ${messages} message${messages === 1 ? "" : "s"} of prior session history`, COLORS.hint);
|
|
14
|
+
}
|
|
15
|
+
for (const block of limited.blocks) {
|
|
16
|
+
deps.appendBlock(null, block.content, colorForHistoryBlock(block.kind));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Seed the copy-out log from full resumed history (not the trimmed display
|
|
20
|
+
// slice) so `/copy all` and `/copy <N>` can reach back further than what is
|
|
21
|
+
// actually rendered in the transcript on resume.
|
|
22
|
+
if (input.history && input.history.length > 0) {
|
|
23
|
+
for (const block of historyDisplayBlocks(input.history)) {
|
|
24
|
+
if (block.kind === "user") {
|
|
25
|
+
// History blocks for users are formatted as `you:\n<text>`; strip the
|
|
26
|
+
// label so the clipboard text matches what the user originally typed.
|
|
27
|
+
const stripped = block.content.replace(/^you:\n?/, "");
|
|
28
|
+
deps.recordTranscriptEntry("user", stripped);
|
|
29
|
+
}
|
|
30
|
+
else if (block.kind === "agent") {
|
|
31
|
+
deps.recordTranscriptEntry("agent", block.content);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function colorForHistoryBlock(kind) {
|
|
37
|
+
if (kind === "user")
|
|
38
|
+
return COLORS.user;
|
|
39
|
+
if (kind === "reasoning")
|
|
40
|
+
return COLORS.reasoning;
|
|
41
|
+
if (kind === "tool")
|
|
42
|
+
return COLORS.tool;
|
|
43
|
+
if (kind === "error")
|
|
44
|
+
return COLORS.error;
|
|
45
|
+
return COLORS.agent;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=history-replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-replay.js","sourceRoot":"","sources":["../../../src/tui/history-replay.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AA0BpC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAuB,EAAE,KAAyB;IACpF,MAAM,QAAQ,GAAG,KAAK,CAAC,qBAAqB,IAAI,MAAM,CAAC,iBAAiB,CAAC;IACzE,IAAI,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,MAAM,OAAO,GAAG,2BAA2B,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC3F,IAAI,OAAO,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,UAAU,CACb,yBAAyB,QAAQ,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,2BAA2B,EAChG,MAAM,CAAC,IAAI,CACZ,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,iDAAiD;IACjD,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,KAAK,IAAI,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,sEAAsE;gBACtE,sEAAsE;gBACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAClC,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAsB;IAClD,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACxC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IAClD,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACxC,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
|