jeo-code 0.5.6 → 0.5.7
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.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/package.json +1 -1
- package/src/commands/launch.ts +57 -84
package/README.ja.md
CHANGED
|
@@ -150,11 +150,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
150
150
|
## 変更履歴 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.7]** (2026-06-15) — `/model` picker is default-only, `/clear` resets to the initial screen, ESC clears the input box, and a launch process-listener leak is fixed.
|
|
153
154
|
- **[0.5.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
|
|
154
155
|
- **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
|
|
155
156
|
- **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
|
|
156
157
|
- **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
|
|
157
|
-
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -150,11 +150,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
150
150
|
## 변경 이력 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.7]** (2026-06-15) — `/model` picker is default-only, `/clear` resets to the initial screen, ESC clears the input box, and a launch process-listener leak is fixed.
|
|
153
154
|
- **[0.5.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
|
|
154
155
|
- **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
|
|
155
156
|
- **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
|
|
156
157
|
- **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
|
|
157
|
-
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -150,11 +150,11 @@ Required npm token permissions (repository secret `NPM_TOKEN`):
|
|
|
150
150
|
## Changelog
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.7]** (2026-06-15) — `/model` picker is default-only, `/clear` resets to the initial screen, ESC clears the input box, and a launch process-listener leak is fixed.
|
|
153
154
|
- **[0.5.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
|
|
154
155
|
- **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
|
|
155
156
|
- **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
|
|
156
157
|
- **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
|
|
157
|
-
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -150,11 +150,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
150
150
|
## 更新日志 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.7]** (2026-06-15) — `/model` picker is default-only, `/clear` resets to the initial screen, ESC clears the input box, and a launch process-listener leak is fixed.
|
|
153
154
|
- **[0.5.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
|
|
154
155
|
- **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
|
|
155
156
|
- **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
|
|
156
157
|
- **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
|
|
157
|
-
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
package/src/commands/launch.ts
CHANGED
|
@@ -1931,6 +1931,15 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1931
1931
|
const multilineInput = !!process.stdin.isTTY && jeoEnv("NO_MULTILINE") !== "1";
|
|
1932
1932
|
const loneLfShiftEnter = jeoEnv("MULTILINE") === "1";
|
|
1933
1933
|
const expandSentinel = (s: string): string => (multilineInput ? s.split(SENTINEL).join("\n") : s);
|
|
1934
|
+
// Prompt-scoped process listeners (stdin data/keypress, stdout resize). Registered
|
|
1935
|
+
// once per launch but previously anonymous and never removed — benign for a single
|
|
1936
|
+
// CLI run, but repeated launch() (test harness) accumulated them past Node's
|
|
1937
|
+
// 10-listener default → MaxListenersExceededWarning + a real leak. Track each remover
|
|
1938
|
+
// and drain it on every exit path so the process listener set returns to baseline.
|
|
1939
|
+
const promptListenerCleanups: Array<() => void> = [];
|
|
1940
|
+
const drainPromptListeners = () => {
|
|
1941
|
+
for (const off of promptListenerCleanups.splice(0)) { try { off(); } catch { /* best effort */ } }
|
|
1942
|
+
};
|
|
1934
1943
|
let keyFilter: PassThrough | undefined;
|
|
1935
1944
|
if (multilineInput) {
|
|
1936
1945
|
const kf = new PassThrough();
|
|
@@ -1951,7 +1960,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1951
1960
|
// off) and the xterm "\x1b[27;2;13~" / kitty "\x1b[13;2u" sequences. Enter ("\r")
|
|
1952
1961
|
// passes through and submits.
|
|
1953
1962
|
let kfInPaste = false;
|
|
1954
|
-
|
|
1963
|
+
const kfDataHandler = (chunk: Buffer) => {
|
|
1955
1964
|
const data = chunk.toString("utf8");
|
|
1956
1965
|
let out = "";
|
|
1957
1966
|
let i = 0;
|
|
@@ -1972,7 +1981,9 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1972
1981
|
out += data[i]; i += 1;
|
|
1973
1982
|
}
|
|
1974
1983
|
kf.write(out);
|
|
1975
|
-
}
|
|
1984
|
+
};
|
|
1985
|
+
process.stdin.on("data", kfDataHandler);
|
|
1986
|
+
promptListenerCleanups.push(() => process.stdin.off("data", kfDataHandler));
|
|
1976
1987
|
keyFilter = kf;
|
|
1977
1988
|
// readline now decodes keypresses on `keyFilter`; keep process.stdin emitting
|
|
1978
1989
|
// 'keypress' too so the footer-redraw / paste-marker / picker listeners (registered
|
|
@@ -2032,13 +2043,15 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2032
2043
|
const pasteMerge: { buf: string[]; endWaiters: Array<() => void> } = { buf: [], endWaiters: [] };
|
|
2033
2044
|
let pasteLineFired = false; // the line that resolved rl.question came from inside a paste
|
|
2034
2045
|
if (process.stdin.isTTY) {
|
|
2035
|
-
|
|
2046
|
+
const pasteKeypressHandler = (_ch: string, key: { name?: string } | undefined) => {
|
|
2036
2047
|
if (key?.name === "paste-start") { promptPasteActive = true; pasteMerge.buf = []; }
|
|
2037
2048
|
else if (key?.name === "paste-end") {
|
|
2038
2049
|
promptPasteActive = false;
|
|
2039
2050
|
for (const w of pasteMerge.endWaiters.splice(0)) w();
|
|
2040
2051
|
}
|
|
2041
|
-
}
|
|
2052
|
+
};
|
|
2053
|
+
process.stdin.on("keypress", pasteKeypressHandler);
|
|
2054
|
+
promptListenerCleanups.push(() => process.stdin.off("keypress", pasteKeypressHandler));
|
|
2042
2055
|
// Enable bracketed paste for the REPL lifetime (restored on exit below):
|
|
2043
2056
|
// terminals only wrap pastes in the 200~/201~ markers once the app opts in.
|
|
2044
2057
|
process.stdout.write("\x1b[?2004h");
|
|
@@ -2540,28 +2553,13 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2540
2553
|
const notReadyWarning = (st: { name: string; label: string }): string =>
|
|
2541
2554
|
` ! ${st.name} is not call-ready yet (${st.label}) — run /provider login antigravity before the first turn.`;
|
|
2542
2555
|
|
|
2543
|
-
|
|
2556
|
+
|
|
2544
2557
|
const MODEL_BADGE_ROLE_ORDER = ["planner", "architect", "executor", "critic"] as const;
|
|
2545
2558
|
|
|
2546
2559
|
const roleBadgeColor = (roleId: string): ModelAssignmentBadge["color"] =>
|
|
2547
2560
|
roleId === "executor" || roleId === "architect" || roleId === "planner" || roleId === "critic" ? roleId : "critic";
|
|
2548
2561
|
|
|
2549
|
-
|
|
2550
|
-
const roles = allSubagentRoles(config);
|
|
2551
|
-
const emitted = new Set<string>();
|
|
2552
|
-
const out: ReturnType<typeof allSubagentRoles> = [];
|
|
2553
|
-
for (const id of CORE_MODEL_ACTION_ROLE_ORDER) {
|
|
2554
|
-
const role = roles.find(r => r.id === id);
|
|
2555
|
-
if (role) {
|
|
2556
|
-
emitted.add(role.id);
|
|
2557
|
-
out.push(role);
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
for (const role of roles) {
|
|
2561
|
-
if (!emitted.has(role.id)) out.push(role);
|
|
2562
|
-
}
|
|
2563
|
-
return out;
|
|
2564
|
-
};
|
|
2562
|
+
|
|
2565
2563
|
|
|
2566
2564
|
const modelPickerAssignments = async (): Promise<ModelAssignmentBadge[]> => {
|
|
2567
2565
|
const cfg = await readGlobalConfig();
|
|
@@ -2732,7 +2730,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2732
2730
|
choices.push({
|
|
2733
2731
|
value: "heading:default",
|
|
2734
2732
|
label: "Set as DEFAULT (Default)",
|
|
2735
|
-
hint: `${config.defaultModel} (${currentDefaultThinking})`,
|
|
2733
|
+
hint: `${config.defaultModel} (${currentDefaultThinking}) · roles → /agents`,
|
|
2736
2734
|
disabled: true,
|
|
2737
2735
|
});
|
|
2738
2736
|
appendChildren([
|
|
@@ -2744,73 +2742,21 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2744
2742
|
})),
|
|
2745
2743
|
]);
|
|
2746
2744
|
|
|
2747
|
-
for (const role of orderedModelRoles(config)) {
|
|
2748
|
-
const roleThinking = resolveSubagentThinking(role.id, config) ?? "inherit";
|
|
2749
|
-
choices.push({
|
|
2750
|
-
value: `heading:${role.id}`,
|
|
2751
|
-
label: `Set as ${role.title.toUpperCase()} (${role.title})`,
|
|
2752
|
-
hint: `${resolveSubagentModel(role.id, config)} (${roleThinking})`,
|
|
2753
|
-
disabled: true,
|
|
2754
|
-
});
|
|
2755
|
-
appendChildren([
|
|
2756
|
-
{ value: `${role.id}:keep`, label: "Set model only", hint: `keep thinking ${roleThinking} · set via /agents edit` },
|
|
2757
|
-
]);
|
|
2758
|
-
}
|
|
2759
|
-
|
|
2760
|
-
choices.push({
|
|
2761
|
-
value: "preset:openai-codex",
|
|
2762
|
-
label: "Apply OpenAI Codex role preset",
|
|
2763
|
-
hint: "Default medium · Executor low · Architect xhigh · Planner medium · Critic high",
|
|
2764
|
-
});
|
|
2765
2745
|
return choices;
|
|
2766
2746
|
};
|
|
2767
2747
|
|
|
2768
|
-
|
|
2769
|
-
const roleThinking: Record<(typeof CORE_MODEL_ACTION_ROLE_ORDER)[number], ThinkLevel> = {
|
|
2770
|
-
executor: "low",
|
|
2771
|
-
architect: "xhigh",
|
|
2772
|
-
planner: "medium",
|
|
2773
|
-
critic: "high",
|
|
2774
|
-
};
|
|
2775
|
-
await saveConfigPatch(raw => {
|
|
2776
|
-
let subagents = raw.subagents ?? {};
|
|
2777
|
-
for (const roleId of CORE_MODEL_ACTION_ROLE_ORDER) {
|
|
2778
|
-
subagents = withSubagentSetting({ subagents }, roleId, { model: target, thinking: roleThinking[roleId] });
|
|
2779
|
-
}
|
|
2780
|
-
return {
|
|
2781
|
-
...rememberModelPatch(raw, target),
|
|
2782
|
-
thinkingLevel: "medium",
|
|
2783
|
-
subagents,
|
|
2784
|
-
};
|
|
2785
|
-
});
|
|
2786
|
-
sessionModel = target;
|
|
2787
|
-
sessionThinking = "medium";
|
|
2788
|
-
const { resolved, provider } = await describeModel(target);
|
|
2789
|
-
const st = (await describeAllProviders(cfgForPick)).find(s => s.name === provider);
|
|
2790
|
-
console.log(`OpenAI Codex role preset applied to ${formatModelLine({ label: target, resolved, provider, ready: st?.ready })} — Default medium, Executor low, Architect xhigh, Planner medium, Critic high`);
|
|
2791
|
-
};
|
|
2748
|
+
|
|
2792
2749
|
|
|
2793
2750
|
|
|
2794
2751
|
const applyPickedModelWithTarget = async (target: string): Promise<boolean> => {
|
|
2795
2752
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
2796
2753
|
const cfgForPick = await readGlobalConfig();
|
|
2754
|
+
// `/model` only assigns the DEFAULT model + (optionally) the default thinking.
|
|
2755
|
+
// Per-role model and thinking are configured in /agents (and /agents edit).
|
|
2797
2756
|
const choice = await pickFromOptions(`Model Name: ${displayModelName(target)}\n\nAction for: ${target}`, modelActionChoices(cfgForPick)) ?? "default:keep";
|
|
2798
|
-
|
|
2799
|
-
await applyOpenAiCodexRolePreset(target, cfgForPick);
|
|
2800
|
-
return true;
|
|
2801
|
-
}
|
|
2802
|
-
const [applyTo, action = "keep"] = choice.split(":", 2);
|
|
2803
|
-
if (applyTo === "heading") return false;
|
|
2804
|
-
const roleTarget = applyTo !== "default" ? getSubagentRole(applyTo, cfgForPick) : undefined;
|
|
2757
|
+
const [, action = "keep"] = choice.split(":", 2);
|
|
2805
2758
|
const { resolved, provider } = await describeModel(target);
|
|
2806
2759
|
const st = (await describeAllProviders(cfgForPick)).find(s => s.name === provider);
|
|
2807
|
-
if (roleTarget) {
|
|
2808
|
-
const thinkPatch = action === "inherit" ? { thinking: undefined } : isThinkingLevel(action) ? { thinking: action } : {};
|
|
2809
|
-
await saveConfigPatch(raw => ({ subagents: withSubagentSetting(raw, roleTarget.id, { model: target, ...thinkPatch }) }));
|
|
2810
|
-
const thinkNote = action !== "keep" ? ` · thinking ${action}` : "";
|
|
2811
|
-
console.log(`Subagent '${roleTarget.id}' model set to ${formatModelLine({ label: target, resolved, provider, ready: st?.ready })}${thinkNote} — saved (change anytime via /model or /agents)`);
|
|
2812
|
-
return true;
|
|
2813
|
-
}
|
|
2814
2760
|
sessionModel = target;
|
|
2815
2761
|
const defaultThinking = isThinkingLevel(action) ? action : undefined;
|
|
2816
2762
|
if (defaultThinking) {
|
|
@@ -2820,7 +2766,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2820
2766
|
...rememberModelPatch(raw, target),
|
|
2821
2767
|
...(defaultThinking ? { thinkingLevel: defaultThinking } : {}),
|
|
2822
2768
|
}));
|
|
2823
|
-
console.log(`Model set to ${formatModelLine({ label: target, resolved, provider, ready: st?.ready })}${defaultThinking ? ` · thinking ${defaultThinking}` : ""} — saved as default`);
|
|
2769
|
+
console.log(`Model set to ${formatModelLine({ label: target, resolved, provider, ready: st?.ready })}${defaultThinking ? ` · thinking ${defaultThinking}` : ""} — saved as default. Role models/thinking: /agents`);
|
|
2824
2770
|
return true;
|
|
2825
2771
|
};
|
|
2826
2772
|
|
|
@@ -2925,7 +2871,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2925
2871
|
|
|
2926
2872
|
if (previewEnabled) {
|
|
2927
2873
|
process.once("exit", () => out.write("\x1b[?25h")); // safety net: never leave the cursor hidden
|
|
2928
|
-
|
|
2874
|
+
const footerKeypressHandler = (_ch: string, key: { name?: string; ctrl?: boolean; meta?: boolean } | undefined) => {
|
|
2929
2875
|
if (key?.ctrl && key.name === "c") {
|
|
2930
2876
|
forceExitFromCtrlC();
|
|
2931
2877
|
return;
|
|
@@ -3040,18 +2986,22 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
3040
2986
|
drawFooter(previewLines(typedLine));
|
|
3041
2987
|
} catch { /* ignore render races */ }
|
|
3042
2988
|
});
|
|
3043
|
-
}
|
|
2989
|
+
};
|
|
2990
|
+
process.stdin.on("keypress", footerKeypressHandler);
|
|
2991
|
+
promptListenerCleanups.push(() => process.stdin.off("keypress", footerKeypressHandler));
|
|
3044
2992
|
// Idle-prompt resize: re-reserve the footer at the new terminal height so the
|
|
3045
2993
|
// fixed reservation stays accurate (otherwise the next paint would target the
|
|
3046
2994
|
// old row count and either over-shoot or under-paint the reserved region).
|
|
3047
|
-
|
|
2995
|
+
const idleResizeHandler = () => {
|
|
3048
2996
|
if (!previewArmed) return;
|
|
3049
2997
|
try {
|
|
3050
2998
|
disarmPreview();
|
|
3051
2999
|
armPreview();
|
|
3052
3000
|
drawFooter(promptHistoryLines ? historyPreviewLines(promptHistoryLines) : previewLines(typedLine, navIdx));
|
|
3053
3001
|
} catch { /* ignore resize render races */ }
|
|
3054
|
-
}
|
|
3002
|
+
};
|
|
3003
|
+
process.stdout.on("resize", idleResizeHandler);
|
|
3004
|
+
promptListenerCleanups.push(() => process.stdout.off("resize", idleResizeHandler));
|
|
3055
3005
|
}
|
|
3056
3006
|
|
|
3057
3007
|
while (true) {
|
|
@@ -3162,7 +3112,14 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
3162
3112
|
}
|
|
3163
3113
|
if (input === "/clear") {
|
|
3164
3114
|
history.length = 1;
|
|
3165
|
-
|
|
3115
|
+
// Back to the initial screen: wipe the conversation, clear the terminal +
|
|
3116
|
+
// scrollback, and re-render the welcome banner so /clear looks like a fresh launch.
|
|
3117
|
+
if (process.stdout.isTTY) {
|
|
3118
|
+
disarmPreview();
|
|
3119
|
+
process.stdout.write("\x1b[2J\x1b[3J\x1b[H"); // clear screen + scrollback + cursor home
|
|
3120
|
+
console.log(renderWelcome(welcomeData).join("\n"));
|
|
3121
|
+
}
|
|
3122
|
+
console.log("(history cleared — back to the start screen)");
|
|
3166
3123
|
continue;
|
|
3167
3124
|
}
|
|
3168
3125
|
if (input === "/compact") {
|
|
@@ -3281,6 +3238,20 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
3281
3238
|
if (history[k]!.role === "assistant" && !lastReply) lastReply = String(history[k]!.content ?? "");
|
|
3282
3239
|
if (lastUserInput && lastReply) break;
|
|
3283
3240
|
}
|
|
3241
|
+
// Seed readline's input history so ↑ in the prompt recalls THIS session's
|
|
3242
|
+
// prior prompts (not just lines typed in the current run). readline history
|
|
3243
|
+
// is newest-first; unshift in chronological order so the session's newest
|
|
3244
|
+
// prompt lands at the front (first ↑). Skip injected/framed messages.
|
|
3245
|
+
const rli = rl as unknown as { history?: string[] };
|
|
3246
|
+
if (Array.isArray(rli.history)) {
|
|
3247
|
+
const priorPrompts = history
|
|
3248
|
+
.filter(m => m.role === "user")
|
|
3249
|
+
.map(m => String(m.content ?? "").trim())
|
|
3250
|
+
.filter(c => c && !c.startsWith("Tool [") && !c.startsWith("[mid-turn steering") && !c.startsWith("[Earlier conversation summary]"));
|
|
3251
|
+
for (const p of priorPrompts) {
|
|
3252
|
+
if (rli.history[0] !== p) rli.history.unshift(p);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3284
3255
|
const sep = "─".repeat(Math.min(48, Math.max(20, (process.stdout.columns ?? 80) - 1)));
|
|
3285
3256
|
logLines([
|
|
3286
3257
|
sep,
|
|
@@ -4359,6 +4330,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
4359
4330
|
} catch { /* best effort */ }
|
|
4360
4331
|
process.removeListener("SIGINT", forceExitFromCtrlC);
|
|
4361
4332
|
process.stdin.off("data", forceExitOnCtrlCByte);
|
|
4333
|
+
drainPromptListeners();
|
|
4362
4334
|
restorePromptRawMode();
|
|
4363
4335
|
process.exit(130);
|
|
4364
4336
|
}
|
|
@@ -4374,6 +4346,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
4374
4346
|
if (sessionId && !flags.noSession) console.log(formatResumeHint(sessionId));
|
|
4375
4347
|
process.removeListener("SIGINT", forceExitFromCtrlC);
|
|
4376
4348
|
process.stdin.off("data", forceExitOnCtrlCByte);
|
|
4349
|
+
drainPromptListeners();
|
|
4377
4350
|
restorePromptRawMode();
|
|
4378
4351
|
gracefulReadlineClose = true;
|
|
4379
4352
|
rl.close();
|