Skip to content

chore: back-merge main into develop after release sync PR #78#83

Merged
samuelds merged 2 commits into
developfrom
back-merge/main-to-develop
Apr 30, 2026
Merged

chore: back-merge main into develop after release sync PR #78#83
samuelds merged 2 commits into
developfrom
back-merge/main-to-develop

Conversation

@samuelds
Copy link
Copy Markdown
Contributor

Back-merge main into develop to resolve the BEHIND state. Needed so that the sync PR #82 (develop→main) can merge. Uses skip-changeset label since this is a back-merge.

samuelds and others added 2 commits April 30, 2026 19:18
* ci: run CodeQL on develop branch as well (#4)

The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(prd): split PRD into per-repo focused docs (#2)

* docs(prd): split monolithic PRD into per-repo PRDs

Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).

Companion PRDs added in client/ and marketplace/ repos.

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

* docs(prd): address Copilot review feedback

- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
  `focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
  namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
  "custom validator (parseManifest)"; JSON Schema is used only
  for tools[].inputSchema

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

* docs(prd): add SPDX headers for REUSE compliance

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add brick loader with abstract source (#3)

* feat(core): add brick loader with abstract source

`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.

Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).

11 tests, 100% line / 94.4% branch coverage.

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

* refactor(core): tighten brick-loader validation per review

- Parse the module-provided brick.manifest with `parseManifest` and
  enforce strict equality against the source manifest (canonical JSON
  comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
  default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
  malformed `brick.manifest` produces an INVALID_MANIFEST error rather
  than a misleading "does not implement Brick contract".

3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): marketplace resolver (parse + find + semver + updates) (#5)

* feat(core): add marketplace resolver (parse + find + semver + updates)

Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.

Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
  published JSON Schema (kebab-case names, semver versions, typed
  source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
  (core + optional pre-release; no build metadata, no range matching
  yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]

20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.

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

* refactor(core): address Copilot review on marketplace resolver

- Thread a full `loc` path into requireString/optionalString and the
  array variants so validation errors now produce
  "bricks[3].owner.email must be a string" instead of just
  "email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
  leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
  matching the manifest parser and semver 2.0 §10. Build metadata is
  captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
  emitting `inputSchema: undefined` when the field is missing.
  Consistent with how other optionals are handled in this file and
  compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
  rejects pre-release identifiers with leading zeros.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: migrate indent from 2 to 4 spaces (#6)

* style: migrate indent from 2 to 4 spaces (Biome config)

Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.

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

* fix: update .editorconfig indent_size to 4

Aligns with Biome formatter config to prevent editor/formatter conflicts.

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

* fix: align biome schema to 2.4.11 and format new develop files

Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)

* docs: add CLAUDE.md capturing the post-pivot agent guidance

Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).

Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.

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

* docs(claude.md): fix repo count and clarify English rule exceptions

Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: configure GitHub Packages for @focusmcp/* packages (#8)

* chore: configure GitHub Packages registry for @focusmcp/* packages

Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.

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

* fix: add SPDX header to .npmrc

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: mandatory tool prefix in brick manifest (#10)

* feat: add mandatory prefix field to brick manifest

Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.

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

* test: add edge case tests for prefix coverage (registry 98%+)

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add Claude Code Review action (#11)

* ci: add Claude Code Review action

* ci: use Claude Max OAuth instead of API key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for OIDC auth

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)

- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)

Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).

- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules

Pure, browser-compatible marketplace management for FocusMCP core:

- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type

All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.

257 tests passing, 0 typecheck errors, 0 lint errors.

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

* fix: export marketplace modules from core package entry point

Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.

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

* chore: clean up knip config — remove stale entries

Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.

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

* refactor: extract shared validation helpers to reduce duplication

Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.

Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: update default catalog URL to raw.githubusercontent.com (#17)

Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add GitHub Packages publish workflow for dev channel (#18)

- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
  GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: rename npm scope from @focusmcp to @focus-mcp (#21)

Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for npm provenance (#22)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ci): dev publish workflow (#24)

* feat(ci): add dev publish workflow with auto-versioning

- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish

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

* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow

- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs

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

* chore: regenerate pnpm-lock.yaml after scope rename

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): use GitHub Packages for dev publish (#25)

* fix(ci): use GitHub Packages registry instead of npmjs.org

- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission

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

* fix: rename scope to @focus-mcp + npmjs.org for dev publish

- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile

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

* chore: regenerate lockfile

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

* docs: update scope references to @focus-mcp in docs

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): add id-token:write permission + remove duplicate workflows (#26)

- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(release): v1.0.0 + stable-publish workflow (#27)

* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0

- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)

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

* chore(ci): remove release.yml — stable-publish.yml handles releases

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

* chore(ci): remove claude-review.yml (fails on workflow changes)

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

* revert: restore claude-review.yml — will work after main merge

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)

* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md

- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator

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

* docs: consolidate AGENTS.md + AI transparency

- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING

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

* chore(ci): allow `release` commit type in commitlint

Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.

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

* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)

Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)

The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ci): add checkout step to claude-review workflow (#34)

The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: sync main back into develop (release bumps) (#35)

* chore: sync develop → main (#9)

* ci: run CodeQL on develop branch as well (#4)

The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(prd): split PRD into per-repo focused docs (#2)

* docs(prd): split monolithic PRD into per-repo PRDs

Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).

Companion PRDs added in client/ and marketplace/ repos.

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

* docs(prd): address Copilot review feedback

- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
  `focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
  namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
  "custom validator (parseManifest)"; JSON Schema is used only
  for tools[].inputSchema

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

* docs(prd): add SPDX headers for REUSE compliance

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add brick loader with abstract source (#3)

* feat(core): add brick loader with abstract source

`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.

Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).

11 tests, 100% line / 94.4% branch coverage.

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

* refactor(core): tighten brick-loader validation per review

- Parse the module-provided brick.manifest with `parseManifest` and
  enforce strict equality against the source manifest (canonical JSON
  comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
  default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
  malformed `brick.manifest` produces an INVALID_MANIFEST error rather
  than a misleading "does not implement Brick contract".

3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): marketplace resolver (parse + find + semver + updates) (#5)

* feat(core): add marketplace resolver (parse + find + semver + updates)

Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.

Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
  published JSON Schema (kebab-case names, semver versions, typed
  source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
  (core + optional pre-release; no build metadata, no range matching
  yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]

20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.

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

* refactor(core): address Copilot review on marketplace resolver

- Thread a full `loc` path into requireString/optionalString and the
  array variants so validation errors now produce
  "bricks[3].owner.email must be a string" instead of just
  "email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
  leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
  matching the manifest parser and semver 2.0 §10. Build metadata is
  captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
  emitting `inputSchema: undefined` when the field is missing.
  Consistent with how other optionals are handled in this file and
  compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
  rejects pre-release identifiers with leading zeros.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: migrate indent from 2 to 4 spaces (#6)

* style: migrate indent from 2 to 4 spaces (Biome config)

Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.

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

* fix: update .editorconfig indent_size to 4

Aligns with Biome formatter config to prevent editor/formatter conflicts.

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

* fix: align biome schema to 2.4.11 and format new develop files

Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)

* docs: add CLAUDE.md capturing the post-pivot agent guidance

Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).

Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.

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

* docs(claude.md): fix repo count and clarify English rule exceptions

Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: configure GitHub Packages for @focusmcp/* packages (#8)

* chore: configure GitHub Packages registry for @focusmcp/* packages

Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.

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

* fix: add SPDX header to .npmrc

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>

* chore: sync develop → main (conflict resolution) (#13)

* feat(core): add brick loader with abstract source (#3)

* feat(core): add brick loader with abstract source

`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.

Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).

11 tests, 100% line / 94.4% branch coverage.

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

* refactor(core): tighten brick-loader validation per review

- Parse the module-provided brick.manifest with `parseManifest` and
  enforce strict equality against the source manifest (canonical JSON
  comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
  default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
  malformed `brick.manifest` produces an INVALID_MANIFEST error rather
  than a misleading "does not implement Brick contract".

3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): marketplace resolver (parse + find + semver + updates) (#5)

* feat(core): add marketplace resolver (parse + find + semver + updates)

Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.

Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
  published JSON Schema (kebab-case names, semver versions, typed
  source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
  (core + optional pre-release; no build metadata, no range matching
  yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]

20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.

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

* refactor(core): address Copilot review on marketplace resolver

- Thread a full `loc` path into requireString/optionalString and the
  array variants so validation errors now produce
  "bricks[3].owner.email must be a string" instead of just
  "email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
  leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
  matching the manifest parser and semver 2.0 §10. Build metadata is
  captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
  emitting `inputSchema: undefined` when the field is missing.
  Consistent with how other optionals are handled in this file and
  compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
  rejects pre-release identifiers with leading zeros.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: migrate indent from 2 to 4 spaces (#6)

* style: migrate indent from 2 to 4 spaces (Biome config)

Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.

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

* fix: update .editorconfig indent_size to 4

Aligns with Biome formatter config to prevent editor/formatter conflicts.

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

* fix: align biome schema to 2.4.11 and format new develop files

Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: configure GitHub Packages for @focusmcp/* packages (#8)

* chore: configure GitHub Packages registry for @focusmcp/* packages

Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.

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

* fix: add SPDX header to .npmrc

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: mandatory tool prefix in brick manifest (#10)

* feat: add mandatory prefix field to brick manifest

Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.

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

* test: add edge case tests for prefix coverage (registry 98%+)

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add Claude Code Review action (#11)

* ci: add Claude Code Review action

* ci: use Claude Max OAuth instead of API key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for OIDC auth

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>

* release: v1.0.0 — sync develop → main (#29)

* ci: run CodeQL on develop branch as well (#4)

The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(prd): split PRD into per-repo focused docs (#2)

* docs(prd): split monolithic PRD into per-repo PRDs

Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).

Companion PRDs added in client/ and marketplace/ repos.

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

* docs(prd): address Copilot review feedback

- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
  `focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
  namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
  "custom validator (parseManifest)"; JSON Schema is used only
  for tools[].inputSchema

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

* docs(prd): add SPDX headers for REUSE compliance

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add brick loader with abstract source (#3)

* feat(core): add brick loader with abstract source

`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.

Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).

11 tests, 100% line / 94.4% branch coverage.

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

* refactor(core): tighten brick-loader validation per review

- Parse the module-provided brick.manifest with `parseManifest` and
  enforce strict equality against the source manifest (canonical JSON
  comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
  default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
  malformed `brick.manifest` produces an INVALID_MANIFEST error rather
  than a misleading "does not implement Brick contract".

3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): marketplace resolver (parse + find + semver + updates) (#5)

* feat(core): add marketplace resolver (parse + find + semver + updates)

Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.

Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
  published JSON Schema (kebab-case names, semver versions, typed
  source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
  (core + optional pre-release; no build metadata, no range matching
  yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]

20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.

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

* refactor(core): address Copilot review on marketplace resolver

- Thread a full `loc` path into requireString/optionalString and the
  array variants so validation errors now produce
  "bricks[3].owner.email must be a string" instead of just
  "email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
  leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
  matching the manifest parser and semver 2.0 §10. Build metadata is
  captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
  emitting `inputSchema: undefined` when the field is missing.
  Consistent with how other optionals are handled in this file and
  compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
  rejects pre-release identifiers with leading zeros.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: migrate indent from 2 to 4 spaces (#6)

* style: migrate indent from 2 to 4 spaces (Biome config)

Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.

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

* fix: update .editorconfig indent_size to 4

Aligns with Biome formatter config to prevent editor/formatter conflicts.

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

* fix: align biome schema to 2.4.11 and format new develop files

Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)

* docs: add CLAUDE.md capturing the post-pivot agent guidance

Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).

Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.

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

* docs(claude.md): fix repo count and clarify English rule exceptions

Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: configure GitHub Packages for @focusmcp/* packages (#8)

* chore: configure GitHub Packages registry for @focusmcp/* packages

Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.

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

* fix: add SPDX header to .npmrc

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: mandatory tool prefix in brick manifest (#10)

* feat: add mandatory prefix field to brick manifest

Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.

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

* test: add edge case tests for prefix coverage (registry 98%+)

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add Claude Code Review action (#11)

* ci: add Claude Code Review action

* ci: use Claude Max OAuth instead of API key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for OIDC auth

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)

- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)

Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).

- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules

Pure, browser-compatible marketplace management for FocusMCP core:

- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type

All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.

257 tests passing, 0 typecheck errors, 0 lint errors.

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

* fix: export marketplace modules from core package entry point

Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.

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

* chore: clean up knip config — remove stale entries

Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.

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

* refactor: extract shared validation helpers to reduce duplication

Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.

Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: update default catalog URL to raw.githubusercontent.com (#17)

Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add GitHub Packages publish workflow for dev channel (#18)

- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
  GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: rename npm scope from @focusmcp to @focus-mcp (#21)

Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for npm provenance (#22)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ci): dev publish workflow (#24)

* feat(ci): add dev publish workflow with auto-versioning

- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish

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

* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow

- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs

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

* chore: regenerate pnpm-lock.yaml after scope rename

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): use GitHub Packages for dev publish (#25)

* fix(ci): use GitHub Packages registry instead of npmjs.org

- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission

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

* fix: rename scope to @focus-mcp + npmjs.org for dev publish

- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile

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

* chore: regenerate lockfile

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

* docs: update scope references to @focus-mcp in docs

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): add id-token:write permission + remove duplicate workflows (#26)

- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(release): v1.0.0 + stable-publish workflow (#27)

* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0

- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)

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

* chore(ci): remove release.yml — stable-publish.yml handles releases

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

* chore(ci): remove claude-review.yml (fails on workflow changes)

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

* revert: restore claude-review.yml — will work after main merge

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>

* release: sync develop → main (workflow fix + v1 cleanup) (#33)

* ci: run CodeQL on develop branch as well (#4)

The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(prd): split PRD into per-repo focused docs (#2)

* docs(prd): split monolithic PRD into per-repo PRDs

Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).

Companion PRDs added in client/ and marketplace/ repos.

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

* docs(prd): address Copilot review feedback

- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
  `focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
  namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
  "custom validator (parseManifest)"; JSON Schema is used only
  for tools[].inputSchema

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

* docs(prd): add SPDX headers for REUSE compliance

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add brick loader with abstract source (#3)

* feat(core): add brick loader with abstract source

`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.

Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).

11 tests, 100% line / 94.4% branch coverage.

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

* refactor(core): tighten brick-loader validation per review

- Parse the module-provided brick.manifest with `parseManifest` and
  enforce strict equality against the source manifest (canonical JSON
  comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
  default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
  malformed `brick.manifest` produces an INVALID_MANIFEST error rather
  than a misleading "does not implement Brick contract".

3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): marketplace resolver (parse + find + semver + updates) (#5)

* feat(core): add marketplace resolver (parse + find + semver + updates)

Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.

Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
  published JSON Schema (kebab-case names, semver versions, typed
  source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
  (core + optional pre-release; no build metadata, no range matching
  yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]

20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.

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

* refactor(core): address Copilot review on marketplace resolver

- Thread a full `loc` path into requireString/optionalString and the
  array variants so validation errors now produce
  "bricks[3].owner.email must be a string" instead of just
  "email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
  leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
  matching the manifest parser and semver 2.0 §10. Build metadata is
  captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
  emitting `inputSchema: undefined` when the field is missing.
  Consistent with how other optionals are handled in this file and
  compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
  rejects pre-release identifiers with leading zeros.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: migrate indent from 2 to 4 spaces (#6)

* style: migrate indent from 2 to 4 spaces (Biome config)

Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.

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

* fix: update .editorconfig indent_size to 4

Aligns with Biome formatter config to prevent editor/formatter conflicts.

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

* fix: align biome schema to 2.4.11 and format new develop files

Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)

* docs: add CLAUDE.md capturing the post-pivot agent guidance

Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).

Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.

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

* docs(claude.md): fix repo count and clarify English rule exceptions

Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: configure GitHub Packages for @focusmcp/* packages (#8)

* chore: configure GitHub Packages registry for @focusmcp/* packages

Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.

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

* fix: add SPDX header to .npmrc

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: mandatory tool prefix in brick manifest (#10)

* feat: add mandatory prefix field to brick manifest

Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.

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

* test: add edge case tests for prefix coverage (registry 98%+)

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add Claude Code Review action (#11)

* ci: add Claude Code Review action

* ci: use Claude Max OAuth instead of API key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for OIDC auth

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)

- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)

Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).

- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)

* feat(marketplace): add catalog-store, catalog-fetcher, installer modules

Pure, browser-compatible marketplace management for FocusMCP core:

- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type

All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.

257 tests passing, 0 typecheck errors, 0 lint errors.

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

* fix: export marketplace modules from core package entry point

Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.

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

* chore: clean up knip config — remove stale entries

Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.

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

* refactor: extract shared validation helpers to reduce duplication

Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.

Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: update default catalog URL to raw.githubusercontent.com (#17)

Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add GitHub Packages publish workflow for dev channel (#18)

- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
  GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: rename npm scope from @focusmcp to @focus-mcp (#21)

Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): add id-token permission for npm provenance (#22)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ci): dev publish workflow (#24)

* feat(ci): add dev publish workflow with auto-versioning

- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish

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

* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow

- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs

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

* chore: regenerate pnpm-lock.yaml after scope rename

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): use GitHub Packages for dev publish (#25)

* fix(ci): use GitHub Packages registry instead of npmjs.org

- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission

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

* fix: rename scope to @focus-mcp + npmjs.org for dev publish

- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile

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

* chore: regenerate lockfile

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

* docs: update scope references to @focus-mcp in docs

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): add id-token:write permission + remove duplicate workflows (#26)

- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(release): v1.0.0 + stable-publish workflow (#27)

* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0

- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)

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

* chore(ci): remove release.yml — stable-publish.yml handles releases

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

* chore(ci): remove claude-review.yml (fails on workflow changes)

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

* revert: restore claude-review.yml — will work after main merge

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

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)

* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md

- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
@samuelds samuelds added the skip-changeset Skip changeset check — used for release PRs label Apr 30, 2026
@samuelds samuelds enabled auto-merge (squash) April 30, 2026 19:38
@samuelds samuelds merged commit 65e8575 into develop Apr 30, 2026
13 checks passed
@samuelds samuelds deleted the back-merge/main-to-develop branch May 6, 2026 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset Skip changeset check — used for release PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant