release: @focus-mcp/core v1.5.1 — sync develop → main#84
Merged
Conversation
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 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
`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): 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 (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 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 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: 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 * 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>
- 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>
…#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>
…les (#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>
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>
- 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>
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>
Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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 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>
* 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 — 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>
…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>
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 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, 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> --------- 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 <claude@localhost>
Adds optional `{ force: true }` third argument to `removeSource` that
bypasses the default-source protection. Without force the existing guard
is preserved. Exports new `RemoveSourceOptions` type from the package.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove "200k to 2k" unverified figures; replace with real benchmark data from equivalence-report.md (sr_summary -99.8%, sr_signatures -99.5%). Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): add back-merge workflow to auto-sync main → develop * chore(reuse): fix SPDX headers in CHANGELOGs and *.md --------- Co-authored-by: claude <claude@localhost>
Extracts upgrade logic from CLI into core/marketplace/upgrader.ts. Pure planUpgrade() + async executeUpgrade() with injected UpgradeIO. 12 tests cover up-to-date, upgrade, missing-catalog, dry-run and disabled-state preservation cases. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…preview tags (#46) Replace custom 0.0.0-dev.X versioning with `pnpm changeset version --snapshot dev` so that dev dist-tags reflect the actual next stable (e.g. 1.2.0-dev-DATE-SHA) instead of a frozen 0.0.0 base. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ets (#50) - Replace GitHub Packages .npmrc config with npmjs.org (was causing E401 because GITHUB_TOKEN was never resolved, while workflow passes NODE_AUTH_TOKEN) - Add explicit guard: skip all publish steps when no .changeset/*.md pending - Remove || true so failures are visible instead of silently masked Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure, browser-compatible module for managing the tools.hidden and tools.alwaysLoad lists in ~/.focus/config.json. Exports: - parseToolConfig / ToolConfigData / ToolConfigIO - matchesToolPattern — trailing glob support - isToolHidden — hidden check with focus_tools immunity - shouldAlwaysLoad — user-pin + server-defaults precedence - hideTool / showTool / pinTool / unpinTool — mutations - listToolsConfig / clearToolsConfig — bulk ops IO is injected (ToolConfigIO) — no filesystem dependency in core. 32 unit tests, 100% branch coverage. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…v tags) (#54) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* feat(core): add update-checker module for cli + bricks update detection Adds `checkForUpdates()` with injectable IO (fully testable), 24h throttle cache in ~/.focus/update-cache.json, npm registry fetch for cli version, and catalog comparison for installed bricks. Network errors are swallowed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(update-checker): cover makeNodeIO without v8 ignore Remove /* v8 ignore next 90 */ from makeNodeIO. Add 17 tests in update-checker-node-io.test.ts that mock node:fs/promises and node:os via vi.mock to exercise the Node I/O adapter. Add 5 tests in update-checker.test.ts for buildCliCommand (pnpm/yarn/npm branches) and parseCacheFile edge cases. Coverage: 94% → 100% stmts/lines/funcs, 81% → 93% branches. Zero v8/istanbul ignore in the codebase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(core): make update-checker browser-compatible, remove makeNodeIO Remove makeNodeIO() from core: size-limit CI runs esbuild in browser mode (no platform:node) so node built-ins cannot resolve. io is now required in UpdateCheckOptions. Refactored checkForUpdates helpers (complexity 41->10). Fixed Biome lint. Removed update-checker-node-io.test.ts (makeNodeIO to cli). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…#59) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace direct `git push origin develop` with a PR-based approach to comply with the develop branch ruleset (GH013 error). Now creates a `chore/back-merge-<sha>` branch, pushes it, opens a PR, and enables auto-merge (squash) so it merges once CI passes. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
Extend CatalogBrick, BrickManifest and parseBrick to support two new optional fields: keywords (free-form tags) and recommendedFor (stack/framework hints). searchBricks now matches on both fields in addition to name, description and tags. Co-authored-by: claude <claude@localhost>
…mendedFor (#64) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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, 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>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (#37)
* 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
- ad…
Co-authored-by: claude <claude@localhost>
* 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, 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>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (#37)
* 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 …
Merge commit (not squash) to reconcile develop with main and preserve original commit authors from previous release merges. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….5.0 chore: back-merge main → develop with merge commit (preserve history)
Add npm upgrade step (npm@latest >= 11.5.1) before each publish step to enable OIDC token-based auth. Comment out .npmrc token line. NODE_AUTH_TOKEN kept as fallback during transition until Trusted Publishers are configured on npmjs.com. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add git branching rules, commit convention details, browser-compat golden rule (core=brains, no Node.js-only imports in packages/core or packages/sdk), update-checker pattern reference, EventBus/Registry coverage >= 95% threshold, common pitfalls (browser-compat breakage, coverage drop, develop↔main divergence, ADR missing) to CONTRIBUTING.md. Add docs/RELEASE.md covering the 3-package monorepo release process (core → cli → marketplace order), manual fallback, back-merge recovery, post-release verification for all 3 packages, and OIDC publishing. Co-authored-by: claude <claude@localhost>
Co-authored-by: claude <claude@localhost>
Co-authored-by: claude <claude@localhost>
Co-authored-by: claude <claude@localhost>
Co-authored-by: claude <claude@localhost>
Co-authored-by: claude <claude@localhost>
…s-mcp/validator@1.0.7 (#80) Co-authored-by: claude <claude@localhost>
* chore(release): bump @focus-mcp/core@1.5.1 @focus-mcp/sdk@1.5.1 @focus-mcp/validator@1.0.7 * fix(ci): remove broken npm upgrade step in stable-publish --------- Co-authored-by: claude <claude@localhost>
* 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. * 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. * 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 * docs(prd): add SPDX headers for REUSE compliance --------- * 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. * 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. --------- * 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. * 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. --------- * 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. * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. * 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). --------- * 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. * 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. --------- * 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. * fix: add SPDX header to .npmrc --------- * 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. * test: add edge case tests for prefix coverage (registry 98%+) --------- * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key * fix(ci): add id-token permission for OIDC auth --------- * 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 * 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 * 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. * 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. * 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. * 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%). --------- * 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 * 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 * 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. * fix(ci): add id-token permission for npm provenance (#22) * 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 * 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 * chore: regenerate pnpm-lock.yaml after scope rename --------- * 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 * 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 * chore: regenerate lockfile * docs: update scope references to @focus-mcp in docs --------- * 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) * 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) * chore(ci): remove release.yml — stable-publish.yml handles releases * chore(ci): remove claude-review.yml (fails on workflow changes) * revert: restore claude-review.yml — will work after main merge --------- * 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 * 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 * 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. * 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. --------- * 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. * 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". * 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. * 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. * 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 * docs(prd): add SPDX headers for REUSE compliance --------- * 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. * 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. --------- * 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. * 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. --------- * 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. * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. * 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). --------- * 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. * 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. --------- * 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. * fix: add SPDX header to .npmrc --------- --------- * 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. * 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. --------- * 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. * 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. --------- * 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. * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. * 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). --------- * 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. * fix: add SPDX header to .npmrc --------- * 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. * test: add edge case tests for prefix coverage (registry 98%+) --------- * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key * fix(ci): add id-token permission for OIDC auth --------- --------- * 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. * 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. * 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 * docs(prd): add SPDX headers for REUSE compliance --------- * 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. * 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. --------- * 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. * 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. --------- * 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. * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. * 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). --------- * 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. * 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. --------- * 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. * fix: add SPDX header to .npmrc --------- * 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. * test: add edge case tests for prefix coverage (registry 98%+) --------- * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key * fix(ci): add id-token permission for OIDC auth --------- * 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 * 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 * 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. * 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. * 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. * 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%). --------- * 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 * 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 * 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. * fix(ci): add id-token permission for npm provenance (#22) * 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 * 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 * chore: regenerate pnpm-lock.yaml after scope rename --------- * 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 * 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 * chore: regenerate lockfile * docs: update scope references to @focus-mcp in docs --------- * 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) * 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) * chore(ci): remove release.yml — stable-publish.yml handles releases * chore(ci): remove claude-review.yml (fails on workflow changes) * revert: restore claude-review.yml — will work after main merge --------- --------- * 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. * 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. * 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 * docs(prd): add SPDX headers for REUSE compliance --------- * 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. * 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. --------- * 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. * 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. --------- * 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. * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. * 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). --------- * 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. * 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. --------- * 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. * fix: add SPDX header to .npmrc --------- * 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. * test: add edge case tests for prefix coverage (registry 98%+) --------- * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key * fix(ci): add id-token permission for OIDC auth --------- * 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 * 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 * 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. * 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. * 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. * 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%). --------- * 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 * 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 * 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. * fix(ci): add id-token permission for npm provenance (#22) * 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 * 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 * chore: regenerate pnpm-lock.yaml after scope rename --------- * 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 * 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 * chore: regenerate lockfile * docs: update scope references to @focus-mcp in docs --------- * 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) * 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) * chore(ci): remove release.yml — stable-publish.yml handles releases * chore(ci): remove claude-review.yml (fails on workflow changes) * revert: restore claude-review.yml — will work after main merge --------- * 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… Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Patch release sync. Bumps: @focus-mcp/core 1.5.0→1.5.1, @focus-mcp/sdk 1.5.0→1.5.1 (linked), @focus-mcp/validator 1.0.6→1.0.7. Branch includes changeset version bump commit and back-merge.