@prover-coder-ai/docker-git 1.0.11 → 1.0.12

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prover-coder-ai/docker-git",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Minimal Vite-powered TypeScript console starter using Effect",
5
5
  "main": "dist/src/docker-git/main.js",
6
6
  "bin": {
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @prover-coder-ai/docker-git
2
2
 
3
+ ## 1.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - chore: automated version bump
8
+
3
9
  ## 1.0.11
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -93,6 +93,9 @@ Disable sharing (per-project auth):
93
93
  Enable during create/clone:
94
94
  - Add `--mcp-playwright`
95
95
 
96
+ Enable for an existing project directory (preserves `.orch/env/project.env` and volumes):
97
+ - `docker-git mcp-playwright [<url>] [--project-dir <path>]`
98
+
96
99
  This will:
97
100
  - Create a Chromium sidecar container: `dg-<repo>-browser`
98
101
  - Configure Codex MCP server `playwright` inside the dev container
@@ -119,7 +122,8 @@ Common toggles:
119
122
  MCP errors in `codex` UI:
120
123
  - `No such file or directory (os error 2)` for `playwright`:
121
124
  - `~/.codex/config.toml` contains `[mcp_servers.playwright]`, but the container was created without `--mcp-playwright`.
122
- - Fix: recreate with `--force --mcp-playwright` (or remove the block from `config.toml`).
125
+ - Fix (recommended): run `docker-git mcp-playwright [<url>]` to enable it for the existing project.
126
+ - Fix (recreate): recreate with `--force-env --mcp-playwright` (keeps volumes) or `--force --mcp-playwright` (wipes volumes).
123
127
  - `handshaking ... initialize response`:
124
128
  - The configured MCP command is not a real MCP server (example: `command="echo"`).
125
129
 
@@ -0,0 +1,18 @@
1
+ import { Either } from "effect";
2
+ import {} from "@effect-template/lib/core/domain";
3
+ import { parseProjectDirWithOptions } from "./parser-shared.js";
4
+ // CHANGE: parse "mcp-playwright" command for existing docker-git projects
5
+ // WHY: allow enabling Playwright MCP in an already created container/project dir
6
+ // QUOTE(ТЗ): "Добавить возможность поднимать MCP Playrgiht в контейнере который уже создан"
7
+ // REF: issue-29
8
+ // SOURCE: n/a
9
+ // FORMAT THEOREM: forall argv: parseMcpPlaywright(argv) = cmd -> deterministic(cmd)
10
+ // PURITY: CORE
11
+ // EFFECT: Effect<McpPlaywrightUpCommand, ParseError, never>
12
+ // INVARIANT: projectDir is never empty
13
+ // COMPLEXITY: O(n) where n = |argv|
14
+ export const parseMcpPlaywright = (args) => Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
15
+ _tag: "McpPlaywrightUp",
16
+ projectDir,
17
+ runUp: raw.up ?? true
18
+ }));
@@ -4,6 +4,7 @@ import { parseAttach } from "./parser-attach.js";
4
4
  import { parseAuth } from "./parser-auth.js";
5
5
  import { parseClone } from "./parser-clone.js";
6
6
  import { buildCreateCommand } from "./parser-create.js";
7
+ import { parseMcpPlaywright } from "./parser-mcp-playwright.js";
7
8
  import { parseRawOptions } from "./parser-options.js";
8
9
  import { parsePanes } from "./parser-panes.js";
9
10
  import { parseScrap } from "./parser-scrap.js";
@@ -40,6 +41,6 @@ export const parseArgs = (args) => {
40
41
  command: command ?? ""
41
42
  };
42
43
  return Match.value(command)
43
- .pipe(Match.when("create", () => parseCreate(rest)), Match.when("init", () => parseCreate(rest)), Match.when("clone", () => parseClone(rest)), Match.when("attach", () => parseAttach(rest)), Match.when("tmux", () => parseAttach(rest)), Match.when("panes", () => parsePanes(rest)), Match.when("terms", () => parsePanes(rest)), Match.when("terminals", () => parsePanes(rest)), Match.when("sessions", () => parseSessions(rest)), Match.when("scrap", () => parseScrap(rest)), Match.when("help", () => Either.right(helpCommand)), Match.when("ps", () => Either.right(statusCommand)), Match.when("status", () => Either.right(statusCommand)), Match.when("down-all", () => Either.right(downAllCommand)), Match.when("stop-all", () => Either.right(downAllCommand)), Match.when("kill-all", () => Either.right(downAllCommand)), Match.when("menu", () => Either.right(menuCommand)), Match.when("ui", () => Either.right(menuCommand)), Match.when("auth", () => parseAuth(rest)), Match.when("state", () => parseState(rest)))
44
- .pipe(Match.orElse(() => Either.left(unknownCommandError)));
44
+ .pipe(Match.when("create", () => parseCreate(rest)), Match.when("init", () => parseCreate(rest)), Match.when("clone", () => parseClone(rest)), Match.when("attach", () => parseAttach(rest)), Match.when("tmux", () => parseAttach(rest)), Match.when("panes", () => parsePanes(rest)), Match.when("terms", () => parsePanes(rest)), Match.when("terminals", () => parsePanes(rest)), Match.when("sessions", () => parseSessions(rest)), Match.when("scrap", () => parseScrap(rest)), Match.when("mcp-playwright", () => parseMcpPlaywright(rest)), Match.when("help", () => Either.right(helpCommand)), Match.when("ps", () => Either.right(statusCommand)), Match.when("status", () => Either.right(statusCommand)), Match.when("down-all", () => Either.right(downAllCommand)), Match.when("stop-all", () => Either.right(downAllCommand)), Match.when("kill-all", () => Either.right(downAllCommand)), Match.when("menu", () => Either.right(menuCommand)), Match.when("ui", () => Either.right(menuCommand)), Match.when("auth", () => parseAuth(rest)))
45
+ .pipe(Match.when("state", () => parseState(rest)), Match.orElse(() => Either.left(unknownCommandError)));
45
46
  };
@@ -2,6 +2,7 @@ import { Match } from "effect";
2
2
  export const usageText = `docker-git menu
3
3
  docker-git create --repo-url <url> [options]
4
4
  docker-git clone <url> [options]
5
+ docker-git mcp-playwright [<url>] [options]
5
6
  docker-git attach [<url>] [options]
6
7
  docker-git panes [<url>] [options]
7
8
  docker-git scrap <action> [<url>] [options]
@@ -17,6 +18,7 @@ Commands:
17
18
  menu Interactive menu (default when no args)
18
19
  create, init Generate docker development environment
19
20
  clone Create + run container and clone repo
21
+ mcp-playwright Enable Playwright MCP + Chromium sidecar for an existing project dir
20
22
  attach, tmux Open tmux workspace for a docker-git project
21
23
  panes, terms List tmux panes for a docker-git project
22
24
  scrap Export/import project scrap (session snapshot + rebuildable deps)
@@ -1,6 +1,7 @@
1
1
  import { createProject } from "@effect-template/lib/usecases/actions";
2
2
  import { authCodexLogin, authCodexLogout, authCodexStatus, authGithubLogin, authGithubLogout, authGithubStatus } from "@effect-template/lib/usecases/auth";
3
3
  import { renderError } from "@effect-template/lib/usecases/errors";
4
+ import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright";
4
5
  import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects";
5
6
  import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap";
6
7
  import { stateCommit, stateInit, statePath, statePull, statePush, stateStatus, stateSync } from "@effect-template/lib/usecases/state-repo";
@@ -22,7 +23,7 @@ const logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)),
22
23
  const logErrorAndExit = (error) => pipe(Effect.logError(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
23
24
  const handleNonBaseCommand = (command) => Match.value(command)
24
25
  .pipe(Match.when({ _tag: "StatePath" }, () => statePath), Match.when({ _tag: "StateInit" }, (cmd) => stateInit(cmd)), Match.when({ _tag: "StateStatus" }, () => stateStatus), Match.when({ _tag: "StatePull" }, () => statePull), Match.when({ _tag: "StateCommit" }, (cmd) => stateCommit(cmd.message)), Match.when({ _tag: "StatePush" }, () => statePush), Match.when({ _tag: "StateSync" }, (cmd) => stateSync(cmd.message)), Match.when({ _tag: "AuthGithubLogin" }, (cmd) => authGithubLogin(cmd)), Match.when({ _tag: "AuthGithubStatus" }, (cmd) => authGithubStatus(cmd)), Match.when({ _tag: "AuthGithubLogout" }, (cmd) => authGithubLogout(cmd)), Match.when({ _tag: "AuthCodexLogin" }, (cmd) => authCodexLogin(cmd)), Match.when({ _tag: "AuthCodexStatus" }, (cmd) => authCodexStatus(cmd)), Match.when({ _tag: "AuthCodexLogout" }, (cmd) => authCodexLogout(cmd)), Match.when({ _tag: "Attach" }, (cmd) => attachTmux(cmd)), Match.when({ _tag: "Panes" }, (cmd) => listTmuxPanes(cmd)), Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd)), Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd)), Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)), Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)), Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)))
25
- .pipe(Match.exhaustive);
26
+ .pipe(Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)), Match.exhaustive);
26
27
  // CHANGE: compose CLI program with typed errors and shell effects
27
28
  // WHY: keep a thin entry layer over pure parsing and template generation
28
29
  // QUOTE(ТЗ): "CLI команду... создавать докер образы"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prover-coder-ai/docker-git",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Minimal Vite-powered TypeScript console starter using Effect",
5
5
  "main": "dist/src/docker-git/main.js",
6
6
  "bin": {
@@ -0,0 +1,25 @@
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
+ }))
25
+
@@ -6,6 +6,7 @@ import { parseAttach } from "./parser-attach.js"
6
6
  import { parseAuth } from "./parser-auth.js"
7
7
  import { parseClone } from "./parser-clone.js"
8
8
  import { buildCreateCommand } from "./parser-create.js"
9
+ import { parseMcpPlaywright } from "./parser-mcp-playwright.js"
9
10
  import { parseRawOptions } from "./parser-options.js"
10
11
  import { parsePanes } from "./parser-panes.js"
11
12
  import { parseScrap } from "./parser-scrap.js"
@@ -61,6 +62,7 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
61
62
  Match.when("terminals", () => parsePanes(rest)),
62
63
  Match.when("sessions", () => parseSessions(rest)),
63
64
  Match.when("scrap", () => parseScrap(rest)),
65
+ Match.when("mcp-playwright", () => parseMcpPlaywright(rest)),
64
66
  Match.when("help", () => Either.right(helpCommand)),
65
67
  Match.when("ps", () => Either.right(statusCommand)),
66
68
  Match.when("status", () => Either.right(statusCommand)),
@@ -69,8 +71,10 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
69
71
  Match.when("kill-all", () => Either.right(downAllCommand)),
70
72
  Match.when("menu", () => Either.right(menuCommand)),
71
73
  Match.when("ui", () => Either.right(menuCommand)),
72
- Match.when("auth", () => parseAuth(rest)),
73
- Match.when("state", () => parseState(rest))
74
+ Match.when("auth", () => parseAuth(rest))
75
+ )
76
+ .pipe(
77
+ Match.when("state", () => parseState(rest)),
78
+ Match.orElse(() => Either.left(unknownCommandError))
74
79
  )
75
- .pipe(Match.orElse(() => Either.left(unknownCommandError)))
76
80
  }
@@ -5,6 +5,7 @@ import type { ParseError } from "@effect-template/lib/core/domain"
5
5
  export const usageText = `docker-git menu
6
6
  docker-git create --repo-url <url> [options]
7
7
  docker-git clone <url> [options]
8
+ docker-git mcp-playwright [<url>] [options]
8
9
  docker-git attach [<url>] [options]
9
10
  docker-git panes [<url>] [options]
10
11
  docker-git scrap <action> [<url>] [options]
@@ -20,6 +21,7 @@ Commands:
20
21
  menu Interactive menu (default when no args)
21
22
  create, init Generate docker development environment
22
23
  clone Create + run container and clone repo
24
+ mcp-playwright Enable Playwright MCP + Chromium sidecar for an existing project dir
23
25
  attach, tmux Open tmux workspace for a docker-git project
24
26
  panes, terms List tmux panes for a docker-git project
25
27
  scrap Export/import project scrap (session snapshot + rebuildable deps)
@@ -10,6 +10,7 @@ import {
10
10
  } from "@effect-template/lib/usecases/auth"
11
11
  import type { AppError } from "@effect-template/lib/usecases/errors"
12
12
  import { renderError } from "@effect-template/lib/usecases/errors"
13
+ import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright"
13
14
  import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects"
14
15
  import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap"
15
16
  import {
@@ -92,7 +93,10 @@ const handleNonBaseCommand = (command: NonBaseCommand) =>
92
93
  Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)),
93
94
  Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd))
94
95
  )
95
- .pipe(Match.exhaustive)
96
+ .pipe(
97
+ Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)),
98
+ Match.exhaustive
99
+ )
96
100
 
97
101
  // CHANGE: compose CLI program with typed errors and shell effects
98
102
  // WHY: keep a thin entry layer over pure parsing and template generation
@@ -152,6 +152,34 @@ describe("parseArgs", () => {
152
152
  expect(command.projectDir).toBe(".docker-git/org/repo/issue-7")
153
153
  }))
154
154
 
155
+ it.effect("parses mcp-playwright command in current directory", () =>
156
+ Effect.sync(() => {
157
+ const command = parseOrThrow(["mcp-playwright"])
158
+ if (command._tag !== "McpPlaywrightUp") {
159
+ throw new Error("expected McpPlaywrightUp command")
160
+ }
161
+ expect(command.projectDir).toBe(".")
162
+ expect(command.runUp).toBe(true)
163
+ }))
164
+
165
+ it.effect("parses mcp-playwright command with --no-up", () =>
166
+ Effect.sync(() => {
167
+ const command = parseOrThrow(["mcp-playwright", "--no-up"])
168
+ if (command._tag !== "McpPlaywrightUp") {
169
+ throw new Error("expected McpPlaywrightUp command")
170
+ }
171
+ expect(command.runUp).toBe(false)
172
+ }))
173
+
174
+ it.effect("parses mcp-playwright with positional repo url into project dir", () =>
175
+ Effect.sync(() => {
176
+ const command = parseOrThrow(["mcp-playwright", "https://github.com/org/repo.git"])
177
+ if (command._tag !== "McpPlaywrightUp") {
178
+ throw new Error("expected McpPlaywrightUp command")
179
+ }
180
+ expect(command.projectDir).toBe(".docker-git/org/repo")
181
+ }))
182
+
155
183
  it.effect("parses down-all command", () =>
156
184
  Effect.sync(() => {
157
185
  const command = parseOrThrow(["down-all"])