@devrouter/cli 0.0.57 → 0.0.59

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.
package/dist/devrouter.js CHANGED
@@ -2291,6 +2291,38 @@ function parseRequiredUniqueStringArray(value, pathLabel) {
2291
2291
  }
2292
2292
  return parseUniqueStringArray(value, pathLabel);
2293
2293
  }
2294
+ function parsePrepareCommand(value, pathLabel) {
2295
+ if (value === void 0) {
2296
+ return void 0;
2297
+ }
2298
+ if (!Array.isArray(value)) {
2299
+ throw new Error(`${pathLabel} must be a non-empty array of strings.`);
2300
+ }
2301
+ if (value.length === 0) {
2302
+ throw new Error(`${pathLabel} must be a non-empty array of strings.`);
2303
+ }
2304
+ if (value.length > MAX_PREPARE_ARGS) {
2305
+ throw new Error(`${pathLabel} exceeds the maximum of ${MAX_PREPARE_ARGS} arguments.`);
2306
+ }
2307
+ return value.map((item, index) => {
2308
+ if (typeof item !== "string") {
2309
+ throw new Error(`${pathLabel}[${index}] must be a string.`);
2310
+ }
2311
+ if (index === 0 && item.length === 0) {
2312
+ throw new Error(`${pathLabel}[0] must be a non-empty executable.`);
2313
+ }
2314
+ if (item.includes("\0")) {
2315
+ throw new Error(`${pathLabel}[${index}] must not contain null bytes.`);
2316
+ }
2317
+ const byteLength = Buffer.byteLength(item, "utf8");
2318
+ if (byteLength > MAX_PREPARE_ARG_BYTES) {
2319
+ throw new Error(
2320
+ `${pathLabel}[${index}] exceeds the maximum of ${MAX_PREPARE_ARG_BYTES} UTF-8 bytes.`
2321
+ );
2322
+ }
2323
+ return item;
2324
+ });
2325
+ }
2294
2326
  function parseManagedRuntime(value, configPath) {
2295
2327
  if (value === void 0) {
2296
2328
  return void 0;
@@ -2303,7 +2335,7 @@ function parseManagedRuntime(value, configPath) {
2303
2335
  );
2304
2336
  ensureAllowedKeys(
2305
2337
  devcontainer,
2306
- ["baseServices", "profileServices"],
2338
+ ["baseServices", "profileServices", "prepareCommand"],
2307
2339
  `${configPath}.managedRuntime.devcontainer`
2308
2340
  );
2309
2341
  const baseServices = parseRequiredUniqueStringArray(
@@ -2314,6 +2346,10 @@ function parseManagedRuntime(value, configPath) {
2314
2346
  devcontainer.profileServices,
2315
2347
  `${configPath}.managedRuntime.devcontainer.profileServices`
2316
2348
  );
2349
+ const prepareCommand = parsePrepareCommand(
2350
+ devcontainer.prepareCommand,
2351
+ `${configPath}.managedRuntime.devcontainer.prepareCommand`
2352
+ );
2317
2353
  const baseSet = new Set(baseServices);
2318
2354
  const overlappingServices = profileServices.filter((service) => baseSet.has(service));
2319
2355
  if (overlappingServices.length > 0) {
@@ -2334,7 +2370,11 @@ function parseManagedRuntime(value, configPath) {
2334
2370
  );
2335
2371
  }
2336
2372
  return {
2337
- devcontainer: { baseServices, profileServices },
2373
+ devcontainer: {
2374
+ baseServices,
2375
+ profileServices,
2376
+ ...prepareCommand ? { prepareCommand } : {}
2377
+ },
2338
2378
  processes
2339
2379
  };
2340
2380
  }
@@ -2781,7 +2821,7 @@ function loadRepoConfig(repoPath) {
2781
2821
  const config = parseConfig(parsed ?? {}, configPath);
2782
2822
  const requiredVersion = config.devrouter?.version;
2783
2823
  if (requiredVersion && !hasWarnedVersionMismatch) {
2784
- const cliVersion = true ? "0.0.57" : "0.0.0-dev";
2824
+ const cliVersion = true ? "0.0.59" : "0.0.0-dev";
2785
2825
  if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
2786
2826
  hasWarnedVersionMismatch = true;
2787
2827
  process.stderr.write(
@@ -3125,7 +3165,7 @@ function resolveAppDependencies(config, app) {
3125
3165
  }
3126
3166
  return results;
3127
3167
  }
3128
- var import_node_fs7, import_node_path7, import_yaml2, hasWarnedVersionMismatch, CONFIG_FILE_NAME, DEFAULT_TCP_PROTOCOL, VALID_HOSTNAME_RE, DEVROUTER_VERSION_RE, VALID_ENV_NAME_RE, VALID_ENV_VAR_RE, UPSTREAM_TEMPLATE_RE, MAX_COMMAND_LENGTH, DEFAULT_HOST_STRATEGY, MAX_READINESS_PATH_LENGTH, MIME_TOKEN_RE, PROFILE_NAME_RE, MAX_PROFILES;
3168
+ var import_node_fs7, import_node_path7, import_yaml2, hasWarnedVersionMismatch, CONFIG_FILE_NAME, DEFAULT_TCP_PROTOCOL, VALID_HOSTNAME_RE, DEVROUTER_VERSION_RE, VALID_ENV_NAME_RE, VALID_ENV_VAR_RE, UPSTREAM_TEMPLATE_RE, MAX_COMMAND_LENGTH, MAX_PREPARE_ARGS, MAX_PREPARE_ARG_BYTES, DEFAULT_HOST_STRATEGY, MAX_READINESS_PATH_LENGTH, MIME_TOKEN_RE, PROFILE_NAME_RE, MAX_PROFILES;
3129
3169
  var init_repo_config = __esm({
3130
3170
  "src/core/repo-config.ts"() {
3131
3171
  "use strict";
@@ -3144,6 +3184,8 @@ var init_repo_config = __esm({
3144
3184
  VALID_ENV_VAR_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
3145
3185
  UPSTREAM_TEMPLATE_RE = /^(?:\$\{WORKSPACE\}|[a-zA-Z0-9._-])+:\d{1,5}$/;
3146
3186
  MAX_COMMAND_LENGTH = 4096;
3187
+ MAX_PREPARE_ARGS = 64;
3188
+ MAX_PREPARE_ARG_BYTES = 4096;
3147
3189
  DEFAULT_HOST_STRATEGY = {
3148
3190
  type: "auto",
3149
3191
  denyPorts: [80, 443, 5432],
@@ -3323,6 +3365,7 @@ function buildOnboardingPrompt(options = {}) {
3323
3365
  "- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
3324
3366
  "- Managed process reuse fingerprints command argv, workspace identity, and the exact adapter snapshot. If a non-secret runtime value also affects reuse, set `DEVROUTER_PROCESS_FINGERPRINT_ENV` to its comma-separated environment names; secret-like names are rejected and raw values are never persisted.",
3325
3367
  "- Managed adapters may use devrouter-process ensure --prepare-command <command> for synchronous dependency preparation under the process lock before app launch. Unchanged owned processes skip preparation; it participates in default fingerprints and must not daemonize or detach.",
3368
+ "- Host-generated Compose inputs may use managedRuntime.devcontainer.prepareCommand as literal argv. Ensure runs it once from the checkout root before Compose inspection, with a 60-second bound and no implicit shell. Keep .devrouter.yml unchanged. Diagnostics never run it; changed warm mounts require separate verification.",
3326
3369
  "- Exact managed Devsy stop validates complete retained container membership, ownership and configuration before stopping residual services after a stopped primary. Unknown evidence blocks cleanup; original provider failures remain errors even after successful residual cleanup.",
3327
3370
  "- Owner status is `present`, `missing`, `locked`, or `conflict`. Dirty or locked full down fails before side effects. `workspace gc` is a dry run; only `--yes` deletes exact eligible missing resources and records, never Git worktrees, branches, or prune state.",
3328
3371
  "- `devrouter workspace cleanup --repo . --inactive-for 30d --check-merged --json` is report-only and never mutates the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. It reports ownership, workspace runtime registration, runtime state (`running|stopped|busy|not-found|absent|unknown`), checkout, advisory activity, route, and integration evidence. Local DevPod/Devsy list/status checks always run; `--check-merged` alone enables read-only origin and matching GitHub/GitLab checks. Treat `not-found` as stale runtime after Docker pruning; busy, unavailable, or conflicting evidence suppresses destructive suggestions. Explicit `gc`/`down` can remove exact stale registration only after expected-ID `NotFound` proof and ownership revalidation. Runtime `lastUsed` remains advisory.",
@@ -3370,7 +3413,7 @@ function buildOnboardingPrompt(options = {}) {
3370
3413
  "- devrouter repo devcontainer write --repo <REPO_PATH> --yes",
3371
3414
  "- devrouter repo devcontainer verify --repo <REPO_PATH> --json",
3372
3415
  "- Normal primary or linked startup: devrouter ensure <REPO_PATH> --json",
3373
- "- Degraded recovery: ordinary ensure automatically repairs the recorded profile before the desired transition; --repair limits the invocation to recorded-profile repair. Both validate unchanged retained configuration; stopped primary requires all project containers stopped and zero checkout routes. It starts only retained IDs, never provider bootstrap/recreation. Inspect failures before retrying.",
3416
+ "- Degraded recovery: ordinary ensure validates retained ownership, then reconciles the requested profile without starting dropped processes first. Same-profile repair and --repair retain the recorded-profile path and start only retained IDs without provider bootstrap/recreation. Stopped primary requires all project containers stopped and zero checkout routes. Failed transitions preserve degraded state without replaying its broken adapter.",
3374
3417
  "- Managed selective startup: devrouter ensure <REPO_PATH> --profile <name> --json",
3375
3418
  "- Container seed/migration: devrouter exec <REPO_PATH> -- <command...>",
3376
3419
  "- Compatibility verification only after ensure: devrouter repo devcontainer verify --repo <REPO_PATH> --live --yes --json",
@@ -9166,7 +9209,7 @@ async function buildDoctorReport(options = {}) {
9166
9209
  const config = runtimeConfig.config;
9167
9210
  loadedConfig = config;
9168
9211
  loadedWorkspace = runtimeConfig.workspace;
9169
- const cliVersion = true ? "0.0.57" : "0.0.0-dev";
9212
+ const cliVersion = true ? "0.0.59" : "0.0.0-dev";
9170
9213
  const configVersion = config.devrouter?.version;
9171
9214
  if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
9172
9215
  addCheck(checks, {
@@ -11008,12 +11051,23 @@ var init_http_route_probe = __esm({
11008
11051
  }
11009
11052
  });
11010
11053
 
11054
+ // src/core/managed-host-preparation.ts
11055
+ var import_node_child_process21, import_node_fs27;
11056
+ var init_managed_host_preparation = __esm({
11057
+ "src/core/managed-host-preparation.ts"() {
11058
+ "use strict";
11059
+ import_node_child_process21 = require("child_process");
11060
+ import_node_fs27 = __toESM(require("fs"));
11061
+ init_repo_config();
11062
+ }
11063
+ });
11064
+
11011
11065
  // src/core/traefik-route-health.ts
11012
11066
  function sleep(ms) {
11013
11067
  return new Promise((resolve) => setTimeout(resolve, ms));
11014
11068
  }
11015
11069
  function inspectRouterApi(protocol) {
11016
- const result = (0, import_node_child_process21.spawnSync)(
11070
+ const result = (0, import_node_child_process22.spawnSync)(
11017
11071
  "curl",
11018
11072
  [
11019
11073
  "--silent",
@@ -11112,7 +11166,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
11112
11166
  throw new Error(
11113
11167
  "Traefik routes did not reload; shared router restart is disabled for this operation."
11114
11168
  );
11115
- import_node_fs27.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
11169
+ import_node_fs28.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
11116
11170
  return withFileLock(
11117
11171
  ROUTER_RELOAD_LOCK_FILE,
11118
11172
  {
@@ -11143,12 +11197,12 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
11143
11197
  async function ensureTraefikRoutesRemoved(routes, options = {}) {
11144
11198
  return ensureTraefikRouteExpectation(routes, "removed", options);
11145
11199
  }
11146
- var import_node_child_process21, import_node_fs27, import_node_path28, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
11200
+ var import_node_child_process22, import_node_fs28, import_node_path28, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
11147
11201
  var init_traefik_route_health = __esm({
11148
11202
  "src/core/traefik-route-health.ts"() {
11149
11203
  "use strict";
11150
- import_node_child_process21 = require("child_process");
11151
- import_node_fs27 = __toESM(require("fs"));
11204
+ import_node_child_process22 = require("child_process");
11205
+ import_node_fs28 = __toESM(require("fs"));
11152
11206
  import_node_path28 = __toESM(require("path"));
11153
11207
  init_file_lock();
11154
11208
  init_host_routes();
@@ -11194,12 +11248,12 @@ function resolveLinkedTarget(repoPath) {
11194
11248
  gitCommonDir: resolveGitCommonDir(repoPath)
11195
11249
  };
11196
11250
  }
11197
- var import_node_child_process22, import_node_fs28, import_node_path29;
11251
+ var import_node_child_process23, import_node_fs29, import_node_path29;
11198
11252
  var init_workspace_ensure = __esm({
11199
11253
  "src/core/workspace-ensure.ts"() {
11200
11254
  "use strict";
11201
- import_node_child_process22 = require("child_process");
11202
- import_node_fs28 = __toESM(require("fs"));
11255
+ import_node_child_process23 = require("child_process");
11256
+ import_node_fs29 = __toESM(require("fs"));
11203
11257
  import_node_path29 = __toESM(require("path"));
11204
11258
  init_devcontainer_profile();
11205
11259
  init_devpod_environment();
@@ -11208,6 +11262,7 @@ var init_workspace_ensure = __esm({
11208
11262
  init_file_lock();
11209
11263
  init_host_routes();
11210
11264
  init_http_route_probe();
11265
+ init_managed_host_preparation();
11211
11266
  init_managed_post_start();
11212
11267
  init_managed_runtime_state();
11213
11268
  init_managed_runtime_status();
@@ -11629,7 +11684,7 @@ async function runOpenCommand(name) {
11629
11684
  );
11630
11685
  return;
11631
11686
  }
11632
- const result = (0, import_node_child_process23.spawnSync)("open", [url], { encoding: "utf-8" });
11687
+ const result = (0, import_node_child_process24.spawnSync)("open", [url], { encoding: "utf-8" });
11633
11688
  if (result.status !== 0) {
11634
11689
  const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
11635
11690
  throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
@@ -11637,11 +11692,11 @@ async function runOpenCommand(name) {
11637
11692
  process.stdout.write(`Opened ${url}
11638
11693
  `);
11639
11694
  }
11640
- var import_node_child_process23;
11695
+ var import_node_child_process24;
11641
11696
  var init_open = __esm({
11642
11697
  "src/commands/open.ts"() {
11643
11698
  "use strict";
11644
- import_node_child_process23 = require("child_process");
11699
+ import_node_child_process24 = require("child_process");
11645
11700
  init_docker();
11646
11701
  init_host_routes();
11647
11702
  init_repo_config();
@@ -11664,7 +11719,7 @@ async function runLogsCommand(options) {
11664
11719
  const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
11665
11720
  if (options.follow) {
11666
11721
  args.splice(1, 0, "-f");
11667
- const child = (0, import_node_child_process24.spawn)("docker", args, { stdio: "inherit" });
11722
+ const child = (0, import_node_child_process25.spawn)("docker", args, { stdio: "inherit" });
11668
11723
  const onSignal = () => {
11669
11724
  child.kill("SIGTERM");
11670
11725
  };
@@ -11678,17 +11733,17 @@ async function runLogsCommand(options) {
11678
11733
  });
11679
11734
  });
11680
11735
  } else {
11681
- const result = (0, import_node_child_process24.spawnSync)("docker", args, { stdio: "inherit" });
11736
+ const result = (0, import_node_child_process25.spawnSync)("docker", args, { stdio: "inherit" });
11682
11737
  if (result.status !== 0) {
11683
11738
  throw new Error("Failed to retrieve router logs.");
11684
11739
  }
11685
11740
  }
11686
11741
  }
11687
- var import_node_child_process24;
11742
+ var import_node_child_process25;
11688
11743
  var init_logs = __esm({
11689
11744
  "src/commands/logs.ts"() {
11690
11745
  "use strict";
11691
- import_node_child_process24 = require("child_process");
11746
+ import_node_child_process25 = require("child_process");
11692
11747
  init_docker();
11693
11748
  init_router();
11694
11749
  }
@@ -11728,11 +11783,11 @@ function asRecord3(value) {
11728
11783
  return value;
11729
11784
  }
11730
11785
  function readJson(filePath) {
11731
- if (!import_node_fs29.default.existsSync(filePath)) {
11786
+ if (!import_node_fs30.default.existsSync(filePath)) {
11732
11787
  return void 0;
11733
11788
  }
11734
11789
  try {
11735
- return JSON.parse(import_node_fs29.default.readFileSync(filePath, "utf-8"));
11790
+ return JSON.parse(import_node_fs30.default.readFileSync(filePath, "utf-8"));
11736
11791
  } catch {
11737
11792
  return void 0;
11738
11793
  }
@@ -11779,7 +11834,7 @@ function inspectPackageManager(repoPath, pkg) {
11779
11834
  ["bun.lock", "bun"]
11780
11835
  ];
11781
11836
  for (const [fileName, name] of lockfiles) {
11782
- if (import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
11837
+ if (import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
11783
11838
  return { name, source: fileName };
11784
11839
  }
11785
11840
  }
@@ -11795,8 +11850,8 @@ function inspectNode(repoPath, pkg) {
11795
11850
  return { version: engines.node, source: "package.json:engines.node" };
11796
11851
  }
11797
11852
  const nvmrc = import_node_path31.default.join(repoPath, ".nvmrc");
11798
- if (import_node_fs29.default.existsSync(nvmrc)) {
11799
- const version = import_node_fs29.default.readFileSync(nvmrc, "utf-8").trim();
11853
+ if (import_node_fs30.default.existsSync(nvmrc)) {
11854
+ const version = import_node_fs30.default.readFileSync(nvmrc, "utf-8").trim();
11800
11855
  return { version, source: ".nvmrc" };
11801
11856
  }
11802
11857
  return void 0;
@@ -11875,7 +11930,7 @@ function composeFiles(repoPath) {
11875
11930
  ...configuredComposeFiles(repoPath)
11876
11931
  ];
11877
11932
  return Array.from(new Set(candidates)).filter(
11878
- (fileName) => import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))
11933
+ (fileName) => import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))
11879
11934
  );
11880
11935
  }
11881
11936
  function stringArray2(value) {
@@ -11913,7 +11968,7 @@ function inspectServices(repoPath) {
11913
11968
  const services = [];
11914
11969
  for (const fileName of composeFiles(repoPath)) {
11915
11970
  try {
11916
- const parsed = import_yaml6.default.parse(import_node_fs29.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8"));
11971
+ const parsed = import_yaml6.default.parse(import_node_fs30.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8"));
11917
11972
  const serviceMap = asRecord3(asRecord3(parsed)?.services);
11918
11973
  for (const [name, value] of Object.entries(serviceMap ?? {})) {
11919
11974
  const service = asRecord3(value);
@@ -11948,8 +12003,8 @@ function inspectServices(repoPath) {
11948
12003
  return services;
11949
12004
  }
11950
12005
  function inspectEnvFiles(repoPath) {
11951
- const files = import_node_fs29.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
11952
- const content = import_node_fs29.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8");
12006
+ const files = import_node_fs30.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
12007
+ const content = import_node_fs30.default.readFileSync(import_node_path31.default.join(repoPath, fileName), "utf-8");
11953
12008
  const names = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && line.includes("=")).map((line) => line.split("=")[0]?.trim()).filter((name) => Boolean(name)).sort();
11954
12009
  return { path: fileName, names };
11955
12010
  });
@@ -11964,17 +12019,17 @@ function inspectEnvFiles(repoPath) {
11964
12019
  }
11965
12020
  function inspectDevcontainer(repoPath) {
11966
12021
  const dir = import_node_path31.default.join(repoPath, ".devcontainer");
11967
- if (!import_node_fs29.default.existsSync(dir)) {
12022
+ if (!import_node_fs30.default.existsSync(dir)) {
11968
12023
  return { exists: false, files: [] };
11969
12024
  }
11970
12025
  return {
11971
12026
  exists: true,
11972
- files: import_node_fs29.default.readdirSync(dir).filter((fileName) => import_node_fs29.default.statSync(import_node_path31.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
12027
+ files: import_node_fs30.default.readdirSync(dir).filter((fileName) => import_node_fs30.default.statSync(import_node_path31.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
11973
12028
  };
11974
12029
  }
11975
12030
  function inspectDevrouter(repoPath) {
11976
12031
  const configPath = getRepoConfigPath(repoPath);
11977
- if (!import_node_fs29.default.existsSync(configPath)) {
12032
+ if (!import_node_fs30.default.existsSync(configPath)) {
11978
12033
  return {
11979
12034
  exists: false,
11980
12035
  configPath,
@@ -12018,15 +12073,15 @@ function inspectAgentGuidance(repoPath) {
12018
12073
  ["AGENTS.md", "agents"],
12019
12074
  ["CLAUDE.md", "claude"]
12020
12075
  ]) {
12021
- if (import_node_fs29.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
12076
+ if (import_node_fs30.default.existsSync(import_node_path31.default.join(repoPath, fileName))) {
12022
12077
  results.push({ path: fileName, kind });
12023
12078
  }
12024
12079
  }
12025
12080
  const skillsDir = import_node_path31.default.join(repoPath, ".agents", "skills");
12026
- if (import_node_fs29.default.existsSync(skillsDir)) {
12027
- for (const name of import_node_fs29.default.readdirSync(skillsDir).sort()) {
12081
+ if (import_node_fs30.default.existsSync(skillsDir)) {
12082
+ for (const name of import_node_fs30.default.readdirSync(skillsDir).sort()) {
12028
12083
  const skillPath = import_node_path31.default.join(skillsDir, name, "SKILL.md");
12029
- if (import_node_fs29.default.existsSync(skillPath)) {
12084
+ if (import_node_fs30.default.existsSync(skillPath)) {
12030
12085
  results.push({ path: relative(repoPath, skillPath), kind: "skill" });
12031
12086
  }
12032
12087
  }
@@ -12088,11 +12143,11 @@ function inspectRepo(options = {}) {
12088
12143
  issues: buildIssues(reportWithoutIssues)
12089
12144
  };
12090
12145
  }
12091
- var import_node_fs29, import_node_path31, import_yaml6;
12146
+ var import_node_fs30, import_node_path31, import_yaml6;
12092
12147
  var init_repo_inspect = __esm({
12093
12148
  "src/core/repo-inspect.ts"() {
12094
12149
  "use strict";
12095
- import_node_fs29 = __toESM(require("fs"));
12150
+ import_node_fs30 = __toESM(require("fs"));
12096
12151
  import_node_path31 = __toESM(require("path"));
12097
12152
  import_yaml6 = __toESM(require("yaml"));
12098
12153
  init_repo_config();
@@ -12198,7 +12253,7 @@ function requiredFileChecks(repoPath) {
12198
12253
  ".devcontainer/docker-compose.yml",
12199
12254
  ".devrouter.yml"
12200
12255
  ];
12201
- const missing = required.filter((fileName) => !import_node_fs30.default.existsSync(import_node_path32.default.join(repoPath, fileName)));
12256
+ const missing = required.filter((fileName) => !import_node_fs31.default.existsSync(import_node_path32.default.join(repoPath, fileName)));
12202
12257
  return {
12203
12258
  id: "repo.devcontainer.verify-files",
12204
12259
  level: missing.length === 0 ? "ok" : "error",
@@ -12423,11 +12478,11 @@ async function verifyDevcontainer(options = {}) {
12423
12478
  nextSteps: collectNextSteps3(checks)
12424
12479
  };
12425
12480
  }
12426
- var import_node_fs30, import_node_path32;
12481
+ var import_node_fs31, import_node_path32;
12427
12482
  var init_devcontainer_verify = __esm({
12428
12483
  "src/core/devcontainer-verify.ts"() {
12429
12484
  "use strict";
12430
- import_node_fs30 = __toESM(require("fs"));
12485
+ import_node_fs31 = __toESM(require("fs"));
12431
12486
  import_node_path32 = __toESM(require("path"));
12432
12487
  init_capabilities();
12433
12488
  init_doctor();
@@ -12771,7 +12826,7 @@ function plannedFiles(repoPath, version) {
12771
12826
  }
12772
12827
  function classifyFile(repoPath, file) {
12773
12828
  const absolutePath = import_node_path33.default.join(repoPath, file.relativePath);
12774
- if (!import_node_fs31.default.existsSync(absolutePath)) {
12829
+ if (!import_node_fs32.default.existsSync(absolutePath)) {
12775
12830
  return {
12776
12831
  path: file.relativePath,
12777
12832
  action: "create",
@@ -12779,7 +12834,7 @@ function classifyFile(repoPath, file) {
12779
12834
  bytes: Buffer.byteLength(file.content)
12780
12835
  };
12781
12836
  }
12782
- const current = import_node_fs31.default.readFileSync(absolutePath, "utf-8");
12837
+ const current = import_node_fs32.default.readFileSync(absolutePath, "utf-8");
12783
12838
  if (!current.includes(MANAGED_MARKER2)) {
12784
12839
  return {
12785
12840
  path: file.relativePath,
@@ -12837,10 +12892,10 @@ function buildPlan(repoPath, dryRun, version) {
12837
12892
  }
12838
12893
  function writeFile(repoPath, file) {
12839
12894
  const absolutePath = import_node_path33.default.join(repoPath, file.relativePath);
12840
- import_node_fs31.default.mkdirSync(import_node_path33.default.dirname(absolutePath), { recursive: true });
12841
- import_node_fs31.default.writeFileSync(absolutePath, file.content, "utf-8");
12895
+ import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(absolutePath), { recursive: true });
12896
+ import_node_fs32.default.writeFileSync(absolutePath, file.content, "utf-8");
12842
12897
  if (file.executable) {
12843
- import_node_fs31.default.chmodSync(absolutePath, 493);
12898
+ import_node_fs32.default.chmodSync(absolutePath, 493);
12844
12899
  }
12845
12900
  }
12846
12901
  function writeDevcontainer(options = {}) {
@@ -12878,11 +12933,11 @@ function writeDevcontainer(options = {}) {
12878
12933
  nextSteps: postWriteNextSteps(repoPath)
12879
12934
  };
12880
12935
  }
12881
- var import_node_fs31, import_node_path33, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
12936
+ var import_node_fs32, import_node_path33, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
12882
12937
  var init_devcontainer_write = __esm({
12883
12938
  "src/core/devcontainer-write.ts"() {
12884
12939
  "use strict";
12885
- import_node_fs31 = __toESM(require("fs"));
12940
+ import_node_fs32 = __toESM(require("fs"));
12886
12941
  import_node_path33 = __toESM(require("path"));
12887
12942
  init_repo_config();
12888
12943
  init_repo_inspect();
@@ -13296,7 +13351,7 @@ function loadContract(repoPath, contractPath) {
13296
13351
  const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
13297
13352
  let metadata;
13298
13353
  try {
13299
- metadata = import_node_fs32.default.lstatSync(absolutePath);
13354
+ metadata = import_node_fs33.default.lstatSync(absolutePath);
13300
13355
  } catch (error) {
13301
13356
  fail(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
13302
13357
  }
@@ -13306,10 +13361,10 @@ function loadContract(repoPath, contractPath) {
13306
13361
  if (metadata.size > MAX_CONTRACT_BYTES) {
13307
13362
  fail(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
13308
13363
  }
13309
- const realRepo = import_node_fs32.default.realpathSync(repoPath);
13310
- const realContract = import_node_fs32.default.realpathSync(absolutePath);
13364
+ const realRepo = import_node_fs33.default.realpathSync(repoPath);
13365
+ const realContract = import_node_fs33.default.realpathSync(absolutePath);
13311
13366
  assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
13312
- const source = import_node_fs32.default.readFileSync(absolutePath, "utf-8");
13367
+ const source = import_node_fs33.default.readFileSync(absolutePath, "utf-8");
13313
13368
  if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
13314
13369
  fail(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
13315
13370
  }
@@ -13329,11 +13384,11 @@ function resolveProfilePlan(options) {
13329
13384
  contractPath: loaded.contractPath
13330
13385
  });
13331
13386
  }
13332
- var import_node_fs32, import_node_path34, import_yaml7, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
13387
+ var import_node_fs33, import_node_path34, import_yaml7, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
13333
13388
  var init_profile_plan = __esm({
13334
13389
  "src/core/profile-plan.ts"() {
13335
13390
  "use strict";
13336
- import_node_fs32 = __toESM(require("fs"));
13391
+ import_node_fs33 = __toESM(require("fs"));
13337
13392
  import_node_path34 = __toESM(require("path"));
13338
13393
  import_yaml7 = require("yaml");
13339
13394
  init_paths();
@@ -13779,10 +13834,10 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
13779
13834
  throw new Error("No docker apps selected to prepare compose overlay.");
13780
13835
  }
13781
13836
  const cachePath = import_node_path36.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
13782
- import_node_fs33.default.mkdirSync(cachePath, { recursive: true });
13837
+ import_node_fs34.default.mkdirSync(cachePath, { recursive: true });
13783
13838
  const overlayPath = import_node_path36.default.join(cachePath, "compose.devrouter.yml");
13784
13839
  const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
13785
- import_node_fs33.default.writeFileSync(overlayPath, import_yaml8.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
13840
+ import_node_fs34.default.writeFileSync(overlayPath, import_yaml8.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
13786
13841
  return {
13787
13842
  overlayPath,
13788
13843
  composeFiles: ensureComposeFiles(dockerApps),
@@ -13796,7 +13851,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
13796
13851
  fileArgs.push("-f", resolved);
13797
13852
  }
13798
13853
  const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
13799
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13854
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13800
13855
  encoding: "utf-8",
13801
13856
  cwd: repoPath
13802
13857
  });
@@ -13824,7 +13879,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
13824
13879
  "--services",
13825
13880
  ...services
13826
13881
  ];
13827
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13882
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13828
13883
  encoding: "utf-8",
13829
13884
  cwd: repoPath
13830
13885
  });
@@ -13850,7 +13905,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
13850
13905
  fileArgs.push("-f", resolved);
13851
13906
  }
13852
13907
  const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
13853
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13908
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13854
13909
  encoding: "utf-8",
13855
13910
  cwd: repoPath
13856
13911
  });
@@ -13876,7 +13931,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
13876
13931
  String(tail),
13877
13932
  ...services
13878
13933
  ];
13879
- (0, import_node_child_process25.spawnSync)("docker", args, {
13934
+ (0, import_node_child_process26.spawnSync)("docker", args, {
13880
13935
  stdio: "inherit",
13881
13936
  cwd: repoPath
13882
13937
  });
@@ -13888,7 +13943,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
13888
13943
  fileArgs.push("-f", resolved);
13889
13944
  }
13890
13945
  const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
13891
- const result = (0, import_node_child_process25.spawnSync)("docker", args, {
13946
+ const result = (0, import_node_child_process26.spawnSync)("docker", args, {
13892
13947
  encoding: "utf-8",
13893
13948
  cwd: repoPath
13894
13949
  });
@@ -13902,13 +13957,13 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
13902
13957
  const port = Number(match[1]);
13903
13958
  return Number.isInteger(port) && port > 0 ? port : void 0;
13904
13959
  }
13905
- var import_node_child_process25, import_node_crypto11, import_node_fs33, import_node_path36, import_yaml8;
13960
+ var import_node_child_process26, import_node_crypto11, import_node_fs34, import_node_path36, import_yaml8;
13906
13961
  var init_docker_run = __esm({
13907
13962
  "src/core/docker-run.ts"() {
13908
13963
  "use strict";
13909
- import_node_child_process25 = require("child_process");
13964
+ import_node_child_process26 = require("child_process");
13910
13965
  import_node_crypto11 = require("crypto");
13911
- import_node_fs33 = __toESM(require("fs"));
13966
+ import_node_fs34 = __toESM(require("fs"));
13912
13967
  import_node_path36 = __toESM(require("path"));
13913
13968
  import_yaml8 = __toESM(require("yaml"));
13914
13969
  init_docker_error_guidance();
@@ -13999,7 +14054,7 @@ function isProcessRunning(pid) {
13999
14054
  }
14000
14055
  }
14001
14056
  function readProcessTree(rootPid) {
14002
- const result = (0, import_node_child_process26.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
14057
+ const result = (0, import_node_child_process27.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
14003
14058
  if (result.status !== 0) {
14004
14059
  return [rootPid];
14005
14060
  }
@@ -14110,12 +14165,12 @@ function detectListeningPorts(pids) {
14110
14165
  if (pids.length === 0) {
14111
14166
  return [];
14112
14167
  }
14113
- const result = (0, import_node_child_process26.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
14168
+ const result = (0, import_node_child_process27.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
14114
14169
  encoding: "utf-8"
14115
14170
  });
14116
14171
  if (result.error && result.error.code === "ENOENT") {
14117
14172
  if (process.platform === "linux") {
14118
- const ssResult = (0, import_node_child_process26.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
14173
+ const ssResult = (0, import_node_child_process27.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
14119
14174
  if (ssResult.status === 0 && ssResult.stdout) {
14120
14175
  return parseSsListeningPorts(ssResult.stdout, new Set(pids));
14121
14176
  }
@@ -14174,7 +14229,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
14174
14229
  app.hostRun.command,
14175
14230
  true
14176
14231
  ) : app.hostRun.command;
14177
- const child = (0, import_node_child_process26.spawn)(spawnCommand, {
14232
+ const child = (0, import_node_child_process27.spawn)(spawnCommand, {
14178
14233
  cwd: commandCwd,
14179
14234
  stdio: "inherit",
14180
14235
  shell: true,
@@ -14542,7 +14597,7 @@ async function execWithAppEnv(options) {
14542
14597
  options.command[0],
14543
14598
  true
14544
14599
  );
14545
- child = (0, import_node_child_process26.spawn)(wrapped, {
14600
+ child = (0, import_node_child_process27.spawn)(wrapped, {
14546
14601
  cwd: deps.repoPath,
14547
14602
  stdio: "inherit",
14548
14603
  shell: true,
@@ -14556,7 +14611,7 @@ async function execWithAppEnv(options) {
14556
14611
  false
14557
14612
  );
14558
14613
  const [cmd, ...wrappedArgs] = wrapped;
14559
- child = (0, import_node_child_process26.spawn)(cmd, wrappedArgs, {
14614
+ child = (0, import_node_child_process27.spawn)(cmd, wrappedArgs, {
14560
14615
  cwd: deps.repoPath,
14561
14616
  stdio: "inherit",
14562
14617
  shell: false,
@@ -14564,7 +14619,7 @@ async function execWithAppEnv(options) {
14564
14619
  });
14565
14620
  }
14566
14621
  } else if (options.shell) {
14567
- child = (0, import_node_child_process26.spawn)(options.command[0], {
14622
+ child = (0, import_node_child_process27.spawn)(options.command[0], {
14568
14623
  cwd: deps.repoPath,
14569
14624
  stdio: "inherit",
14570
14625
  shell: true,
@@ -14572,7 +14627,7 @@ async function execWithAppEnv(options) {
14572
14627
  });
14573
14628
  } else {
14574
14629
  const [command, ...args] = options.command;
14575
- child = (0, import_node_child_process26.spawn)(command, args, {
14630
+ child = (0, import_node_child_process27.spawn)(command, args, {
14576
14631
  cwd: deps.repoPath,
14577
14632
  stdio: "inherit",
14578
14633
  shell: false,
@@ -14593,11 +14648,11 @@ async function execWithAppEnv(options) {
14593
14648
  deps.stopDeps();
14594
14649
  }
14595
14650
  }
14596
- var import_node_child_process26, import_node_net, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
14651
+ var import_node_child_process27, import_node_net, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
14597
14652
  var init_app_run = __esm({
14598
14653
  "src/core/app-run.ts"() {
14599
14654
  "use strict";
14600
- import_node_child_process26 = require("child_process");
14655
+ import_node_child_process27 = require("child_process");
14601
14656
  import_node_net = __toESM(require("net"));
14602
14657
  import_node_process = require("process");
14603
14658
  import_promises = require("readline/promises");
@@ -14731,13 +14786,13 @@ function measureWorktreeConsumption(worktreePath, options) {
14731
14786
  const startedAt = Date.now();
14732
14787
  let rootStat;
14733
14788
  try {
14734
- rootStat = import_node_fs34.default.lstatSync(worktreePath);
14789
+ rootStat = import_node_fs35.default.lstatSync(worktreePath);
14735
14790
  } catch (error) {
14736
14791
  return { status: "unknown", reason: describeError(error, worktreePath) };
14737
14792
  }
14738
14793
  let rootEntries;
14739
14794
  try {
14740
- rootEntries = import_node_fs34.default.readdirSync(worktreePath, { withFileTypes: true });
14795
+ rootEntries = import_node_fs35.default.readdirSync(worktreePath, { withFileTypes: true });
14741
14796
  } catch (error) {
14742
14797
  return { status: "unknown", reason: describeError(error, worktreePath) };
14743
14798
  }
@@ -14766,7 +14821,7 @@ function measureWorktreeConsumption(worktreePath, options) {
14766
14821
  const entryPath = import_node_path37.default.join(dirPath, entry.name);
14767
14822
  let entryStat;
14768
14823
  try {
14769
- entryStat = import_node_fs34.default.lstatSync(entryPath);
14824
+ entryStat = import_node_fs35.default.lstatSync(entryPath);
14770
14825
  } catch (error) {
14771
14826
  if (error?.code === "ENOENT") continue;
14772
14827
  unreadableReason = describeIncompleteWalk(error);
@@ -14776,7 +14831,7 @@ function measureWorktreeConsumption(worktreePath, options) {
14776
14831
  if (!entryStat.isDirectory()) continue;
14777
14832
  let childEntries;
14778
14833
  try {
14779
- childEntries = import_node_fs34.default.readdirSync(entryPath, { withFileTypes: true });
14834
+ childEntries = import_node_fs35.default.readdirSync(entryPath, { withFileTypes: true });
14780
14835
  } catch (error) {
14781
14836
  unreadableReason = describeIncompleteWalk(error);
14782
14837
  break;
@@ -14840,11 +14895,11 @@ function describeError(error, worktreePath) {
14840
14895
  }
14841
14896
  return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
14842
14897
  }
14843
- var import_node_fs34, import_node_path37, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
14898
+ var import_node_fs35, import_node_path37, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
14844
14899
  var init_workspace_consumption = __esm({
14845
14900
  "src/core/workspace-consumption.ts"() {
14846
14901
  "use strict";
14847
- import_node_fs34 = __toESM(require("fs"));
14902
+ import_node_fs35 = __toESM(require("fs"));
14848
14903
  import_node_path37 = __toESM(require("path"));
14849
14904
  init_devpod_environment();
14850
14905
  DEFAULT_DEADLINE_MS = 1e4;
@@ -14946,7 +15001,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
14946
15001
  }
14947
15002
  function readGitSnapshot(worktree, commandRunner) {
14948
15003
  const comparablePath = comparableWorkspacePath(worktree.path);
14949
- if (worktree.prunable || !import_node_fs35.default.existsSync(comparablePath)) {
15004
+ if (worktree.prunable || !import_node_fs36.default.existsSync(comparablePath)) {
14950
15005
  return { worktree, checkout: "missing", head: null, committerDate: null };
14951
15006
  }
14952
15007
  const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
@@ -15520,12 +15575,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
15520
15575
  workspaces: rows
15521
15576
  };
15522
15577
  }
15523
- var import_node_child_process27, import_node_fs35, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
15578
+ var import_node_child_process28, import_node_fs36, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
15524
15579
  var init_workspace_cleanup = __esm({
15525
15580
  "src/core/workspace-cleanup.ts"() {
15526
15581
  "use strict";
15527
- import_node_child_process27 = require("child_process");
15528
- import_node_fs35 = __toESM(require("fs"));
15582
+ import_node_child_process28 = require("child_process");
15583
+ import_node_fs36 = __toESM(require("fs"));
15529
15584
  init_devpod_workspaces();
15530
15585
  init_host_routes();
15531
15586
  init_repo_config();
@@ -15542,7 +15597,7 @@ var init_workspace_cleanup = __esm({
15542
15597
  ];
15543
15598
  defaultCommandRunner = (command, args, input2) => {
15544
15599
  const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
15545
- const result = (0, import_node_child_process27.spawnSync)(command, args, {
15600
+ const result = (0, import_node_child_process28.spawnSync)(command, args, {
15546
15601
  encoding: "utf-8",
15547
15602
  env: environment,
15548
15603
  ...input2 === void 0 ? {} : { input: input2 }
@@ -15575,7 +15630,7 @@ function defaultWorktreePath(mainRepo, ws) {
15575
15630
  return import_node_path38.default.join(mainRepo, "trees", ws);
15576
15631
  }
15577
15632
  function assertDefaultWorktreeRootIgnored(mainRepo) {
15578
- const ignored = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
15633
+ const ignored = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
15579
15634
  encoding: "utf-8"
15580
15635
  });
15581
15636
  if (ignored.status !== 0) {
@@ -15697,13 +15752,13 @@ function assertFullDownPreflight(mainRepo, target) {
15697
15752
  if (sameWorkspacePath(target.worktreePath, mainRepo)) {
15698
15753
  throw new Error("Refusing to remove the primary Git checkout.");
15699
15754
  }
15700
- if (!target.worktree || target.worktree.prunable || !import_node_fs36.default.existsSync(target.worktreePath)) return;
15755
+ if (!target.worktree || target.worktree.prunable || !import_node_fs37.default.existsSync(target.worktreePath)) return;
15701
15756
  if (target.worktree.locked) {
15702
15757
  throw new Error(
15703
15758
  `Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
15704
15759
  );
15705
15760
  }
15706
- const status = (0, import_node_child_process28.spawnSync)(
15761
+ const status = (0, import_node_child_process29.spawnSync)(
15707
15762
  "git",
15708
15763
  ["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
15709
15764
  { encoding: "utf-8" }
@@ -15737,7 +15792,7 @@ async function workspaceUp(branch, opts = {}) {
15737
15792
  (candidate) => defaultWorktreePath(mainRepo, candidate)
15738
15793
  );
15739
15794
  const generatedPath = candidatePaths.find(
15740
- (candidate) => !import_node_fs36.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
15795
+ (candidate) => !import_node_fs37.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
15741
15796
  );
15742
15797
  const selectedPath = opts.path ? import_node_path38.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
15743
15798
  if (!selectedPath) {
@@ -15748,7 +15803,7 @@ async function workspaceUp(branch, opts = {}) {
15748
15803
  `Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
15749
15804
  );
15750
15805
  }
15751
- if (import_node_fs36.default.existsSync(selectedPath)) {
15806
+ if (import_node_fs37.default.existsSync(selectedPath)) {
15752
15807
  const registered = worktrees.find(
15753
15808
  (worktree) => sameWorkspacePath(worktree.path, selectedPath)
15754
15809
  );
@@ -15767,11 +15822,11 @@ async function workspaceUp(branch, opts = {}) {
15767
15822
  return selectedPath;
15768
15823
  }
15769
15824
  if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
15770
- const add = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
15825
+ const add = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
15771
15826
  encoding: "utf-8"
15772
15827
  });
15773
15828
  if (add.status !== 0) {
15774
- const addNew = (0, import_node_child_process28.spawnSync)(
15829
+ const addNew = (0, import_node_child_process29.spawnSync)(
15775
15830
  "git",
15776
15831
  ["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
15777
15832
  { encoding: "utf-8" }
@@ -15888,8 +15943,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
15888
15943
  }
15889
15944
  const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
15890
15945
  if (removeWorktree) {
15891
- if (resolved.worktree && !resolved.worktree.prunable && import_node_fs36.default.existsSync(resolved.worktreePath)) {
15892
- const rm = (0, import_node_child_process28.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
15946
+ if (resolved.worktree && !resolved.worktree.prunable && import_node_fs37.default.existsSync(resolved.worktreePath)) {
15947
+ const rm = (0, import_node_child_process29.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
15893
15948
  encoding: "utf-8"
15894
15949
  });
15895
15950
  if (rm.status !== 0) {
@@ -15907,7 +15962,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
15907
15962
  }
15908
15963
  return result;
15909
15964
  };
15910
- return resolved.worktree && !resolved.worktree.prunable && import_node_fs36.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
15965
+ return resolved.worktree && !resolved.worktree.prunable && import_node_fs37.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
15911
15966
  }
15912
15967
  async function workspaceStop(target, opts = {}) {
15913
15968
  return runWorkspaceLifecycle("stop", target, opts);
@@ -15915,12 +15970,12 @@ async function workspaceStop(target, opts = {}) {
15915
15970
  async function workspaceDown(target, opts = {}) {
15916
15971
  return runWorkspaceLifecycle("down", target, opts);
15917
15972
  }
15918
- var import_node_child_process28, import_node_fs36, import_node_path38, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
15973
+ var import_node_child_process29, import_node_fs37, import_node_path38, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
15919
15974
  var init_workspace_lifecycle = __esm({
15920
15975
  "src/core/workspace-lifecycle.ts"() {
15921
15976
  "use strict";
15922
- import_node_child_process28 = require("child_process");
15923
- import_node_fs36 = __toESM(require("fs"));
15977
+ import_node_child_process29 = require("child_process");
15978
+ import_node_fs37 = __toESM(require("fs"));
15924
15979
  import_node_path38 = __toESM(require("path"));
15925
15980
  init_devpod_mutation();
15926
15981
  init_devpod_workspaces();
@@ -16080,7 +16135,7 @@ var init_version = __esm({
16080
16135
 
16081
16136
  // src/cli.ts
16082
16137
  var import_commander = require("commander");
16083
- var CLI_VERSION = true ? "0.0.57" : "0.0.0-dev";
16138
+ var CLI_VERSION = true ? "0.0.59" : "0.0.0-dev";
16084
16139
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
16085
16140
  function withErrorHandling(action2) {
16086
16141
  return async (...args) => {