Skip to content

feat(cli): flatten Lobu context config#955

Merged
buremba merged 3 commits into
mainfrom
feat/simplify-context-config
May 20, 2026
Merged

feat(cli): flatten Lobu context config#955
buremba merged 3 commits into
mainfrom
feat/simplify-context-config

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 20, 2026

Summary

  • switch user context storage to flat url, lifecycle, and optional cwd fields
  • read legacy apiUrl + server.lifecycle/cwd only for migration into the flat shape
  • derive managed server port/host from the context URL instead of storing server.port
  • update the Mac app submodule to read/write the flat context shape (owletto PR: https://github.com/lobu-ai/owletto/pull/200)
  • update CLI docs to use lobu context add --url

Validation

  • claude -p "Review the current git diff..." to check for legacy writers / breakage
  • bun test packages/cli/src/internal/__tests__/context.test.ts packages/server/src/utils/__tests__/user-config.test.ts
  • make build-packages
  • cd packages/landing && bun run build
  • cd packages/owletto/apps/mac && xcodebuild -project Owletto.xcodeproj -scheme Owletto -configuration Debug -destination "platform=macOS" build CODE_SIGNING_ALLOWED=NO

Summary by CodeRabbit

  • Refactor

    • Updated lobu context add to use --url instead of --api-url for specifying context base URLs.
    • Removed server configuration options (--port, --host, --database-url, --data-dir) from context commands.
  • Documentation

    • Updated CLI reference and testing guides to reflect new context flag syntax.
  • Tests

    • Updated context and configuration tests to align with schema changes.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates context storage from apiUrl with nested server objects to a flattened url with top-level lifecycle/cwd, removes per-context databaseUrl/dataDir support, derives managed server settings by parsing context URL, and updates CLI commands and user config integration accordingly.

Changes

Context Configuration Schema Migration and Integration

Layer / File(s) Summary
Schema definitions and type updates
packages/cli/src/internal/context.ts, packages/server/src/utils/user-config.ts
Introduces DEFAULT_CONTEXT_URL and redefines persisted context entries with top-level url, lifecycle, and cwd; removes databaseUrl and dataDir from LobuServerConfig and UserServerConfig exported types; adds tolerant StoredContextEntry/StoredContextConfig types to support reading both old and new JSON shapes.
Context normalization and legacy upgrade
packages/cli/src/internal/context.ts, packages/cli/src/internal/__tests__/context.test.ts
Implements normalizeContextEntry and related helpers to read legacy apiUrl/nested server fields and normalize them into the new flat url/lifecycle/cwd shape; seeds default context with normalized URL; filters invalid lifecycle values during normalization; test fixtures validate legacy→new upgrade and that old configs persist in new form.
Context management API updates
packages/cli/src/internal/context.ts
Refactors resolveContext to delegate to contextToResolvedContext; renames addContext parameter from apiUrl to url; updates setCurrentContext error message to reference --url; changes findContextByUrl and contextToResolvedContext to match and derive from context.url rather than apiUrl.
Server config derivation from context
packages/cli/src/internal/context.ts, packages/server/src/utils/user-config.ts
Rewrites getServerConfig to return settings only for "managed" lifecycle contexts and derive host/port by parsing context.url; updates setServerConfig to mutate top-level context.lifecycle and context.cwd instead of nested server objects; adds deriveManagedServerConfig helper to parse and validate URLs into port/host with graceful fallback.
Context command implementations
packages/cli/src/commands/context.ts
contextListCommand renders context.url instead of context.apiUrl; contextCurrentCommand changes label from "API URL" to "URL"; contextAddCommand signature accepts { name, url, cwd?, lifecycle? } (removes apiUrl, port, host, databaseUrl, dataDir); contextUseCommand displays context.url post-switch.
CLI option and handler wiring
packages/cli/src/index.ts
Introduces --url <url> flag for lobu context add (replacing --api-url); removes --port, --host, --database-url, --data-dir options; updates action handler to parse only url, cwd, lifecycle and forward minimal { name, url, cwd, lifecycle } to contextAddCommand.
User server config module
packages/server/src/utils/user-config.ts, packages/server/src/utils/__tests__/user-config.test.ts
Removes databaseUrl and dataDir from UserServerConfig; adds cwd; rewrites config loader to produce output only for "managed" contexts by parsing normalized url/apiUrl with validation; narrows applyUserServerConfigToEnv to populate only process.env.PORT and process.env.HOST, omitting database/data-dir assignments; test suite updated to validate new config shapes and lifecycle-gated behavior.
Dev command integration
packages/cli/src/commands/dev.ts
devCommand now propagates only PORT and HOST from user server config to userServerEnv; refactors shared DATABASE_URL safety gate to check whether DATABASE_URL is defined in project .env rather than whether it came from user config, allowing "inherited from shell" refusal when DATABASE_URL is present but not pinned in .env.
Comprehensive test coverage
packages/cli/src/internal/__tests__/context.test.ts, packages/cli/src/internal/__tests__/org-resolution.test.ts, packages/cli/src/commands/_lib/apply/__tests__/apply-cmd-dryrun.test.ts, packages/server/src/utils/__tests__/user-config.test.ts
Updates test fixtures and mocks to use url instead of apiUrl; validates active-org and context persistence with new shape; tests legacy apiUrl/server upgrade paths; confirms managed vs external lifecycle behavior; verifies server setting derivation from flat context fields; reduces test env-var assertions to PORT, HOST, LOBU_CONTEXT; tests context override, fallback, and missing-lifecycle cases.
Documentation, setup script, and comment updates
packages/landing/src/content/docs/guides/testing.md, packages/landing/src/content/docs/reference/cli.md, packages/server/src/start-local.ts, scripts/task-setup.sh, scripts/e2e-lobu-apply.sh
Updates user-facing docs and setup scripts to use --url instead of --api-url when registering contexts; clarifies that managed context config contributes only PORT/HOST while upstream lobu run handles external-Postgres routing via project .env or shell variables; updates task-setup to pass single --url argument instead of separate --api-url/--port.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

skip-size-check

Poem

🐰 From apiUrl to just url we hop,
Flat lifecycle and cwd now top!
No nested server blocks to explore,
Managed contexts parse the host they roar.
Legacy configs migrate with care,
A cleaner schema floats in the air! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(cli): flatten Lobu context config' clearly and concisely summarizes the main change—flattening the context configuration structure from nested server objects to a flat shape with url, lifecycle, and cwd fields.
Description check ✅ Passed The PR description includes a comprehensive summary covering the main objectives, detailed validation steps performed, and commit notes explaining the implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/simplify-context-config

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 20, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 84.49612% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/cli/src/commands/dev.ts 47.36% 10 Missing ⚠️
packages/cli/src/internal/context.ts 93.13% 7 Missing ⚠️
packages/cli/src/commands/chat.ts 0.00% 1 Missing ⚠️
packages/cli/src/commands/token.ts 0.00% 1 Missing ⚠️
packages/cli/src/internal/credentials.ts 50.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli/src/commands/dev.ts (1)

89-93: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared-DB safety gate is bypassed when shell overrides a pinned .env URL.

Line 92 gives shell env precedence, but Line 102 only checks whether .env has any DATABASE_URL. If .env has a local URL and shell has a shared/prod URL, mergedEnv.DATABASE_URL is shared and this guard is skipped.

Proposed fix
   const mergedEnv = {
     ...userServerEnv,
     ...envVars,
     ...(process.env as Record<string, string>),
   };
   const hasDatabaseUrl = Boolean(mergedEnv.DATABASE_URL?.trim());
+  const projectDatabaseUrl = envVars.DATABASE_URL?.trim();
+  const effectiveDatabaseUrl = mergedEnv.DATABASE_URL?.trim();
+  const projectPinsEffectiveDatabaseUrl =
+    Boolean(projectDatabaseUrl) && projectDatabaseUrl === effectiveDatabaseUrl;

   if (
     hasDatabaseUrl &&
-    !envVars.DATABASE_URL?.trim() &&
-    isSharedDatabaseUrl(mergedEnv.DATABASE_URL!) &&
+    !projectPinsEffectiveDatabaseUrl &&
+    isSharedDatabaseUrl(effectiveDatabaseUrl!) &&
     !options.unsafeSharedDb
   ) {

Also applies to: 100-104

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/commands/dev.ts` around lines 89 - 93, The mergedEnv
construction gives shell envs precedence so the shared-DB safety check (which
reads mergedEnv.DATABASE_URL) can be bypassed when a pinned .env has a local URL
but the shell overrides it; change the guard to derive the DATABASE_URL from the
pinned/env-file sources instead of mergedEnv by computing a pinnedDbUrl =
userServerEnv.DATABASE_URL ?? envVars.DATABASE_URL (or equivalent lookup of the
.env-supplied value) and use pinnedDbUrl for the shared-DB safety gate checks
(also update the other similar check at the second occurrence around lines
100-104) so shell overrides do not bypass the protection.
🧹 Nitpick comments (1)
packages/cli/src/index.ts (1)

532-573: 💤 Low value

Deprecation notice for --api-url should be communicated to users.

The --api-url option is marked as a deprecated alias in the code, but there's no runtime warning when users use it. Consider emitting a deprecation warning to stderr when options.apiUrl is used instead of options.url.

📝 Optional: Add deprecation warning
       async (
         name: string,
         options: {
           url?: string;
           apiUrl?: string;
           cwd?: string;
           lifecycle?: "managed" | "external";
         }
       ) => {
         const url = options.url ?? options.apiUrl;
+        if (options.apiUrl && !options.url) {
+          console.error(chalk.yellow("  Warning: --api-url is deprecated; use --url instead."));
+        }
         if (!url) {
           throw new Error("Missing required option --url <url>");
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/index.ts` around lines 532 - 573, Add a runtime deprecation
warning when the deprecated alias is used: in the action handler for the "add
<name>" command (the async function that reads options and assigns const url =
options.url ?? options.apiUrl), detect when options.apiUrl is present and
options.url is not, and emit a stderr deprecation message (e.g., console.warn or
process.stderr.write) advising users to use --url instead; keep behavior
unchanged (still prefer options.url when both provided) and include the option
name (--api-url) and suggested replacement (--url) in the message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/owletto`:
- Line 1: The submodule pointer for packages/owletto is set to a SHA that isn't
an ancestor of the remote branch owletto/main, causing the drift guard to fail;
update the submodule ref so it's reachable from owletto/main by either (a)
merging the pinned commit into the remote branch owletto/main and then updating
the submodule pointer, or (b) changing the packages/owletto submodule SHA to a
commit that is an ancestor of owletto/main (use git fetch on the owletto remote,
list reachable commits, and set the submodule to a valid SHA), then commit the
updated submodule entry so CI's hard drift check passes.

---

Outside diff comments:
In `@packages/cli/src/commands/dev.ts`:
- Around line 89-93: The mergedEnv construction gives shell envs precedence so
the shared-DB safety check (which reads mergedEnv.DATABASE_URL) can be bypassed
when a pinned .env has a local URL but the shell overrides it; change the guard
to derive the DATABASE_URL from the pinned/env-file sources instead of mergedEnv
by computing a pinnedDbUrl = userServerEnv.DATABASE_URL ?? envVars.DATABASE_URL
(or equivalent lookup of the .env-supplied value) and use pinnedDbUrl for the
shared-DB safety gate checks (also update the other similar check at the second
occurrence around lines 100-104) so shell overrides do not bypass the
protection.

---

Nitpick comments:
In `@packages/cli/src/index.ts`:
- Around line 532-573: Add a runtime deprecation warning when the deprecated
alias is used: in the action handler for the "add <name>" command (the async
function that reads options and assigns const url = options.url ??
options.apiUrl), detect when options.apiUrl is present and options.url is not,
and emit a stderr deprecation message (e.g., console.warn or
process.stderr.write) advising users to use --url instead; keep behavior
unchanged (still prefer options.url when both provided) and include the option
name (--api-url) and suggested replacement (--url) in the message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bd276425-03d5-40ec-9731-0381269fb049

📥 Commits

Reviewing files that changed from the base of the PR and between fcc99b7 and f30d785.

📒 Files selected for processing (10)
  • packages/cli/src/commands/context.ts
  • packages/cli/src/commands/dev.ts
  • packages/cli/src/index.ts
  • packages/cli/src/internal/__tests__/context.test.ts
  • packages/cli/src/internal/context.ts
  • packages/owletto
  • packages/server/src/start-local.ts
  • packages/server/src/utils/__tests__/user-config.test.ts
  • packages/server/src/utils/user-config.ts
  • scripts/task-setup.sh
💤 Files with no reviewable changes (1)
  • scripts/task-setup.sh

Comment thread packages/owletto Outdated
@buremba buremba force-pushed the feat/simplify-context-config branch 2 times, most recently from 13444c6 to 29aa6ad Compare May 20, 2026 02:52
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/commands/context.ts`:
- Line 40: The current-context output prints context.apiUrl which is
inconsistent with the flattened schema and can be undefined; update the console
output to use the normalized context.url value instead (replace uses of
context.apiUrl in the current-context display with context.url) so the command
shows the correct URL after migration and across Lines referencing context
(e.g., where current-context prints `URL:`).

In `@packages/cli/src/commands/dev.ts`:
- Around line 94-105: The guard currently only checks that envVars.DATABASE_URL
exists, which lets a shell-provided shared DATABASE_URL in mergedEnv bypass the
refusal if the project .env contains any DATABASE_URL (even a different one).
Update the condition around hasDatabaseUrl / isSharedDatabaseUrl to treat a
shell override as unsafe unless the project .env explicitly pins the same value:
replace the !envVars.DATABASE_URL?.trim() check with a comparison that requires
envVars.DATABASE_URL?.trim() === mergedEnv.DATABASE_URL?.trim() (i.e., refuse
when envVars.DATABASE_URL is absent OR present but not equal to
mergedEnv.DATABASE_URL), keeping references to hasDatabaseUrl,
mergedEnv.DATABASE_URL, envVars.DATABASE_URL, isSharedDatabaseUrl and
options.unsafeSharedDb to locate the logic to change.

In `@packages/cli/src/internal/context.ts`:
- Around line 203-205: The code saves entry.cwd even when lifecycle is absent,
which later causes contexts without an explicit "managed" lifecycle to be
dropped; to fix this either (1) materialize the inferred lifecycle before
persisting (call normalizeLifecycle(server?.lifecycle) and set entry.lifecycle
to the resolved value — e.g., lifecycle || inferredLifecycle — before assigning
entry.cwd) or (2) enforce a guard that only assigns entry.cwd when the resolved
lifecycle equals "managed" (check the normalized lifecycle value before setting
entry.cwd). Apply the same change to the other places mentioned (the blocks
around the code paths referenced by lines 341-343 and 357-365) so cwd is only
persisted when the final lifecycle is managed or the lifecycle is explicitly
materialized first, and ensure getServerConfig callers see consistent behavior.
- Around line 281-285: The code currently accepts any non-empty
raw.url/raw.apiUrl and constructs a LobuContextEntry using
normalizeApiUrl(rawUrl) without handling malformed results; update
loadContextConfig() so that after computing rawUrl and calling
normalizeApiUrl(rawUrl) you check the normalization result and drop the entry
(return undefined or skip it) when normalizeApiUrl returns a falsy/invalid
value, and apply the same validation in resolveContext() where entries are
re-normalized (ensure you validate the output of normalizeApiUrl before using
it); reference symbols: rawUrl, normalizeApiUrl, loadContextConfig,
resolveContext, LobuContextEntry, normalizeString.
- Around line 377-385: The code reads parsed.port only, so URLs without explicit
ports (e.g., http://host/path or https://host/path) leave out.port undefined;
update the logic in the block handling context.url (the parsed, out, and
context.url variables) to infer and set the implicit default port when
parsed.port is empty by checking parsed.protocol ('http:' -> 80, 'https:' ->
443), then validate the derived port (Number.isInteger and 1..65535) and assign
it to out.port; keep existing hostname handling for out.host.

In `@packages/server/src/utils/user-config.ts`:
- Around line 83-90: The code assigns parsed.hostname directly to out.host,
which leaves IPv6 literals wrapped in brackets (e.g., "[::1]") and later causes
httpServer.listen to fail; update the logic around parsed.hostname in the rawUrl
handling (where parsed is created from new URL(rawUrl) and out.host is set) to
strip leading "[" and trailing "]" if present (only for bracketed IPv6 literals)
before assigning to out.host so applyUserServerConfigToEnv()/process.env.HOST
and subsequent httpServer.listen receive an unbracketed IPv6 address.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 728f85c1-ad03-4648-a69b-a74c6a0ac4b8

📥 Commits

Reviewing files that changed from the base of the PR and between f30d785 and 13444c6.

📒 Files selected for processing (12)
  • packages/cli/src/commands/context.ts
  • packages/cli/src/commands/dev.ts
  • packages/cli/src/index.ts
  • packages/cli/src/internal/__tests__/context.test.ts
  • packages/cli/src/internal/context.ts
  • packages/landing/src/content/docs/guides/testing.md
  • packages/landing/src/content/docs/reference/cli.md
  • packages/owletto
  • packages/server/src/start-local.ts
  • packages/server/src/utils/__tests__/user-config.test.ts
  • packages/server/src/utils/user-config.ts
  • scripts/task-setup.sh
💤 Files with no reviewable changes (1)
  • scripts/task-setup.sh
✅ Files skipped from review due to trivial changes (2)
  • packages/landing/src/content/docs/guides/testing.md
  • packages/server/src/start-local.ts

Comment thread packages/cli/src/commands/context.ts Outdated
Comment thread packages/cli/src/commands/dev.ts Outdated
Comment thread packages/cli/src/internal/context.ts Outdated
Comment thread packages/cli/src/internal/context.ts
Comment thread packages/cli/src/internal/context.ts
Comment thread packages/server/src/utils/user-config.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
packages/cli/src/internal/context.ts (3)

377-381: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Infer default port for http/https when URL omits an explicit port.

Line 379 only reads parsed.port, so http://host/... and https://host/... produce no derived port even though it is implicit.

Proposed fix
-    const port = parsed.port ? Number.parseInt(parsed.port, 10) : undefined;
+    const port = parsed.port
+      ? Number.parseInt(parsed.port, 10)
+      : parsed.protocol === "http:"
+        ? 80
+        : parsed.protocol === "https:"
+          ? 443
+          : undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 377 - 381, When parsing
context.url in the try block that creates `parsed` and sets `out.port`, infer
the default port when `parsed.port` is empty by checking `parsed.protocol` (use
80 for "http:" and 443 for "https:") before validation; compute port =
parsed.port ? Number.parseInt(parsed.port, 10) : (parsed.protocol === "https:" ?
443 : parsed.protocol === "http:" ? 80 : undefined), then run the same
Number.isInteger/0<port<=65535 validation and assign to `out.port` if valid
(preserve existing behavior for non-http(s) schemes).

281-285: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate migrated URLs before keeping a stored context.

Line 284 still accepts malformed url/apiUrl during normalization, so hand-edited/bad entries survive load and can later break resolution paths.

Proposed fix
-  const entry: LobuContextEntry = { url: normalizeApiUrl(rawUrl) };
+  let normalizedUrl: string;
+  try {
+    normalizedUrl = normalizeAndValidateApiUrl(rawUrl);
+  } catch {
+    return undefined;
+  }
+  const entry: LobuContextEntry = { url: normalizedUrl };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 281 - 285, The code
accepts a rawUrl and blindly stores a normalized value into LobuContextEntry
even if the input was malformed; update the loading logic around
rawUrl/normalizeApiUrl so you validate the normalized result before keeping the
entry: after computing rawUrl and calling normalizeApiUrl(rawUrl), verify the
normalized URL is a valid API URL (non-empty and passes whatever URL validation
function the project uses) and only construct/return the LobuContextEntry when
that validation succeeds; otherwise skip or discard the entry (and avoid
returning undefined prematurely). Also ensure activeOrg still uses
normalizeString(raw.activeOrg) only for validated entries so the stored context
is guaranteed to contain a valid url and org.

203-205: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent persisting cwd when lifecycle won’t resolve to managed.

Lines 203-205 and 357-365 still allow cwd without an effective managed lifecycle, which makes cwd a silent no-op for getServerConfig(). Migration logic at Lines 289-292 carries the same inconsistency forward.

Proposed fix
   const lifecycle = normalizeLifecycle(server?.lifecycle);
-  if (lifecycle) entry.lifecycle = lifecycle;
-  if (server?.cwd?.trim()) entry.cwd = server.cwd.trim();
+  const cwd = server?.cwd?.trim();
+  if (cwd && lifecycle === "external") {
+    throw new Error("`cwd` can only be set for managed contexts.");
+  }
+  if (cwd) {
+    entry.cwd = cwd;
+    entry.lifecycle = lifecycle ?? "managed";
+  } else if (lifecycle) {
+    entry.lifecycle = lifecycle;
+  }
-    const lifecycle = normalizeLifecycle(server.lifecycle);
-    if (lifecycle) context.lifecycle = lifecycle;
-    else delete context.lifecycle;
-    if (server.cwd?.trim()) context.cwd = server.cwd.trim();
-    else delete context.cwd;
+    const lifecycle = normalizeLifecycle(server.lifecycle);
+    const cwd = server.cwd?.trim();
+    if (cwd && lifecycle === "external") {
+      throw new Error("`cwd` can only be set for managed contexts.");
+    }
+    if (cwd) {
+      context.cwd = cwd;
+      context.lifecycle = lifecycle ?? "managed";
+    } else {
+      if (lifecycle) context.lifecycle = lifecycle;
+      else delete context.lifecycle;
+      delete context.cwd;
+    }
-  const lifecycle = normalizeLifecycle(raw.lifecycle) ?? legacyServer.lifecycle;
-  if (lifecycle) entry.lifecycle = lifecycle;
-  const cwd = normalizeString(raw.cwd) ?? legacyServer.cwd;
-  if (cwd) entry.cwd = cwd;
+  const lifecycle = normalizeLifecycle(raw.lifecycle) ?? legacyServer.lifecycle;
+  const cwd = normalizeString(raw.cwd) ?? legacyServer.cwd;
+  const effectiveLifecycle = cwd ? (lifecycle ?? "managed") : lifecycle;
+  if (effectiveLifecycle) entry.lifecycle = effectiveLifecycle;
+  if (cwd && effectiveLifecycle === "managed") entry.cwd = cwd;

Also applies to: 289-292, 357-365

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 203 - 205, The code sets
entry.cwd whenever server?.cwd is present even if the normalized lifecycle isn't
a managed lifecycle, making cwd a no-op; update each spot where
normalizeLifecycle(server?.lifecycle) is used (e.g., the block that sets
entry.lifecycle and entry.cwd, and the migration logic and the other identical
block) to only assign entry.cwd = server.cwd.trim() when the resolved lifecycle
indicates a managed lifecycle (i.e., check the normalized lifecycle value for
the managed state before setting cwd), leaving cwd unset otherwise. Ensure you
change all three occurrences that currently set cwd unconditionally after
normalizeLifecycle.
🧹 Nitpick comments (2)
packages/cli/src/internal/__tests__/context.test.ts (2)

76-89: ⚡ Quick win

Assert the flattened field in the normalized URL test.

This test still validates matched?.apiUrl (Line 88), which keeps the legacy contract alive in a migration-focused suite. Prefer asserting matched?.url (or assert both if backward compatibility is explicitly required).

Suggested update
-    expect(matched?.apiUrl).toBe("https://custom.lobu.ai/api/v1");
+    expect(matched?.url).toBe("https://custom.lobu.ai/api/v1");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/__tests__/context.test.ts` around lines 76 - 89,
The test for findContextByUrl is asserting the legacy field matched?.apiUrl;
update the assertion to check the normalized field matched?.url instead (or
assert both matched?.url and matched?.apiUrl only if backward compatibility must
be preserved) so the test validates the new contract for the returned context
object from findContextByUrl.

145-185: ⚡ Quick win

Add a malformed managed URL test case.

Managed server settings are URL-derived; add one negative test for lifecycle: "managed" + invalid url to lock parse-failure behavior and prevent regressions in normalization/derivation paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/__tests__/context.test.ts` around lines 145 - 185,
Add a new test in packages/cli/src/internal/__tests__/context.test.ts that
verifies a managed context with a malformed url does not produce derived server
settings: create configData with contexts.local { url: "not-a-valid-url",
lifecycle: "managed" }, mock
readFileSpy.mockResolvedValue(JSON.stringify(configData)), then assert await
getServerConfig("local") returns undefined (same behavior as external contexts);
reference getServerConfig to locate the logic and use the existing test patterns
for mocking readFileSpy and expectation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/cli/src/internal/context.ts`:
- Around line 377-381: When parsing context.url in the try block that creates
`parsed` and sets `out.port`, infer the default port when `parsed.port` is empty
by checking `parsed.protocol` (use 80 for "http:" and 443 for "https:") before
validation; compute port = parsed.port ? Number.parseInt(parsed.port, 10) :
(parsed.protocol === "https:" ? 443 : parsed.protocol === "http:" ? 80 :
undefined), then run the same Number.isInteger/0<port<=65535 validation and
assign to `out.port` if valid (preserve existing behavior for non-http(s)
schemes).
- Around line 281-285: The code accepts a rawUrl and blindly stores a normalized
value into LobuContextEntry even if the input was malformed; update the loading
logic around rawUrl/normalizeApiUrl so you validate the normalized result before
keeping the entry: after computing rawUrl and calling normalizeApiUrl(rawUrl),
verify the normalized URL is a valid API URL (non-empty and passes whatever URL
validation function the project uses) and only construct/return the
LobuContextEntry when that validation succeeds; otherwise skip or discard the
entry (and avoid returning undefined prematurely). Also ensure activeOrg still
uses normalizeString(raw.activeOrg) only for validated entries so the stored
context is guaranteed to contain a valid url and org.
- Around line 203-205: The code sets entry.cwd whenever server?.cwd is present
even if the normalized lifecycle isn't a managed lifecycle, making cwd a no-op;
update each spot where normalizeLifecycle(server?.lifecycle) is used (e.g., the
block that sets entry.lifecycle and entry.cwd, and the migration logic and the
other identical block) to only assign entry.cwd = server.cwd.trim() when the
resolved lifecycle indicates a managed lifecycle (i.e., check the normalized
lifecycle value for the managed state before setting cwd), leaving cwd unset
otherwise. Ensure you change all three occurrences that currently set cwd
unconditionally after normalizeLifecycle.

---

Nitpick comments:
In `@packages/cli/src/internal/__tests__/context.test.ts`:
- Around line 76-89: The test for findContextByUrl is asserting the legacy field
matched?.apiUrl; update the assertion to check the normalized field matched?.url
instead (or assert both matched?.url and matched?.apiUrl only if backward
compatibility must be preserved) so the test validates the new contract for the
returned context object from findContextByUrl.
- Around line 145-185: Add a new test in
packages/cli/src/internal/__tests__/context.test.ts that verifies a managed
context with a malformed url does not produce derived server settings: create
configData with contexts.local { url: "not-a-valid-url", lifecycle: "managed" },
mock readFileSpy.mockResolvedValue(JSON.stringify(configData)), then assert
await getServerConfig("local") returns undefined (same behavior as external
contexts); reference getServerConfig to locate the logic and use the existing
test patterns for mocking readFileSpy and expectation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 89a540e1-d565-4b78-b610-5b607ceed6e5

📥 Commits

Reviewing files that changed from the base of the PR and between 13444c6 and 29aa6ad.

📒 Files selected for processing (13)
  • packages/cli/src/commands/context.ts
  • packages/cli/src/commands/dev.ts
  • packages/cli/src/index.ts
  • packages/cli/src/internal/__tests__/context.test.ts
  • packages/cli/src/internal/context.ts
  • packages/landing/src/content/docs/guides/testing.md
  • packages/landing/src/content/docs/reference/cli.md
  • packages/owletto
  • packages/server/src/start-local.ts
  • packages/server/src/utils/__tests__/user-config.test.ts
  • packages/server/src/utils/user-config.ts
  • scripts/e2e-lobu-apply.sh
  • scripts/task-setup.sh
✅ Files skipped from review due to trivial changes (3)
  • packages/landing/src/content/docs/reference/cli.md
  • packages/server/src/start-local.ts
  • packages/landing/src/content/docs/guides/testing.md

buremba added 2 commits May 20, 2026 14:12
…owletto to reachable main

The context-config flatten changed loadContextConfig()'s normalized
return shape from { apiUrl } to flat { url }. Two test fixtures still
mocked loadContextConfig with the old { apiUrl } shape, so context.url
came back undefined and normalizeApiUrl(undefined) threw inside
findContextByUrl — failing 6 applyCommand dry-run / org-resolution
tests. Update the mocks to the normalized flat { url } contract that
loadContextConfig actually returns.

Also reset the owletto submodule pointer from the unreachable SHA
2c9c4c9 to owletto/main HEAD 2a2cc35, which is reachable and safe for
production cloning.
@buremba buremba force-pushed the feat/simplify-context-config branch from 49f4348 to 290c465 Compare May 20, 2026 13:13
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
packages/cli/src/commands/context.ts (1)

40-40: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use normalized context.url in current-context output.

Line 40 still reads context.apiUrl, which is inconsistent with the new flat schema and with Lines 19/82. This can surface undefined after migration paths that persist only url.

🔧 Proposed fix
-  console.log(chalk.dim(`  URL: ${context.apiUrl}`));
+  console.log(chalk.dim(`  URL: ${context.url}`));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/commands/context.ts` at line 40, The current-context output
still references context.apiUrl which is outdated; update the console.log call
that prints the URL to use context.url instead of context.apiUrl so it matches
the new flat schema and the other usages (e.g., the earlier lines that reference
context.url) and avoids printing undefined after migrations.
packages/server/src/utils/user-config.ts (1)

90-90: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Strip IPv6 brackets before exporting host.

Line 90 forwards parsed.hostname as-is. For IPv6 URLs this can remain bracketed and break downstream host binding when set via HOST.

Proposed fix
-      if (parsed.hostname) out.host = parsed.hostname;
+      if (parsed.hostname) {
+        out.host = parsed.hostname.replace(/^\[|\]$/g, "");
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/utils/user-config.ts` at line 90, When exporting the host
in user-config.ts the code assigns parsed.hostname directly to out.host (the
line with parsed.hostname), which leaves IPv6 addresses bracketed and can break
downstream binding; update the assignment so that before setting out.host you
strip surrounding '[' and ']' from parsed.hostname (e.g., trim leading '[' and
trailing ']' if present) so both IPv4/hostname and IPv6 addresses are exported
unbracketed; modify the logic around the out.host assignment where
parsed.hostname is used to perform this normalization.
packages/cli/src/internal/context.ts (3)

203-205: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard cwd behind managed lifecycle to avoid silent no-op config.

Line 205 and Line 364 persist cwd even when lifecycle is absent/non-managed, but Line 341 ignores those contexts in getServerConfig(). Reject this combination (or materialize lifecycle) at write time.

Proposed fix
   const lifecycle = normalizeLifecycle(server?.lifecycle);
-  if (lifecycle) entry.lifecycle = lifecycle;
-  if (server?.cwd?.trim()) entry.cwd = server.cwd.trim();
+  const cwd = server?.cwd?.trim();
+  if (cwd && lifecycle !== "managed") {
+    throw new Error("`cwd` can only be set for managed contexts.");
+  }
+  if (lifecycle) entry.lifecycle = lifecycle;
+  if (cwd) entry.cwd = cwd;

   if (!server) {
     delete context.lifecycle;
     delete context.cwd;
   } else {
     const lifecycle = normalizeLifecycle(server.lifecycle);
+    const cwd = server.cwd?.trim();
+    if (cwd && lifecycle !== "managed") {
+      throw new Error("`cwd` can only be set for managed contexts.");
+    }
     if (lifecycle) context.lifecycle = lifecycle;
     else delete context.lifecycle;
-    if (server.cwd?.trim()) context.cwd = server.cwd.trim();
+    if (cwd) context.cwd = cwd;
     else delete context.cwd;
   }

Also applies to: 357-365

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 203 - 205, The code
currently sets entry.cwd from server.cwd regardless of lifecycle; because
getServerConfig ignores contexts without a managed lifecycle, persistently
storing cwd with no lifecycle becomes a silent no-op—update the write-time logic
(the block using normalizeLifecycle and assigning entry.lifecycle and entry.cwd)
to only set entry.cwd when the computed lifecycle indicates a managed lifecycle
(i.e., when normalizeLifecycle(server?.lifecycle) returns a managed value),
otherwise either refuse/save an error (reject the write) or also materialize/set
a default managed lifecycle before assigning entry.cwd; specifically change the
logic around normalizeLifecycle, lifecycle, entry.cwd and server.cwd so cwd is
guarded by the managed lifecycle invariant used by getServerConfig.

379-381: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Infer implicit default ports when deriving managed host/port.

Line 379 only reads explicit ports. For http://host/... or https://host/..., inferred managed config misses an implied port and can drift from context.url.

Proposed fix
-    const port = parsed.port ? Number.parseInt(parsed.port, 10) : undefined;
+    const port = parsed.port
+      ? Number.parseInt(parsed.port, 10)
+      : parsed.protocol === "http:"
+        ? 80
+        : parsed.protocol === "https:"
+          ? 443
+          : undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 379 - 381, When deriving
the managed host/port, if parsed.port is absent infer the standard port from the
URL scheme before assigning out.port: if parsed.protocol (or parsed.scheme) is
"http:" => 80, "https:" => 443; then validate with Number.isInteger and range
check (1-65535) and assign to out.port the inferred value when valid. Update the
logic around parsed.port/port and out.port in context.ts so that the code uses
the parsed.protocol to set the implicit default port for http/https when no
explicit port is provided.

284-285: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Drop malformed stored URLs during normalization.

Line 284 normalizes but does not validate persisted url/apiUrl, so malformed values can still be loaded and later resolved.

Proposed fix
-  const entry: LobuContextEntry = { url: normalizeApiUrl(rawUrl) };
+  let normalizedUrl: string;
+  try {
+    normalizedUrl = normalizeAndValidateApiUrl(rawUrl);
+  } catch {
+    return undefined;
+  }
+  const entry: LobuContextEntry = { url: normalizedUrl };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/context.ts` around lines 284 - 285, The code
creates a LobuContextEntry with url: normalizeApiUrl(rawUrl) but does not
validate the normalized URL, so malformed stored URLs are kept; update the logic
around normalizeApiUrl (used when constructing the entry) to validate the
returned value (e.g., ensure normalizeApiUrl(rawUrl) returns a well-formed
URL/string or passes URL parsing) and if it is invalid/empty, drop that stored
entry (do not push the LobuContextEntry) or set url to undefined and skip later
resolution; adjust the surrounding code that builds entries (where entry is
constructed) to filter out invalid urls rather than blindly using
normalizeApiUrl(rawUrl).
🧹 Nitpick comments (1)
packages/cli/src/internal/__tests__/context.test.ts (1)

187-229: ⚡ Quick win

Add regression tests for invalid persisted shapes (cwd/URL).

Please add cases that (1) reject/ignore cwd without managed lifecycle and (2) drop malformed persisted url/apiUrl during normalization. These are high-value guardrails for this migration path.

Also applies to: 270-287

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/internal/__tests__/context.test.ts` around lines 187 - 229,
Add two regression tests for addContext: one that verifies addContext rejects or
strips a persisted cwd when lifecycle is not "managed" (ensure
addContext("name", url) does not accept saved cwd unless lifecycle === "managed"
and that writeFileSpy shows no cwd saved), and another that ensures malformed
persisted url/apiUrl values are dropped during normalization (call addContext on
a context with a bad url/apiUrl and assert the saved context contains a
normalized/omitted url field as expected). Reference addContext and
DEFAULT_CONTEXT_NAME in the tests and reuse readFileSpy/writeFileSpy patterns
from existing tests to mock persisted shapes and assert the resulting saved JSON
shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/server/src/utils/user-config.ts`:
- Around line 86-88: The current derivation only sets out.port when parsed.port
exists, causing scheme-only URLs to lose implied ports; update the logic around
parsed.port/out.port to apply defaults: if parsed.port is present parse and
validate it as now, otherwise if parsed.protocol (or parsed.scheme) indicates
'http:' set out.port = 80 and if 'https:' set out.port = 443, keeping the
existing Number.isInteger and range checks so the code still validates ports
before assigning to out.port.

---

Duplicate comments:
In `@packages/cli/src/commands/context.ts`:
- Line 40: The current-context output still references context.apiUrl which is
outdated; update the console.log call that prints the URL to use context.url
instead of context.apiUrl so it matches the new flat schema and the other usages
(e.g., the earlier lines that reference context.url) and avoids printing
undefined after migrations.

In `@packages/cli/src/internal/context.ts`:
- Around line 203-205: The code currently sets entry.cwd from server.cwd
regardless of lifecycle; because getServerConfig ignores contexts without a
managed lifecycle, persistently storing cwd with no lifecycle becomes a silent
no-op—update the write-time logic (the block using normalizeLifecycle and
assigning entry.lifecycle and entry.cwd) to only set entry.cwd when the computed
lifecycle indicates a managed lifecycle (i.e., when
normalizeLifecycle(server?.lifecycle) returns a managed value), otherwise either
refuse/save an error (reject the write) or also materialize/set a default
managed lifecycle before assigning entry.cwd; specifically change the logic
around normalizeLifecycle, lifecycle, entry.cwd and server.cwd so cwd is guarded
by the managed lifecycle invariant used by getServerConfig.
- Around line 379-381: When deriving the managed host/port, if parsed.port is
absent infer the standard port from the URL scheme before assigning out.port: if
parsed.protocol (or parsed.scheme) is "http:" => 80, "https:" => 443; then
validate with Number.isInteger and range check (1-65535) and assign to out.port
the inferred value when valid. Update the logic around parsed.port/port and
out.port in context.ts so that the code uses the parsed.protocol to set the
implicit default port for http/https when no explicit port is provided.
- Around line 284-285: The code creates a LobuContextEntry with url:
normalizeApiUrl(rawUrl) but does not validate the normalized URL, so malformed
stored URLs are kept; update the logic around normalizeApiUrl (used when
constructing the entry) to validate the returned value (e.g., ensure
normalizeApiUrl(rawUrl) returns a well-formed URL/string or passes URL parsing)
and if it is invalid/empty, drop that stored entry (do not push the
LobuContextEntry) or set url to undefined and skip later resolution; adjust the
surrounding code that builds entries (where entry is constructed) to filter out
invalid urls rather than blindly using normalizeApiUrl(rawUrl).

In `@packages/server/src/utils/user-config.ts`:
- Line 90: When exporting the host in user-config.ts the code assigns
parsed.hostname directly to out.host (the line with parsed.hostname), which
leaves IPv6 addresses bracketed and can break downstream binding; update the
assignment so that before setting out.host you strip surrounding '[' and ']'
from parsed.hostname (e.g., trim leading '[' and trailing ']' if present) so
both IPv4/hostname and IPv6 addresses are exported unbracketed; modify the logic
around the out.host assignment where parsed.hostname is used to perform this
normalization.

---

Nitpick comments:
In `@packages/cli/src/internal/__tests__/context.test.ts`:
- Around line 187-229: Add two regression tests for addContext: one that
verifies addContext rejects or strips a persisted cwd when lifecycle is not
"managed" (ensure addContext("name", url) does not accept saved cwd unless
lifecycle === "managed" and that writeFileSpy shows no cwd saved), and another
that ensures malformed persisted url/apiUrl values are dropped during
normalization (call addContext on a context with a bad url/apiUrl and assert the
saved context contains a normalized/omitted url field as expected). Reference
addContext and DEFAULT_CONTEXT_NAME in the tests and reuse
readFileSpy/writeFileSpy patterns from existing tests to mock persisted shapes
and assert the resulting saved JSON shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 61602705-b843-419d-9477-af577abe43b2

📥 Commits

Reviewing files that changed from the base of the PR and between 49f4348 and 290c465.

📒 Files selected for processing (14)
  • packages/cli/src/commands/_lib/apply/__tests__/apply-cmd-dryrun.test.ts
  • packages/cli/src/commands/context.ts
  • packages/cli/src/commands/dev.ts
  • packages/cli/src/index.ts
  • packages/cli/src/internal/__tests__/context.test.ts
  • packages/cli/src/internal/__tests__/org-resolution.test.ts
  • packages/cli/src/internal/context.ts
  • packages/landing/src/content/docs/guides/testing.md
  • packages/landing/src/content/docs/reference/cli.md
  • packages/server/src/start-local.ts
  • packages/server/src/utils/__tests__/user-config.test.ts
  • packages/server/src/utils/user-config.ts
  • scripts/e2e-lobu-apply.sh
  • scripts/task-setup.sh
✅ Files skipped from review due to trivial changes (5)
  • scripts/e2e-lobu-apply.sh
  • scripts/task-setup.sh
  • packages/landing/src/content/docs/guides/testing.md
  • packages/landing/src/content/docs/reference/cli.md
  • packages/server/src/start-local.ts

Comment thread packages/server/src/utils/user-config.ts Outdated
…alization, managed-cwd invariant

- dev: evaluate the EFFECTIVE merged DATABASE_URL in the shared-DB refusal
  (shell precedence over .env), not just project-.env presence; extract
  shouldRefuseSharedDatabaseUrl() and unit-test the shell-override footgun
- context: rename ResolvedContext.apiUrl -> url for flat-schema consistency;
  fix `lobu context current` to print context.url
- context/user-config: derive default port 80/443 for scheme-only URLs and
  strip IPv6 brackets from the derived host in both managed-config paths
- context: reject `cwd` on non-managed contexts (was a silent no-op) in
  addContext + setServerConfig
- context: drop malformed stored URLs during normalization so resolveContext
  always returns a usable endpoint
- tests: regression coverage for every behavior above
@buremba buremba merged commit 50dd706 into main May 20, 2026
66 of 67 checks passed
@buremba buremba deleted the feat/simplify-context-config branch May 20, 2026 13:58
buremba added a commit that referenced this pull request May 20, 2026
Resolves conflicts from main's context-config flatten (#955) + shared-DB
guard refactor against this branch's embedded-PG work:

- cli/index.ts: context add adopts main's flat --url/--cwd/--lifecycle
  (drops the nested --port/--host/--database-url/--data-dir options).
- cli/commands/dev.ts: keep embedded helpers (isExternalDatabaseUrl,
  resolveEmbeddedDataRoot, mode selection) + adopt main's
  shouldRefuseSharedDatabaseUrl guard; drop dead BackendBundleKind
  (pglite removed) and the userServerConfig.databaseUrl/dataDir block
  (flattened away).
- server/utils/user-config.ts: drop the dataDir→DATABASE_URL mapping
  (dataDir no longer in the flattened config schema).
- server/start-local.ts: stays deleted (consolidated into server.ts).
- owletto: bumped to merged 872a6ea (Keychain key + main's flat-context).

Verified: cli + server tsc clean, dev.test.ts 12/12, init still writes
DATABASE_URL to .env (embedded-vs-external feature intact).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants