Skip to content

Commit 1809273

Browse files
authored
feat: auto-open SSH after clone (#45)
* chore(state): update octocat/hello-world * test(e2e): harden CI system check * chore(state): update octocat/hello-world * test(e2e): harden CI system check * test(e2e): assert auto-ssh after clone * fix(lint): satisfy effect-ts checks * test(e2e): make ssh wrapper log writable
1 parent 03c97dd commit 1809273

File tree

17 files changed

+392
-14
lines changed

17 files changed

+392
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dev_ssh_key.pub
88

99
# Local docker-git work dirs
1010
.docker-git/
11+
.e2e/
1112
effect-template1/
1213

1314
# Node / build artifacts

AGENTS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,12 @@ describe("Message invariants", () => {
373373
Каждый эффект — это контролируемое взаимодействие с реальным миром.
374374

375375
ПРИНЦИП: Сначала формализуем, потом программируем.
376+
377+
<!-- docker-git:issue-managed:start -->
378+
Issue workspace: #39
379+
Issue URL: https://github.com/ProverCoderAI/docker-git/issues/39
380+
Workspace path: /home/dev/provercoderai/docker-git/issue-39
381+
382+
Работай только над этим issue, если пользователь не попросил другое.
383+
Если нужен первоисточник требований, открой Issue URL.
384+
<!-- docker-git:issue-managed:end -->

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
"@parcel/watcher",
4848
"msgpackr-extract",
4949
"unrs-resolver"
50-
]
50+
],
51+
"patchedDependencies": {
52+
"@ton-ai-core/vibecode-linter@1.0.6": "patches/@ton-ai-core__vibecode-linter@1.0.6.patch"
53+
}
5154
}
5255
}

packages/app/src/docker-git/cli/parser-clone.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export const parseClone = (args: ReadonlyArray<string>): Either.Either<Command,
4949
const withRef = resolvedRepo.repoRef !== undefined && raw.repoRef === undefined
5050
? { ...withDefaults, repoRef: resolvedRepo.repoRef }
5151
: withDefaults
52+
const openSsh = raw.openSsh ?? true
5253
const create = yield* _(buildCreateCommand(withRef))
53-
return { ...create, waitForClone: true }
54+
return { ...create, waitForClone: true, openSsh }
5455
})
5556
}

packages/app/src/docker-git/cli/parser-options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type ValueKey = ValueOptionSpec["key"]
6969
const booleanFlagUpdaters: Readonly<Record<string, (raw: RawOptions) => RawOptions>> = {
7070
"--up": (raw) => ({ ...raw, up: true }),
7171
"--no-up": (raw) => ({ ...raw, up: false }),
72+
"--ssh": (raw) => ({ ...raw, openSsh: true }),
73+
"--no-ssh": (raw) => ({ ...raw, openSsh: false }),
7274
"--force": (raw) => ({ ...raw, force: true }),
7375
"--force-env": (raw) => ({ ...raw, forceEnv: true }),
7476
"--mcp-playwright": (raw) => ({ ...raw, enableMcpPlaywright: true }),

packages/app/src/docker-git/cli/usage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Options:
5252
--lines <n> Tail last N lines for sessions logs (default: 200)
5353
--include-default Show default/system processes in sessions list
5454
--up | --no-up Run docker compose up after init (default: --up)
55+
--ssh | --no-ssh Auto-open SSH after create/clone (default: clone=--ssh, create=--no-ssh)
5556
--mcp-playwright | --no-mcp-playwright Enable Playwright MCP + Chromium sidecar (default: --no-mcp-playwright)
5657
--force Overwrite existing files and wipe compose volumes (docker compose down -v)
5758
--force-env Reset project env defaults only (keep workspace volume/data)

packages/app/tests/docker-git/parser.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ describe("parseArgs", () => {
5656
it.effect("parses create command with defaults", () =>
5757
expectCreateCommand(["create", "--repo-url", "https://github.com/org/repo.git"], (command) => {
5858
expectCreateDefaults(command)
59+
expect(command.openSsh).toBe(false)
60+
expect(command.waitForClone).toBe(false)
5961
expect(command.config.containerName).toBe("dg-repo")
6062
expect(command.config.serviceName).toBe("dg-repo")
6163
expect(command.config.volumeName).toBe("dg-repo-home")
@@ -67,6 +69,8 @@ describe("parseArgs", () => {
6769
expect(command.config.repoUrl).toBe("https://github.com/org/repo.git")
6870
expect(command.config.repoRef).toBe("issue-9")
6971
expect(command.outDir).toBe(".docker-git/org/repo/issue-9")
72+
expect(command.openSsh).toBe(false)
73+
expect(command.waitForClone).toBe(false)
7074
expect(command.config.containerName).toBe("dg-repo-issue-9")
7175
expect(command.config.serviceName).toBe("dg-repo-issue-9")
7276
expect(command.config.volumeName).toBe("dg-repo-issue-9-home")
@@ -77,6 +81,8 @@ describe("parseArgs", () => {
7781
it.effect("parses clone command with positional repo url", () =>
7882
expectCreateCommand(["clone", "https://github.com/org/repo.git"], (command) => {
7983
expectCreateDefaults(command)
84+
expect(command.openSsh).toBe(true)
85+
expect(command.waitForClone).toBe(true)
8086
expect(command.config.targetDir).toBe("/home/dev/org/repo")
8187
}))
8288

@@ -85,6 +91,16 @@ describe("parseArgs", () => {
8591
expect(command.config.repoRef).toBe("feature-x")
8692
}))
8793

94+
it.effect("supports disabling SSH auto-open for clone", () =>
95+
expectCreateCommand(["clone", "https://github.com/org/repo.git", "--no-ssh"], (command) => {
96+
expect(command.openSsh).toBe(false)
97+
}))
98+
99+
it.effect("supports enabling SSH auto-open for create", () =>
100+
expectCreateCommand(["create", "--repo-url", "https://github.com/org/repo.git", "--ssh"], (command) => {
101+
expect(command.openSsh).toBe(true)
102+
}))
103+
88104
it.effect("parses force-env flag for clone", () =>
89105
expectCreateCommand(["clone", "https://github.com/org/repo.git", "--force-env"], (command) => {
90106
expect(command.force).toBe(false)

packages/docker-git/src/server/http.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ export const makeRouter = ({ cwd, projectsRoot, webRoot, vendorRoot, terminalPor
11151115
config: nextTemplate,
11161116
outDir: project.directory,
11171117
runUp: false,
1118+
openSsh: false,
11181119
force: true,
11191120
forceEnv: false,
11201121
waitForClone: false
@@ -1455,6 +1456,7 @@ data: ${JSON.stringify(data)}
14551456
config: nextTemplate,
14561457
outDir: project.directory,
14571458
runUp: false,
1459+
openSsh: false,
14581460
force: true,
14591461
forceEnv: false,
14601462
waitForClone: false

packages/lib/src/core/command-builders.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export const buildCreateCommand = (
200200
const names = yield* _(resolveNames(raw, repo.projectSlug))
201201
const paths = yield* _(resolvePaths(raw, repo.projectSlug, repo.repoPath))
202202
const runUp = raw.up ?? true
203+
const openSsh = raw.openSsh ?? false
203204
const force = raw.force ?? false
204205
const forceEnv = raw.forceEnv ?? false
205206
const enableMcpPlaywright = raw.enableMcpPlaywright ?? false
@@ -208,6 +209,7 @@ export const buildCreateCommand = (
208209
_tag: "Create",
209210
outDir: paths.outDir,
210211
runUp,
212+
openSsh,
211213
force,
212214
forceEnv,
213215
waitForClone: false,

packages/lib/src/core/command-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface RawOptions {
3939
readonly lines?: string
4040
readonly includeDefault?: boolean
4141
readonly up?: boolean
42+
readonly openSsh?: boolean
4243
readonly force?: boolean
4344
readonly forceEnv?: boolean
4445
}

0 commit comments

Comments
 (0)