-
Notifications
You must be signed in to change notification settings - Fork 88
feat(cli): make local-assistant lifecycle ops programmatically reusable #32619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
vex-assistant-bot
merged 3 commits into
main
from
devin/lum-2051-cli-lib-programmatic-reuse
May 30, 2026
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
a8e3836
feat(cli): make local-assistant lifecycle ops programmatically reusable
devin-ai-integration[bot] 4d164b5
test(cli): drop retire-local unit test that collides with global modu…
devin-ai-integration[bot] 12b5a9f
Merge remote-tracking branch 'origin/main' into devin/lum-2051-cli-li…
devin-ai-integration[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; | ||
|
|
||
| import { consoleLifecycleReporter } from "../lifecycle-reporter.js"; | ||
|
|
||
| describe("consoleLifecycleReporter", () => { | ||
| const originalDesktopApp = process.env.VELLUM_DESKTOP_APP; | ||
| let stdoutWriteSpy: ReturnType<typeof spyOn>; | ||
|
|
||
| beforeEach(() => { | ||
| stdoutWriteSpy = spyOn(process.stdout, "write").mockImplementation( | ||
| () => true, | ||
| ); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| stdoutWriteSpy.mockRestore(); | ||
| if (originalDesktopApp === undefined) { | ||
| delete process.env.VELLUM_DESKTOP_APP; | ||
| } else { | ||
| process.env.VELLUM_DESKTOP_APP = originalDesktopApp; | ||
| } | ||
| }); | ||
|
|
||
| test("routes log/warn/error to the matching console methods", () => { | ||
| const logSpy = spyOn(console, "log").mockImplementation(() => {}); | ||
| const warnSpy = spyOn(console, "warn").mockImplementation(() => {}); | ||
| const errorSpy = spyOn(console, "error").mockImplementation(() => {}); | ||
|
|
||
| consoleLifecycleReporter.log("hello"); | ||
| consoleLifecycleReporter.warn("careful"); | ||
| consoleLifecycleReporter.error("boom"); | ||
|
|
||
| expect(logSpy).toHaveBeenCalledWith("hello"); | ||
| expect(warnSpy).toHaveBeenCalledWith("careful"); | ||
| expect(errorSpy).toHaveBeenCalledWith("boom"); | ||
|
|
||
| logSpy.mockRestore(); | ||
| warnSpy.mockRestore(); | ||
| errorSpy.mockRestore(); | ||
| }); | ||
|
|
||
| test("emits the HATCH_PROGRESS stdout contract under VELLUM_DESKTOP_APP", () => { | ||
| process.env.VELLUM_DESKTOP_APP = "1"; | ||
|
|
||
| consoleLifecycleReporter.progress(3, 6, "Starting assistant..."); | ||
|
|
||
| expect(stdoutWriteSpy).toHaveBeenCalledWith( | ||
| `HATCH_PROGRESS:${JSON.stringify({ step: 3, total: 6, label: "Starting assistant..." })}\n`, | ||
| ); | ||
| }); | ||
|
|
||
| test("suppresses progress output when not running under the desktop app", () => { | ||
| delete process.env.VELLUM_DESKTOP_APP; | ||
|
|
||
| consoleLifecycleReporter.progress(1, 6, "Allocating resources..."); | ||
|
|
||
| expect(stdoutWriteSpy).not.toHaveBeenCalled(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { emitProgress } from "./desktop-progress.js"; | ||
|
|
||
| /** | ||
| * Sink for the human-facing and structured output of long-running lifecycle | ||
| * operations (hatch, retire). Injecting it lets an in-process caller (e.g. a | ||
| * desktop main process embedding these functions) observe progress without the | ||
| * operation writing to the terminal, while the CLI keeps its existing stdout. | ||
| */ | ||
| export interface LifecycleReporter { | ||
| /** | ||
| * Coarse step progress. The CLI reporter mirrors this to the desktop | ||
| * `HATCH_PROGRESS:` stdout channel. | ||
| */ | ||
| progress(step: number, total: number, label: string): void; | ||
| log(message: string): void; | ||
| warn(message: string): void; | ||
| error(message: string): void; | ||
| } | ||
|
|
||
| /** | ||
| * Reporter used by the CLI commands: human-readable lines to the console plus | ||
| * structured step events on the desktop progress channel. Reproduces the exact | ||
| * terminal output — and the `HATCH_PROGRESS:` lines under `VELLUM_DESKTOP_APP` — | ||
| * that existing subprocess consumers parse. | ||
| */ | ||
| export const consoleLifecycleReporter: LifecycleReporter = { | ||
| progress: (step, total, label) => emitProgress(step, total, label), | ||
| log: (message) => console.log(message), | ||
| warn: (message) => console.warn(message), | ||
| error: (message) => console.error(message), | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an in-process caller supplies
options.reporter, this still invokes the nested lifecycle helpers without any reporting hook;startGateway(and the precedingstartLocalDaemon) write status and warning messages directly withconsole.log/console.warnincli/src/lib/local.ts. In a normal successful hatch this means the new reusable API still pollutes stdout/stderr and the caller cannot observe all lifecycle output viaLifecycleReporter, defeating the purpose of avoiding subprocess stdout scraping. Please thread the reporter through these helpers or otherwise suppress their console output when a custom reporter is provided.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, and correct —
hatchLocal's own progress/log lines now route through the reporter, but the nestedstartLocalDaemon/startGatewayhelpers inlib/local.tsstillconsole.log/console.warndirectly, so an in-process caller with a custom reporter won't yet see every line through it.Deliberately scoping that out of this PR rather than fixing it here. Threading the reporter through
local.tstouches shared infrastructure with several callers, and it's better landed alongside the first real in-process consumer (the Electron adapter) so it can be validated against an actualLifecycleReporterrather than plumbed speculatively. Captured this as the explicit next layer in the PR description's "Scope boundary / known follow-up" section.For the CLI today it's a non-issue: the default
consoleLifecycleReporteralready routes everything to the console, so output is unchanged. Resolving this thread with that decision noted.