@prover-coder-ai/docker-git 1.0.37 → 1.0.38

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.
@@ -971,6 +971,191 @@ const logDockerAccessInfo = (cwd, config) => ensureDockerDnsHost(cwd, config.con
971
971
  Effect.zipRight(logDockerDnsAccess(config)),
972
972
  Effect.zipRight(logContainerIpAccess(cwd, config))
973
973
  );
974
+ const normalizeAccountLabel = (value, fallback) => {
975
+ const trimmed = value?.trim() ?? "";
976
+ if (trimmed.length === 0) {
977
+ return fallback;
978
+ }
979
+ const normalized = trimmed.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-");
980
+ const withoutLeading = trimLeftChar(normalized, "-");
981
+ const cleaned = trimRightChar(withoutLeading, "-");
982
+ return cleaned.length > 0 ? cleaned : fallback;
983
+ };
984
+ const buildDockerAuthSpec = (input) => ({
985
+ cwd: input.cwd,
986
+ image: input.image,
987
+ volume: { hostPath: input.hostPath, containerPath: input.containerPath },
988
+ ...typeof input.entrypoint === "string" ? { entrypoint: input.entrypoint } : {},
989
+ ...input.env === void 0 ? {} : { env: input.env },
990
+ args: input.args,
991
+ interactive: input.interactive
992
+ });
993
+ const JsonValueSchema = Schema.suspend(
994
+ () => Schema.Union(
995
+ Schema.Null,
996
+ Schema.Boolean,
997
+ Schema.String,
998
+ Schema.JsonNumber,
999
+ Schema.Array(JsonValueSchema),
1000
+ Schema.Record({ key: Schema.String, value: JsonValueSchema })
1001
+ )
1002
+ );
1003
+ const JsonRecordSchema = Schema.Record({
1004
+ key: Schema.String,
1005
+ value: JsonValueSchema
1006
+ });
1007
+ const JsonRecordFromStringSchema = Schema.parseJson(JsonRecordSchema);
1008
+ const defaultEnvContents$1 = "# docker-git env\n# KEY=value\n";
1009
+ const codexConfigMarker = "# docker-git codex config";
1010
+ const defaultCodexConfig = [
1011
+ "# docker-git codex config",
1012
+ 'model = "gpt-5.4"',
1013
+ "model_context_window = 1050000",
1014
+ "model_auto_compact_token_limit = 945000",
1015
+ 'model_reasoning_effort = "xhigh"',
1016
+ 'plan_mode_reasoning_effort = "xhigh"',
1017
+ 'personality = "pragmatic"',
1018
+ "",
1019
+ 'approval_policy = "never"',
1020
+ 'sandbox_mode = "danger-full-access"',
1021
+ 'web_search = "live"',
1022
+ "",
1023
+ "[features]",
1024
+ "shell_snapshot = true",
1025
+ "multi_agent = true",
1026
+ "apps = true",
1027
+ "shell_tool = true"
1028
+ ].join("\n");
1029
+ const resolvePathFromBase$1 = (path, baseDir, targetPath) => path.isAbsolute(targetPath) ? targetPath : path.resolve(baseDir, targetPath);
1030
+ const isPermissionDeniedSystemError = (error) => error._tag === "SystemError" && error.reason === "PermissionDenied";
1031
+ const skipCodexConfigPermissionDenied = (configPath, error) => isPermissionDeniedSystemError(error) ? Effect.logWarning(
1032
+ `Skipped Codex config sync at ${configPath}: permission denied (${error.description ?? "no details"}).`
1033
+ ) : Effect.fail(error);
1034
+ const normalizeConfigText = (text) => text.replaceAll("\r\n", "\n").trim();
1035
+ const shouldRewriteDockerGitCodexConfig = (existing) => {
1036
+ const normalized = normalizeConfigText(existing);
1037
+ if (normalized.length === 0) {
1038
+ return true;
1039
+ }
1040
+ if (!normalized.startsWith(codexConfigMarker)) {
1041
+ return false;
1042
+ }
1043
+ return normalized !== normalizeConfigText(defaultCodexConfig);
1044
+ };
1045
+ const shouldCopyEnv = (sourceText, targetText) => {
1046
+ if (sourceText.trim().length === 0) {
1047
+ return "skip";
1048
+ }
1049
+ if (targetText.trim().length === 0) {
1050
+ return "copy";
1051
+ }
1052
+ if (targetText.trim() === defaultEnvContents$1.trim() && sourceText.trim() !== defaultEnvContents$1.trim()) {
1053
+ return "copy";
1054
+ }
1055
+ return "skip";
1056
+ };
1057
+ const parseJsonRecord = (text) => Either.match(ParseResult.decodeUnknownEither(JsonRecordFromStringSchema)(text), {
1058
+ onLeft: () => Effect.succeed(null),
1059
+ onRight: (record) => Effect.succeed(record)
1060
+ });
1061
+ const hasClaudeOauthAccount = (record) => record !== null && typeof record["oauthAccount"] === "object" && record["oauthAccount"] !== null;
1062
+ const hasClaudeCredentials = (record) => record !== null && typeof record["claudeAiOauth"] === "object" && record["claudeAiOauth"] !== null;
1063
+ const isGithubTokenKey = (key) => key === "GITHUB_TOKEN" || key === "GH_TOKEN" || key.startsWith("GITHUB_TOKEN__");
1064
+ const hasNonEmptyFile = (fs, filePath) => Effect.gen(function* (_) {
1065
+ const exists = yield* _(fs.exists(filePath));
1066
+ if (!exists) {
1067
+ return false;
1068
+ }
1069
+ const info = yield* _(fs.stat(filePath));
1070
+ if (info.type !== "File") {
1071
+ return false;
1072
+ }
1073
+ const text = yield* _(fs.readFileString(filePath), Effect.orElseSucceed(() => ""));
1074
+ return text.trim().length > 0;
1075
+ });
1076
+ const autoOptionError = (reason) => ({
1077
+ _tag: "InvalidOption",
1078
+ option: "--auto",
1079
+ reason
1080
+ });
1081
+ const isRegularFile$1 = (fs, filePath) => Effect.gen(function* (_) {
1082
+ const exists = yield* _(fs.exists(filePath));
1083
+ if (!exists) {
1084
+ return false;
1085
+ }
1086
+ const info = yield* _(fs.stat(filePath));
1087
+ return info.type === "File";
1088
+ });
1089
+ const hasCodexAuth = (fs, rootPath, label) => {
1090
+ const normalized = normalizeAccountLabel(label ?? null, "default");
1091
+ const authPath = normalized === "default" ? `${rootPath}/auth.json` : `${rootPath}/${normalized}/auth.json`;
1092
+ return hasNonEmptyFile(fs, authPath);
1093
+ };
1094
+ const resolveClaudeAccountPath$1 = (rootPath, label) => {
1095
+ const normalized = normalizeAccountLabel(label ?? null, "default");
1096
+ if (normalized !== "default") {
1097
+ return [`${rootPath}/${normalized}`];
1098
+ }
1099
+ return [rootPath, `${rootPath}/default`];
1100
+ };
1101
+ const hasClaudeAuth = (fs, rootPath, label) => Effect.gen(function* (_) {
1102
+ for (const accountPath of resolveClaudeAccountPath$1(rootPath, label)) {
1103
+ const oauthToken = yield* _(hasNonEmptyFile(fs, `${accountPath}/.oauth-token`));
1104
+ if (oauthToken) {
1105
+ return true;
1106
+ }
1107
+ const credentials = yield* _(isRegularFile$1(fs, `${accountPath}/.credentials.json`));
1108
+ if (credentials) {
1109
+ return true;
1110
+ }
1111
+ const nestedCredentials = yield* _(isRegularFile$1(fs, `${accountPath}/.claude/.credentials.json`));
1112
+ if (nestedCredentials) {
1113
+ return true;
1114
+ }
1115
+ }
1116
+ return false;
1117
+ });
1118
+ const resolveClaudeRoot = (codexSharedAuthPath) => `${codexSharedAuthPath.slice(0, codexSharedAuthPath.lastIndexOf("/"))}/claude`;
1119
+ const resolveAvailableAgentAuth = (fs, config) => Effect.gen(function* (_) {
1120
+ const claudeAvailable = yield* _(
1121
+ hasClaudeAuth(fs, resolveClaudeRoot(config.codexSharedAuthPath), config.claudeAuthLabel)
1122
+ );
1123
+ const codexAvailable = yield* _(hasCodexAuth(fs, config.codexSharedAuthPath, config.codexAuthLabel));
1124
+ return { claudeAvailable, codexAvailable };
1125
+ });
1126
+ const resolveExplicitAutoAgentMode = (available, mode) => {
1127
+ if (mode === "claude") {
1128
+ return available.claudeAvailable ? Effect.succeed("claude") : Effect.fail(autoOptionError("Claude auth not found"));
1129
+ }
1130
+ if (mode === "codex") {
1131
+ return available.codexAvailable ? Effect.succeed("codex") : Effect.fail(autoOptionError("Codex auth not found"));
1132
+ }
1133
+ return Effect.sync(() => mode);
1134
+ };
1135
+ const pickRandomAutoAgentMode = (available) => {
1136
+ if (!available.claudeAvailable && !available.codexAvailable) {
1137
+ return Effect.fail(autoOptionError("no Claude or Codex auth found"));
1138
+ }
1139
+ if (available.claudeAvailable && !available.codexAvailable) {
1140
+ return Effect.succeed("claude");
1141
+ }
1142
+ if (!available.claudeAvailable && available.codexAvailable) {
1143
+ return Effect.succeed("codex");
1144
+ }
1145
+ return Effect.sync(() => process.hrtime.bigint() % 2n === 0n ? "claude" : "codex");
1146
+ };
1147
+ const resolveAutoAgentMode = (config) => Effect.gen(function* (_) {
1148
+ const fs = yield* _(FileSystem.FileSystem);
1149
+ if (config.agentAuto !== true) {
1150
+ return config.agentMode;
1151
+ }
1152
+ const available = yield* _(resolveAvailableAgentAuth(fs, config));
1153
+ const explicitMode = yield* _(resolveExplicitAutoAgentMode(available, config.agentMode));
1154
+ if (explicitMode !== void 0) {
1155
+ return explicitMode;
1156
+ }
1157
+ return yield* _(pickRandomAutoAgentMode(available));
1158
+ });
974
1159
  const formatParseError$1 = (error) => Match.value(error).pipe(
975
1160
  Match.when({ _tag: "UnknownCommand" }, ({ command }) => `Unknown command: ${command}`),
976
1161
  Match.when({ _tag: "UnknownOption" }, ({ option }) => `Unknown option: ${option}`),
@@ -1071,25 +1256,6 @@ const renderError = (error) => {
1071
1256
  }
1072
1257
  return renderNonParseError(error);
1073
1258
  };
1074
- const normalizeAccountLabel = (value, fallback) => {
1075
- const trimmed = value?.trim() ?? "";
1076
- if (trimmed.length === 0) {
1077
- return fallback;
1078
- }
1079
- const normalized = trimmed.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-");
1080
- const withoutLeading = trimLeftChar(normalized, "-");
1081
- const cleaned = trimRightChar(withoutLeading, "-");
1082
- return cleaned.length > 0 ? cleaned : fallback;
1083
- };
1084
- const buildDockerAuthSpec = (input) => ({
1085
- cwd: input.cwd,
1086
- image: input.image,
1087
- volume: { hostPath: input.hostPath, containerPath: input.containerPath },
1088
- ...typeof input.entrypoint === "string" ? { entrypoint: input.entrypoint } : {},
1089
- ...input.env === void 0 ? {} : { env: input.env },
1090
- args: input.args,
1091
- interactive: input.interactive
1092
- });
1093
1259
  const splitLines = (input) => input.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
1094
1260
  const joinLines = (lines) => lines.join("\n");
1095
1261
  const normalizeEnvText = (input) => {
@@ -1181,23 +1347,23 @@ const upsertEnvKey = (input, key, value) => {
1181
1347
  return normalizeEnvText(joinLines([...cleaned, `${trimmedKey}=${value}`]));
1182
1348
  };
1183
1349
  const removeEnvKey = (input, key) => upsertEnvKey(input, key, "");
1184
- const defaultEnvContents$1 = "# docker-git env\n# KEY=value\n";
1350
+ const defaultEnvContents = "# docker-git env\n# KEY=value\n";
1185
1351
  const ensureEnvFile$1 = (fs, path, envPath) => Effect.gen(function* (_) {
1186
1352
  const exists = yield* _(fs.exists(envPath));
1187
1353
  if (exists) {
1188
1354
  return;
1189
1355
  }
1190
1356
  yield* _(fs.makeDirectory(path.dirname(envPath), { recursive: true }));
1191
- yield* _(fs.writeFileString(envPath, defaultEnvContents$1));
1357
+ yield* _(fs.writeFileString(envPath, defaultEnvContents));
1192
1358
  });
1193
1359
  const readEnvText = (fs, envPath) => Effect.gen(function* (_) {
1194
1360
  const exists = yield* _(fs.exists(envPath));
1195
1361
  if (!exists) {
1196
- return defaultEnvContents$1;
1362
+ return defaultEnvContents;
1197
1363
  }
1198
1364
  const info = yield* _(fs.stat(envPath));
1199
1365
  if (info.type !== "File") {
1200
- return defaultEnvContents$1;
1366
+ return defaultEnvContents;
1201
1367
  }
1202
1368
  return yield* _(fs.readFileString(envPath));
1203
1369
  });
@@ -4135,7 +4301,7 @@ RUN oh-my-opencode --version
4135
4301
  RUN npm install -g @anthropic-ai/claude-code@latest
4136
4302
  RUN claude --version`;
4137
4303
  const renderDockerfileOpenCode = () => `# Tooling: OpenCode (binary)
4138
- RUN curl -fsSL https://opencode.ai/install | HOME=/usr/local bash -s -- --no-modify-path
4304
+ RUN set -eu; for attempt in 1 2 3 4 5; do if curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 https://opencode.ai/install | HOME=/usr/local bash -s -- --no-modify-path; then exit 0; fi; echo "opencode install attempt \${attempt} failed; retrying..." >&2; sleep $((attempt * 2)); done; echo "opencode install failed after retries" >&2; exit 1
4139
4305
  RUN ln -sf /usr/local/.opencode/bin/opencode /usr/local/bin/opencode
4140
4306
  RUN opencode --version`;
4141
4307
  const gitleaksVersion = "8.28.0";
@@ -5025,77 +5191,6 @@ const copyDirIfEmpty = (fs, path, sourceDir, targetDir, label) => Effect.gen(fun
5025
5191
  yield* _(copyDirRecursive(fs, path, sourceDir, targetDir));
5026
5192
  yield* _(Effect.log(`Copied ${label} from ${sourceDir} to ${targetDir}`));
5027
5193
  });
5028
- const JsonValueSchema = Schema.suspend(
5029
- () => Schema.Union(
5030
- Schema.Null,
5031
- Schema.Boolean,
5032
- Schema.String,
5033
- Schema.JsonNumber,
5034
- Schema.Array(JsonValueSchema),
5035
- Schema.Record({ key: Schema.String, value: JsonValueSchema })
5036
- )
5037
- );
5038
- const JsonRecordSchema = Schema.Record({
5039
- key: Schema.String,
5040
- value: JsonValueSchema
5041
- });
5042
- const JsonRecordFromStringSchema = Schema.parseJson(JsonRecordSchema);
5043
- const defaultEnvContents = "# docker-git env\n# KEY=value\n";
5044
- const codexConfigMarker = "# docker-git codex config";
5045
- const defaultCodexConfig = [
5046
- "# docker-git codex config",
5047
- 'model = "gpt-5.4"',
5048
- "model_context_window = 1050000",
5049
- "model_auto_compact_token_limit = 945000",
5050
- 'model_reasoning_effort = "xhigh"',
5051
- 'plan_mode_reasoning_effort = "xhigh"',
5052
- 'personality = "pragmatic"',
5053
- "",
5054
- 'approval_policy = "never"',
5055
- 'sandbox_mode = "danger-full-access"',
5056
- 'web_search = "live"',
5057
- "",
5058
- "[features]",
5059
- "shell_snapshot = true",
5060
- "multi_agent = true",
5061
- "apps = true",
5062
- "shell_tool = true"
5063
- ].join("\n");
5064
- const resolvePathFromBase$1 = (path, baseDir, targetPath) => path.isAbsolute(targetPath) ? targetPath : path.resolve(baseDir, targetPath);
5065
- const isPermissionDeniedSystemError = (error) => error._tag === "SystemError" && error.reason === "PermissionDenied";
5066
- const skipCodexConfigPermissionDenied = (configPath, error) => isPermissionDeniedSystemError(error) ? Effect.logWarning(
5067
- `Skipped Codex config sync at ${configPath}: permission denied (${error.description ?? "no details"}).`
5068
- ) : Effect.fail(error);
5069
- const normalizeConfigText = (text) => text.replaceAll("\r\n", "\n").trim();
5070
- const shouldRewriteDockerGitCodexConfig = (existing) => {
5071
- const normalized = normalizeConfigText(existing);
5072
- if (normalized.length === 0) {
5073
- return true;
5074
- }
5075
- if (!normalized.startsWith(codexConfigMarker)) {
5076
- return false;
5077
- }
5078
- return normalized !== normalizeConfigText(defaultCodexConfig);
5079
- };
5080
- const shouldCopyEnv = (sourceText, targetText) => {
5081
- if (sourceText.trim().length === 0) {
5082
- return "skip";
5083
- }
5084
- if (targetText.trim().length === 0) {
5085
- return "copy";
5086
- }
5087
- if (targetText.trim() === defaultEnvContents.trim() && sourceText.trim() !== defaultEnvContents.trim()) {
5088
- return "copy";
5089
- }
5090
- return "skip";
5091
- };
5092
- const parseJsonRecord = (text) => Either.match(ParseResult.decodeUnknownEither(JsonRecordFromStringSchema)(text), {
5093
- onLeft: () => Effect.succeed(null),
5094
- onRight: (record) => Effect.succeed(record)
5095
- });
5096
- const hasClaudeOauthAccount = (record) => record !== null && typeof record["oauthAccount"] === "object" && record["oauthAccount"] !== null;
5097
- const hasClaudeCredentials = (record) => record !== null && typeof record["claudeAiOauth"] === "object" && record["claudeAiOauth"] !== null;
5098
- const isGithubTokenKey = (key) => key === "GITHUB_TOKEN" || key === "GH_TOKEN" || key.startsWith("GITHUB_TOKEN__");
5099
5194
  const syncClaudeJsonFile = (fs, path, spec) => Effect.gen(function* (_) {
5100
5195
  const sourceExists = yield* _(fs.exists(spec.sourcePath));
5101
5196
  if (!sourceExists) {
@@ -5146,18 +5241,6 @@ const syncClaudeCredentialsJson = (fs, path, sourcePath, targetPath) => syncClau
5146
5241
  seedLabel: "Claude credentials",
5147
5242
  updateLabel: "Claude credentials"
5148
5243
  });
5149
- const hasNonEmptyFile = (fs, filePath) => Effect.gen(function* (_) {
5150
- const exists = yield* _(fs.exists(filePath));
5151
- if (!exists) {
5152
- return false;
5153
- }
5154
- const info = yield* _(fs.stat(filePath));
5155
- if (info.type !== "File") {
5156
- return false;
5157
- }
5158
- const text = yield* _(fs.readFileString(filePath), Effect.orElseSucceed(() => ""));
5159
- return text.trim().length > 0;
5160
- });
5161
5244
  const ensureClaudeAuthSeedFromHome = (baseDir, claudeAuthPath) => withFsPathContext(
5162
5245
  ({ fs, path }) => Effect.gen(function* (_) {
5163
5246
  const homeDir = (process.env["HOME"] ?? "").trim();
@@ -6080,6 +6163,20 @@ const maybeOpenSsh = (command, hasAgent, waitForAgent, projectConfig) => Effect.
6080
6163
  const remoteCommand = resolveInteractiveRemoteCommand(projectConfig, interactiveAgent);
6081
6164
  yield* _(openSshBestEffort(projectConfig, remoteCommand));
6082
6165
  }).pipe(Effect.asVoid);
6166
+ const resolveFinalAgentConfig = (resolvedConfig) => Effect.gen(function* (_) {
6167
+ const resolvedAgentMode = yield* _(resolveAutoAgentMode(resolvedConfig));
6168
+ if ((resolvedConfig.agentAuto ?? false) && resolvedConfig.agentMode === void 0 && resolvedAgentMode !== void 0) {
6169
+ yield* _(Effect.log(`Auto agent selected: ${resolvedAgentMode}`));
6170
+ }
6171
+ return resolvedAgentMode === void 0 ? resolvedConfig : { ...resolvedConfig, agentMode: resolvedAgentMode };
6172
+ });
6173
+ const maybeCleanupAfterAgent = (waitForAgent, resolvedOutDir) => Effect.gen(function* (_) {
6174
+ if (!waitForAgent) {
6175
+ return;
6176
+ }
6177
+ yield* _(Effect.log("Agent finished. Cleaning up container..."));
6178
+ yield* _(runDockerDownCleanup(resolvedOutDir));
6179
+ });
6083
6180
  const runCreateProject = (path, command) => Effect.gen(function* (_) {
6084
6181
  if (command.runUp) {
6085
6182
  yield* _(ensureDockerDaemonAccess(process.cwd()));
@@ -6087,7 +6184,8 @@ const runCreateProject = (path, command) => Effect.gen(function* (_) {
6087
6184
  const ctx = makeCreateContext(path, process.cwd());
6088
6185
  const resolvedOutDir = path.resolve(ctx.resolveRootPath(command.outDir));
6089
6186
  const resolvedConfig = yield* _(resolveCreateConfig(command, ctx, resolvedOutDir));
6090
- const { globalConfig, projectConfig } = buildProjectConfigs(path, ctx.baseDir, resolvedOutDir, resolvedConfig);
6187
+ const finalConfig = yield* _(resolveFinalAgentConfig(resolvedConfig));
6188
+ const { globalConfig, projectConfig } = buildProjectConfigs(path, ctx.baseDir, resolvedOutDir, finalConfig);
6091
6189
  yield* _(migrateProjectOrchLayout(ctx.baseDir, globalConfig, ctx.resolveRootPath));
6092
6190
  const createdFiles = yield* _(
6093
6191
  prepareProjectFiles(resolvedOutDir, ctx.baseDir, globalConfig, projectConfig, {
@@ -6096,8 +6194,8 @@ const runCreateProject = (path, command) => Effect.gen(function* (_) {
6096
6194
  })
6097
6195
  );
6098
6196
  yield* _(logCreatedProject(resolvedOutDir, createdFiles));
6099
- const hasAgent = resolvedConfig.agentMode !== void 0;
6100
- const waitForAgent = hasAgent && (resolvedConfig.agentAuto ?? false);
6197
+ const hasAgent = finalConfig.agentMode !== void 0;
6198
+ const waitForAgent = hasAgent && (finalConfig.agentAuto ?? false);
6101
6199
  yield* _(
6102
6200
  runDockerUpIfNeeded(resolvedOutDir, projectConfig, {
6103
6201
  runUp: command.runUp,
@@ -6110,10 +6208,7 @@ const runCreateProject = (path, command) => Effect.gen(function* (_) {
6110
6208
  if (command.runUp) {
6111
6209
  yield* _(logDockerAccessInfo(resolvedOutDir, projectConfig));
6112
6210
  }
6113
- if (waitForAgent) {
6114
- yield* _(Effect.log("Agent finished. Cleaning up container..."));
6115
- yield* _(runDockerDownCleanup(resolvedOutDir));
6116
- }
6211
+ yield* _(maybeCleanupAfterAgent(waitForAgent, resolvedOutDir));
6117
6212
  yield* _(autoSyncState(`chore(state): update ${formatStateSyncLabel(projectConfig.repoUrl)}`));
6118
6213
  yield* _(maybeOpenSsh(command, hasAgent, waitForAgent, projectConfig));
6119
6214
  }).pipe(Effect.asVoid);
@@ -7768,7 +7863,8 @@ const valueOptionSpecs = [
7768
7863
  { flag: "-m", key: "message" },
7769
7864
  { flag: "--out-dir", key: "outDir" },
7770
7865
  { flag: "--project-dir", key: "projectDir" },
7771
- { flag: "--lines", key: "lines" }
7866
+ { flag: "--lines", key: "lines" },
7867
+ { flag: "--auto", key: "agentAutoMode" }
7772
7868
  ];
7773
7869
  const valueOptionSpecByFlag = new Map(
7774
7870
  valueOptionSpecs.map((spec) => [spec.flag, spec])
@@ -7786,9 +7882,7 @@ const booleanFlagUpdaters = {
7786
7882
  "--no-wipe": (raw) => ({ ...raw, wipe: false }),
7787
7883
  "--web": (raw) => ({ ...raw, authWeb: true }),
7788
7884
  "--include-default": (raw) => ({ ...raw, includeDefault: true }),
7789
- "--claude": (raw) => ({ ...raw, agentClaude: true }),
7790
- "--codex": (raw) => ({ ...raw, agentCodex: true }),
7791
- "--auto": (raw) => ({ ...raw, agentAuto: true })
7885
+ "--auto": (raw) => ({ ...raw, agentAutoMode: "auto" })
7792
7886
  };
7793
7887
  const valueFlagUpdaters = {
7794
7888
  repoUrl: (raw, value) => ({ ...raw, repoUrl: value }),
@@ -7818,7 +7912,8 @@ const valueFlagUpdaters = {
7818
7912
  message: (raw, value) => ({ ...raw, message: value }),
7819
7913
  outDir: (raw, value) => ({ ...raw, outDir: value }),
7820
7914
  projectDir: (raw, value) => ({ ...raw, projectDir: value }),
7821
- lines: (raw, value) => ({ ...raw, lines: value })
7915
+ lines: (raw, value) => ({ ...raw, lines: value }),
7916
+ agentAutoMode: (raw, value) => ({ ...raw, agentAutoMode: value.trim().toLowerCase() })
7822
7917
  };
7823
7918
  const applyCommandBooleanFlag = (raw, token) => {
7824
7919
  const updater = booleanFlagUpdaters[token];
@@ -7841,25 +7936,55 @@ const parseInlineValueToken = (raw, token) => {
7841
7936
  const inlineValue = token.slice(equalIndex + 1);
7842
7937
  return applyCommandValueFlag(raw, flag, inlineValue);
7843
7938
  };
7844
- const parseRawOptionsStep = (args, index, raw) => {
7845
- const token = args[index] ?? "";
7939
+ const legacyAgentFlagError = (token) => {
7940
+ if (token === "--claude") {
7941
+ return {
7942
+ _tag: "InvalidOption",
7943
+ option: token,
7944
+ reason: "use --auto=claude"
7945
+ };
7946
+ }
7947
+ if (token === "--codex") {
7948
+ return {
7949
+ _tag: "InvalidOption",
7950
+ option: token,
7951
+ reason: "use --auto=codex"
7952
+ };
7953
+ }
7954
+ return null;
7955
+ };
7956
+ const toParseStep = (parsed, nextIndex) => Either.isLeft(parsed) ? { _tag: "error", error: parsed.left } : { _tag: "ok", raw: parsed.right, nextIndex };
7957
+ const parseValueOptionStep = (raw, token, value, index) => {
7958
+ if (value === void 0) {
7959
+ return { _tag: "error", error: { _tag: "MissingOptionValue", option: token } };
7960
+ }
7961
+ return toParseStep(applyCommandValueFlag(raw, token, value), index + 2);
7962
+ };
7963
+ const parseSpecialFlagStep = (raw, token, index) => {
7846
7964
  const inlineApplied = parseInlineValueToken(raw, token);
7847
7965
  if (inlineApplied !== null) {
7848
- return Either.isLeft(inlineApplied) ? { _tag: "error", error: inlineApplied.left } : { _tag: "ok", raw: inlineApplied.right, nextIndex: index + 1 };
7966
+ return toParseStep(inlineApplied, index + 1);
7849
7967
  }
7850
7968
  const booleanApplied = applyCommandBooleanFlag(raw, token);
7851
7969
  if (booleanApplied !== null) {
7852
7970
  return { _tag: "ok", raw: booleanApplied, nextIndex: index + 1 };
7853
7971
  }
7972
+ const deprecatedAgentFlag = legacyAgentFlagError(token);
7973
+ if (deprecatedAgentFlag !== null) {
7974
+ return { _tag: "error", error: deprecatedAgentFlag };
7975
+ }
7976
+ return null;
7977
+ };
7978
+ const parseRawOptionsStep = (args, index, raw) => {
7979
+ const token = args[index] ?? "";
7980
+ const specialStep = parseSpecialFlagStep(raw, token, index);
7981
+ if (specialStep !== null) {
7982
+ return specialStep;
7983
+ }
7854
7984
  if (!token.startsWith("-")) {
7855
7985
  return { _tag: "error", error: { _tag: "UnexpectedArgument", value: token } };
7856
7986
  }
7857
- const value = args[index + 1];
7858
- if (value === void 0) {
7859
- return { _tag: "error", error: { _tag: "MissingOptionValue", option: token } };
7860
- }
7861
- const nextRaw = applyCommandValueFlag(raw, token, value);
7862
- return Either.isLeft(nextRaw) ? { _tag: "error", error: nextRaw.left } : { _tag: "ok", raw: nextRaw.right, nextIndex: index + 2 };
7987
+ return parseValueOptionStep(raw, token, args[index + 1], index);
7863
7988
  };
7864
7989
  const parseRawOptions = (args) => {
7865
7990
  let index = 0;
@@ -8009,6 +8134,23 @@ const parseAuth = (args) => {
8009
8134
  const rest = args.slice(2);
8010
8135
  return Either.flatMap(parseRawOptions(rest), (raw) => buildAuthCommand(provider, action, resolveAuthOptions(raw)));
8011
8136
  };
8137
+ const resolveAutoAgentFlags = (raw) => {
8138
+ const requested = raw.agentAutoMode;
8139
+ if (requested === void 0) {
8140
+ return Either.right({ agentMode: void 0, agentAuto: false });
8141
+ }
8142
+ if (requested === "auto") {
8143
+ return Either.right({ agentMode: void 0, agentAuto: true });
8144
+ }
8145
+ if (requested === "claude" || requested === "codex") {
8146
+ return Either.right({ agentMode: requested, agentAuto: true });
8147
+ }
8148
+ return Either.left({
8149
+ _tag: "InvalidOption",
8150
+ option: "--auto",
8151
+ reason: "expected one of: claude, codex"
8152
+ });
8153
+ };
8012
8154
  const parsePort = (value) => {
8013
8155
  const parsed = Number(value);
8014
8156
  if (!Number.isInteger(parsed)) {
@@ -8134,11 +8276,6 @@ const resolveCreateBehavior = (raw) => ({
8134
8276
  forceEnv: raw.forceEnv ?? false,
8135
8277
  enableMcpPlaywright: raw.enableMcpPlaywright ?? false
8136
8278
  });
8137
- const resolveAgentMode = (raw) => {
8138
- if (raw.agentClaude) return "claude";
8139
- if (raw.agentCodex) return "codex";
8140
- return void 0;
8141
- };
8142
8279
  const buildTemplateConfig = ({
8143
8280
  agentAuto,
8144
8281
  agentMode,
@@ -8189,8 +8326,7 @@ const buildCreateCommand = (raw) => Either.gen(function* (_) {
8189
8326
  const dockerSharedNetworkName = yield* _(
8190
8327
  nonEmpty("--shared-network", raw.dockerSharedNetworkName, defaultTemplateConfig.dockerSharedNetworkName)
8191
8328
  );
8192
- const agentMode = resolveAgentMode(raw);
8193
- const agentAuto = raw.agentAuto ?? false;
8329
+ const { agentAuto, agentMode } = yield* _(resolveAutoAgentFlags(raw));
8194
8330
  return {
8195
8331
  _tag: "Create",
8196
8332
  outDir: paths.outDir,
@@ -8514,9 +8650,7 @@ Options:
8514
8650
  --up | --no-up Run docker compose up after init (default: --up)
8515
8651
  --ssh | --no-ssh Auto-open SSH after create/clone (default: clone=--ssh, create=--no-ssh)
8516
8652
  --mcp-playwright | --no-mcp-playwright Enable Playwright MCP + Chromium sidecar (default: --no-mcp-playwright)
8517
- --claude Start Claude Code agent inside container after clone
8518
- --codex Start Codex agent inside container after clone
8519
- --auto Auto-execute: agent completes the task, creates PR and pushes (requires --claude or --codex)
8653
+ --auto[=claude|codex] Auto-execute an agent; without value picks by auth, random if both are available
8520
8654
  --force Overwrite existing files and wipe compose volumes (docker compose down -v)
8521
8655
  --force-env Reset project env defaults only (keep workspace volume/data)
8522
8656
  -h, --help Show this help