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

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 (76) hide show
  1. package/dist/src/docker-git/main.js +14 -2
  2. package/dist/src/docker-git/main.js.map +1 -1
  3. package/package.json +4 -1
  4. package/.jscpd.json +0 -16
  5. package/.package.json.release.bak +0 -111
  6. package/CHANGELOG.md +0 -139
  7. package/biome.json +0 -34
  8. package/eslint.config.mts +0 -305
  9. package/eslint.effect-ts-check.config.mjs +0 -220
  10. package/linter.config.json +0 -33
  11. package/src/app/main.ts +0 -18
  12. package/src/app/program.ts +0 -78
  13. package/src/docker-git/cli/input.ts +0 -29
  14. package/src/docker-git/cli/parser-apply.ts +0 -28
  15. package/src/docker-git/cli/parser-attach.ts +0 -22
  16. package/src/docker-git/cli/parser-auth.ts +0 -154
  17. package/src/docker-git/cli/parser-clone.ts +0 -50
  18. package/src/docker-git/cli/parser-create.ts +0 -3
  19. package/src/docker-git/cli/parser-mcp-playwright.ts +0 -24
  20. package/src/docker-git/cli/parser-options.ts +0 -211
  21. package/src/docker-git/cli/parser-panes.ts +0 -22
  22. package/src/docker-git/cli/parser-scrap.ts +0 -106
  23. package/src/docker-git/cli/parser-sessions.ts +0 -101
  24. package/src/docker-git/cli/parser-shared.ts +0 -51
  25. package/src/docker-git/cli/parser-state.ts +0 -86
  26. package/src/docker-git/cli/parser.ts +0 -83
  27. package/src/docker-git/cli/read-command.ts +0 -26
  28. package/src/docker-git/cli/usage.ts +0 -131
  29. package/src/docker-git/main.ts +0 -18
  30. package/src/docker-git/menu-actions.ts +0 -273
  31. package/src/docker-git/menu-auth-data.ts +0 -184
  32. package/src/docker-git/menu-auth-helpers.ts +0 -30
  33. package/src/docker-git/menu-auth.ts +0 -311
  34. package/src/docker-git/menu-buffer-input.ts +0 -18
  35. package/src/docker-git/menu-create.ts +0 -310
  36. package/src/docker-git/menu-input-handler.ts +0 -183
  37. package/src/docker-git/menu-input-utils.ts +0 -85
  38. package/src/docker-git/menu-input.ts +0 -2
  39. package/src/docker-git/menu-labeled-env.ts +0 -37
  40. package/src/docker-git/menu-menu.ts +0 -58
  41. package/src/docker-git/menu-project-auth-claude.ts +0 -70
  42. package/src/docker-git/menu-project-auth-data.ts +0 -292
  43. package/src/docker-git/menu-project-auth.ts +0 -271
  44. package/src/docker-git/menu-render-auth.ts +0 -65
  45. package/src/docker-git/menu-render-common.ts +0 -67
  46. package/src/docker-git/menu-render-layout.ts +0 -30
  47. package/src/docker-git/menu-render-project-auth.ts +0 -70
  48. package/src/docker-git/menu-render-select.ts +0 -250
  49. package/src/docker-git/menu-render.ts +0 -292
  50. package/src/docker-git/menu-select-actions.ts +0 -150
  51. package/src/docker-git/menu-select-connect.ts +0 -27
  52. package/src/docker-git/menu-select-load.ts +0 -33
  53. package/src/docker-git/menu-select-order.ts +0 -37
  54. package/src/docker-git/menu-select-runtime.ts +0 -143
  55. package/src/docker-git/menu-select-view.ts +0 -25
  56. package/src/docker-git/menu-select.ts +0 -145
  57. package/src/docker-git/menu-shared.ts +0 -256
  58. package/src/docker-git/menu-startup.ts +0 -83
  59. package/src/docker-git/menu-types.ts +0 -170
  60. package/src/docker-git/menu.ts +0 -303
  61. package/src/docker-git/program.ts +0 -154
  62. package/src/docker-git/tmux.ts +0 -292
  63. package/tests/app/main.test.ts +0 -65
  64. package/tests/docker-git/entrypoint-auth.test.ts +0 -40
  65. package/tests/docker-git/fixtures/project-item.ts +0 -24
  66. package/tests/docker-git/menu-select-connect.test.ts +0 -55
  67. package/tests/docker-git/menu-select-order.test.ts +0 -84
  68. package/tests/docker-git/menu-startup.test.ts +0 -51
  69. package/tests/docker-git/parser-helpers.ts +0 -76
  70. package/tests/docker-git/parser-network-options.test.ts +0 -47
  71. package/tests/docker-git/parser.test.ts +0 -284
  72. package/tsconfig.build.json +0 -8
  73. package/tsconfig.json +0 -20
  74. package/vite.config.ts +0 -32
  75. package/vite.docker-git.config.ts +0 -34
  76. 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
- )