@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,220 +0,0 @@
1
- // CHANGE: add Effect-TS compliance lint profile
2
- // WHY: detect current deviations from strict Effect-TS guidance
3
- // QUOTE(TZ): n/a
4
- // REF: AGENTS.md Effect-TS compliance checks
5
- // SOURCE: n/a
6
- // PURITY: SHELL
7
- // EFFECT: eslint config
8
- // INVARIANT: config only flags explicit policy deviations
9
- // COMPLEXITY: O(1)/O(1)
10
- import eslintComments from "@eslint-community/eslint-plugin-eslint-comments"
11
- import globals from "globals"
12
- import tseslint from "typescript-eslint"
13
-
14
- const restrictedImports = [
15
- {
16
- name: "node:fs",
17
- message: "Use @effect/platform FileSystem instead of node:fs."
18
- },
19
- {
20
- name: "fs",
21
- message: "Use @effect/platform FileSystem instead of fs."
22
- },
23
- {
24
- name: "node:fs/promises",
25
- message: "Use @effect/platform FileSystem instead of node:fs/promises."
26
- },
27
- {
28
- name: "node:path/posix",
29
- message: "Use @effect/platform Path instead of node:path/posix."
30
- },
31
- {
32
- name: "node:path",
33
- message: "Use @effect/platform Path instead of node:path."
34
- },
35
- {
36
- name: "path",
37
- message: "Use @effect/platform Path instead of path."
38
- },
39
- {
40
- name: "node:child_process",
41
- message: "Use @effect/platform Command instead of node:child_process."
42
- },
43
- {
44
- name: "child_process",
45
- message: "Use @effect/platform Command instead of child_process."
46
- },
47
- {
48
- name: "node:process",
49
- message: "Use @effect/platform Runtime instead of node:process."
50
- },
51
- {
52
- name: "process",
53
- message: "Use @effect/platform Runtime instead of process."
54
- }
55
- ]
56
-
57
- const restrictedSyntaxBase = [
58
- {
59
- selector: "SwitchStatement",
60
- message: "Switch is forbidden. Use Match.exhaustive."
61
- },
62
- {
63
- selector: "TryStatement",
64
- message: "Avoid try/catch in product code. Use Effect.try / Effect.catch*."
65
- },
66
- {
67
- selector: "AwaitExpression",
68
- message: "Avoid await. Use Effect.gen / Effect.flatMap."
69
- },
70
- {
71
- selector: "FunctionDeclaration[async=true], FunctionExpression[async=true], ArrowFunctionExpression[async=true]",
72
- message: "Avoid async/await. Use Effect.gen / Effect.tryPromise."
73
- },
74
- {
75
- selector: "NewExpression[callee.name='Promise']",
76
- message: "Avoid new Promise. Use Effect.async / Effect.tryPromise."
77
- },
78
- {
79
- selector: "CallExpression[callee.object.name='Promise']",
80
- message: "Avoid Promise.*. Use Effect combinators."
81
- },
82
- {
83
- selector: "CallExpression[callee.name='require']",
84
- message: "Avoid require(). Use ES module imports."
85
- },
86
- {
87
- selector: "TSAsExpression",
88
- message: "Casting is only allowed in src/core/axioms.ts."
89
- },
90
- {
91
- selector: "TSTypeAssertion",
92
- message: "Casting is only allowed in src/core/axioms.ts."
93
- },
94
- {
95
- selector: "CallExpression[callee.name='makeFilesystemService']",
96
- message: "Do not instantiate FilesystemService directly. Provide Layer and access via Tag."
97
- },
98
- {
99
- selector: "CallExpression[callee.property.name='catchAll']",
100
- message: "Avoid catchAll that discards typed errors; map or propagate explicitly."
101
- }
102
- ]
103
-
104
- const restrictedSyntaxCore = [
105
- ...restrictedSyntaxBase,
106
- {
107
- selector: "TSUnknownKeyword",
108
- message: "unknown is allowed only at shell boundaries with decoding."
109
- },
110
- {
111
- selector: "CallExpression[callee.property.name='runSyncExit']",
112
- message: "Effect.runSyncExit is shell-only. Move to a runner."
113
- },
114
- {
115
- selector: "CallExpression[callee.property.name='runSync']",
116
- message: "Effect.runSync is shell-only. Move to a runner."
117
- },
118
- {
119
- selector: "CallExpression[callee.property.name='runPromise']",
120
- message: "Effect.runPromise is shell-only. Move to a runner."
121
- }
122
- ]
123
-
124
- const restrictedSyntaxCoreNoAs = [
125
- ...restrictedSyntaxCore.filter((rule) =>
126
- rule.selector !== "TSAsExpression" && rule.selector !== "TSTypeAssertion"
127
- )
128
- ]
129
-
130
- const restrictedSyntaxBaseNoServiceFactory = [
131
- ...restrictedSyntaxBase.filter((rule) =>
132
- rule.selector !== "CallExpression[callee.name='makeFilesystemService']"
133
- )
134
- ]
135
-
136
- export default tseslint.config(
137
- {
138
- name: "effect-ts-compliance-check",
139
- files: ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts"],
140
- languageOptions: {
141
- parser: tseslint.parser,
142
- globals: { ...globals.node }
143
- },
144
- plugins: {
145
- "@typescript-eslint": tseslint.plugin,
146
- "eslint-comments": eslintComments
147
- },
148
- rules: {
149
- "no-console": "error",
150
- "no-restricted-imports": ["error", {
151
- paths: restrictedImports,
152
- patterns: [
153
- {
154
- group: ["node:*"],
155
- message: "Do not import from node:* directly. Use @effect/platform-node or @effect/platform services."
156
- }
157
- ]
158
- }],
159
- "no-restricted-syntax": ["error", ...restrictedSyntaxBase],
160
- "@typescript-eslint/no-explicit-any": "error",
161
- "@typescript-eslint/ban-ts-comment": ["error", {
162
- "ts-ignore": true,
163
- "ts-nocheck": true,
164
- "ts-check": false,
165
- "ts-expect-error": true
166
- }],
167
- "@typescript-eslint/no-restricted-types": ["error", {
168
- types: {
169
- Promise: {
170
- message: "Avoid Promise in types. Use Effect.Effect<A, E, R>."
171
- },
172
- "Promise<*>": {
173
- message: "Avoid Promise<T>. Use Effect.Effect<T, E, R>."
174
- }
175
- }
176
- }],
177
- "eslint-comments/no-use": "error",
178
- "eslint-comments/no-unlimited-disable": "error",
179
- "eslint-comments/disable-enable-pair": "error",
180
- "eslint-comments/no-unused-disable": "error"
181
- }
182
- },
183
- {
184
- name: "effect-ts-compliance-core",
185
- files: ["src/core/**/*.ts"],
186
- rules: {
187
- "no-restricted-syntax": ["error", ...restrictedSyntaxCore],
188
- "no-restricted-imports": ["error", {
189
- paths: restrictedImports,
190
- patterns: [
191
- {
192
- group: [
193
- "../shell/**",
194
- "../../shell/**",
195
- "../../../shell/**",
196
- "./shell/**",
197
- "src/shell/**",
198
- "shell/**"
199
- ],
200
- message: "CORE must not import from SHELL."
201
- }
202
- ]
203
- }]
204
- }
205
- },
206
- {
207
- name: "effect-ts-compliance-axioms",
208
- files: ["src/core/axioms.ts"],
209
- rules: {
210
- "no-restricted-syntax": ["error", ...restrictedSyntaxCoreNoAs]
211
- }
212
- },
213
- {
214
- name: "effect-ts-compliance-filesystem-service",
215
- files: ["src/shell/services/filesystem.ts"],
216
- rules: {
217
- "no-restricted-syntax": ["error", ...restrictedSyntaxBaseNoServiceFactory]
218
- }
219
- }
220
- )
@@ -1,33 +0,0 @@
1
- {
2
- "priorityLevels": [
3
- {
4
- "level": 1,
5
- "name": "Critical Compiler Errors",
6
- "rules": [
7
- "ts(2835)",
8
- "ts(2307)",
9
- "@prover-coder-ai/suggest-members/suggest-members",
10
- "@prover-coder-ai/suggest-members/suggest-imports",
11
- "@prover-coder-ai/suggest-members/suggest-module-paths",
12
- "@prover-coder-ai/suggest-members/suggest-exports",
13
- "@prover-coder-ai/suggest-members/suggest-missing-names",
14
- "@typescript-eslint/no-explicit-any"
15
- ]
16
- },
17
- {
18
- "level": 2,
19
- "name": "Critical Compiler Errors",
20
- "rules": ["all"]
21
- },
22
- {
23
- "level": 3,
24
- "name": "Critical Compiler Errors (Code must follow Clean Code and best practices)",
25
- "rules": ["max-lines-per-function", "max-lines"]
26
- },
27
- {
28
- "level": 4,
29
- "name": "Critical Compiler Errors (Code must follow Clean Code and best practices)",
30
- "rules": ["complexity", "max-params", "max-depth"]
31
- }
32
- ]
33
- }
package/src/app/main.ts DELETED
@@ -1,18 +0,0 @@
1
- import { NodeContext, NodeRuntime } from "@effect/platform-node"
2
- import { Effect, pipe } from "effect"
3
-
4
- import { program } from "./program.js"
5
-
6
- // CHANGE: run the program through the Node platform runtime with its layer
7
- // WHY: ensure effects execute under the platform runtime with proper teardown/logging behavior
8
- // QUOTE(TZ): "\u0414\u0430 \u0434\u0430\u0432\u0430\u0439 \u0442\u0430\u043a \u044d\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f"
9
- // REF: user-2025-12-19-platform-node
10
- // SOURCE: https://effect.website/docs/platform/runtime/ "runMain helps you execute a main effect with built-in error handling, logging, and signal management."
11
- // FORMAT THEOREM: forall args in Argv: decode(args) = v -> runMain(program)
12
- // PURITY: SHELL
13
- // EFFECT: Effect<string, S.ParseError, Console>
14
- // INVARIANT: program executed with NodeContext.layer
15
- // COMPLEXITY: O(1)/O(1)
16
- const main = pipe(program, Effect.provide(NodeContext.layer))
17
-
18
- NodeRuntime.runMain(main)
@@ -1,78 +0,0 @@
1
- import { listProjects, readCloneRequest, runDockerGitClone, runDockerGitOpen } from "@effect-template/lib"
2
- import { Console, Effect, Match, pipe } from "effect"
3
-
4
- /**
5
- * Compose the CLI program as a single effect.
6
- *
7
- * @returns Effect that either runs docker-git clone/open or prints usage.
8
- *
9
- * @pure false - uses Console output and spawns commands when running shortcuts
10
- * @effect Console, CommandExecutor, Path
11
- * @invariant forall args in Argv: shortcut(args) -> docker_git_invoked(args)
12
- * @precondition true
13
- * @postcondition shortcut(args) -> docker_git_invoked(args); otherwise usage printed
14
- * @complexity O(build + shortcut)
15
- * @throws Never - all errors are typed in the Effect error channel
16
- */
17
- // CHANGE: replace greeting demo with deterministic usage text
18
- // WHY: greeting was scaffolding noise and should not ship in docker-git tooling
19
- // QUOTE(ТЗ): "Можешь удалить использование greting ...? Это старый мусор который остался"
20
- // REF: user-request-2026-02-06-remove-greeting
21
- // SOURCE: n/a
22
- // FORMAT THEOREM: usageText is constant -> deterministic(help)
23
- // PURITY: CORE
24
- // EFFECT: n/a
25
- // INVARIANT: usageText does not depend on argv/env
26
- // COMPLEXITY: O(1)
27
- const usageText = [
28
- "Usage:",
29
- " pnpm docker-git",
30
- " pnpm clone <repo-url> [ref]",
31
- " pnpm open <repo-url>",
32
- " pnpm list",
33
- "",
34
- "Notes:",
35
- " - docker-git is the interactive TUI.",
36
- " - clone builds + runs docker-git clone for you.",
37
- " - open builds + runs docker-git open for existing projects."
38
- ].join("\n")
39
-
40
- // PURITY: SHELL
41
- // EFFECT: Effect<void, never, Console>
42
- const runHelp = Console.log(usageText)
43
-
44
- // CHANGE: route between shortcut runners and help based on CLI context
45
- // WHY: allow pnpm run clone/open <url> while keeping a single entrypoint
46
- // QUOTE(ТЗ): "Добавить команду open."
47
- // REF: user-request-2026-01-27
48
- // SOURCE: n/a
49
- // FORMAT THEOREM: forall argv: shortcut(argv) -> docker_git_invoked(argv)
50
- // PURITY: SHELL
51
- // EFFECT: Effect<void, Error, Console | CommandExecutor | Path>
52
- // INVARIANT: help is printed when shortcut is not requested
53
- // COMPLEXITY: O(build + shortcut)
54
- const runDockerGit = pipe(
55
- readCloneRequest,
56
- Effect.flatMap((request) =>
57
- Match.value(request).pipe(
58
- Match.when({ _tag: "Clone" }, ({ args }) => runDockerGitClone(args)),
59
- Match.when({ _tag: "Open" }, ({ args }) => runDockerGitOpen(args)),
60
- Match.when({ _tag: "None" }, () => runHelp),
61
- Match.exhaustive
62
- )
63
- )
64
- )
65
-
66
- const readListFlag = Effect.sync(() => {
67
- const command = process.argv.slice(2)[0] ?? ""
68
- return command === "list" || command === "ls"
69
- })
70
-
71
- export const program = Effect.gen(function*(_) {
72
- const isList = yield* _(readListFlag)
73
- if (isList) {
74
- yield* _(listProjects)
75
- return
76
- }
77
- yield* _(runDockerGit)
78
- })
@@ -1,29 +0,0 @@
1
- import * as Terminal from "@effect/platform/Terminal"
2
- import { Effect } from "effect"
3
-
4
- import { InputCancelledError, InputReadError } from "@effect-template/lib/shell/errors"
5
-
6
- const normalizeMessage = (error: Error): string => error.message
7
-
8
- const toReadError = (error: Error): InputReadError => new InputReadError({ message: normalizeMessage(error) })
9
-
10
- const mapReadLineError = (_error: Terminal.QuitException): InputCancelledError => new InputCancelledError({})
11
-
12
- // CHANGE: prompt for a single line of user input
13
- // WHY: provide an interactive CLI without raw terminal mode issues
14
- // QUOTE(ТЗ): "Хочу что бы открылось менюшка"
15
- // REF: user-request-2026-01-07
16
- // SOURCE: n/a
17
- // FORMAT THEOREM: forall p: prompt(p) -> line(p)
18
- // PURITY: SHELL
19
- // EFFECT: Effect<string, InputCancelledError | InputReadError, never>
20
- // INVARIANT: restores raw mode if it was enabled before prompting
21
- // COMPLEXITY: O(1)
22
- export const promptLine = (
23
- prompt: string
24
- ): Effect.Effect<string, InputCancelledError | InputReadError, Terminal.Terminal> =>
25
- Effect.gen(function*(_) {
26
- const terminal = yield* _(Terminal.Terminal)
27
- yield* _(terminal.display(prompt).pipe(Effect.mapError(toReadError)))
28
- return yield* _(terminal.readLine.pipe(Effect.mapError(mapReadLineError)))
29
- })
@@ -1,28 +0,0 @@
1
- import { Either } from "effect"
2
-
3
- import { type ApplyCommand, type ParseError } from "@effect-template/lib/core/domain"
4
-
5
- import { parseProjectDirWithOptions } from "./parser-shared.js"
6
-
7
- // CHANGE: parse "apply" command for existing docker-git projects
8
- // WHY: update managed docker-git config on the current project/container without creating a new project
9
- // QUOTE(ТЗ): "Не создавать новый... а прямо в текущем обновить её на актуальную"
10
- // REF: issue-72-followup-apply-current-config
11
- // SOURCE: n/a
12
- // FORMAT THEOREM: forall argv: parseApply(argv) = cmd -> deterministic(cmd)
13
- // PURITY: CORE
14
- // EFFECT: Effect<ApplyCommand, ParseError, never>
15
- // INVARIANT: projectDir is never empty
16
- // COMPLEXITY: O(n) where n = |argv|
17
- export const parseApply = (
18
- args: ReadonlyArray<string>
19
- ): Either.Either<ApplyCommand, ParseError> =>
20
- Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
21
- _tag: "Apply",
22
- projectDir,
23
- runUp: raw.up ?? true,
24
- gitTokenLabel: raw.gitTokenLabel,
25
- codexTokenLabel: raw.codexTokenLabel,
26
- claudeTokenLabel: raw.claudeTokenLabel,
27
- enableMcpPlaywright: raw.enableMcpPlaywright
28
- }))
@@ -1,22 +0,0 @@
1
- import { Either } from "effect"
2
-
3
- import { type AttachCommand, type ParseError } from "@effect-template/lib/core/domain"
4
-
5
- import { parseProjectDirArgs } from "./parser-shared.js"
6
-
7
- // CHANGE: parse attach command into a project selection
8
- // WHY: allow "docker-git attach" to open a tmux workspace
9
- // QUOTE(ТЗ): "окей Давай подключим tmux"
10
- // REF: user-request-2026-02-02-tmux
11
- // SOURCE: n/a
12
- // FORMAT THEOREM: forall argv: parseAttach(argv) = cmd -> deterministic(cmd)
13
- // PURITY: CORE
14
- // EFFECT: Effect<AttachCommand, ParseError, never>
15
- // INVARIANT: projectDir is never empty
16
- // COMPLEXITY: O(n) where n = |argv|
17
- export const parseAttach = (args: ReadonlyArray<string>): Either.Either<AttachCommand, ParseError> => {
18
- return Either.map(parseProjectDirArgs(args), ({ projectDir }) => ({
19
- _tag: "Attach",
20
- projectDir
21
- }))
22
- }
@@ -1,154 +0,0 @@
1
- import { Either, Match } from "effect"
2
-
3
- import type { RawOptions } from "@effect-template/lib/core/command-options"
4
- import { type AuthCommand, type Command, type ParseError } from "@effect-template/lib/core/domain"
5
-
6
- import { parseRawOptions } from "./parser-options.js"
7
-
8
- type AuthOptions = {
9
- readonly envGlobalPath: string
10
- readonly codexAuthPath: string
11
- readonly claudeAuthPath: string
12
- readonly label: string | null
13
- readonly token: string | null
14
- readonly scopes: string | null
15
- readonly authWeb: boolean
16
- }
17
-
18
- const missingArgument = (name: string): ParseError => ({
19
- _tag: "MissingRequiredOption",
20
- option: name
21
- })
22
-
23
- const invalidArgument = (name: string, reason: string): ParseError => ({
24
- _tag: "InvalidOption",
25
- option: name,
26
- reason
27
- })
28
-
29
- const normalizeLabel = (value: string | undefined): string | null => {
30
- const trimmed = value?.trim() ?? ""
31
- return trimmed.length === 0 ? null : trimmed
32
- }
33
-
34
- const defaultEnvGlobalPath = ".docker-git/.orch/env/global.env"
35
- const defaultCodexAuthPath = ".docker-git/.orch/auth/codex"
36
- const defaultClaudeAuthPath = ".docker-git/.orch/auth/claude"
37
-
38
- const resolveAuthOptions = (raw: RawOptions): AuthOptions => ({
39
- envGlobalPath: raw.envGlobalPath ?? defaultEnvGlobalPath,
40
- codexAuthPath: raw.codexAuthPath ?? defaultCodexAuthPath,
41
- claudeAuthPath: defaultClaudeAuthPath,
42
- label: normalizeLabel(raw.label),
43
- token: normalizeLabel(raw.token),
44
- scopes: normalizeLabel(raw.scopes),
45
- authWeb: raw.authWeb === true
46
- })
47
-
48
- const buildGithubCommand = (action: string, options: AuthOptions): Either.Either<AuthCommand, ParseError> =>
49
- Match.value(action).pipe(
50
- Match.when("login", () =>
51
- options.authWeb && options.token !== null
52
- ? Either.left(invalidArgument("--token", "cannot be combined with --web"))
53
- : Either.right<AuthCommand>({
54
- _tag: "AuthGithubLogin",
55
- label: options.label,
56
- token: options.authWeb ? null : options.token,
57
- scopes: options.scopes,
58
- envGlobalPath: options.envGlobalPath
59
- })),
60
- Match.when("status", () =>
61
- Either.right<AuthCommand>({
62
- _tag: "AuthGithubStatus",
63
- envGlobalPath: options.envGlobalPath
64
- })),
65
- Match.when("logout", () =>
66
- Either.right<AuthCommand>({
67
- _tag: "AuthGithubLogout",
68
- label: options.label,
69
- envGlobalPath: options.envGlobalPath
70
- })),
71
- Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`)))
72
- )
73
-
74
- const buildCodexCommand = (action: string, options: AuthOptions): Either.Either<AuthCommand, ParseError> =>
75
- Match.value(action).pipe(
76
- Match.when("login", () =>
77
- Either.right<AuthCommand>({
78
- _tag: "AuthCodexLogin",
79
- label: options.label,
80
- codexAuthPath: options.codexAuthPath
81
- })),
82
- Match.when("status", () =>
83
- Either.right<AuthCommand>({
84
- _tag: "AuthCodexStatus",
85
- label: options.label,
86
- codexAuthPath: options.codexAuthPath
87
- })),
88
- Match.when("logout", () =>
89
- Either.right<AuthCommand>({
90
- _tag: "AuthCodexLogout",
91
- label: options.label,
92
- codexAuthPath: options.codexAuthPath
93
- })),
94
- Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`)))
95
- )
96
-
97
- const buildClaudeCommand = (action: string, options: AuthOptions): Either.Either<AuthCommand, ParseError> =>
98
- Match.value(action).pipe(
99
- Match.when("login", () =>
100
- Either.right<AuthCommand>({
101
- _tag: "AuthClaudeLogin",
102
- label: options.label,
103
- claudeAuthPath: options.claudeAuthPath
104
- })),
105
- Match.when("status", () =>
106
- Either.right<AuthCommand>({
107
- _tag: "AuthClaudeStatus",
108
- label: options.label,
109
- claudeAuthPath: options.claudeAuthPath
110
- })),
111
- Match.when("logout", () =>
112
- Either.right<AuthCommand>({
113
- _tag: "AuthClaudeLogout",
114
- label: options.label,
115
- claudeAuthPath: options.claudeAuthPath
116
- })),
117
- Match.orElse(() => Either.left(invalidArgument("auth action", `unknown action '${action}'`)))
118
- )
119
-
120
- const buildAuthCommand = (
121
- provider: string,
122
- action: string,
123
- options: AuthOptions
124
- ): Either.Either<AuthCommand, ParseError> =>
125
- Match.value(provider).pipe(
126
- Match.when("github", () => buildGithubCommand(action, options)),
127
- Match.when("gh", () => buildGithubCommand(action, options)),
128
- Match.when("codex", () => buildCodexCommand(action, options)),
129
- Match.when("claude", () => buildClaudeCommand(action, options)),
130
- Match.when("cc", () => buildClaudeCommand(action, options)),
131
- Match.orElse(() => Either.left(invalidArgument("auth provider", `unknown provider '${provider}'`)))
132
- )
133
-
134
- // CHANGE: parse docker-git auth subcommands
135
- // WHY: keep auth flows in the same typed CLI parser
136
- // QUOTE(ТЗ): "система авторизации"
137
- // REF: user-request-2026-01-28-auth
138
- // SOURCE: n/a
139
- // FORMAT THEOREM: forall argv: parseAuth(argv) = cmd | error
140
- // PURITY: CORE
141
- // EFFECT: Effect<Command, ParseError, never>
142
- // INVARIANT: no IO or side effects
143
- // COMPLEXITY: O(n) where n = |argv|
144
- export const parseAuth = (args: ReadonlyArray<string>): Either.Either<Command, ParseError> => {
145
- if (args.length < 2) {
146
- return Either.left(missingArgument(args.length === 0 ? "auth provider" : "auth action"))
147
- }
148
-
149
- const provider = args[0] ?? ""
150
- const action = args[1] ?? ""
151
- const rest = args.slice(2)
152
-
153
- return Either.flatMap(parseRawOptions(rest), (raw) => buildAuthCommand(provider, action, resolveAuthOptions(raw)))
154
- }
@@ -1,50 +0,0 @@
1
- import { Either } from "effect"
2
-
3
- import { buildCreateCommand, nonEmpty } from "@effect-template/lib/core/command-builders"
4
- import type { RawOptions } from "@effect-template/lib/core/command-options"
5
- import { type Command, type ParseError, resolveRepoInput } from "@effect-template/lib/core/domain"
6
-
7
- import { parseRawOptions } from "./parser-options.js"
8
- import { resolveWorkspaceRepoPath, splitPositionalRepo } from "./parser-shared.js"
9
-
10
- const applyCloneDefaults = (
11
- raw: RawOptions,
12
- rawRepoUrl: string,
13
- resolvedRepo: ReturnType<typeof resolveRepoInput>
14
- ): RawOptions => {
15
- const repoPath = resolveWorkspaceRepoPath(resolvedRepo)
16
- const targetHome = "~"
17
- return {
18
- ...raw,
19
- repoUrl: rawRepoUrl,
20
- outDir: raw.outDir ?? `.docker-git/${repoPath}`,
21
- targetDir: raw.targetDir ?? `${targetHome}/workspaces/${repoPath}`
22
- }
23
- }
24
-
25
- // CHANGE: parse clone command with positional repo url
26
- // WHY: allow "docker-git clone <url>" to build + run a container
27
- // QUOTE(ТЗ): "docker-git clone url"
28
- // REF: user-request-2026-01-27
29
- // SOURCE: n/a
30
- // FORMAT THEOREM: forall argv: parseClone(argv) = cmd -> deterministic(cmd)
31
- // PURITY: CORE
32
- // EFFECT: Effect<Command, ParseError, never>
33
- // INVARIANT: first positional arg is treated as repo url
34
- // COMPLEXITY: O(n) where n = |argv|
35
- export const parseClone = (args: ReadonlyArray<string>): Either.Either<Command, ParseError> => {
36
- const { positionalRepoUrl, restArgs } = splitPositionalRepo(args)
37
-
38
- return Either.gen(function*(_) {
39
- const raw = yield* _(parseRawOptions(restArgs))
40
- const rawRepoUrl = yield* _(nonEmpty("--repo-url", raw.repoUrl ?? positionalRepoUrl))
41
- const resolvedRepo = resolveRepoInput(rawRepoUrl)
42
- const withDefaults = applyCloneDefaults(raw, rawRepoUrl, resolvedRepo)
43
- const withRef = resolvedRepo.repoRef !== undefined && raw.repoRef === undefined
44
- ? { ...withDefaults, repoRef: resolvedRepo.repoRef }
45
- : withDefaults
46
- const openSsh = raw.openSsh ?? true
47
- const create = yield* _(buildCreateCommand(withRef))
48
- return { ...create, waitForClone: true, openSsh }
49
- })
50
- }
@@ -1,3 +0,0 @@
1
- export { buildCreateCommand, nonEmpty } from "@effect-template/lib/core/command-builders"
2
- export type { RawOptions } from "@effect-template/lib/core/command-options"
3
- export type { CreateCommand, ParseError } from "@effect-template/lib/core/domain"
@@ -1,24 +0,0 @@
1
- import { Either } from "effect"
2
-
3
- import { type McpPlaywrightUpCommand, type ParseError } from "@effect-template/lib/core/domain"
4
-
5
- import { parseProjectDirWithOptions } from "./parser-shared.js"
6
-
7
- // CHANGE: parse "mcp-playwright" command for existing docker-git projects
8
- // WHY: allow enabling Playwright MCP in an already created container/project dir
9
- // QUOTE(ТЗ): "Добавить возможность поднимать MCP Playrgiht в контейнере который уже создан"
10
- // REF: issue-29
11
- // SOURCE: n/a
12
- // FORMAT THEOREM: forall argv: parseMcpPlaywright(argv) = cmd -> deterministic(cmd)
13
- // PURITY: CORE
14
- // EFFECT: Effect<McpPlaywrightUpCommand, ParseError, never>
15
- // INVARIANT: projectDir is never empty
16
- // COMPLEXITY: O(n) where n = |argv|
17
- export const parseMcpPlaywright = (
18
- args: ReadonlyArray<string>
19
- ): Either.Either<McpPlaywrightUpCommand, ParseError> =>
20
- Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
21
- _tag: "McpPlaywrightUp",
22
- projectDir,
23
- runUp: raw.up ?? true
24
- }))