Skip to content

Project foundations + mcp install (C1) - #44

Open
thedogwiththedataonit wants to merge 8 commits into
mainfrom
claude/project-foundations-mcp-install
Open

thedogwiththedataonit wants to merge 8 commits into
mainfrom
claude/project-foundations-mcp-install

Conversation

@thedogwiththedataonit

@thedogwiththedataonit thedogwiththedataonit commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Update 2026-10-16: rebased on main 2c1deff (0.16.0 + canvas session commands + canonical payload contracts + Loops export deadlines).

Summary

First CLI step of the DX push (plan wave 0, "brew-cli C1"): the libraries init and migrate will stand on, a launcher-aware invocation prefix, and a purely local brew-cli mcp install.

brew-cli mcp install

  • Merges a brew entry into each client's MCP config and keeps every other server and setting (indentation too):
    Client project (default) user (--scope user)
    Cursor .cursor/mcp.json {mcpServers:{brew:{url}}} ~/.cursor/mcp.json
    VS Code .vscode/mcp.json {servers:{brew:{type:"http",url}}} profile mcp.json
    Claude Code .mcp.json {mcpServers:{brew:{type:"http",url}}} prints claude mcp add --scope user …
    Gemini CLI .gemini/settings.json {mcpServers:{brew:{httpUrl}}} ~/.gemini/settings.json
    Devin Desktop (Windsurf) user only, explicit ~/.config/devin/mcp_config.json {url} ($XDG_CONFIG_HOME, %APPDATA%\devin on Windows)
    Codex user only, explicit ~/.codex/config.toml [mcp_servers.brew] (appended once, $CODEX_HOME honoured)
  • Default clients come from .cursor/, .vscode/, .claude/ or CLAUDECODE=1, .gemini/. If .windsurf/, .devin/, .codex/ or CODEX_* shows up, that client is reported manual with its snippet and written only when you name it. If no client is found, the command exits 2.
  • --auth oauth (default) writes the URL only. --auth env adds Authorization: Bearer ${env:BREW_API_KEY} (Cursor, VS Code, Windsurf), Bearer ${BREW_API_KEY} (Claude Code, Gemini) or Codex's bearer_token_env_var. It never writes a literal key, and a test checks that.
  • A JSONC config (comments or trailing commas) or broken JSON is never rewritten: you get manual plus a snippet. --print and --dry-run write nothing.
  • The MCP URL is the --api-url origin + /api/mcp. The Cursor deeplink is byte-identical to brew-v2 lib/mcp/handoff.ts#buildCursorInstallUrl (base64url, unpadded). The VS Code deeplink is vscode:mcp/install?<encodeURIComponent(JSON)>.
  • JSON output: { mcpUrl, dryRun, clients: [{ client, path?, action: created|updated|unchanged|manual|printed, deeplink?, snippet?, note?, nextStep? }] }. mcpUrl, dryRun, snippet, note and nextStep (codex mcp login brew after a Codex OAuth entry) are additions to the planned shape.
  • An existing brew entry is never replaced: equal settings report unchanged, other settings come back manual with the snippet. A config that is a symbolic link is written through only when its target stays in the project (or the home directory for user files).
  • Command class write, sdkMethod: null, no route, no derivedFrom, the same as config. Registry invariants and both parity sentinels pass with no new skips.

src/lib/project/ (no new runtime dependency)

package-manager, monorepo, fs-writer (create-only, managed hashes, block upsert, CRLF, atomic writes, modes, dry-run plans with diffs), env-file (round-trips byte for byte; adds or fills, never replaces or prints a value; refuses NEXT_PUBLIC_/VITE_/PUBLIC_/EXPO_PUBLIC_ for secrets), state (brew.json with hand validation, stable order, unknown keys preserved; JSON Schema shipped in schemas/brew.json at the $schema URL), gitignore (git check-ignore -q through the spawn seam, plus a fallback parser), diff, vendor-http (bounded like rawFetch, never recorded in ctx.transport, VENDOR_ERROR {vendor,status} with credentials redacted, exits 1, bounded retry-after on 429). CliIo gains optional spawn, openUrl and cwd.

Invocation prefix

getInvocationPrefix(env) reads BREW_CLI_INVOKED_AS and falls back to brew-cli for anything that isn't plain shell words. It now leads confirmCommand, the confirm envelope's command, retryCommand, and the "run … login", types --check and preview-poll hints, plus the usage-error "see …" hints.

Skills, metadata, docs

  • scripts/generate-skills.ts embeds skills/**/SKILL.md into src/generated/skills.ts. generate:skills:check runs in CI, release and bun run check.
  • The brew-cli skill documents mcp install and no longer hard-codes a command count.
  • New npm keywords: mcp, model-context-protocol, transactional-email, resend, resend-alternative, react-email, ai-agent, agents, auth-emails (email was already present). files now includes schemas.
  • README section, CHANGELOG Unreleased entry, regenerated docs/commands/README.md.

Review round 1 (Greptile, commit 5912641)

Fixes for all 8 Greptile findings:

  • Codex configs that already define mcp_servers.brew (quoted names, dotted keys, sub-tables, inline tables) now come back manual instead of getting a second table.
  • Symlinked configs are written through to the target, and the link stays.
  • KEY= # todo reads as empty.
  • Mixed line endings round-trip byte for byte.
  • Credentials are redacted before vendor error text is cut to length.
  • ensureIgnored reports conflicts for paths that a later rule re-includes.
  • Schema path rules match the CLI's validator.
  • Brace globs expand in workspace patterns.

One addition of my own: sensitive writes (.env.local) carry no dry-run diff.

Round 2 (commit 40df837): the Codex checks now run on the TOML structure, with comments stripped and string values blanked. A comment or string that only mentions mcp_servers.brew no longer blocks the install.

Not in this PR

  • Reserving npm names brew.new / create-brew: that is a publish, which this PR does not do.
  • mcp install does not write brew.json#mcp. init (C4) owns that file.

Test plan

  • bun run tsc
  • bun run lint
  • bun run test: 52 files, 688 tests. Covers project libs on fixture projects (tests/fixtures/projects/*: pnpm+turbo, npm workspaces, nx, yarn berry PnP, yarn classic, bun, deno, next app, no package.json); mcp install end to end on temp dirs (create, merge, unchanged, JSONC manual, broken JSON, dry-run, print, user scope, Codex append/unchanged, CODEX_HOME, --api-url, monorepo root, no literal key, deeplink bytes); invocation prefix in confirm/retry envelopes; generate-skills --check. Everything runs in-process under MSW (onUnhandledRequest: 'error'); one gitignore test calls the real git binary when it is installed.
  • bun run docs:commands:check, bun run generate:skills:check
  • bun run build, bin smoke, bun run smoke:signals

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

RetriggerView in GreptileConfidence Score: 2/5

Hold merging until workspace selection and the outstanding Windows and pnpm install failures are fixed.

Fix All in Claude CodeFindings

  1. P1 Setup targets the wrong package ▶
  2. P1 Windows SDK installs fail ▶
  3. P1 pnpm root installs fail ▶
  4. P2 Block edits change other lines ▶
  5. P2 Ignore plans report false success ▶
  6. P2 Workspace lists lose their folders ▶
  7. P2 Workspace folders disappear ▶
Fix with agent prompt
### Issue 1
src/lib/project/monorepo.ts:207-211
`expandGlob` misses valid workspace patterns such as `apps/[ab]*` because `segmentRegex` escapes the brackets instead of matching either character. Patterns containing only a character choice also take the literal-directory branch.

This loses the current workspace and its app candidates. PR #55's `detectProject` then falls back to the monorepo root, so running `init` inside an app can generate files and install dependencies in the wrong package. Support character choices, or return a clear unsupported-pattern error instead of silently finding no matches.

### Issue 2
src/lib/project/process.ts:36-38
`defaultSpawn` passes commands directly to `nodeSpawn` with `shell: false`. If Windows provides npm through the usual `npm.cmd` file, this cannot start it. PR #55's `src/lib/init/pipeline.ts` passes the `npm` command from `addCommand` straight to this helper, so `init` fails to install the SDK and leaves its generated imports without their dependency.

Add a Windows-safe way to run these command files while preserving argument quoting.

### Issue 3
src/lib/project/package-manager.ts:260-264
When no workspace is selected, `addCommand` produces `pnpm add <package>`. At a pnpm workspace root, pnpm refuses this without `--workspace-root`.

PR #55's `planInstall` reaches this case when the root is the selected app, including `init --app .`. SDK installation then fails, leaving generated imports without their dependency. Add an explicit root-workspace option and have that caller pass it.

### Issue 4
src/lib/project/fs-writer.ts:473-478
`upsertBlock` changes line endings outside its managed block when a file mixes LF and CRLF. `applyBlock` normalizes the whole file, then `withEol` changes every line to CRLF if any original line used CRLF. This creates unrelated edits and breaks the helper's promise to leave surrounding text untouched. Preserve the original text outside the block.

### Issue 5
src/lib/project/gitignore.ts:228-233
`ensureIgnored` reports a path as `added` during a dry run even when the writer refuses the edit. A `.gitignore` link pointing outside the project returns `refused`, but this branch still checks the proposed text and reports success. The plan therefore disagrees with the real run. Treat `refused` as a conflict before checking the planned rules.

### Issue 6
src/lib/project/monorepo.ts:131-133
`parsePnpmWorkspaceYaml` silently drops a valid bracket list split across lines, such as `packages: ["services/*",` followed by `"modules/*"]`. It reads bracket lists only when they end on the same line; later entries must start with `-`. `findMonorepo` then substitutes conventional folders, losing the actual workspace and app candidates. Read multi-line bracket lists or report that this form is unsupported instead of substituting other folders.

### Issue 7
src/lib/project/monorepo.ts:133-134
`parsePnpmWorkspaceYaml` splits quoted patterns at every comma. A valid list such as `packages: ['{apps,packages}/*']` becomes two broken patterns before `expandBraces` sees it, so neither folder yields any workspace packages. `resolveProjectRoot` then loses the current workspace and its app candidates, including for the `init` caller in PR #55.

Split only between YAML list items, not inside quoted strings.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds local mcp install, project-file helpers, launcher-aware command hints, and embedded agent skills.

  • Developers can add Brew MCP to supported editor configs from the CLI.
  • Project helpers can identify package managers and workspace roots.
  • Project helpers can safely update files and keep project state.
  • Project commands can make bounded requests to outside services.
  • Printed follow-up commands can use the package runner that launched the CLI.
  • The CLI build now carries skill files as generated source.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[mcp install] --> B[Resolve project root]
  B --> C[Select named or detected clients]
  C --> D{Print or report only?}
  D -->|Yes| E[Return snippets]
  D -->|No| F[Read client config]
  F --> G{Safe to merge?}
  G -->|No| E
  G -->|Yes| H[Plan or write Brew entry]
  H --> I[Return actions and next steps]
Loading

Reviews (9) · Last reviewed commit: "docs(changelog): #44's entries are unrel..." · Reviewed by Greptile

Comment thread src/commands/mcp/install.ts Outdated
Comment thread src/lib/project/fs-writer.ts
Comment thread src/lib/project/env-file.ts Outdated
Comment thread src/lib/project/vendor-http.ts Outdated
Comment thread src/lib/project/gitignore.ts Outdated
Comment thread schemas/brew.json Outdated
Comment thread src/lib/project/monorepo.ts
Comment thread src/lib/project/env-file.ts Outdated
Comment thread src/commands/mcp/install.ts Outdated
@thedogwiththedataonit
thedogwiththedataonit force-pushed the claude/project-foundations-mcp-install branch from 40df837 to df53432 Compare October 12, 2026 22:49
Comment on lines +473 to +478
const crlf = usesCrlf(existing)
return this.#commit(absolute, {
path,
action: 'updated',
sha256: sha256Hex(next),
content: withEol(next, crlf),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Block edits change other lines

upsertBlock changes line endings outside its managed block when a file mixes LF and CRLF. applyBlock normalizes the whole file, then withEol changes every line to CRLF if any original line used CRLF. This creates unrelated edits and breaks the helper's promise to leave surrounding text untouched. Preserve the original text outside the block.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/fs-writer.ts
Line: 473-478

Comment:
**Block edits change other lines**

`upsertBlock` changes line endings outside its managed block when a file mixes LF and CRLF. `applyBlock` normalizes the whole file, then `withEol` changes every line to CRLF if any original line used CRLF. This creates unrelated edits and breaks the helper's promise to leave surrounding text untouched. Preserve the original text outside the block.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +228 to +233
const isNowIgnored =
change.action === 'kept-user-version' || planned === 'unbalanced'
? false
: writer.dryRun
? matchesGitignore(planned, path)
: await check(path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Ignore plans report false success

ensureIgnored reports a path as added during a dry run even when the writer refuses the edit. A .gitignore link pointing outside the project returns refused, but this branch still checks the proposed text and reports success. The plan therefore disagrees with the real run. Treat refused as a conflict before checking the planned rules.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/gitignore.ts
Line: 228-233

Comment:
**Ignore plans report false success**

`ensureIgnored` reports a path as `added` during a dry run even when the writer refuses the edit. A `.gitignore` link pointing outside the project returns `refused`, but this branch still checks the proposed text and reports success. The plan therefore disagrees with the real run. Treat `refused` as a conflict before checking the planned rules.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +131 to +133
const inline = /^packages\s*:\s*\[(.*)\]\s*$/.exec(line)
if (inline !== null) {
for (const item of (inline[1] ?? '').split(',')) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Workspace lists lose their folders

parsePnpmWorkspaceYaml silently drops a valid bracket list split across lines, such as packages: ["services/*", followed by "modules/*"]. It reads bracket lists only when they end on the same line; later entries must start with -. findMonorepo then substitutes conventional folders, losing the actual workspace and app candidates. Read multi-line bracket lists or report that this form is unsupported instead of substituting other folders.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/monorepo.ts
Line: 131-133

Comment:
**Workspace lists lose their folders**

`parsePnpmWorkspaceYaml` silently drops a valid bracket list split across lines, such as `packages: ["services/*",` followed by `"modules/*"]`. It reads bracket lists only when they end on the same line; later entries must start with `-`. `findMonorepo` then substitutes conventional folders, losing the actual workspace and app candidates. Read multi-line bracket lists or report that this form is unsupported instead of substituting other folders.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +36 to +38
const child = nodeSpawn(command, [...args], {
cwd: options.cwd,
shell: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Windows SDK installs fail

defaultSpawn passes commands directly to nodeSpawn with shell: false. If Windows provides npm through the usual npm.cmd file, this cannot start it. PR #55's src/lib/init/pipeline.ts passes the npm command from addCommand straight to this helper, so init fails to install the SDK and leaves its generated imports without their dependency.

Add a Windows-safe way to run these command files while preserving argument quoting.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/process.ts
Line: 36-38

Comment:
**Windows SDK installs fail**

`defaultSpawn` passes commands directly to `nodeSpawn` with `shell: false`. If Windows provides npm through the usual `npm.cmd` file, this cannot start it. PR #55's `src/lib/init/pipeline.ts` passes the `npm` command from `addCommand` straight to this helper, so `init` fails to install the SDK and leaves its generated imports without their dependency.

Add a Windows-safe way to run these command files while preserving argument quoting.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +133 to +134
for (const item of (inline[1] ?? '').split(',')) {
const value = unquote(item.trim())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Workspace folders disappear

parsePnpmWorkspaceYaml splits quoted patterns at every comma. A valid list such as packages: ['{apps,packages}/*'] becomes two broken patterns before expandBraces sees it, so neither folder yields any workspace packages. resolveProjectRoot then loses the current workspace and its app candidates, including for the init caller in PR #55.

Split only between YAML list items, not inside quoted strings.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/monorepo.ts
Line: 133-134

Comment:
**Workspace folders disappear**

`parsePnpmWorkspaceYaml` splits quoted patterns at every comma. A valid list such as `packages: ['{apps,packages}/*']` becomes two broken patterns before `expandBraces` sees it, so neither folder yields any workspace packages. `resolveProjectRoot` then loses the current workspace and its app candidates, including for the `init` caller in PR #55.

Split only between YAML list items, not inside quoted strings.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +260 to +264
'add',
...(dev ? ['--save-dev'] : []),
...(exact ? ['--save-exact'] : []),
...(ws === undefined ? [] : ['--filter', ws.name]),
...packages,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 pnpm root installs fail

When no workspace is selected, addCommand produces pnpm add <package>. At a pnpm workspace root, pnpm refuses this without --workspace-root.

PR #55's planInstall reaches this case when the root is the selected app, including init --app .. SDK installation then fails, leaving generated imports without their dependency. Add an explicit root-workspace option and have that caller pass it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/package-manager.ts
Line: 260-264

Comment:
**pnpm root installs fail**

When no workspace is selected, `addCommand` produces `pnpm add <package>`. At a pnpm workspace root, pnpm refuses this without `--workspace-root`.

PR #55's `planInstall` reaches this case when the root is the selected app, including `init --app .`. SDK installation then fails, leaving generated imports without their dependency. Add an explicit root-workspace option and have that caller pass it.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

thedogwiththedataonit added a commit that referenced this pull request Oct 20, 2026
main released 0.17.0 and renamed its Unreleased section; this PR's items
stay under a new Unreleased heading. Also drop the unpublished launcher
name from a confirm.ts comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedogwiththedataonit
thedogwiththedataonit force-pushed the claude/project-foundations-mcp-install branch from d599c0d to 37325ff Compare October 20, 2026 16:29
thedogwiththedataonit and others added 8 commits October 21, 2026 13:00
src/lib/project/ holds the building blocks the coming `init` and
`migrate` commands share, with no new runtime dependency:

- package-manager: npm, pnpm, yarn classic vs berry (PnP from
  .yarnrc.yml), bun (bun.lock / bun.lockb), deno; packageManager pin,
  then lockfiles, then npm_config_user_agent; add/install/exec/dlx
  command builders with workspace filters.
- monorepo: pnpm-workspace.yaml, package.json workspaces, Turborepo,
  Nx; app candidates by framework dependency; resolveProjectRoot.
- fs-writer: create-only files (created, unchanged, kept-user-version,
  missing), sha256 managed-file hashes, marked-block upserts, CRLF
  tolerant, atomic temp+rename writes, file modes, dry-run plans with
  unified diffs.
- env-file: dotenv round-trips byte for byte; ensureVar only adds or
  fills, never replaces or returns a value, and refuses secrets under
  NEXT_PUBLIC_, VITE_, PUBLIC_, EXPO_PUBLIC_.
- state: brew.json read/validate/write with a stable key order, plus
  the JSON Schema in schemas/brew.json.
- gitignore: git check-ignore through the spawn seam, fallback parser,
  managed block.
- diff: a small unified line diff.
- vendor-http: third-party fetch bounded like rawFetch, never recorded
  in ctx.transport, VENDOR_ERROR {vendor, status} with credentials
  redacted, bounded retry-after on 429.

CliIo gains optional spawn, openUrl and cwd seams; tests run every
library against fixture projects in bounded temp directories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getInvocationPrefix() reads BREW_CLI_INVOKED_AS, which launcher packages
(`npx -y brew.new@<version>`) will set, and defaults to `brew-cli`.
confirmCommand, the confirmation envelope's command, retryCommand, and
every "run ..." / "see ..." hint (missing key, doctor, types --check,
preview polling, usage errors) now start with it, so an agent re-runs
through the same launcher instead of a brew-cli binary that is not on
the PATH. Anything other than plain shell words falls back to brew-cli.
The manifest lists the variable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndsurf, Codex

`brew-cli mcp install` merges a `brew` server entry into each client's
config and keeps every other server: .cursor/mcp.json,
.vscode/mcp.json, .mcp.json, .gemini/settings.json (project scope, the
default) or their user-scope files; Windsurf
(~/.codeium/windsurf/mcp_config.json) and Codex (~/.codex/config.toml,
appended once) only when named. Without --client it uses the clients
the project shows (.cursor/, .vscode/, .claude/ or CLAUDECODE=1,
.gemini/). --auth oauth writes the URL only; --auth env adds an
Authorization header each client expands from BREW_API_KEY, never a
literal key. A config with comments is reported `manual` with the
snippet; --print and --dry-run write nothing. The MCP URL follows the
--api-url origin. Cursor's deeplink is byte-identical to brew-v2
buildCursorInstallUrl; VS Code's is vscode:mcp/install?<json>.

Also: scripts/generate-skills.ts embeds skills/**/SKILL.md into
src/generated/skills.ts (generate:skills:check in CI, release and
`check`); the brew-cli skill documents mcp install and drops its stale
command count; npm keywords for MCP, transactional/auth email, Resend
and React Email; the package ships schemas/; README section, CHANGELOG
Unreleased entry, regenerated docs/commands/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ses)

- mcp install: a Codex config that already defines mcp_servers.brew in
  any other form (quoted table names, dotted keys, sub-tables, an
  [mcp_servers] table or inline table with `brew =`) is reported
  `manual` instead of getting a second, config-breaking table.
- fs-writer: a symbolic link (a dotfiles-managed config) is written
  through to its target and survives; `sensitive` writes and block
  upserts carry no diff, so a dry-run plan never prints a secret.
- env-file: `KEY= # todo` reads as empty and gets filled; each line
  keeps its own ending, so mixed LF/CRLF files round-trip byte for byte.
- vendor-http: credentials are redacted before the 300-character cut, so
  no fragment survives it.
- gitignore: ensureIgnored re-checks after editing and reports paths a
  later rule still re-includes as `conflicts`, never as added.
- schemas/brew.json: contractsPath and files keys use the same path rule
  as the CLI's validator (tested against it).
- monorepo: brace patterns (`{apps,packages}/*`) expand in workspace
  globs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comment or string that merely mentions mcp_servers.brew (`# … was
removed`, a commented-out header, `notes = "…"`, a multi-line array or
string) no longer reads as an existing definition: the checks run on
the TOML structure with comments stripped and string values blanked,
quoted keys kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…evin Desktop path, symlink containment)

mcp install
- Claude Code user scope: `--header` is variadic in `claude mcp add`, so it
  now follows the name and URL (before them it swallowed both: "missing
  required argument 'name'"). Verified against claude 2.1.212.
- Codex: the regex checks are replaced by a small TOML key reader over the
  comment- and value-blanked structure. An inline or empty
  `mcp_servers = {...}`, `[[mcp_servers]]`, `brew.url = ...` under
  `[mcp_servers]`, or a `[mcp_servers.brew.*]` sub-table now comes back
  `manual` with the snippet (each shape checked against `codex mcp list`);
  dotted root keys (`mcp_servers.docs.url = ...`) still take the table.
  Unreadable TOML is `manual`, never appended to.
- Codex OAuth results carry `nextStep: "codex mcp login brew"`.
- Windsurf is now Devin Desktop: its config is
  `$XDG_CONFIG_HOME/devin/mcp_config.json` (`~/.config/devin`,
  `%APPDATA%\devin` on Windows) per docs.devin.ai, read by both its agents,
  and the entry uses `url` (Devin Local documents only `url`). The legacy
  `~/.codeium/windsurf` path is gone: the docs no longer mention it.
  `.devin/` is detected beside `.windsurf/`.
- An existing `brew` entry is never replaced: equal is `unchanged`, other
  settings (an env header, a stale URL) are `manual` with the snippet.
- JSON configs that start with a UTF-8 BOM are parsed, and keep the mark.
- No package.json up to the repo: the project is the git root
  (`git rev-parse --show-toplevel` through the spawn seam), not the cwd.

project foundations
- fs-writer resolves where a write really lands: a symbolic link (or a
  linked parent directory) whose target leaves the project root, or the
  home directory for user configs, is `refused`; so is a dangling link,
  and a secret is never written through any link. A home config linked
  into ~/dotfiles still works.
- An unchanged file with a looser mode than requested (0644 .env.local)
  is tightened to 0600 and the change reports `permissions`.
- env-file: an unterminated quote reads as one unquoted line (as dotenv
  does), `KEY: value` is an assignment, and a line that names the
  variable inside text the parser cannot read yields `needs-review`
  instead of a second BREW_API_KEY. No value is ever returned.
- pnpm-workspace.yaml comments at column 0 no longer end the package list;
  package.json, brew.json and lockfile JSON are read past a BOM.
- BREW_CLI_INVOKED_AS accepts only npx, npx -y, pnpm dlx, bunx, yarn dlx,
  npm exec or npm create followed by one package spec (or brew-cli).

Tests never spawn host programs: run-cli's default spawn fails as if the
program were missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The npm names `brew.new` and `create-brew` are not ours yet, so README,
the brew-cli skill, `docs --agent` and the changelog show the
package-runner prefix with `@brew.new/cli`, which is on npm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main released 0.17.0 and renamed its Unreleased section; this PR's items
stay under a new Unreleased heading. Also drop the unpublished launcher
name from a confirm.ts comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedogwiththedataonit
thedogwiththedataonit force-pushed the claude/project-foundations-mcp-install branch from 37325ff to 8b352aa Compare October 21, 2026 04:12
Comment on lines +207 to +211
function segmentRegex(segment: string): RegExp {
const body = segment
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
.replaceAll('*', '[^/]*')
.replaceAll('?', '[^/]')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Setup targets the wrong package

expandGlob misses valid workspace patterns such as apps/[ab]* because segmentRegex escapes the brackets instead of matching either character. Patterns containing only a character choice also take the literal-directory branch.

This loses the current workspace and its app candidates. PR #55's detectProject then falls back to the monorepo root, so running init inside an app can generate files and install dependencies in the wrong package. Support character choices, or return a clear unsupported-pattern error instead of silently finding no matches.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/project/monorepo.ts
Line: 207-211

Comment:
**Setup targets the wrong package**

`expandGlob` misses valid workspace patterns such as `apps/[ab]*` because `segmentRegex` escapes the brackets instead of matching either character. Patterns containing only a character choice also take the literal-directory branch.

This loses the current workspace and its app candidates. PR #55's `detectProject` then falls back to the monorepo root, so running `init` inside an app can generate files and install dependencies in the wrong package. Support character choices, or return a clear unsupported-pattern error instead of silently finding no matches.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

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