Skip to content

FE-545: Local-first storage; npx distribution#43

Merged
lunelson merged 2 commits into
mainfrom
ln/fe-545-storage-and-distro
Apr 12, 2026
Merged

FE-545: Local-first storage; npx distribution#43
lunelson merged 2 commits into
mainfrom
ln/fe-545-storage-and-distro

Conversation

@lunelson
Copy link
Copy Markdown
Contributor

feat: local-first .brunch/ storage, project resolution, and launcher

BrunchProject resolution with walk-up discovery (find/init/resolve).
Express launcher serves API + static dist/ on one port. Bin entry for
npx @hashintel/brunch. Drizzle migrations path resolved via import.meta.url
so it works from any cwd.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

chore: mark slice 14 done, add project resolution invariant I100

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

@linear
Copy link
Copy Markdown

linear Bot commented Apr 12, 2026

FE-545 Local-first storage; npx distribution

resolveBrunchProject() with shallow walk-up discovery creates/finds .brunch/ directory. bin entry, Express launcher serves built Vite assets + API on one port, opens browser. npx brunch for web UI. Single env var: ANTHROPIC_API_KEY.

@lunelson lunelson marked this pull request as ready for review April 12, 2026 15:59
Copy link
Copy Markdown
Contributor Author

lunelson commented Apr 12, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented Apr 12, 2026

🤖 Augment PR Summary

Summary: Implements “local-first” project storage by introducing a discoverable .brunch/ project directory and a simple launcher/CLI path aimed at supporting npx brunch.

Changes:

  • Added BrunchProject resolution utilities (find/init/resolve) with shallow walk-up discovery and tests.
  • Updated the dev server entry (src/server/index.ts) to use BRUNCH_DB when provided, otherwise resolve/create a .brunch/ project and use its DB path.
  • Made Drizzle migrations resolution independent of the current working directory by deriving the migrations folder via import.meta.url.
  • Added an Express-based launcher that can serve the API plus built Vite assets from dist/, and attempts to open the browser automatically.
  • Added a CLI entrypoint and package.json bin mapping for brunch; added the open dependency.
  • Updated planning/spec docs to mark Slice 14 done and document invariant I100 with new test coverage references.

Technical Notes: The launcher resolves the project from an arbitrary cwd, uses the resolved DB path, and conditionally installs an SPA fallback when dist/ exists; tests cover project resolution and basic launcher integration expectations.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 7 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread package.json
"license": "(MIT OR Apache-2.0)",
"type": "module",
"bin": {
"brunch": "./src/server/cli.ts"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

package.json:16 sets bin.brunch to ./src/server/cli.ts, which npx will execute with plain Node; Node typically can’t run .ts bin entries and cli.ts imports ./launcher.js (which won’t exist without a build). This may cause the distributed CLI to fail to start unless the published artifact includes compiled JS with matching paths.

Severity: high

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread src/server/index.ts

const { app } = createApp(DB_PATH);
// In dev mode, use BRUNCH_DB env var if set, otherwise resolve .brunch/ project
const dbPath = DB_PATH ?? resolveBrunchProject(process.cwd()).dbPath;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/server/index.ts:8 uses DB_PATH ?? ..., so BRUNCH_DB="" (empty string) will not fall back to .brunch/ resolution and may be passed as an invalid SQLite path (previously || would have defaulted). This can lead to startup failures if an env var is present-but-empty.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread src/server/launcher.ts
app.use(express.static(DIST_DIR));

// SPA fallback: serve index.html for all non-API routes
app.get('*', (_req, res) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/server/launcher.ts:24 installs a catch-all app.get('*', ...) SPA fallback, which will also serve index.html for unknown /api/* GET routes when dist/ exists (masking API 404s). That can break API error handling/debugging in production mode.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread src/server/project.ts

for (let i = 0; i <= MAX_WALK_UP; i++) {
const candidate = join(current, BRUNCH_DIR);
if (existsSync(candidate)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/server/project.ts:34 treats any existing .brunch path as a valid project; if a file (not a directory) exists at .brunch, dbPath becomes nonsensical and DB init will fail later with a less clear error. It may be worth ensuring the candidate is actually a directory before returning a BrunchProject.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

expect(Array.isArray(res.body)).toBe(true);
});

it('serves static files when dist/ exists alongside the API', async () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/server/launcher.test.ts:35 is named as if it verifies static serving, but it never creates a dist/ tree or requests a static asset, so it can’t fail if express.static/SPA fallback breaks. As written it only re-checks the API route.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

expect(res.body).toBeDefined();
});

it('resolves drizzle migrations when cwd differs from package root', () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/server/launcher.test.ts:48 doesn’t change process.cwd() before calling createApp, so it likely would have passed even when migrations were resolved via ./drizzle relative to the test runner’s cwd. As written, it may not actually protect the “cwd differs from package root” invariant it describes.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread memory/PLAN.md
- Acceptance: `npx brunch` in a project directory creates `.brunch/`, opens working app; running from subdirectory finds parent `.brunch/`; `BrunchProject` struct exposes root, dbPath, cwd
- **Verification approach**: inner — launcher/path-resolution tests plus packaged app smoke checks. Outer — packaged manual walkthrough includes seeded closed-project knowledge/export routes using `forced-close-all-phases-closed` and `low-readiness-all-phases-closed`, so the deferred Phase 6 browser coherence pass lands at the real distribution boundary rather than in the pre-distribution refactor thread.
- Design: `docs/design/LOCAL_STORAGE.md`
14. **Local-first storage + npx distribution** `done`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

memory/PLAN.md:159 (process) — the PR title doesn’t match the repo PR title convention (FE-XXX: ...). See (Rule: AGENTS.md).

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@lunelson lunelson changed the title feat: local-first .brunch/ storage, project resolution, and launcher FE-545: Local-first storage; npx distribution Apr 12, 2026
Copy link
Copy Markdown
Contributor Author

lunelson commented Apr 12, 2026

Merge activity

  • Apr 12, 5:03 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 12, 5:06 PM UTC: Graphite rebased this pull request as part of a merge.
  • Apr 12, 5:07 PM UTC: @lunelson merged this pull request with Graphite.

@lunelson lunelson changed the base branch from ln/fe-578-ui-refine-expand to graphite-base/43 April 12, 2026 17:04
@lunelson lunelson changed the base branch from graphite-base/43 to main April 12, 2026 17:05
lunelson and others added 2 commits April 12, 2026 17:06
BrunchProject resolution with walk-up discovery (find/init/resolve).
Express launcher serves API + static dist/ on one port. Bin entry for
npx @hashintel/brunch. Drizzle migrations path resolved via import.meta.url
so it works from any cwd.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@lunelson lunelson force-pushed the ln/fe-545-storage-and-distro branch from 6306579 to 0b3dc6a Compare April 12, 2026 17:06
@lunelson lunelson merged commit e7eadb0 into main Apr 12, 2026
2 checks passed
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.

1 participant