Skip to content

Commit 7963a87

Browse files
skulidropekclaude
andcommitted
fix(shell): fall back to listProjectStatus when no TTY available
Instead of failing with InputReadError, docker-git menu now runs listProjectStatus in non-interactive environments (Docker without -t, CI, piped stdin). Zero errors for the user. ∀ env: isTTY(env) → TUI, ¬isTTY(env) → listProjectStatus ∧ exit 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 79d945a commit 7963a87

1 file changed

Lines changed: 23 additions & 27 deletions

File tree

packages/app/src/docker-git/menu.ts

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { runDockerPsNames } from "@effect-template/lib/shell/docker"
22
import { type InputCancelledError, InputReadError } from "@effect-template/lib/shell/errors"
33
import { type AppError, renderError } from "@effect-template/lib/usecases/errors"
4-
import { listProjectItems } from "@effect-template/lib/usecases/projects"
4+
import { listProjectItems, listProjectStatus } from "@effect-template/lib/usecases/projects"
55
import { NodeContext } from "@effect/platform-node"
66
import { Effect, pipe } from "effect"
77
import { render, useApp, useInput } from "ink"
@@ -286,38 +286,34 @@ const TuiApp = () => {
286286
// CHANGE: guard against non-TTY environments (Docker without -t)
287287
// WHY: Ink calls setRawMode(true) on mount — without a TTY stdin does not support
288288
// raw mode, causing an unhandled error and a hang in waitUntilExit().
289-
// Fail fast with a descriptive error instead.
289+
// Fall back to listProjectStatus in non-interactive environments.
290290
// QUOTE(ТЗ): "вечный цикл зависания на TUI из за ошибки Raw mode is not supported"
291291
// REF: issue-100
292292
// SOURCE: https://github.com/vadimdemedes/ink/#israwmodesupported
293-
// FORMAT THEOREM: ∀ env: ¬isTTY(env) → fail(InputReadError) ∧ ¬hang
293+
// FORMAT THEOREM: ∀ env: isTTY(env) → renderTui ∧ ¬isTTY(env) → listProjectStatus
294294
// INVARIANT: render() is only called when stdin.isTTY ∧ setRawMode ∈ stdin
295-
export const runMenu = pipe(
296-
Effect.sync(() => {
297-
resumeTui()
298-
}),
299-
Effect.zipRight(
300-
Effect.suspend(() => {
301-
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
302-
return Effect.fail(
303-
new InputReadError({
304-
message:
305-
"TUI requires a TTY. Attach a terminal: ssh into the container or use `docker run -it`."
306-
})
307-
)
308-
}
309-
return Effect.tryPromise({
295+
export const runMenu = Effect.suspend(() => {
296+
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
297+
return listProjectStatus
298+
}
299+
300+
return pipe(
301+
Effect.sync(() => {
302+
resumeTui()
303+
}),
304+
Effect.zipRight(
305+
Effect.tryPromise({
310306
try: () => render(React.createElement(TuiApp)).waitUntilExit(),
311307
catch: (error) => new InputReadError({ message: error instanceof Error ? error.message : String(error) })
312308
})
313-
})
314-
),
315-
Effect.ensuring(
316-
Effect.sync(() => {
317-
leaveTui()
318-
})
319-
),
320-
Effect.asVoid
321-
)
309+
),
310+
Effect.ensuring(
311+
Effect.sync(() => {
312+
leaveTui()
313+
})
314+
),
315+
Effect.asVoid
316+
)
317+
})
322318

323319
export type MenuError = AppError | InputCancelledError

0 commit comments

Comments
 (0)