summaryrefslogtreecommitdiff
path: root/internal/askcli
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-11 08:34:41 +0300
committerPaul Buetow <paul@buetow.org>2026-06-11 08:34:41 +0300
commite95f3fdf0a66ba05ba2c8fb7e755e107f9cf7991 (patch)
tree412c7e21ec9c317beb99ed7fe0d4d93a7dacbe50 /internal/askcli
parent73dadb573f92dca310036e8793932e94277abd62 (diff)
Thread context.Context through blocking I/O entry points
Accept ctx as the first parameter on the blocking I/O entry points and propagate it to downstream blocking calls so the work is cancellable from the process entry point: - appconfig.Load / LoadWithOptions: honor ctx before the blocking file reads, returning defaults on a cancelled context. - LSP: lsp.Server.Run(ctx) ties the serve loop to the caller context via a new watchParentContext bridge (cancels the server context, aborting in-flight LLM work). Threaded through hexailsp.Run/RunWithConfig/ RunWithFactory and runtimeconfig.Store.Reload. - MCP: mcp.Server.Run(ctx) stops accepting requests once ctx is cancelled; threaded through hexaimcp.Run/RunWithFactory/RunBackfill. - editor: RunEditor/OpenTempAndEdit/OpenFile take ctx and use exec.CommandContext so a cancelled context kills the editor subprocess; threaded through hexaicli, hexaiaction and askcli call sites. Top-level callers (cmd/hexai-lsp-server, cmd/hexai-mcp-server) now build a signal-cancelled context (SIGINT/SIGTERM) so shutdown tears the run down cleanly. Updated comments to explain the cancellation flow and added cancellation tests for the LSP/MCP loops, editor, and config load. All tests pass with -race; cross-package coverage 86.2%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/askcli')
-rw-r--r--internal/askcli/command_edit.go11
-rw-r--r--internal/askcli/command_edit_test.go4
2 files changed, 8 insertions, 7 deletions
diff --git a/internal/askcli/command_edit.go b/internal/askcli/command_edit.go
index 27c9106..a82d575 100644
--- a/internal/askcli/command_edit.go
+++ b/internal/askcli/command_edit.go
@@ -10,9 +10,10 @@ import (
// captureFromEditor opens the user's editor on a temporary file pre-filled with
// the given initial content and returns its trimmed contents after the editor
-// exits. It is a variable so tests can stub it.
-var captureFromEditor = func(initial []byte) (string, error) {
- return editor.OpenTempAndEdit(initial)
+// exits. ctx is forwarded to the editor subprocess so it can be cancelled with
+// the surrounding command. It is a variable so tests can stub it.
+var captureFromEditor = func(ctx context.Context, initial []byte) (string, error) {
+ return editor.OpenTempAndEdit(ctx, initial)
}
// handleEdit opens the configured editor on a temporary file. With no selector
@@ -22,7 +23,7 @@ func (d *Dispatcher) handleEdit(ctx context.Context, args []string, stdout, stde
if len(args) >= 2 {
return d.editTaskDescription(ctx, args[1], stdout, stderr)
}
- description, err := captureFromEditor(nil)
+ description, err := captureFromEditor(ctx, nil)
if err != nil {
writeInfoError(stderr, err)
return 1, nil
@@ -43,7 +44,7 @@ func (d *Dispatcher) editTaskDescription(ctx context.Context, selector string, s
return code, nil
}
- description, err := captureFromEditor([]byte(tasks[0].Description))
+ description, err := captureFromEditor(ctx, []byte(tasks[0].Description))
if err != nil {
writeInfoError(stderr, err)
return 1, nil
diff --git a/internal/askcli/command_edit_test.go b/internal/askcli/command_edit_test.go
index 80cbd56..4db19c1 100644
--- a/internal/askcli/command_edit_test.go
+++ b/internal/askcli/command_edit_test.go
@@ -12,7 +12,7 @@ import (
func stubEditorCapture(t *testing.T, content string, err error) {
t.Helper()
old := captureFromEditor
- captureFromEditor = func(initial []byte) (string, error) {
+ captureFromEditor = func(_ context.Context, initial []byte) (string, error) {
return content, err
}
t.Cleanup(func() { captureFromEditor = old })
@@ -60,7 +60,7 @@ func TestHandleEdit_ExistingTaskModifiesDescription(t *testing.T) {
var initialContent []byte
old := captureFromEditor
- captureFromEditor = func(initial []byte) (string, error) {
+ captureFromEditor = func(_ context.Context, initial []byte) (string, error) {
initialContent = initial
return "updated description", nil
}