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