From c02306291ca0793ed093e9936db82ebd1540fe4a Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Thu, 2 Jul 2026 19:56:03 +0200 Subject: [PATCH] 159-work-on-headless-non-interactive --- CHANGELOG.md | 4 ++++ pkg/ops/workon.go | 5 ++++- pkg/ops/workon_test.go | 7 +++++++ .../159-work-on-headless-non-interactive.md | 7 ++++++- 4 files changed, 21 insertions(+), 2 deletions(-) rename prompts/{in-progress => completed}/159-work-on-headless-non-interactive.md (94%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a438a..e1a2969 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- fix(workon): append `--non-interactive` to the headless `claude --print` bootstrap prompt in `handleClaudeSession` to prevent the 5-minute hang when `work-on` command needs input that cannot be answered headlessly + All notable changes to this project will be documented in this file. Please choose versions by [Semantic Versioning](http://semver.org/). diff --git a/pkg/ops/workon.go b/pkg/ops/workon.go index 03be09a..694b6f0 100644 --- a/pkg/ops/workon.go +++ b/pkg/ops/workon.go @@ -188,7 +188,10 @@ func (w *workOnOperation) handleClaudeSession( if w.starter == nil { return "", ErrStarterUnavailable } - prompt := fmt.Sprintf(`%s "%s"`, vault.GetWorkOnCommand(), task.FilePath) + // The bootstrap always runs headless `claude --print`, which cannot answer + // AskUserQuestion; --non-interactive tells the work-on command to take safe + // defaults instead of prompting (prevents the 5m headless hang). + prompt := fmt.Sprintf(`%s "%s" --non-interactive`, vault.GetWorkOnCommand(), task.FilePath) slog.Info("starting claude session", "task", task.Name) sessionID, err := w.starter.StartSession(ctx, prompt, vaultPath, task.Name) if err != nil { diff --git a/pkg/ops/workon_test.go b/pkg/ops/workon_test.go index 75930ed..c86b6ad 100644 --- a/pkg/ops/workon_test.go +++ b/pkg/ops/workon_test.go @@ -194,6 +194,13 @@ var _ = Describe("WorkOnOperation", func() { _, prompt, _, _ := mockStarter.StartSessionArgsForCall(0) Expect(prompt).To(MatchRegexp(`^/custom-cmd "`)) }) + + It("appends --non-interactive to the bootstrap prompt", func() { + Expect(mockStarter.StartSessionCallCount()).To(Equal(1)) + _, prompt, _, _ := mockStarter.StartSessionArgsForCall(0) + Expect(prompt).To(MatchRegexp(` --non-interactive$`)) + Expect(prompt).To(MatchRegexp(`/path/to/vault/tasks/my-task\.md`)) + }) }) Context("when starter is nil and task has no cached session ID", func() { diff --git a/prompts/in-progress/159-work-on-headless-non-interactive.md b/prompts/completed/159-work-on-headless-non-interactive.md similarity index 94% rename from prompts/in-progress/159-work-on-headless-non-interactive.md rename to prompts/completed/159-work-on-headless-non-interactive.md index 1d5bced..db93640 100644 --- a/prompts/in-progress/159-work-on-headless-non-interactive.md +++ b/prompts/completed/159-work-on-headless-non-interactive.md @@ -1,7 +1,12 @@ --- -status: approved +status: completed +summary: Appended `--non-interactive` to the headless bootstrap prompt in `handleClaudeSession` and added unit test verifying the flag is present and the task file path is preserved +execution_id: vault-cli-exec-159-work-on-headless-non-interactive +dark-factory-version: v0.191.0 created: "2026-07-02T18:15:00Z" queued: "2026-07-02T17:51:16Z" +started: "2026-07-02T17:54:37Z" +completed: "2026-07-02T17:56:03Z" ---