@prover-coder-ai/docker-git 1.0.23 → 1.0.24

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.
Files changed (74) hide show
  1. package/package.json +4 -1
  2. package/.jscpd.json +0 -16
  3. package/.package.json.release.bak +0 -111
  4. package/CHANGELOG.md +0 -139
  5. package/biome.json +0 -34
  6. package/eslint.config.mts +0 -305
  7. package/eslint.effect-ts-check.config.mjs +0 -220
  8. package/linter.config.json +0 -33
  9. package/src/app/main.ts +0 -18
  10. package/src/app/program.ts +0 -78
  11. package/src/docker-git/cli/input.ts +0 -29
  12. package/src/docker-git/cli/parser-apply.ts +0 -28
  13. package/src/docker-git/cli/parser-attach.ts +0 -22
  14. package/src/docker-git/cli/parser-auth.ts +0 -154
  15. package/src/docker-git/cli/parser-clone.ts +0 -50
  16. package/src/docker-git/cli/parser-create.ts +0 -3
  17. package/src/docker-git/cli/parser-mcp-playwright.ts +0 -24
  18. package/src/docker-git/cli/parser-options.ts +0 -211
  19. package/src/docker-git/cli/parser-panes.ts +0 -22
  20. package/src/docker-git/cli/parser-scrap.ts +0 -106
  21. package/src/docker-git/cli/parser-sessions.ts +0 -101
  22. package/src/docker-git/cli/parser-shared.ts +0 -51
  23. package/src/docker-git/cli/parser-state.ts +0 -86
  24. package/src/docker-git/cli/parser.ts +0 -83
  25. package/src/docker-git/cli/read-command.ts +0 -26
  26. package/src/docker-git/cli/usage.ts +0 -131
  27. package/src/docker-git/main.ts +0 -18
  28. package/src/docker-git/menu-actions.ts +0 -273
  29. package/src/docker-git/menu-auth-data.ts +0 -184
  30. package/src/docker-git/menu-auth-helpers.ts +0 -30
  31. package/src/docker-git/menu-auth.ts +0 -311
  32. package/src/docker-git/menu-buffer-input.ts +0 -18
  33. package/src/docker-git/menu-create.ts +0 -310
  34. package/src/docker-git/menu-input-handler.ts +0 -183
  35. package/src/docker-git/menu-input-utils.ts +0 -85
  36. package/src/docker-git/menu-input.ts +0 -2
  37. package/src/docker-git/menu-labeled-env.ts +0 -37
  38. package/src/docker-git/menu-menu.ts +0 -58
  39. package/src/docker-git/menu-project-auth-claude.ts +0 -70
  40. package/src/docker-git/menu-project-auth-data.ts +0 -292
  41. package/src/docker-git/menu-project-auth.ts +0 -271
  42. package/src/docker-git/menu-render-auth.ts +0 -65
  43. package/src/docker-git/menu-render-common.ts +0 -67
  44. package/src/docker-git/menu-render-layout.ts +0 -30
  45. package/src/docker-git/menu-render-project-auth.ts +0 -70
  46. package/src/docker-git/menu-render-select.ts +0 -250
  47. package/src/docker-git/menu-render.ts +0 -292
  48. package/src/docker-git/menu-select-actions.ts +0 -150
  49. package/src/docker-git/menu-select-connect.ts +0 -27
  50. package/src/docker-git/menu-select-load.ts +0 -33
  51. package/src/docker-git/menu-select-order.ts +0 -37
  52. package/src/docker-git/menu-select-runtime.ts +0 -143
  53. package/src/docker-git/menu-select-view.ts +0 -25
  54. package/src/docker-git/menu-select.ts +0 -145
  55. package/src/docker-git/menu-shared.ts +0 -256
  56. package/src/docker-git/menu-startup.ts +0 -83
  57. package/src/docker-git/menu-types.ts +0 -170
  58. package/src/docker-git/menu.ts +0 -303
  59. package/src/docker-git/program.ts +0 -154
  60. package/src/docker-git/tmux.ts +0 -292
  61. package/tests/app/main.test.ts +0 -65
  62. package/tests/docker-git/entrypoint-auth.test.ts +0 -40
  63. package/tests/docker-git/fixtures/project-item.ts +0 -24
  64. package/tests/docker-git/menu-select-connect.test.ts +0 -55
  65. package/tests/docker-git/menu-select-order.test.ts +0 -84
  66. package/tests/docker-git/menu-startup.test.ts +0 -51
  67. package/tests/docker-git/parser-helpers.ts +0 -76
  68. package/tests/docker-git/parser-network-options.test.ts +0 -47
  69. package/tests/docker-git/parser.test.ts +0 -284
  70. package/tsconfig.build.json +0 -8
  71. package/tsconfig.json +0 -20
  72. package/vite.config.ts +0 -32
  73. package/vite.docker-git.config.ts +0 -34
  74. package/vitest.config.ts +0 -85
@@ -1,303 +0,0 @@
1
- import { runDockerPsNames } from "@effect-template/lib/shell/docker"
2
- import { type InputCancelledError, InputReadError } from "@effect-template/lib/shell/errors"
3
- import { type AppError, renderError } from "@effect-template/lib/usecases/errors"
4
- import { listProjectItems } from "@effect-template/lib/usecases/projects"
5
- import { NodeContext } from "@effect/platform-node"
6
- import { Effect, pipe } from "effect"
7
- import { render, useApp, useInput } from "ink"
8
- import React, { useEffect, useMemo, useState } from "react"
9
-
10
- import { resolveCreateInputs } from "./menu-create.js"
11
- import { handleUserInput, type InputStage } from "./menu-input-handler.js"
12
- import {
13
- renderAuthMenu,
14
- renderAuthPrompt,
15
- renderCreate,
16
- renderMenu,
17
- renderProjectAuthMenu,
18
- renderProjectAuthPrompt,
19
- renderSelect,
20
- renderStepLabel
21
- } from "./menu-render.js"
22
- import { leaveTui, resumeTui } from "./menu-shared.js"
23
- import { defaultMenuStartupSnapshot, resolveMenuStartupSnapshot } from "./menu-startup.js"
24
- import { createSteps, type MenuEnv, type MenuState, type ViewState } from "./menu-types.js"
25
-
26
- // CHANGE: keep menu state in the TUI layer
27
- // WHY: provide a dynamic interface with live selection and inputs
28
- // QUOTE(ТЗ): "TUI? Красивый, удобный"
29
- // REF: user-request-2026-02-01-tui
30
- // SOURCE: n/a
31
- // FORMAT THEOREM: forall s: input(s) -> state'(s)
32
- // PURITY: SHELL
33
- // EFFECT: Effect<void, AppError, FileSystem | Path | CommandExecutor>
34
- // INVARIANT: activeDir updated only after successful create
35
- // COMPLEXITY: O(1) per keypress
36
-
37
- const useRunner = (
38
- setBusy: (busy: boolean) => void,
39
- setMessage: (message: string | null) => void
40
- ) => {
41
- const runEffect = function<E extends AppError>(effect: Effect.Effect<void, E, MenuEnv>) {
42
- setBusy(true)
43
- const program = pipe(
44
- effect,
45
- Effect.matchEffect({
46
- onFailure: (error) =>
47
- Effect.sync(() => {
48
- setMessage(renderError(error))
49
- }),
50
- onSuccess: () => Effect.void
51
- }),
52
- Effect.ensuring(
53
- Effect.sync(() => {
54
- setBusy(false)
55
- })
56
- )
57
- )
58
- void Effect.runPromise(Effect.provide(program, NodeContext.layer))
59
- }
60
-
61
- return { runEffect }
62
- }
63
-
64
- type RenderContext = {
65
- readonly state: MenuState
66
- readonly view: ViewState
67
- readonly activeDir: string | null
68
- readonly runningDockerGitContainers: number
69
- readonly selected: number
70
- readonly busy: boolean
71
- readonly message: string | null
72
- }
73
-
74
- const renderView = (context: RenderContext) => {
75
- if (context.view._tag === "Menu") {
76
- return renderMenu({
77
- cwd: context.state.cwd,
78
- activeDir: context.activeDir,
79
- runningDockerGitContainers: context.runningDockerGitContainers,
80
- selected: context.selected,
81
- busy: context.busy,
82
- message: context.message
83
- })
84
- }
85
-
86
- if (context.view._tag === "Create") {
87
- const currentDefaults = resolveCreateInputs(context.state.cwd, context.view.values)
88
- const step = createSteps[context.view.step] ?? "repoUrl"
89
- const label = renderStepLabel(step, currentDefaults)
90
-
91
- return renderCreate(label, context.view.buffer, context.message, context.view.step, currentDefaults)
92
- }
93
-
94
- if (context.view._tag === "AuthMenu") {
95
- return renderAuthMenu(context.view.snapshot, context.view.selected, context.message)
96
- }
97
-
98
- if (context.view._tag === "AuthPrompt") {
99
- return renderAuthPrompt(context.view, context.message)
100
- }
101
-
102
- if (context.view._tag === "ProjectAuthMenu") {
103
- return renderProjectAuthMenu(context.view.snapshot, context.view.selected, context.message)
104
- }
105
-
106
- if (context.view._tag === "ProjectAuthPrompt") {
107
- return renderProjectAuthPrompt(context.view, context.message)
108
- }
109
-
110
- return renderSelect({
111
- purpose: context.view.purpose,
112
- items: context.view.items,
113
- selected: context.view.selected,
114
- runtimeByProject: context.view.runtimeByProject,
115
- confirmDelete: context.view.confirmDelete,
116
- connectEnableMcpPlaywright: context.view.connectEnableMcpPlaywright,
117
- message: context.message
118
- })
119
- }
120
-
121
- const useMenuState = () => {
122
- const [activeDir, setActiveDir] = useState<string | null>(null)
123
- const [runningDockerGitContainers, setRunningDockerGitContainers] = useState(0)
124
- const [selected, setSelected] = useState(0)
125
- const [busy, setBusy] = useState(false)
126
- const [message, setMessage] = useState<string | null>(null)
127
- const [view, setView] = useState<ViewState>({ _tag: "Menu" })
128
- const [inputStage, setInputStage] = useState<InputStage>("cold")
129
- const [ready, setReady] = useState(false)
130
- const [skipInputs, setSkipInputs] = useState(2)
131
- const [sshActive, setSshActive] = useState(false)
132
- const ignoreUntil = useMemo(() => Date.now() + 400, [])
133
- const state = useMemo<MenuState>(() => ({ cwd: process.cwd(), activeDir }), [activeDir])
134
- const runner = useRunner(setBusy, setMessage)
135
-
136
- return {
137
- activeDir,
138
- setActiveDir,
139
- runningDockerGitContainers,
140
- setRunningDockerGitContainers,
141
- selected,
142
- setSelected,
143
- busy,
144
- message,
145
- setMessage,
146
- view,
147
- setView,
148
- inputStage,
149
- setInputStage,
150
- ready,
151
- setReady,
152
- skipInputs,
153
- setSkipInputs,
154
- sshActive,
155
- setSshActive,
156
- ignoreUntil,
157
- state,
158
- runner
159
- }
160
- }
161
-
162
- const useReadyGate = (setReady: (ready: boolean) => void) => {
163
- useEffect(() => {
164
- const timer = setTimeout(() => {
165
- setReady(true)
166
- }, 150)
167
- return () => {
168
- clearTimeout(timer)
169
- }
170
- }, [setReady])
171
- }
172
-
173
- const useStartupSnapshot = (
174
- setActiveDir: (value: string | null) => void,
175
- setRunningDockerGitContainers: (value: number) => void,
176
- setMessage: (message: string | null) => void
177
- ) => {
178
- useEffect(() => {
179
- let cancelled = false
180
-
181
- const startup = pipe(
182
- Effect.all([listProjectItems, runDockerPsNames(process.cwd())]),
183
- Effect.map(([items, runningNames]) => resolveMenuStartupSnapshot(items, runningNames)),
184
- Effect.match({
185
- onFailure: () => defaultMenuStartupSnapshot(),
186
- onSuccess: (snapshot) => snapshot
187
- }),
188
- Effect.provide(NodeContext.layer)
189
- )
190
-
191
- void Effect.runPromise(startup).then((snapshot) => {
192
- if (cancelled) {
193
- return
194
- }
195
- setRunningDockerGitContainers(snapshot.runningDockerGitContainers)
196
- setMessage(snapshot.message)
197
- if (snapshot.activeDir !== null) {
198
- setActiveDir(snapshot.activeDir)
199
- }
200
- })
201
-
202
- return () => {
203
- cancelled = true
204
- }
205
- }, [setActiveDir, setMessage, setRunningDockerGitContainers])
206
- }
207
-
208
- const useSigintGuard = (exit: () => void, sshActive: boolean) => {
209
- useEffect(() => {
210
- const handleSigint = () => {
211
- if (sshActive) {
212
- return
213
- }
214
- exit()
215
- }
216
- process.on("SIGINT", handleSigint)
217
- return () => {
218
- process.off("SIGINT", handleSigint)
219
- }
220
- }, [exit, sshActive])
221
- }
222
-
223
- const TuiApp = () => {
224
- const { exit } = useApp()
225
- const menu = useMenuState()
226
-
227
- useReadyGate(menu.setReady)
228
- useStartupSnapshot(menu.setActiveDir, menu.setRunningDockerGitContainers, menu.setMessage)
229
- useSigintGuard(exit, menu.sshActive)
230
-
231
- useInput(
232
- (input, key) => {
233
- if (!menu.ready) {
234
- return
235
- }
236
- if (Date.now() < menu.ignoreUntil) {
237
- return
238
- }
239
- if (menu.skipInputs > 0) {
240
- menu.setSkipInputs((value) => (value > 0 ? value - 1 : 0))
241
- return
242
- }
243
- handleUserInput(input, key, {
244
- busy: menu.busy,
245
- view: menu.view,
246
- inputStage: menu.inputStage,
247
- setInputStage: menu.setInputStage,
248
- selected: menu.selected,
249
- setSelected: menu.setSelected,
250
- setSkipInputs: menu.setSkipInputs,
251
- sshActive: menu.sshActive,
252
- setSshActive: menu.setSshActive,
253
- state: menu.state,
254
- runner: menu.runner,
255
- exit,
256
- setView: menu.setView,
257
- setMessage: menu.setMessage,
258
- setActiveDir: menu.setActiveDir
259
- })
260
- },
261
- { isActive: !menu.sshActive }
262
- )
263
-
264
- return renderView({
265
- state: menu.state,
266
- view: menu.view,
267
- activeDir: menu.activeDir,
268
- runningDockerGitContainers: menu.runningDockerGitContainers,
269
- selected: menu.selected,
270
- busy: menu.busy,
271
- message: menu.message
272
- })
273
- }
274
-
275
- // CHANGE: provide an interactive TUI menu for docker-git
276
- // WHY: allow dynamic selection and inline create flow without raw prompts
277
- // QUOTE(ТЗ): "TUI? Красивый, удобный"
278
- // REF: user-request-2026-02-01-tui
279
- // SOURCE: n/a
280
- // FORMAT THEOREM: forall s: tui(s) -> state transitions
281
- // PURITY: SHELL
282
- // EFFECT: Effect<void, AppError, FileSystem | Path | CommandExecutor>
283
- // INVARIANT: app exits only on Quit or ctrl+c
284
- // COMPLEXITY: O(1) per input
285
- export const runMenu = pipe(
286
- Effect.sync(() => {
287
- resumeTui()
288
- }),
289
- Effect.zipRight(
290
- Effect.tryPromise({
291
- try: () => render(React.createElement(TuiApp)).waitUntilExit(),
292
- catch: (error) => new InputReadError({ message: error instanceof Error ? error.message : String(error) })
293
- })
294
- ),
295
- Effect.ensuring(
296
- Effect.sync(() => {
297
- leaveTui()
298
- })
299
- ),
300
- Effect.asVoid
301
- )
302
-
303
- export type MenuError = AppError | InputCancelledError
@@ -1,154 +0,0 @@
1
- import type { Command, ParseError } from "@effect-template/lib/core/domain"
2
- import { createProject } from "@effect-template/lib/usecases/actions"
3
- import { applyProjectConfig } from "@effect-template/lib/usecases/apply"
4
- import {
5
- authClaudeLogin,
6
- authClaudeLogout,
7
- authClaudeStatus,
8
- authCodexLogin,
9
- authCodexLogout,
10
- authCodexStatus,
11
- authGithubLogin,
12
- authGithubLogout,
13
- authGithubStatus
14
- } from "@effect-template/lib/usecases/auth"
15
- import type { AppError } from "@effect-template/lib/usecases/errors"
16
- import { renderError } from "@effect-template/lib/usecases/errors"
17
- import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright"
18
- import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects"
19
- import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap"
20
- import {
21
- stateCommit,
22
- stateInit,
23
- statePath,
24
- statePull,
25
- statePush,
26
- stateStatus,
27
- stateSync
28
- } from "@effect-template/lib/usecases/state-repo"
29
- import {
30
- killTerminalProcess,
31
- listTerminalSessions,
32
- tailTerminalLogs
33
- } from "@effect-template/lib/usecases/terminal-sessions"
34
- import { Effect, Match, pipe } from "effect"
35
- import { readCommand } from "./cli/read-command.js"
36
- import { attachTmux, listTmuxPanes } from "./tmux.js"
37
-
38
- import { runMenu } from "./menu.js"
39
-
40
- const isParseError = (error: AppError): error is ParseError =>
41
- error._tag === "UnknownCommand" ||
42
- error._tag === "UnknownOption" ||
43
- error._tag === "MissingOptionValue" ||
44
- error._tag === "MissingRequiredOption" ||
45
- error._tag === "InvalidOption" ||
46
- error._tag === "UnexpectedArgument"
47
-
48
- const setExitCode = (code: number) =>
49
- Effect.sync(() => {
50
- process.exitCode = code
51
- })
52
-
53
- const logWarningAndExit = (error: AppError) =>
54
- pipe(
55
- Effect.logWarning(renderError(error)),
56
- Effect.tap(() => setExitCode(1)),
57
- Effect.asVoid
58
- )
59
-
60
- const logErrorAndExit = (error: AppError) =>
61
- pipe(
62
- Effect.logError(renderError(error)),
63
- Effect.tap(() => setExitCode(1)),
64
- Effect.asVoid
65
- )
66
-
67
- type NonBaseCommand = Exclude<
68
- Command,
69
- | { readonly _tag: "Help" }
70
- | { readonly _tag: "Create" }
71
- | { readonly _tag: "Status" }
72
- | { readonly _tag: "DownAll" }
73
- | { readonly _tag: "Menu" }
74
- >
75
-
76
- const handleNonBaseCommand = (command: NonBaseCommand) =>
77
- Match.value(command)
78
- .pipe(
79
- Match.when({ _tag: "StatePath" }, () => statePath),
80
- Match.when({ _tag: "StateInit" }, (cmd) => stateInit(cmd)),
81
- Match.when({ _tag: "StateStatus" }, () => stateStatus),
82
- Match.when({ _tag: "StatePull" }, () => statePull),
83
- Match.when({ _tag: "StateCommit" }, (cmd) => stateCommit(cmd.message)),
84
- Match.when({ _tag: "StatePush" }, () => statePush),
85
- Match.when({ _tag: "StateSync" }, (cmd) => stateSync(cmd.message)),
86
- Match.when({ _tag: "AuthGithubLogin" }, (cmd) => authGithubLogin(cmd)),
87
- Match.when({ _tag: "AuthGithubStatus" }, (cmd) => authGithubStatus(cmd)),
88
- Match.when({ _tag: "AuthGithubLogout" }, (cmd) => authGithubLogout(cmd)),
89
- Match.when({ _tag: "AuthCodexLogin" }, (cmd) => authCodexLogin(cmd)),
90
- Match.when({ _tag: "AuthCodexStatus" }, (cmd) => authCodexStatus(cmd)),
91
- Match.when({ _tag: "AuthCodexLogout" }, (cmd) => authCodexLogout(cmd)),
92
- Match.when({ _tag: "AuthClaudeLogin" }, (cmd) => authClaudeLogin(cmd)),
93
- Match.when({ _tag: "AuthClaudeStatus" }, (cmd) => authClaudeStatus(cmd)),
94
- Match.when({ _tag: "AuthClaudeLogout" }, (cmd) => authClaudeLogout(cmd)),
95
- Match.when({ _tag: "Attach" }, (cmd) => attachTmux(cmd)),
96
- Match.when({ _tag: "Panes" }, (cmd) => listTmuxPanes(cmd)),
97
- Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd)),
98
- Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd))
99
- )
100
- .pipe(
101
- Match.when({ _tag: "Apply" }, (cmd) => applyProjectConfig(cmd)),
102
- Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)),
103
- Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)),
104
- Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)),
105
- Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)),
106
- Match.exhaustive
107
- )
108
-
109
- // CHANGE: compose CLI program with typed errors and shell effects
110
- // WHY: keep a thin entry layer over pure parsing and template generation
111
- // QUOTE(ТЗ): "CLI команду... создавать докер образы"
112
- // REF: user-request-2026-01-07
113
- // SOURCE: n/a
114
- // FORMAT THEOREM: forall cmd: handle(cmd) terminates with typed outcome
115
- // PURITY: SHELL
116
- // EFFECT: Effect<void, AppError, FileSystem | Path | CommandExecutor>
117
- // INVARIANT: help is printed without side effects beyond logs
118
- // COMPLEXITY: O(n) where n = |files|
119
- export const program = pipe(
120
- readCommand,
121
- Effect.flatMap((command: Command) =>
122
- Match.value(command).pipe(
123
- Match.when({ _tag: "Help" }, ({ message }) => Effect.log(message)),
124
- Match.when({ _tag: "Create" }, (create) => createProject(create)),
125
- Match.when({ _tag: "Status" }, () => listProjectStatus),
126
- Match.when({ _tag: "DownAll" }, () => downAllDockerGitProjects),
127
- Match.when({ _tag: "Menu" }, () => runMenu),
128
- Match.orElse((cmd) => handleNonBaseCommand(cmd))
129
- )
130
- ),
131
- Effect.catchTag("FileExistsError", (error) =>
132
- pipe(
133
- Effect.logWarning(renderError(error)),
134
- Effect.asVoid
135
- )),
136
- Effect.catchTag("DockerAccessError", logWarningAndExit),
137
- Effect.catchTag("DockerCommandError", logWarningAndExit),
138
- Effect.catchTag("AuthError", logWarningAndExit),
139
- Effect.catchTag("CommandFailedError", logWarningAndExit),
140
- Effect.catchTag("ScrapArchiveNotFoundError", logErrorAndExit),
141
- Effect.catchTag("ScrapTargetDirUnsupportedError", logErrorAndExit),
142
- Effect.catchTag("ScrapWipeRefusedError", logErrorAndExit),
143
- Effect.matchEffect({
144
- onFailure: (error) =>
145
- isParseError(error)
146
- ? logErrorAndExit(error)
147
- : pipe(
148
- Effect.logError(renderError(error)),
149
- Effect.flatMap(() => Effect.fail(error))
150
- ),
151
- onSuccess: () => Effect.void
152
- }),
153
- Effect.asVoid
154
- )