gsd-pi 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +18 -1
- package/dist/onboarding.js +3 -0
- package/dist/resource-loader.d.ts +2 -0
- package/dist/resource-loader.js +36 -1
- package/dist/resources/extensions/bg-shell/index.ts +51 -7
- package/dist/resources/extensions/gsd/auto-worktree.ts +509 -0
- package/dist/resources/extensions/gsd/auto.ts +381 -13
- package/dist/resources/extensions/gsd/commands.ts +9 -3
- package/dist/resources/extensions/gsd/doctor.ts +254 -3
- package/dist/resources/extensions/gsd/git-self-heal.ts +198 -0
- package/dist/resources/extensions/gsd/git-service.ts +11 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +81 -9
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
- package/dist/resources/extensions/gsd/preferences.ts +209 -1
- package/dist/resources/extensions/gsd/prompt-loader.ts +28 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +10 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -3
- package/dist/resources/extensions/gsd/prompts/queue.md +3 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +32 -29
- package/dist/resources/extensions/gsd/templates/context.md +1 -1
- package/dist/resources/extensions/gsd/templates/state.md +3 -3
- package/dist/resources/extensions/gsd/tests/auto-worktree-merge.test.ts +282 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +259 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +147 -0
- package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +246 -0
- package/dist/resources/extensions/gsd/tests/doctor.test.ts +115 -1
- package/dist/resources/extensions/gsd/tests/git-self-heal.test.ts +234 -0
- package/dist/resources/extensions/gsd/tests/isolation-resolver.test.ts +107 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +88 -0
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
- package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
- package/dist/resources/extensions/gsd/tests/worktree-e2e.test.ts +315 -0
- package/dist/resources/extensions/gsd/types.ts +109 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +6 -4
- package/dist/resources/extensions/search-the-web/command-search-provider.ts +8 -4
- package/dist/resources/extensions/search-the-web/native-search.ts +15 -10
- package/dist/resources/extensions/search-the-web/provider.ts +19 -2
- package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
- package/dist/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
- package/dist/resources/extensions/search-the-web/tool-search.ts +62 -3
- package/dist/wizard.js +1 -0
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +169 -55
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +13 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +16 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +91 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +273 -63
- package/packages/pi-agent-core/src/agent.ts +24 -0
- package/packages/pi-agent-core/src/types.ts +98 -0
- package/packages/pi-ai/dist/env-api-keys.js +1 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +314 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +236 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/env-api-keys.ts +1 -0
- package/packages/pi-ai/src/models.generated.ts +236 -0
- package/packages/pi-ai/src/types.ts +2 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +2 -1
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +69 -8
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +3 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +2 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +76 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +3 -3
- package/packages/pi-coding-agent/src/core/system-prompt.ts +9 -0
- package/packages/pi-tui/dist/components/editor.d.ts +11 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +64 -6
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +71 -6
- package/src/resources/extensions/bg-shell/index.ts +51 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +509 -0
- package/src/resources/extensions/gsd/auto.ts +381 -13
- package/src/resources/extensions/gsd/commands.ts +9 -3
- package/src/resources/extensions/gsd/doctor.ts +254 -3
- package/src/resources/extensions/gsd/git-self-heal.ts +198 -0
- package/src/resources/extensions/gsd/git-service.ts +11 -0
- package/src/resources/extensions/gsd/guided-flow.ts +81 -9
- package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
- package/src/resources/extensions/gsd/preferences.ts +209 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +28 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
- package/src/resources/extensions/gsd/prompts/queue.md +3 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +32 -29
- package/src/resources/extensions/gsd/templates/context.md +1 -1
- package/src/resources/extensions/gsd/templates/state.md +3 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-merge.test.ts +282 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +259 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/git-self-heal.test.ts +234 -0
- package/src/resources/extensions/gsd/tests/isolation-resolver.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +315 -0
- package/src/resources/extensions/gsd/types.ts +109 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +6 -4
- package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
- package/src/resources/extensions/search-the-web/native-search.ts +15 -10
- package/src/resources/extensions/search-the-web/provider.ts +19 -2
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
|
@@ -150,6 +150,12 @@ export class Editor implements Component, Focusable {
|
|
|
150
150
|
private autocompletePrefix: string = "";
|
|
151
151
|
private autocompleteMaxVisible: number = 5;
|
|
152
152
|
|
|
153
|
+
// Debounce for @ file autocomplete to prevent blocking the event loop
|
|
154
|
+
// with synchronous fuzzyFind calls on every keystroke
|
|
155
|
+
private autocompleteDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
156
|
+
private lastAutocompleteLookupPrefix: string | null = null;
|
|
157
|
+
private static readonly AUTOCOMPLETE_DEBOUNCE_MS = 150;
|
|
158
|
+
|
|
153
159
|
// Paste tracking for large pastes
|
|
154
160
|
private pastes: Map<number, string> = new Map();
|
|
155
161
|
private pasteCounter: number = 0;
|
|
@@ -965,9 +971,10 @@ export class Editor implements Component, Focusable {
|
|
|
965
971
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
966
972
|
this.tryTriggerAutocomplete();
|
|
967
973
|
}
|
|
968
|
-
// Check if we're in an @ file reference context
|
|
974
|
+
// Check if we're in an @ file reference context (debounce to avoid
|
|
975
|
+
// blocking the event loop with synchronous fuzzyFind on every keystroke)
|
|
969
976
|
else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
|
|
970
|
-
this.
|
|
977
|
+
this.debouncedTriggerAutocomplete();
|
|
971
978
|
}
|
|
972
979
|
}
|
|
973
980
|
} else {
|
|
@@ -975,6 +982,23 @@ export class Editor implements Component, Focusable {
|
|
|
975
982
|
}
|
|
976
983
|
}
|
|
977
984
|
|
|
985
|
+
/**
|
|
986
|
+
* Debounced version of tryTriggerAutocomplete for @ file reference context.
|
|
987
|
+
* Prevents synchronous fuzzyFind calls from blocking the event loop on every keystroke.
|
|
988
|
+
*/
|
|
989
|
+
private debouncedTriggerAutocomplete(): void {
|
|
990
|
+
if (this.autocompleteDebounceTimer) {
|
|
991
|
+
clearTimeout(this.autocompleteDebounceTimer);
|
|
992
|
+
this.autocompleteDebounceTimer = null;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
this.autocompleteDebounceTimer = setTimeout(() => {
|
|
996
|
+
this.autocompleteDebounceTimer = null;
|
|
997
|
+
this.tryTriggerAutocomplete();
|
|
998
|
+
this.tui.requestRender();
|
|
999
|
+
}, Editor.AUTOCOMPLETE_DEBOUNCE_MS);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
978
1002
|
private handlePaste(pastedText: string): void {
|
|
979
1003
|
this.historyIndex = -1; // Exit history browsing mode
|
|
980
1004
|
this.lastAction = null;
|
|
@@ -1133,9 +1157,9 @@ export class Editor implements Component, Focusable {
|
|
|
1133
1157
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
1134
1158
|
this.tryTriggerAutocomplete();
|
|
1135
1159
|
}
|
|
1136
|
-
// @ file reference context
|
|
1160
|
+
// @ file reference context (debounced to avoid blocking event loop)
|
|
1137
1161
|
else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
|
|
1138
|
-
this.
|
|
1162
|
+
this.debouncedTriggerAutocomplete();
|
|
1139
1163
|
}
|
|
1140
1164
|
}
|
|
1141
1165
|
}
|
|
@@ -1440,9 +1464,9 @@ export class Editor implements Component, Focusable {
|
|
|
1440
1464
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
1441
1465
|
this.tryTriggerAutocomplete();
|
|
1442
1466
|
}
|
|
1443
|
-
// @ file reference context
|
|
1467
|
+
// @ file reference context (debounced to avoid blocking event loop)
|
|
1444
1468
|
else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
|
|
1445
|
-
this.
|
|
1469
|
+
this.debouncedTriggerAutocomplete();
|
|
1446
1470
|
}
|
|
1447
1471
|
}
|
|
1448
1472
|
}
|
|
@@ -2020,6 +2044,15 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
|
|
|
2020
2044
|
this.autocompleteState = null;
|
|
2021
2045
|
this.autocompleteList = undefined;
|
|
2022
2046
|
this.autocompletePrefix = "";
|
|
2047
|
+
this.clearAutocompleteDebounce();
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
private clearAutocompleteDebounce(): void {
|
|
2051
|
+
if (this.autocompleteDebounceTimer) {
|
|
2052
|
+
clearTimeout(this.autocompleteDebounceTimer);
|
|
2053
|
+
this.autocompleteDebounceTimer = null;
|
|
2054
|
+
}
|
|
2055
|
+
this.lastAutocompleteLookupPrefix = null;
|
|
2023
2056
|
}
|
|
2024
2057
|
|
|
2025
2058
|
public isShowingAutocomplete(): boolean {
|
|
@@ -2034,6 +2067,38 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
|
|
|
2034
2067
|
return;
|
|
2035
2068
|
}
|
|
2036
2069
|
|
|
2070
|
+
// Check if we're in an @ file reference context — these trigger expensive
|
|
2071
|
+
// synchronous fuzzyFind calls that block the event loop. Debounce them so
|
|
2072
|
+
// rapid typing doesn't cascade into dozens of blocking searches.
|
|
2073
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
2074
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
2075
|
+
if (this.autocompletePrefix.startsWith("@") || textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
|
|
2076
|
+
this.debouncedUpdateAutocompleteSuggestions();
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
this.applyAutocompleteSuggestions();
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
private debouncedUpdateAutocompleteSuggestions(): void {
|
|
2084
|
+
// Clear any pending debounce
|
|
2085
|
+
if (this.autocompleteDebounceTimer) {
|
|
2086
|
+
clearTimeout(this.autocompleteDebounceTimer);
|
|
2087
|
+
this.autocompleteDebounceTimer = null;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
this.autocompleteDebounceTimer = setTimeout(() => {
|
|
2091
|
+
this.autocompleteDebounceTimer = null;
|
|
2092
|
+
// Guard: autocomplete may have been cancelled during debounce wait
|
|
2093
|
+
if (!this.autocompleteState || !this.autocompleteProvider) return;
|
|
2094
|
+
this.applyAutocompleteSuggestions();
|
|
2095
|
+
this.tui.requestRender();
|
|
2096
|
+
}, Editor.AUTOCOMPLETE_DEBOUNCE_MS);
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
private applyAutocompleteSuggestions(): void {
|
|
2100
|
+
if (!this.autocompleteProvider) return;
|
|
2101
|
+
|
|
2037
2102
|
const suggestions = this.autocompleteProvider.getSuggestions(
|
|
2038
2103
|
this.state.lines,
|
|
2039
2104
|
this.state.cursorLine,
|
|
@@ -574,6 +574,7 @@ interface StartOptions {
|
|
|
574
574
|
type?: ProcessType;
|
|
575
575
|
readyPattern?: string;
|
|
576
576
|
readyPort?: number;
|
|
577
|
+
readyTimeout?: number;
|
|
577
578
|
group?: string;
|
|
578
579
|
env?: Record<string, string>;
|
|
579
580
|
}
|
|
@@ -689,7 +690,7 @@ function startProcess(opts: StartOptions): BgProcess {
|
|
|
689
690
|
|
|
690
691
|
// Port probing for server-type processes
|
|
691
692
|
if (bg.readyPort) {
|
|
692
|
-
startPortProbing(bg, bg.readyPort);
|
|
693
|
+
startPortProbing(bg, bg.readyPort, opts.readyTimeout);
|
|
693
694
|
}
|
|
694
695
|
|
|
695
696
|
// Shell sessions are ready immediately after spawn
|
|
@@ -707,9 +708,17 @@ function startProcess(opts: StartOptions): BgProcess {
|
|
|
707
708
|
|
|
708
709
|
// ── Port Probing Loop ──────────────────────────────────────────────────────
|
|
709
710
|
|
|
710
|
-
function startPortProbing(bg: BgProcess, port: number): void {
|
|
711
|
+
function startPortProbing(bg: BgProcess, port: number, customTimeout?: number): void {
|
|
712
|
+
const timeout = customTimeout || DEFAULT_READY_TIMEOUT;
|
|
711
713
|
const interval = setInterval(async () => {
|
|
712
|
-
if (!bg.alive
|
|
714
|
+
if (!bg.alive) {
|
|
715
|
+
clearInterval(interval);
|
|
716
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-10).map(l => l.line);
|
|
717
|
+
const detail = `Process exited (code ${bg.exitCode}) before port ${port} opened${stderrLines.length > 0 ? ` — ${stderrLines.join("; ").slice(0, 200)}` : ""}`;
|
|
718
|
+
addEvent(bg, { type: "port_timeout", detail, data: { port, exitCode: bg.exitCode } });
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (bg.status !== "starting") {
|
|
713
722
|
clearInterval(interval);
|
|
714
723
|
return;
|
|
715
724
|
}
|
|
@@ -722,8 +731,18 @@ function startPortProbing(bg: BgProcess, port: number): void {
|
|
|
722
731
|
}
|
|
723
732
|
}, READY_POLL_INTERVAL);
|
|
724
733
|
|
|
725
|
-
// Stop probing after timeout
|
|
726
|
-
|
|
734
|
+
// Stop probing after timeout — transition to error state so the process
|
|
735
|
+
// doesn't stay in "starting" forever (fixes #428)
|
|
736
|
+
setTimeout(() => {
|
|
737
|
+
clearInterval(interval);
|
|
738
|
+
if (bg.alive && bg.status === "starting") {
|
|
739
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-10).map(l => l.line);
|
|
740
|
+
const detail = `Port ${port} not open after ${timeout}ms${stderrLines.length > 0 ? ` — ${stderrLines.join("; ").slice(0, 200)}` : ""}`;
|
|
741
|
+
bg.status = "error";
|
|
742
|
+
addEvent(bg, { type: "port_timeout", detail, data: { port, timeout } });
|
|
743
|
+
pushAlert(bg, `Port ${port} readiness timeout after ${timeout / 1000}s`);
|
|
744
|
+
}
|
|
745
|
+
}, timeout);
|
|
727
746
|
}
|
|
728
747
|
|
|
729
748
|
// ── Process Kill ───────────────────────────────────────────────────────────
|
|
@@ -864,9 +883,19 @@ async function waitForReady(bg: BgProcess, timeout: number, signal?: AbortSignal
|
|
|
864
883
|
return { ready: false, detail: "Cancelled" };
|
|
865
884
|
}
|
|
866
885
|
if (!bg.alive) {
|
|
886
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
887
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
888
|
+
return {
|
|
889
|
+
ready: false,
|
|
890
|
+
detail: `Process exited before becoming ready (code ${bg.exitCode})${bg.recentErrors.length > 0 ? ` — ${bg.recentErrors.slice(-1)[0]}` : ""}${stderrContext}`,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
if (bg.status === "error") {
|
|
894
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
895
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
867
896
|
return {
|
|
868
897
|
ready: false,
|
|
869
|
-
detail: `Process
|
|
898
|
+
detail: `Process entered error state${bg.readyPort ? ` (port ${bg.readyPort} never opened)` : ""}${stderrContext}`,
|
|
870
899
|
};
|
|
871
900
|
}
|
|
872
901
|
if (bg.status === "ready") {
|
|
@@ -887,7 +916,9 @@ async function waitForReady(bg: BgProcess, timeout: number, signal?: AbortSignal
|
|
|
887
916
|
}
|
|
888
917
|
}
|
|
889
918
|
|
|
890
|
-
|
|
919
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
920
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
921
|
+
return { ready: false, detail: `Timed out after ${timeout}ms waiting for ready signal${stderrContext}` };
|
|
891
922
|
}
|
|
892
923
|
|
|
893
924
|
// ── Query Shell Environment ────────────────────────────────────────────────
|
|
@@ -1234,6 +1265,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
1234
1265
|
cleanupAll();
|
|
1235
1266
|
});
|
|
1236
1267
|
|
|
1268
|
+
// Register signal handlers to clean up bg processes on unexpected exit (fixes #428)
|
|
1269
|
+
// This prevents orphan processes and helps the parent restore terminal state
|
|
1270
|
+
const signalCleanup = () => {
|
|
1271
|
+
cleanupAll();
|
|
1272
|
+
};
|
|
1273
|
+
process.on("SIGTERM", signalCleanup);
|
|
1274
|
+
process.on("SIGINT", signalCleanup);
|
|
1275
|
+
process.on("beforeExit", signalCleanup);
|
|
1276
|
+
|
|
1237
1277
|
// ── Compaction Awareness: Survive Context Resets ───────────────────
|
|
1238
1278
|
|
|
1239
1279
|
/** Build a compact state summary of all alive processes for context re-injection */
|
|
@@ -1424,6 +1464,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1424
1464
|
ready_port: Type.Optional(
|
|
1425
1465
|
Type.Number({ description: "Port to probe for readiness (for start). When open, process is considered ready." }),
|
|
1426
1466
|
),
|
|
1467
|
+
ready_timeout: Type.Optional(
|
|
1468
|
+
Type.Number({ description: "Max milliseconds to wait for ready_port/ready_pattern before marking as error (default: 30000)" }),
|
|
1469
|
+
),
|
|
1427
1470
|
group: Type.Optional(
|
|
1428
1471
|
Type.String({ description: "Group name for related processes (for start, group_status)" }),
|
|
1429
1472
|
),
|
|
@@ -1449,6 +1492,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1449
1492
|
type: params.type as ProcessType | undefined,
|
|
1450
1493
|
readyPattern: params.ready_pattern,
|
|
1451
1494
|
readyPort: params.ready_port,
|
|
1495
|
+
readyTimeout: params.ready_timeout,
|
|
1452
1496
|
group: params.group,
|
|
1453
1497
|
});
|
|
1454
1498
|
|