@codions/portta 0.0.0-develop.169.sha-64ee68c → 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 [];
@@ -22326,6 +22433,7 @@ var init_src = __esm({
22326
22433
  init_namespace();
22327
22434
  init_paths();
22328
22435
  init_password();
22436
+ init_project_config();
22329
22437
  init_project_order();
22330
22438
  init_projects_home();
22331
22439
  init_protections();
@@ -22393,7 +22501,7 @@ var init_package = __esm({
22393
22501
  "package.json"() {
22394
22502
  package_default = {
22395
22503
  name: "@codions/portta",
22396
- version: "0.0.0-develop.169.sha-64ee68c",
22504
+ version: "0.0.0-develop.171.sha-d749180",
22397
22505
  description: "Machine-first CLI for a shared Docker development gateway",
22398
22506
  license: "MIT",
22399
22507
  type: "module",
@@ -22467,7 +22575,7 @@ var init_version = __esm({
22467
22575
  "use strict";
22468
22576
  init_package();
22469
22577
  CLI_VERSION = package_default.version;
22470
- CLI_BUILD_DATE = true ? "2026-09-18T00:48:59Z" : void 0;
22578
+ CLI_BUILD_DATE = true ? "2026-09-18T00:56:55Z" : void 0;
22471
22579
  }
22472
22580
  });
22473
22581
 
@@ -36943,7 +37051,7 @@ function agentTemplatePlaceholder(variable) {
36943
37051
  function hostPortFromEnv(env) {
36944
37052
  return parseInt(env[ENV_NAMES.hostPort] || env.PORT || String(APP_DEFAULTS.port), 10);
36945
37053
  }
36946
- 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;
36947
37055
  var init_config2 = __esm({
36948
37056
  "../core/src/taskflow/config.ts"() {
36949
37057
  "use strict";
@@ -36972,7 +37080,7 @@ var init_config2 = __esm({
36972
37080
  archiveState: "archive.json",
36973
37081
  openSessionsState: "open-sessions.json"
36974
37082
  };
36975
- PROJECT_CONFIG_PATH = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectConfig}`;
37083
+ PROJECT_CONFIG_PATH2 = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectConfig}`;
36976
37084
  PROJECT_LOCAL_CONFIG_PATH = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectLocalConfig}`;
36977
37085
  APP_DEFAULTS = {
36978
37086
  port: 5111,
@@ -47734,7 +47842,7 @@ var require_Schema = __commonJS({
47734
47842
  var string4 = require_string();
47735
47843
  var tags = require_tags();
47736
47844
  var sortMapEntriesByKey = (a3, b) => a3.key < b.key ? -1 : a3.key > b.key ? 1 : 0;
47737
- var Schema = class _Schema {
47845
+ var Schema2 = class _Schema {
47738
47846
  constructor({ compat, customTags, merge: merge2, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) {
47739
47847
  this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null;
47740
47848
  this.name = typeof schema === "string" && schema || "core";
@@ -47752,7 +47860,7 @@ var require_Schema = __commonJS({
47752
47860
  return copy;
47753
47861
  }
47754
47862
  };
47755
- exports.Schema = Schema;
47863
+ exports.Schema = Schema2;
47756
47864
  }
47757
47865
  });
47758
47866
 
@@ -47845,7 +47953,7 @@ var require_Document = __commonJS({
47845
47953
  var identity3 = require_identity();
47846
47954
  var Pair = require_Pair();
47847
47955
  var toJS = require_toJS();
47848
- var Schema = require_Schema();
47956
+ var Schema2 = require_Schema();
47849
47957
  var stringifyDocument = require_stringifyDocument();
47850
47958
  var anchors = require_anchors();
47851
47959
  var applyReviver = require_applyReviver();
@@ -48096,7 +48204,7 @@ var require_Document = __commonJS({
48096
48204
  if (options.schema instanceof Object)
48097
48205
  this.schema = options.schema;
48098
48206
  else if (opt)
48099
- this.schema = new Schema.Schema(Object.assign(opt, options));
48207
+ this.schema = new Schema2.Schema(Object.assign(opt, options));
48100
48208
  else
48101
48209
  throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
48102
48210
  }
@@ -51760,7 +51868,7 @@ var require_dist = __commonJS({
51760
51868
  "use strict";
51761
51869
  var composer = require_composer();
51762
51870
  var Document = require_Document();
51763
- var Schema = require_Schema();
51871
+ var Schema2 = require_Schema();
51764
51872
  var errors = require_errors();
51765
51873
  var Alias = require_Alias();
51766
51874
  var identity3 = require_identity();
@@ -51776,7 +51884,7 @@ var require_dist = __commonJS({
51776
51884
  var visit2 = require_visit();
51777
51885
  exports.Composer = composer.Composer;
51778
51886
  exports.Document = Document.Document;
51779
- exports.Schema = Schema.Schema;
51887
+ exports.Schema = Schema2.Schema;
51780
51888
  exports.YAMLError = errors.YAMLError;
51781
51889
  exports.YAMLParseError = errors.YAMLParseError;
51782
51890
  exports.YAMLWarning = errors.YAMLWarning;
@@ -52061,7 +52169,7 @@ function parseConfigDocument(text) {
52061
52169
  const parsed = (0, import_yaml.parse)(text);
52062
52170
  return isRecord2(parsed) ? parsed : {};
52063
52171
  }
52064
- function parseProjectConfig(parsed) {
52172
+ function parseProjectConfig2(parsed) {
52065
52173
  const worktreeRoot = parseWorktreeRoot(parsed.workspace, DEFAULT_CONFIG.workspace.worktreeRoot);
52066
52174
  return {
52067
52175
  name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : DEFAULT_CONFIG.name,
@@ -52091,7 +52199,7 @@ function parseProjectConfig(parsed) {
52091
52199
  };
52092
52200
  }
52093
52201
  function defaultConfig() {
52094
- return parseProjectConfig({});
52202
+ return parseProjectConfig2({});
52095
52203
  }
52096
52204
  function parseTeamKeyList(raw2) {
52097
52205
  if (!Array.isArray(raw2)) return void 0;
@@ -52226,7 +52334,7 @@ function loadConfig(dir, options = {}) {
52226
52334
  let projectConfig;
52227
52335
  try {
52228
52336
  const text = readConfigFile(root2).trim();
52229
- projectConfig = text ? parseProjectConfig(parseConfigDocument(text)) : defaultConfig();
52337
+ projectConfig = text ? parseProjectConfig2(parseConfigDocument(text)) : defaultConfig();
52230
52338
  } catch {
52231
52339
  projectConfig = defaultConfig();
52232
52340
  }
@@ -90043,7 +90151,7 @@ function createDaemonTaskflowHost(context) {
90043
90151
  port: hostListen(env).port,
90044
90152
  workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
90045
90153
  version: env.PORTTA_VERSION || "0.0.0-local",
90046
- buildDate: true ? "2026-09-18T00:48:59Z" : (/* @__PURE__ */ new Date()).toISOString(),
90154
+ buildDate: true ? "2026-09-18T00:56:55Z" : (/* @__PURE__ */ new Date()).toISOString(),
90047
90155
  environmentStore: createEnvironmentStore(paths3.database),
90048
90156
  hostEnvironmentProvider: new HostEnvironmentProvider(),
90049
90157
  devContainerProvider: new DevContainerProvider(),
@@ -110169,8 +110277,8 @@ function detectInitEnvironment(gitRoot2, requested = "auto") {
110169
110277
  function buildInitPromptSpec(context) {
110170
110278
  const systemPrompt = [
110171
110279
  `You are bootstrapping a local repository for ${APP_NAME}.`,
110172
- `A starter \`${PROJECT_CONFIG_PATH}\` already exists at the repo root.`,
110173
- `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.`,
110174
110282
  "Do not modify any other file.",
110175
110283
  "Do not ask the user questions. Infer the config from the repository contents.",
110176
110284
  "Be efficient: inspect only the files needed to determine the project name, main branch, service layout, dev commands, and ports.",
@@ -110195,11 +110303,11 @@ function buildInitPromptSpec(context) {
110195
110303
  "Only include optional sections like auto_name, lifecycleHooks, sandbox/docker config, mounts, or systemPrompt if the repository gives clear evidence they are needed.",
110196
110304
  "Prefer editing the existing keys over replacing the file with a completely different shape.",
110197
110305
  "Preserve the existing template structure and comments unless a specific change requires updating them.",
110198
- `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.`
110199
110307
  ].join("\n");
110200
110308
  return {
110201
110309
  systemPrompt,
110202
- userPrompt: `Adapt the existing starter \`${PROJECT_CONFIG_PATH}\` for this repository.`
110310
+ userPrompt: `Adapt the existing starter \`${PROJECT_CONFIG_PATH2}\` for this repository.`
110203
110311
  };
110204
110312
  }
110205
110313
  function buildInitAgentCommand(agent, prompt, outputPrefix = `${APP_SLUG}-init`) {
@@ -110751,13 +110859,13 @@ var init_init_deps = __esm({
110751
110859
  tool: "claude",
110752
110860
  args: ["--version"],
110753
110861
  required: false,
110754
- 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}`
110755
110863
  },
110756
110864
  {
110757
110865
  tool: "codex",
110758
110866
  args: ["--version"],
110759
110867
  required: false,
110760
- 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}`
110761
110869
  },
110762
110870
  { tool: "docker", args: ["--version"], required: false, hint: "https://docs.docker.com/get-started/get-docker/" }
110763
110871
  ];
@@ -110962,7 +111070,7 @@ async function runInit(options) {
110962
111070
  let selectedAnalyze = resolveInitAnalysis(options, selectedRuntime);
110963
111071
  const configExists = existsSync29(taskflowYaml);
110964
111072
  if (configExists) {
110965
- log2.info(`${PROJECT_CONFIG_PATH} already exists`);
111073
+ log2.info(`${PROJECT_CONFIG_PATH2} already exists`);
110966
111074
  } else if (needsInteractiveAnalysisFallback(options, selectedRuntime)) {
110967
111075
  selectedAnalyze = await selectFallbackAgent();
110968
111076
  }
@@ -110983,7 +111091,7 @@ async function runInit(options) {
110983
111091
  environmentProvider: detection.selectedRuntime
110984
111092
  })
110985
111093
  );
110986
- log2.success(`${PROJECT_CONFIG_PATH} created from deterministic detection`);
111094
+ log2.success(`${PROJECT_CONFIG_PATH2} created from deterministic detection`);
110987
111095
  } else {
110988
111096
  const available2 = isInitAgentSelectable(
110989
111097
  depStatuses.find((status) => status.tool === selectedAnalyze) ?? { found: false }
@@ -111006,11 +111114,11 @@ async function runInit(options) {
111006
111114
  const prompt = buildInitPromptSpec({ ...context, defaultAgent: selectedAnalyze });
111007
111115
  const command = buildInitAgentCommand(selectedAnalyze, prompt);
111008
111116
  const streamPrinter = createAgentStreamPrinter(label3);
111009
- log2.step(`Running ${label3} to adapt ${PROJECT_CONFIG_PATH}...`);
111117
+ log2.step(`Running ${label3} to adapt ${PROJECT_CONFIG_PATH2}...`);
111010
111118
  const result = await runInitAgentCommand(command, gitRoot2, { onEvent: streamPrinter.onEvent });
111011
111119
  streamPrinter.finish();
111012
111120
  if (!existsSync29(taskflowYaml)) {
111013
- log2.error(`${label3} removed ${PROJECT_CONFIG_PATH}`);
111121
+ log2.error(`${label3} removed ${PROJECT_CONFIG_PATH2}`);
111014
111122
  const details = [
111015
111123
  result.summary ? `Summary:
111016
111124
  ${result.summary}` : "",
@@ -111027,12 +111135,12 @@ ${result.stderr.trim()}` : ""
111027
111135
  const finalYaml = await readFile12(taskflowYaml, "utf8");
111028
111136
  const changedTemplate = configExists || finalYaml !== starterTemplate;
111029
111137
  if (result.exitCode === 0 && changedTemplate) {
111030
- log2.success(`${label3} adapted ${PROJECT_CONFIG_PATH}`);
111138
+ log2.success(`${label3} adapted ${PROJECT_CONFIG_PATH2}`);
111031
111139
  } else if (result.exitCode === 0) {
111032
111140
  log2.warning(`${label3} left the starter template unchanged`);
111033
- 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.`);
111034
111142
  } else if (changedTemplate) {
111035
- log2.warning(`${label3} updated ${PROJECT_CONFIG_PATH}`);
111143
+ log2.warning(`${label3} updated ${PROJECT_CONFIG_PATH2}`);
111036
111144
  log2.warning(`${label3} exited with code ${result.exitCode}. Review the generated file before using it.`);
111037
111145
  } else {
111038
111146
  log2.warning(`${label3} left the starter template in place`);
@@ -111055,7 +111163,7 @@ ${result.stderr.trim()}` : ""
111055
111163
  );
111056
111164
  outro("You're all set! Next steps:");
111057
111165
  console.log();
111058
- 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`);
111059
111167
  console.log(" 2. Keep the host daemon running: portta host serve --detach (or portta host service install)");
111060
111168
  console.log(` 3. Enable tab completion: eval "$(${flowInvocation()} completion zsh)" (or bash)`);
111061
111169
  console.log();
@@ -111913,7 +112021,7 @@ import { setTimeout as delay4 } from "node:timers/promises";
111913
112021
  function projectSetupPhaseLabel(phase) {
111914
112022
  switch (phase) {
111915
112023
  case "creating_config":
111916
- return `Creating ${PROJECT_CONFIG_PATH}`;
112024
+ return `Creating ${PROJECT_CONFIG_PATH2}`;
111917
112025
  case "analyzing":
111918
112026
  return "Analyzing project structure";
111919
112027
  case "ready":
@@ -119653,6 +119761,7 @@ async function dbRestore(file2, command) {
119653
119761
 
119654
119762
  // src/commands/projects.ts
119655
119763
  init_src();
119764
+ var import_yaml2 = __toESM(require_dist(), 1);
119656
119765
  init_zod();
119657
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";
119658
119767
  import { createHash as createHash15 } from "node:crypto";
@@ -119793,6 +119902,21 @@ function declaredComposeName(file2) {
119793
119902
  return null;
119794
119903
  }
119795
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
+ }
119796
119920
  async function analyze(pathValue, options = {}, containers) {
119797
119921
  const base2 = resolve24(pathValue);
119798
119922
  let composeFile2;
@@ -119843,6 +119967,7 @@ async function analyze(pathValue, options = {}, containers) {
119843
119967
  path: path7,
119844
119968
  compose_file: basename14(composeFile2),
119845
119969
  gateway_overlay: overlay2,
119970
+ project_config: readProjectConfig(path7),
119846
119971
  project: { name: project2, source: projectFromEnv ? ".env" : declaredName ? "compose name:" : "directory name (implicit)" },
119847
119972
  domain: gatewayContext({ required: false }).config.domain,
119848
119973
  services,
@@ -119874,6 +119999,19 @@ async function analyzeCommand(path7, options, command) {
119874
119999
  output2.line(` project namespace ${report.project.name} (${report.project.source})`);
119875
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}`)))];
119876
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
+ }
119877
120015
  output2.line("\nServices");
119878
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(",") || "-"}`);
119879
120017
  output2.line("\nFindings");
@@ -120416,7 +120554,7 @@ function helpAfter(command, text) {
120416
120554
  ${text(commandInvocation(context.command))}`);
120417
120555
  }
120418
120556
  function runOptions(command) {
120419
- 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(
120420
120558
  new Option("--workspace <strategy>", "Execution workspace (default: workflow policy or worktree)").choices([
120421
120559
  "worktree",
120422
120560
  "branch",
@@ -120486,12 +120624,12 @@ ${environment.map(([name = "", text]) => ` ${name.padEnd(width)} ${text}`).joi
120486
120624
  Analysis: --analyze=auto uses the default agent only when runtime detection falls back to host.
120487
120625
  --analyze=claude | --analyze=codex always run that agent.`
120488
120626
  );
120489
- 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(
120490
120628
  "--from-linear <issue-id>",
120491
120629
  `Bootstrap from a Linear issue, plus any saved ${APP_NAME} session or linked PR`
120492
120630
  ).option("--branch <name>", "Override the branch when --from-linear resolves to one");
120493
120631
  helpAfter(
120494
- 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(
120495
120633
  "--linear <id|team>",
120496
120634
  "Tie this oneshot to Linear: ENG-123 loads the issue and posts back; ENG creates an issue when done"
120497
120635
  ).option("--branch <name>", "Override the branch when --linear resolves to one"),
@@ -120524,7 +120662,7 @@ running, resume with \`${invocation2} --resume <branch> --prompt <text>\`).`
120524
120662
  "--profile <name>",
120525
120663
  "Profile name (alternative to positional arg)"
120526
120664
  ),
120527
- () => `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
120528
120666
  worktree is restarted with the new pane layout and commands; a closed
120529
120667
  one picks the new profile up on its next open.`
120530
120668
  );