@codions/portta 0.0.0-develop.166.sha-d31f10f → 0.0.0-develop.171.sha-d749180

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/cli.js CHANGED
@@ -21311,6 +21311,113 @@ var init_password = __esm({
21311
21311
  }
21312
21312
  });
21313
21313
 
21314
+ // ../core/src/project-config.ts
21315
+ function hostShape(value3) {
21316
+ if (/:\/\//.test(value3)) return "looks like a URL";
21317
+ if (value3.startsWith("//")) return "looks like a URL";
21318
+ if (value3.startsWith("/")) return "is an absolute path";
21319
+ if (/^[a-z]:[\\/]/i.test(value3)) return "is an absolute path";
21320
+ if (value3.startsWith("~")) return "is a home-relative path, which differs per machine";
21321
+ if (/^\d{1,5}:\d{1,5}$/.test(value3)) return "looks like a published host port";
21322
+ return null;
21323
+ }
21324
+ function defaultProjectConfig() {
21325
+ return {
21326
+ version: 1,
21327
+ name: null,
21328
+ mainBranch: null,
21329
+ worktrees: {
21330
+ root: PROJECT_CONFIG_DEFAULTS.worktreeRoot,
21331
+ branchPattern: PROJECT_CONFIG_DEFAULTS.branchPattern
21332
+ },
21333
+ stack: [],
21334
+ instructions: []
21335
+ };
21336
+ }
21337
+ function parseProjectConfig(document) {
21338
+ if (document === null || document === void 0) {
21339
+ return { ok: false, issues: [{ path: "", message: `${PROJECT_CONFIG_FILE} is empty` }] };
21340
+ }
21341
+ const parsed = Schema.safeParse(document);
21342
+ if (!parsed.success) {
21343
+ return {
21344
+ ok: false,
21345
+ issues: parsed.error.issues.map((issue2) => ({
21346
+ path: issue2.path.join("."),
21347
+ message: issue2.message
21348
+ }))
21349
+ };
21350
+ }
21351
+ const value3 = parsed.data;
21352
+ const defaults = defaultProjectConfig();
21353
+ return {
21354
+ ok: true,
21355
+ config: {
21356
+ version: 1,
21357
+ name: value3.name ?? null,
21358
+ mainBranch: value3.mainBranch ?? null,
21359
+ worktrees: {
21360
+ root: value3.worktrees?.root ?? defaults.worktrees.root,
21361
+ branchPattern: value3.worktrees?.branchPattern ?? defaults.worktrees.branchPattern
21362
+ },
21363
+ stack: value3.stack ?? [],
21364
+ instructions: value3.instructions ?? []
21365
+ }
21366
+ };
21367
+ }
21368
+ var PROJECT_CONFIG_FILE, PROJECT_CONFIG_PATH, PROJECT_CONFIG_DEFAULTS, BRANCH_PATTERN_PLACEHOLDERS, portable, repoRelativePath, branchPattern, Schema;
21369
+ var init_project_config = __esm({
21370
+ "../core/src/project-config.ts"() {
21371
+ "use strict";
21372
+ init_zod();
21373
+ PROJECT_CONFIG_FILE = "project.yaml";
21374
+ PROJECT_CONFIG_PATH = `.portta/${PROJECT_CONFIG_FILE}`;
21375
+ PROJECT_CONFIG_DEFAULTS = {
21376
+ worktreeRoot: ".portta/worktrees",
21377
+ branchPattern: "{type}/{slug}"
21378
+ };
21379
+ BRANCH_PATTERN_PLACEHOLDERS = ["type", "slug"];
21380
+ portable = (label3) => external_exports.string().trim().min(1).superRefine((value3, ctx) => {
21381
+ const shape = hostShape(value3);
21382
+ if (shape) {
21383
+ ctx.addIssue({
21384
+ code: "custom",
21385
+ message: `${label3} ${shape}; that belongs to the host, not to the project (ADR 0052)`
21386
+ });
21387
+ }
21388
+ });
21389
+ repoRelativePath = (label3) => portable(label3).superRefine((value3, ctx) => {
21390
+ const normalized = value3.replaceAll("\\", "/");
21391
+ if (normalized.split("/").includes("..")) {
21392
+ ctx.addIssue({ code: "custom", message: `${label3} escapes the repository with '..'` });
21393
+ }
21394
+ });
21395
+ branchPattern = portable("the branch pattern").superRefine((value3, ctx) => {
21396
+ const used = [...value3.matchAll(/\{([^}]*)\}/g)].map((match2) => match2[1] ?? "");
21397
+ const allowed = new Set(BRANCH_PATTERN_PLACEHOLDERS);
21398
+ for (const placeholder of used) {
21399
+ if (!allowed.has(placeholder)) {
21400
+ ctx.addIssue({
21401
+ code: "custom",
21402
+ message: `unknown placeholder {${placeholder}}; the branch pattern accepts ${BRANCH_PATTERN_PLACEHOLDERS.map((name) => `{${name}}`).join(" and ")}`
21403
+ });
21404
+ }
21405
+ }
21406
+ });
21407
+ Schema = external_exports.strictObject({
21408
+ version: external_exports.literal(1),
21409
+ name: portable("the project name").optional(),
21410
+ mainBranch: portable("the main branch").optional(),
21411
+ worktrees: external_exports.strictObject({
21412
+ root: repoRelativePath("the worktree root").optional(),
21413
+ branchPattern: branchPattern.optional()
21414
+ }).optional(),
21415
+ stack: external_exports.array(external_exports.string().trim().min(1).regex(/^[a-z0-9][a-z0-9.+-]*$/i, "a stack entry is a plain identifier such as node or mysql")).optional(),
21416
+ instructions: external_exports.array(repoRelativePath("an instruction path")).optional()
21417
+ });
21418
+ }
21419
+ });
21420
+
21314
21421
  // ../core/src/project-order.ts
21315
21422
  function parseDependsOn(label3) {
21316
21423
  if (!label3 || label3.trim() === "") return [];
@@ -21757,6 +21864,14 @@ var init_runner = __esm({
21757
21864
  }
21758
21865
  });
21759
21866
 
21867
+ // ../core/src/module-endpoints.ts
21868
+ var init_module_endpoints = __esm({
21869
+ "../core/src/module-endpoints.ts"() {
21870
+ "use strict";
21871
+ init_dynamic();
21872
+ }
21873
+ });
21874
+
21760
21875
  // ../core/src/shares.ts
21761
21876
  function shareRouterName(id) {
21762
21877
  return `portta-share-${id}`;
@@ -22318,6 +22433,7 @@ var init_src = __esm({
22318
22433
  init_namespace();
22319
22434
  init_paths();
22320
22435
  init_password();
22436
+ init_project_config();
22321
22437
  init_project_order();
22322
22438
  init_projects_home();
22323
22439
  init_protections();
@@ -22326,6 +22442,7 @@ var init_src = __esm({
22326
22442
  init_audit_actions();
22327
22443
  init_roles();
22328
22444
  init_runner();
22445
+ init_module_endpoints();
22329
22446
  init_shares();
22330
22447
  init_tunnel();
22331
22448
  init_activity();
@@ -22384,7 +22501,7 @@ var init_package = __esm({
22384
22501
  "package.json"() {
22385
22502
  package_default = {
22386
22503
  name: "@codions/portta",
22387
- version: "0.0.0-develop.166.sha-d31f10f",
22504
+ version: "0.0.0-develop.171.sha-d749180",
22388
22505
  description: "Machine-first CLI for a shared Docker development gateway",
22389
22506
  license: "MIT",
22390
22507
  type: "module",
@@ -22458,7 +22575,7 @@ var init_version = __esm({
22458
22575
  "use strict";
22459
22576
  init_package();
22460
22577
  CLI_VERSION = package_default.version;
22461
- CLI_BUILD_DATE = true ? "2026-09-18T00:00:48Z" : void 0;
22578
+ CLI_BUILD_DATE = true ? "2026-09-18T00:56:55Z" : void 0;
22462
22579
  }
22463
22580
  });
22464
22581
 
@@ -36934,7 +37051,7 @@ function agentTemplatePlaceholder(variable) {
36934
37051
  function hostPortFromEnv(env) {
36935
37052
  return parseInt(env[ENV_NAMES.hostPort] || env.PORT || String(APP_DEFAULTS.port), 10);
36936
37053
  }
36937
- var APP_NAME, APP_SLUG, CLI_NAME, GLOBAL_CONFIG_DIR, HOST_STATE_DIR, PROJECT_CONFIG_DIR, PATH_NAMES, PROJECT_CONFIG_PATH, PROJECT_LOCAL_CONFIG_PATH, APP_DEFAULTS, ENV_NAMES, RUNTIME_IDENTITY, SERVICE_IDENTITY, LINEAR_IDENTITY;
37054
+ var APP_NAME, APP_SLUG, CLI_NAME, GLOBAL_CONFIG_DIR, HOST_STATE_DIR, PROJECT_CONFIG_DIR, PATH_NAMES, PROJECT_CONFIG_PATH2, PROJECT_LOCAL_CONFIG_PATH, APP_DEFAULTS, ENV_NAMES, RUNTIME_IDENTITY, SERVICE_IDENTITY, LINEAR_IDENTITY;
36938
37055
  var init_config2 = __esm({
36939
37056
  "../core/src/taskflow/config.ts"() {
36940
37057
  "use strict";
@@ -36963,7 +37080,7 @@ var init_config2 = __esm({
36963
37080
  archiveState: "archive.json",
36964
37081
  openSessionsState: "open-sessions.json"
36965
37082
  };
36966
- PROJECT_CONFIG_PATH = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectConfig}`;
37083
+ PROJECT_CONFIG_PATH2 = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectConfig}`;
36967
37084
  PROJECT_LOCAL_CONFIG_PATH = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectLocalConfig}`;
36968
37085
  APP_DEFAULTS = {
36969
37086
  port: 5111,
@@ -47725,7 +47842,7 @@ var require_Schema = __commonJS({
47725
47842
  var string4 = require_string();
47726
47843
  var tags = require_tags();
47727
47844
  var sortMapEntriesByKey = (a3, b) => a3.key < b.key ? -1 : a3.key > b.key ? 1 : 0;
47728
- var Schema = class _Schema {
47845
+ var Schema2 = class _Schema {
47729
47846
  constructor({ compat, customTags, merge: merge2, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) {
47730
47847
  this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null;
47731
47848
  this.name = typeof schema === "string" && schema || "core";
@@ -47743,7 +47860,7 @@ var require_Schema = __commonJS({
47743
47860
  return copy;
47744
47861
  }
47745
47862
  };
47746
- exports.Schema = Schema;
47863
+ exports.Schema = Schema2;
47747
47864
  }
47748
47865
  });
47749
47866
 
@@ -47836,7 +47953,7 @@ var require_Document = __commonJS({
47836
47953
  var identity3 = require_identity();
47837
47954
  var Pair = require_Pair();
47838
47955
  var toJS = require_toJS();
47839
- var Schema = require_Schema();
47956
+ var Schema2 = require_Schema();
47840
47957
  var stringifyDocument = require_stringifyDocument();
47841
47958
  var anchors = require_anchors();
47842
47959
  var applyReviver = require_applyReviver();
@@ -48087,7 +48204,7 @@ var require_Document = __commonJS({
48087
48204
  if (options.schema instanceof Object)
48088
48205
  this.schema = options.schema;
48089
48206
  else if (opt)
48090
- this.schema = new Schema.Schema(Object.assign(opt, options));
48207
+ this.schema = new Schema2.Schema(Object.assign(opt, options));
48091
48208
  else
48092
48209
  throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
48093
48210
  }
@@ -51751,7 +51868,7 @@ var require_dist = __commonJS({
51751
51868
  "use strict";
51752
51869
  var composer = require_composer();
51753
51870
  var Document = require_Document();
51754
- var Schema = require_Schema();
51871
+ var Schema2 = require_Schema();
51755
51872
  var errors = require_errors();
51756
51873
  var Alias = require_Alias();
51757
51874
  var identity3 = require_identity();
@@ -51767,7 +51884,7 @@ var require_dist = __commonJS({
51767
51884
  var visit2 = require_visit();
51768
51885
  exports.Composer = composer.Composer;
51769
51886
  exports.Document = Document.Document;
51770
- exports.Schema = Schema.Schema;
51887
+ exports.Schema = Schema2.Schema;
51771
51888
  exports.YAMLError = errors.YAMLError;
51772
51889
  exports.YAMLParseError = errors.YAMLParseError;
51773
51890
  exports.YAMLWarning = errors.YAMLWarning;
@@ -52052,7 +52169,7 @@ function parseConfigDocument(text) {
52052
52169
  const parsed = (0, import_yaml.parse)(text);
52053
52170
  return isRecord2(parsed) ? parsed : {};
52054
52171
  }
52055
- function parseProjectConfig(parsed) {
52172
+ function parseProjectConfig2(parsed) {
52056
52173
  const worktreeRoot = parseWorktreeRoot(parsed.workspace, DEFAULT_CONFIG.workspace.worktreeRoot);
52057
52174
  return {
52058
52175
  name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : DEFAULT_CONFIG.name,
@@ -52082,7 +52199,7 @@ function parseProjectConfig(parsed) {
52082
52199
  };
52083
52200
  }
52084
52201
  function defaultConfig() {
52085
- return parseProjectConfig({});
52202
+ return parseProjectConfig2({});
52086
52203
  }
52087
52204
  function parseTeamKeyList(raw2) {
52088
52205
  if (!Array.isArray(raw2)) return void 0;
@@ -52217,7 +52334,7 @@ function loadConfig(dir, options = {}) {
52217
52334
  let projectConfig;
52218
52335
  try {
52219
52336
  const text = readConfigFile(root2).trim();
52220
- projectConfig = text ? parseProjectConfig(parseConfigDocument(text)) : defaultConfig();
52337
+ projectConfig = text ? parseProjectConfig2(parseConfigDocument(text)) : defaultConfig();
52221
52338
  } catch {
52222
52339
  projectConfig = defaultConfig();
52223
52340
  }
@@ -90034,7 +90151,7 @@ function createDaemonTaskflowHost(context) {
90034
90151
  port: hostListen(env).port,
90035
90152
  workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
90036
90153
  version: env.PORTTA_VERSION || "0.0.0-local",
90037
- buildDate: true ? "2026-09-18T00:00:48Z" : (/* @__PURE__ */ new Date()).toISOString(),
90154
+ buildDate: true ? "2026-09-18T00:56:55Z" : (/* @__PURE__ */ new Date()).toISOString(),
90038
90155
  environmentStore: createEnvironmentStore(paths3.database),
90039
90156
  hostEnvironmentProvider: new HostEnvironmentProvider(),
90040
90157
  devContainerProvider: new DevContainerProvider(),
@@ -110160,8 +110277,8 @@ function detectInitEnvironment(gitRoot2, requested = "auto") {
110160
110277
  function buildInitPromptSpec(context) {
110161
110278
  const systemPrompt = [
110162
110279
  `You are bootstrapping a local repository for ${APP_NAME}.`,
110163
- `A starter \`${PROJECT_CONFIG_PATH}\` already exists at the repo root.`,
110164
- `Inspect the repository in the current working directory and edit that existing \`${PROJECT_CONFIG_PATH}\` in place.`,
110280
+ `A starter \`${PROJECT_CONFIG_PATH2}\` already exists at the repo root.`,
110281
+ `Inspect the repository in the current working directory and edit that existing \`${PROJECT_CONFIG_PATH2}\` in place.`,
110165
110282
  "Do not modify any other file.",
110166
110283
  "Do not ask the user questions. Infer the config from the repository contents.",
110167
110284
  "Be efficient: inspect only the files needed to determine the project name, main branch, service layout, dev commands, and ports.",
@@ -110186,11 +110303,11 @@ function buildInitPromptSpec(context) {
110186
110303
  "Only include optional sections like auto_name, lifecycleHooks, sandbox/docker config, mounts, or systemPrompt if the repository gives clear evidence they are needed.",
110187
110304
  "Prefer editing the existing keys over replacing the file with a completely different shape.",
110188
110305
  "Preserve the existing template structure and comments unless a specific change requires updating them.",
110189
- `Before finishing, verify that \`${PROJECT_CONFIG_PATH}\` exists and contains the final YAML.`
110306
+ `Before finishing, verify that \`${PROJECT_CONFIG_PATH2}\` exists and contains the final YAML.`
110190
110307
  ].join("\n");
110191
110308
  return {
110192
110309
  systemPrompt,
110193
- userPrompt: `Adapt the existing starter \`${PROJECT_CONFIG_PATH}\` for this repository.`
110310
+ userPrompt: `Adapt the existing starter \`${PROJECT_CONFIG_PATH2}\` for this repository.`
110194
110311
  };
110195
110312
  }
110196
110313
  function buildInitAgentCommand(agent, prompt, outputPrefix = `${APP_SLUG}-init`) {
@@ -110742,13 +110859,13 @@ var init_init_deps = __esm({
110742
110859
  tool: "claude",
110743
110860
  args: ["--version"],
110744
110861
  required: false,
110745
- hint: `Install the Claude Code CLI to let Claude scaffold ${PROJECT_CONFIG_PATH}`
110862
+ hint: `Install the Claude Code CLI to let Claude scaffold ${PROJECT_CONFIG_PATH2}`
110746
110863
  },
110747
110864
  {
110748
110865
  tool: "codex",
110749
110866
  args: ["--version"],
110750
110867
  required: false,
110751
- hint: `Install the Codex CLI to let Codex scaffold ${PROJECT_CONFIG_PATH}`
110868
+ hint: `Install the Codex CLI to let Codex scaffold ${PROJECT_CONFIG_PATH2}`
110752
110869
  },
110753
110870
  { tool: "docker", args: ["--version"], required: false, hint: "https://docs.docker.com/get-started/get-docker/" }
110754
110871
  ];
@@ -110953,7 +111070,7 @@ async function runInit(options) {
110953
111070
  let selectedAnalyze = resolveInitAnalysis(options, selectedRuntime);
110954
111071
  const configExists = existsSync29(taskflowYaml);
110955
111072
  if (configExists) {
110956
- log2.info(`${PROJECT_CONFIG_PATH} already exists`);
111073
+ log2.info(`${PROJECT_CONFIG_PATH2} already exists`);
110957
111074
  } else if (needsInteractiveAnalysisFallback(options, selectedRuntime)) {
110958
111075
  selectedAnalyze = await selectFallbackAgent();
110959
111076
  }
@@ -110974,7 +111091,7 @@ async function runInit(options) {
110974
111091
  environmentProvider: detection.selectedRuntime
110975
111092
  })
110976
111093
  );
110977
- log2.success(`${PROJECT_CONFIG_PATH} created from deterministic detection`);
111094
+ log2.success(`${PROJECT_CONFIG_PATH2} created from deterministic detection`);
110978
111095
  } else {
110979
111096
  const available2 = isInitAgentSelectable(
110980
111097
  depStatuses.find((status) => status.tool === selectedAnalyze) ?? { found: false }
@@ -110997,11 +111114,11 @@ async function runInit(options) {
110997
111114
  const prompt = buildInitPromptSpec({ ...context, defaultAgent: selectedAnalyze });
110998
111115
  const command = buildInitAgentCommand(selectedAnalyze, prompt);
110999
111116
  const streamPrinter = createAgentStreamPrinter(label3);
111000
- log2.step(`Running ${label3} to adapt ${PROJECT_CONFIG_PATH}...`);
111117
+ log2.step(`Running ${label3} to adapt ${PROJECT_CONFIG_PATH2}...`);
111001
111118
  const result = await runInitAgentCommand(command, gitRoot2, { onEvent: streamPrinter.onEvent });
111002
111119
  streamPrinter.finish();
111003
111120
  if (!existsSync29(taskflowYaml)) {
111004
- log2.error(`${label3} removed ${PROJECT_CONFIG_PATH}`);
111121
+ log2.error(`${label3} removed ${PROJECT_CONFIG_PATH2}`);
111005
111122
  const details = [
111006
111123
  result.summary ? `Summary:
111007
111124
  ${result.summary}` : "",
@@ -111018,12 +111135,12 @@ ${result.stderr.trim()}` : ""
111018
111135
  const finalYaml = await readFile12(taskflowYaml, "utf8");
111019
111136
  const changedTemplate = configExists || finalYaml !== starterTemplate;
111020
111137
  if (result.exitCode === 0 && changedTemplate) {
111021
- log2.success(`${label3} adapted ${PROJECT_CONFIG_PATH}`);
111138
+ log2.success(`${label3} adapted ${PROJECT_CONFIG_PATH2}`);
111022
111139
  } else if (result.exitCode === 0) {
111023
111140
  log2.warning(`${label3} left the starter template unchanged`);
111024
- log2.warning(`${label3} did not change the starter template. Review ${PROJECT_CONFIG_PATH} manually.`);
111141
+ log2.warning(`${label3} did not change the starter template. Review ${PROJECT_CONFIG_PATH2} manually.`);
111025
111142
  } else if (changedTemplate) {
111026
- log2.warning(`${label3} updated ${PROJECT_CONFIG_PATH}`);
111143
+ log2.warning(`${label3} updated ${PROJECT_CONFIG_PATH2}`);
111027
111144
  log2.warning(`${label3} exited with code ${result.exitCode}. Review the generated file before using it.`);
111028
111145
  } else {
111029
111146
  log2.warning(`${label3} left the starter template in place`);
@@ -111046,7 +111163,7 @@ ${result.stderr.trim()}` : ""
111046
111163
  );
111047
111164
  outro("You're all set! Next steps:");
111048
111165
  console.log();
111049
- console.log(` 1. Review ${PROJECT_CONFIG_PATH} and adjust runtime and exposure overrides if needed`);
111166
+ console.log(` 1. Review ${PROJECT_CONFIG_PATH2} and adjust runtime and exposure overrides if needed`);
111050
111167
  console.log(" 2. Keep the host daemon running: portta host serve --detach (or portta host service install)");
111051
111168
  console.log(` 3. Enable tab completion: eval "$(${flowInvocation()} completion zsh)" (or bash)`);
111052
111169
  console.log();
@@ -111904,7 +112021,7 @@ import { setTimeout as delay4 } from "node:timers/promises";
111904
112021
  function projectSetupPhaseLabel(phase) {
111905
112022
  switch (phase) {
111906
112023
  case "creating_config":
111907
- return `Creating ${PROJECT_CONFIG_PATH}`;
112024
+ return `Creating ${PROJECT_CONFIG_PATH2}`;
111908
112025
  case "analyzing":
111909
112026
  return "Analyzing project structure";
111910
112027
  case "ready":
@@ -119644,6 +119761,7 @@ async function dbRestore(file2, command) {
119644
119761
 
119645
119762
  // src/commands/projects.ts
119646
119763
  init_src();
119764
+ var import_yaml2 = __toESM(require_dist(), 1);
119647
119765
  init_zod();
119648
119766
  import { existsSync as existsSync23, mkdirSync as mkdirSync15, readFileSync as readFileSync22, realpathSync as realpathSync4, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "node:fs";
119649
119767
  import { createHash as createHash15 } from "node:crypto";
@@ -119784,6 +119902,21 @@ function declaredComposeName(file2) {
119784
119902
  return null;
119785
119903
  }
119786
119904
  }
119905
+ function readProjectConfig(root2) {
119906
+ const path7 = join46(root2, PROJECT_CONFIG_PATH);
119907
+ if (!existsSync23(path7)) {
119908
+ return { source: "defaults", path: PROJECT_CONFIG_PATH, config: defaultProjectConfig(), issues: [] };
119909
+ }
119910
+ let document;
119911
+ try {
119912
+ document = (0, import_yaml2.parse)(readFileSync22(path7, "utf8"));
119913
+ } catch (error61) {
119914
+ const message = error61 instanceof Error ? error61.message : String(error61);
119915
+ return { source: "file", path: PROJECT_CONFIG_PATH, config: null, issues: [{ path: "", message }] };
119916
+ }
119917
+ const parsed = parseProjectConfig(document);
119918
+ return parsed.ok ? { source: "file", path: PROJECT_CONFIG_PATH, config: parsed.config, issues: [] } : { source: "file", path: PROJECT_CONFIG_PATH, config: null, issues: parsed.issues };
119919
+ }
119787
119920
  async function analyze(pathValue, options = {}, containers) {
119788
119921
  const base2 = resolve24(pathValue);
119789
119922
  let composeFile2;
@@ -119834,6 +119967,7 @@ async function analyze(pathValue, options = {}, containers) {
119834
119967
  path: path7,
119835
119968
  compose_file: basename14(composeFile2),
119836
119969
  gateway_overlay: overlay2,
119970
+ project_config: readProjectConfig(path7),
119837
119971
  project: { name: project2, source: projectFromEnv ? ".env" : declaredName ? "compose name:" : "directory name (implicit)" },
119838
119972
  domain: gatewayContext({ required: false }).config.domain,
119839
119973
  services,
@@ -119865,6 +119999,19 @@ async function analyzeCommand(path7, options, command) {
119865
119999
  output2.line(` project namespace ${report.project.name} (${report.project.source})`);
119866
120000
  const identities = [...new Set(report.services.flatMap((service2) => Object.entries(service2.labels).filter(([key]) => key.startsWith("portta.project") || key.startsWith("portta.repo") || key.startsWith("portta.git.root")).map(([key, value3]) => `${key}=${value3}`)))];
119867
120001
  output2.line(` declared identity ${identities.length ? identities.join(" ") : "none (inferred from the Compose labels)"}`);
120002
+ const declared = report.project_config;
120003
+ if (declared.issues.length) {
120004
+ output2.line(` project.yaml invalid, so its defaults are not applied:`);
120005
+ for (const issue2 of declared.issues) output2.line(` ${issue2.path ? `${issue2.path}: ` : ""}${issue2.message}`);
120006
+ } else if (declared.config) {
120007
+ const summary = [
120008
+ declared.config.name ? `name ${declared.config.name}` : null,
120009
+ declared.config.mainBranch ? `main branch ${declared.config.mainBranch}` : null,
120010
+ `worktrees ${declared.config.worktrees.root}`,
120011
+ `branches ${declared.config.worktrees.branchPattern}`
120012
+ ].filter((part) => part !== null);
120013
+ output2.line(` project.yaml ${declared.source === "file" ? summary.join(", ") : `absent, so defaults apply: ${summary.join(", ")}`}`);
120014
+ }
119868
120015
  output2.line("\nServices");
119869
120016
  for (const service2 of report.services) output2.line(` ${service2.name} ${service2.image ?? "<built>"} ${service2.kind} ${service2.published_ports.map((port) => `${port.host}:${port.container}`).join(",") || "-"}`);
119870
120017
  output2.line("\nFindings");
@@ -120407,7 +120554,7 @@ function helpAfter(command, text) {
120407
120554
  ${text(commandInvocation(context.command))}`);
120408
120555
  }
120409
120556
  function runOptions(command) {
120410
- return command.addOption(new Option("--input <text>", "Run input text").conflicts("inputJson")).option("--input-json <json>", "Run input as a JSON object").option("--profile <name>", `Profile from ${PROJECT_CONFIG_PATH}`).addOption(
120557
+ return command.addOption(new Option("--input <text>", "Run input text").conflicts("inputJson")).option("--input-json <json>", "Run input as a JSON object").option("--profile <name>", `Profile from ${PROJECT_CONFIG_PATH2}`).addOption(
120411
120558
  new Option("--workspace <strategy>", "Execution workspace (default: workflow policy or worktree)").choices([
120412
120559
  "worktree",
120413
120560
  "branch",
@@ -120477,12 +120624,12 @@ ${environment.map(([name = "", text]) => ` ${name.padEnd(width)} ${text}`).joi
120477
120624
  Analysis: --analyze=auto uses the default agent only when runtime detection falls back to host.
120478
120625
  --analyze=claude | --analyze=codex always run that agent.`
120479
120626
  );
120480
- leaf(flow, "add [branch]", "Create a worktree using the dashboard lifecycle").option("--existing", "Use an existing local or remote branch instead of creating a new one").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH}`).option("--agent <id>", "Agent id to launch (repeatable)", collect).option("--prompt <text>", "Initial agent prompt").option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--interface <mode>", "Session UI: terminal or web-chat").option("-d, --detach", "Create worktree without switching to it").option(
120627
+ leaf(flow, "add [branch]", "Create a worktree using the dashboard lifecycle").option("--existing", "Use an existing local or remote branch instead of creating a new one").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH2}`).option("--agent <id>", "Agent id to launch (repeatable)", collect).option("--prompt <text>", "Initial agent prompt").option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--interface <mode>", "Session UI: terminal or web-chat").option("-d, --detach", "Create worktree without switching to it").option(
120481
120628
  "--from-linear <issue-id>",
120482
120629
  `Bootstrap from a Linear issue, plus any saved ${APP_NAME} session or linked PR`
120483
120630
  ).option("--branch <name>", "Override the branch when --from-linear resolves to one");
120484
120631
  helpAfter(
120485
- leaf(flow, "oneshot [branch]", "Run a worktree start-to-finish, streaming logs to stdout").option("--resume <branch>", "Resume an existing local worktree instead of creating one").option("--prompt <text>", "Initial agent prompt (required; follow-up nudge when --resume)").option("--agent <id>", "Agent id to launch").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH}`).option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--keep-open", "Don't auto-close the worktree session when the agent finishes").option(
120632
+ leaf(flow, "oneshot [branch]", "Run a worktree start-to-finish, streaming logs to stdout").option("--resume <branch>", "Resume an existing local worktree instead of creating one").option("--prompt <text>", "Initial agent prompt (required; follow-up nudge when --resume)").option("--agent <id>", "Agent id to launch").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH2}`).option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--keep-open", "Don't auto-close the worktree session when the agent finishes").option(
120486
120633
  "--linear <id|team>",
120487
120634
  "Tie this oneshot to Linear: ENG-123 loads the issue and posts back; ENG creates an issue when done"
120488
120635
  ).option("--branch <name>", "Override the branch when --linear resolves to one"),
@@ -120515,7 +120662,7 @@ running, resume with \`${invocation2} --resume <branch> --prompt <text>\`).`
120515
120662
  "--profile <name>",
120516
120663
  "Profile name (alternative to positional arg)"
120517
120664
  ),
120518
- () => `Switches the worktree to another profile from ${PROJECT_CONFIG_PATH}. An open
120665
+ () => `Switches the worktree to another profile from ${PROJECT_CONFIG_PATH2}. An open
120519
120666
  worktree is restarted with the new pane layout and commands; a closed
120520
120667
  one picks the new profile up on its next open.`
120521
120668
  );