feat: rename npm scope from @focusmcp to @focus-mcp#21
Merged
Conversation
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 Sonnet 4.6 <noreply@anthropic.com>
samuelds
added a commit
that referenced
this pull request
Apr 23, 2026
* 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>
samuelds
added a commit
that referenced
this pull request
Apr 23, 2026
* 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>
samuelds
added a commit
that referenced
this pull request
Apr 24, 2026
* 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>
samuelds
added a commit
that referenced
this pull request
Apr 24, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) o…
samuelds
added a commit
that referenced
this pull request
Apr 28, 2026
…49) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm metadata (description, keywords, author, homepage) on sdk and validator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: consolidate AGENTS.md + AI transparency - merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec) - add AI-assisted development section to README - add AI-assisted contributions section to CONTRIBUTING Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ci): allow `release` commit type in commitlint Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum. Add `release` as a valid type since we already use it on main branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq) Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31) The v1 action deprecated direct_prompt. Without a valid trigger input, PRs got a green check but Claude never posted any review. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): add checkout step to claude-review workflow (#34) The claude-code-action needs full git history to fetch base branches. Without a preceding actions/checkout@v5 with fetch-depth: 0, the action fails with "git fetch origin develop --depth=1: exit 128". Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: sync main back into develop (release bumps) (#35) * chore: sync develop → main (#9) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * chore: sync develop → main (conflict resolution) (#13) * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: v1.0.0 — sync develop → main (#29) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: sync develop → main (workflow fix + v1 cleanup) (#33) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm metadata (de…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* 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…
samuelds
added a commit
that referenced
this pull request
Apr 29, 2026
* 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 …
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
* 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>
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, ke…
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, key…
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (description, keywords, author, homepage) on sdk and validator
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: consolidate AGENTS.md + AI transparency
- merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec)
- add AI-assisted development section to README
- add AI-assisted contributions section to CONTRIBUTING
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): allow `release` commit type in commitlint
Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum.
Add `release` as a valid type since we already use it on main branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq)
Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31)
The v1 action deprecated direct_prompt. Without a valid trigger input,
PRs got a green check but Claude never posted any review.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): add checkout step to claude-review workflow (#34)
The claude-code-action needs full git history to fetch base branches.
Without a preceding actions/checkout@v5 with fetch-depth: 0, the action
fails with "git fetch origin develop --depth=1: exit 128".
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync main back into develop (release bumps) (#35)
* chore: sync develop → main (#9)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* chore: sync develop → main (conflict resolution) (#13)
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: v1.0.0 — sync develop → main (#29)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude <claude@localhost>
* release: sync develop → main (workflow fix + v1 cleanup) (#33)
* ci: run CodeQL on develop branch as well (#4)
The develop ruleset requires CodeQL results before merge, but the
workflow only triggered on main. PRs targeting develop were
deadlocked. Add develop to push and pull_request triggers, mirroring
the client repo's setup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): split PRD into per-repo focused docs (#2)
* docs(prd): split monolithic PRD into per-repo PRDs
Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS):
3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader.
Reflects current architecture (core in WebView, Tauri sole HTTP gateway).
Companion PRDs added in client/ and marketplace/ repos.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): address Copilot review feedback
- Replace cross-repo relative links with absolute GitHub URLs
- Clarify monorepo layout: package `packages/core`, not `core/`
- Manifest naming: parser only enforces kebab-case; the
`focus-` prefix is a marketplace convention
- Validator section: list only checks actually implemented; defer
namespace/dependency/bypass checks to P1
- Stack and Decisions tables: replace "Zod / JSON Schema" with
"custom validator (parseManifest)"; JSON Schema is used only
for tools[].inputSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(prd): add SPDX headers for REUSE compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add brick loader with abstract source (#3)
* feat(core): add brick loader with abstract source
`loadBricks({ source })` reads installed bricks via an abstract `BrickSource`
(list/readManifest/loadModule), validates manifests with `parseManifest`,
ensures the loaded module exports a Brick whose manifest matches the source
declaration, and collects per-brick failures without aborting the load.
Browser-compatible: no direct FS access — the source is injected by the host
(Tauri commands for desktop, in-memory for tests).
11 tests, 100% line / 94.4% branch coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): tighten brick-loader validation per review
- Parse the module-provided brick.manifest with `parseManifest` and
enforce strict equality against the source manifest (canonical JSON
comparison). Catches divergence in deps/tools, not just name.
- Split default-export check into two messages: missing default vs
default-not-an-object (clearer diagnostics for `default: 42` etc.).
- Move the manifest-shape check out of the Brick contract assertion so
malformed `brick.manifest` produces an INVALID_MANIFEST error rather
than a misleading "does not implement Brick contract".
3 new tests (divergence, malformed module manifest, default-not-object).
14 tests total, 100% line / 96.3% branch coverage on the loader.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): marketplace resolver (parse + find + semver + updates) (#5)
* feat(core): add marketplace resolver (parse + find + semver + updates)
Pure, browser-compatible module that consumes a catalog.json as
published by the FocusMCP marketplace. Does no I/O — the host injects
raw JSON and this module validates, normalizes and queries it.
Public API:
- parseCatalog(raw): Catalog — structural validation aligned with the
published JSON Schema (kebab-case names, semver versions, typed
source variants).
- findBrick(catalog, name): CatalogBrick | undefined
- compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation
(core + optional pre-release; no build metadata, no range matching
yet — added at need).
- listUpdates(installed, catalog): UpdateInfo[]
20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release
ordering per semver §11, listUpdates). Full suite: 127 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(core): address Copilot review on marketplace resolver
- Thread a full `loc` path into requireString/optionalString and the
array variants so validation errors now produce
"bricks[3].owner.email must be a string" instead of just
"email must be a string". Much easier to diagnose.
- Tighten SEMVER regex: reject numeric pre-release identifiers with
leading zeros (per semver 2.0 §9).
- Extend SEMVER regex to accept optional build metadata ("+..."),
matching the manifest parser and semver 2.0 §10. Build metadata is
captured but discarded for precedence comparisons, as required.
- parseTool: use conditional spread for `inputSchema` instead of
emitting `inputSchema: undefined` when the field is missing.
Consistent with how other optionals are handled in this file and
compatible with `exactOptionalPropertyTypes`.
- Add 2 tests: build metadata is ignored when comparing; compareSemver
rejects pre-release identifiers with leading zeros.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: migrate indent from 2 to 4 spaces (#6)
* style: migrate indent from 2 to 4 spaces (Biome config)
Standardize indentation to 4 spaces across all projects.
Biome formatter config updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update .editorconfig indent_size to 4
Aligns with Biome formatter config to prevent editor/formatter conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align biome schema to 2.4.11 and format new develop files
Update $schema version to match installed Biome CLI.
Reformat brick-loader and marketplace resolver (merged from develop).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7)
* docs: add CLAUDE.md capturing the post-pivot agent guidance
Replaces the former personal memory system under
~/.claude/projects/**/memory/ with an in-repo, version-controlled
file that is auto-loaded by Claude Code (and any agents.md-compatible
tool).
Covers: project overview, the 4-repo ecosystem post CLI-first pivot
(2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro
standards, English public-facing, gitflow, npm orgs, rulesets
checklist), this repo's specifics (lib-only, packages/core, cli moved
out, browser-compatible, no HTTP transport), and the standard feature
workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(claude.md): fix repo count and clarify English rule exceptions
Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5
(English public-facing): reframe as from-now-on plus list explicit
exceptions (PRD.md and CLAUDE.md stay French, existing docs stay
French until substantial rewrite) so the rule no longer contradicts
the current state of the repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: configure GitHub Packages for @focusmcp/* packages (#8)
* chore: configure GitHub Packages registry for @focusmcp/* packages
Add publishConfig with npm.pkg.github.com registry and .npmrc for
scoped package resolution. Preparation for dev package publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add SPDX header to .npmrc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mandatory tool prefix in brick manifest (#10)
* feat: add mandatory prefix field to brick manifest
Tools are exposed as {prefix}_{toolName} to prevent collisions
between bricks and protect internal tools (no prefix).
Prefix must be unique per registry, lowercase alphanumeric.
Reserved prefixes: focus, focusmcp, mcp, internal, system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for prefix coverage (registry 98%+)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add Claude Code Review action (#11)
* ci: add Claude Code Review action
* ci: use Claude Max OAuth instead of API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for OIDC auth
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14)
- actions/checkout v4 → v5
- actions/setup-node v4 → v5
- actions/upload-artifact v4 → v5
- github/codeql-action/init v3 → v4
- github/codeql-action/analyze v3 → v4
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: enforce bare tool names in manifest — prefix applied at runtime (#15)
Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search"
not "indexer_search"). The prefix is added by the runtime when exposing
tools via MCP (prefix_toolname).
- manifest.ts: reject tool names containing non-alphanumeric chars
- tool.ts: update JSDoc to reflect new convention
- Tests updated to use bare tool names throughout
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16)
* feat(marketplace): add catalog-store, catalog-fetcher, installer modules
Pure, browser-compatible marketplace management for FocusMCP core:
- catalog-store: manage catalog source URLs (CRUD, multi-marketplace)
- catalog-fetcher: fetch + aggregate catalogs from multiple sources
- installer: plan + execute brick install/remove via npm
- resolver: extend CatalogBrickSource with npm source type
All modules use dependency injection (IO interfaces) — no direct
node: imports. The CLI provides concrete implementations.
257 tests passing, 0 typecheck errors, 0 lint errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: export marketplace modules from core package entry point
Add catalog-store, catalog-fetcher, and installer exports to index.ts
so they are part of the public API and pass knip unused-export checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up knip config — remove stale entries
Remove deprecated packages/cli workspace, redundant entry patterns
(vitest.config, playwright.config), and unused playwright binary ignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared validation helpers to reduce duplication
Move requireObject, requireString, optionalString, requireArray,
requireStringArray, optionalStringArray, requireBoolean into a shared
helpers.ts module. Imported by resolver, catalog-store, and installer.
Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: update default catalog URL to raw.githubusercontent.com (#17)
Switch from gh-pages to raw GitHub content serving for the catalog.
URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add GitHub Packages publish workflow for dev channel (#18)
- Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to
GitHub Packages on push to develop (skips packages with private:true)
- Set "private": false in packages/core, sdk, validator package.json
- Add direct_prompt to claude-review.yml for inline PR review guidance
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: rename npm scope from @focusmcp to @focus-mcp (#21)
Rename all package names, workflow scopes, .npmrc registry bindings,
and source/test file references from @focusmcp/* to @focus-mcp/*.
Email addresses unchanged.
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add id-token permission for npm provenance (#22)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ci): dev publish workflow (#24)
* feat(ci): add dev publish workflow with auto-versioning
- dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev
- Auto-computed version: <base>-dev.<N> (N = commits since last tag)
- Mark packages/cli stub as private to prevent accidental publish
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rename scope @focus-mcp → @focusmcp + dev publish workflow
- Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator}
- Add dev-publish.yml for auto-versioned dev releases
- Mark packages/cli stub as private
- Update all docs, workflows, configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate pnpm-lock.yaml after scope rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use GitHub Packages for dev publish (#25)
* fix(ci): use GitHub Packages registry instead of npmjs.org
- registry-url → npm.pkg.github.com
- NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed)
- Add packages: write permission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename scope to @focus-mcp + npmjs.org for dev publish
- All packages: @focus-mcp/{core,sdk,validator}
- dev-publish.yml: registry.npmjs.org + NPM_TOKEN
- Regenerated lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate lockfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update scope references to @focus-mcp in docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add id-token:write permission + remove duplicate workflows (#26)
- Add id-token:write to dev-publish.yml (required for npm provenance)
- Remove publish-dev.yml (duplicate, was targeting GitHub Packages)
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(release): v1.0.0 + stable-publish workflow (#27)
* chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0
- Bump all 3 public packages from 0.0.0 to 1.0.0
- Add stable-publish.yml workflow (triggers on push to main → npm @latest)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove release.yml — stable-publish.yml handles releases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(ci): remove claude-review.yml (fails on workflow changes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: restore claude-review.yml — will work after main merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: v1 cleanup — README, VISION, ARCHITECTURE (#30)
* docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md
- README.md: English public-facing with install, quick start, architecture
- VISION.md: short "why" doc (1 page)
- ARCHITECTURE.md: technical doc for contributors
- AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state
- Archived pre-v1 PRD (internal French planning doc)
- npm metadata (descripti…
samuelds
added a commit
that referenced
this pull request
Apr 30, 2026
…92) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm metadata (description, keywords, author, homepage) on sdk and validator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: consolidate AGENTS.md + AI transparency - merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec) - add AI-assisted development section to README - add AI-assisted contributions section to CONTRIBUTING Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ci): allow `release` commit type in commitlint Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum. Add `release` as a valid type since we already use it on main branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq) Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31) The v1 action deprecated direct_prompt. Without a valid trigger input, PRs got a green check but Claude never posted any review. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): add checkout step to claude-review workflow (#34) The claude-code-action needs full git history to fetch base branches. Without a preceding actions/checkout@v5 with fetch-depth: 0, the action fails with "git fetch origin develop --depth=1: exit 128". Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: sync main back into develop (release bumps) (#35) * chore: sync develop → main (#9) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * chore: sync develop → main (conflict resolution) (#13) * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: v1.0.0 — sync develop → main (#29) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: sync develop → main (workflow fix + v1 cleanup) (#33) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm metadata (de…
samuelds
added a commit
that referenced
this pull request
May 1, 2026
…stic guard) (#96) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm metadata (description, keywords, author, homepage) on sdk and validator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: consolidate AGENTS.md + AI transparency - merge CLAUDE.md into AGENTS.md (single source of truth per agents.md spec) - add AI-assisted development section to README - add AI-assisted contributions section to CONTRIBUTING Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ci): allow `release` commit type in commitlint Release commits (e.g. `release: v1.0.0`) were rejected by the type-enum. Add `release` as a valid type since we already use it on main branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(deps): pin uuid to ^14.0.0 via pnpm override (GHSA-w5hq-g745-h8pq) Transitive from @cyclonedx/cdxgen (dev only). Not in runtime bundle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): rename \`direct_prompt\` → \`prompt\` in claude-code-action (#31) The v1 action deprecated direct_prompt. Without a valid trigger input, PRs got a green check but Claude never posted any review. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): add checkout step to claude-review workflow (#34) The claude-code-action needs full git history to fetch base branches. Without a preceding actions/checkout@v5 with fetch-depth: 0, the action fails with "git fetch origin develop --depth=1: exit 128". Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: sync main back into develop (release bumps) (#35) * chore: sync develop → main (#9) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * chore: sync develop → main (conflict resolution) (#13) * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: v1.0.0 — sync develop → main (#29) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: claude <claude@localhost> * release: sync develop → main (workflow fix + v1 cleanup) (#33) * ci: run CodeQL on develop branch as well (#4) The develop ruleset requires CodeQL results before merge, but the workflow only triggered on main. PRs targeting develop were deadlocked. Add develop to push and pull_request triggers, mirroring the client repo's setup. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): split PRD into per-repo focused docs (#2) * docs(prd): split monolithic PRD into per-repo PRDs Rewrite core/PRD.md to focus solely on @focusmcp/core (lib TS): 3 piliers, manifest, SDK, validator, CLI, marketplace client, brick loader. Reflects current architecture (core in WebView, Tauri sole HTTP gateway). Companion PRDs added in client/ and marketplace/ repos. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): address Copilot review feedback - Replace cross-repo relative links with absolute GitHub URLs - Clarify monorepo layout: package `packages/core`, not `core/` - Manifest naming: parser only enforces kebab-case; the `focus-` prefix is a marketplace convention - Validator section: list only checks actually implemented; defer namespace/dependency/bypass checks to P1 - Stack and Decisions tables: replace "Zod / JSON Schema" with "custom validator (parseManifest)"; JSON Schema is used only for tools[].inputSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(prd): add SPDX headers for REUSE compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): add brick loader with abstract source (#3) * feat(core): add brick loader with abstract source `loadBricks({ source })` reads installed bricks via an abstract `BrickSource` (list/readManifest/loadModule), validates manifests with `parseManifest`, ensures the loaded module exports a Brick whose manifest matches the source declaration, and collects per-brick failures without aborting the load. Browser-compatible: no direct FS access — the source is injected by the host (Tauri commands for desktop, in-memory for tests). 11 tests, 100% line / 94.4% branch coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): tighten brick-loader validation per review - Parse the module-provided brick.manifest with `parseManifest` and enforce strict equality against the source manifest (canonical JSON comparison). Catches divergence in deps/tools, not just name. - Split default-export check into two messages: missing default vs default-not-an-object (clearer diagnostics for `default: 42` etc.). - Move the manifest-shape check out of the Brick contract assertion so malformed `brick.manifest` produces an INVALID_MANIFEST error rather than a misleading "does not implement Brick contract". 3 new tests (divergence, malformed module manifest, default-not-object). 14 tests total, 100% line / 96.3% branch coverage on the loader. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(core): marketplace resolver (parse + find + semver + updates) (#5) * feat(core): add marketplace resolver (parse + find + semver + updates) Pure, browser-compatible module that consumes a catalog.json as published by the FocusMCP marketplace. Does no I/O — the host injects raw JSON and this module validates, normalizes and queries it. Public API: - parseCatalog(raw): Catalog — structural validation aligned with the published JSON Schema (kebab-case names, semver versions, typed source variants). - findBrick(catalog, name): CatalogBrick | undefined - compareSemver(a, b): -1 | 0 | 1 — minimal inline implementation (core + optional pre-release; no build metadata, no range matching yet — added at need). - listUpdates(installed, catalog): UpdateInfo[] 20 unit tests (parseCatalog, findBrick, compareSemver incl. pre-release ordering per semver §11, listUpdates). Full suite: 127 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(core): address Copilot review on marketplace resolver - Thread a full `loc` path into requireString/optionalString and the array variants so validation errors now produce "bricks[3].owner.email must be a string" instead of just "email must be a string". Much easier to diagnose. - Tighten SEMVER regex: reject numeric pre-release identifiers with leading zeros (per semver 2.0 §9). - Extend SEMVER regex to accept optional build metadata ("+..."), matching the manifest parser and semver 2.0 §10. Build metadata is captured but discarded for precedence comparisons, as required. - parseTool: use conditional spread for `inputSchema` instead of emitting `inputSchema: undefined` when the field is missing. Consistent with how other optionals are handled in this file and compatible with `exactOptionalPropertyTypes`. - Add 2 tests: build metadata is ignored when comparing; compareSemver rejects pre-release identifiers with leading zeros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: migrate indent from 2 to 4 spaces (#6) * style: migrate indent from 2 to 4 spaces (Biome config) Standardize indentation to 4 spaces across all projects. Biome formatter config updated accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update .editorconfig indent_size to 4 Aligns with Biome formatter config to prevent editor/formatter conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align biome schema to 2.4.11 and format new develop files Update $schema version to match installed Biome CLI. Reformat brick-loader and marketplace resolver (merged from develop). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add CLAUDE.md (agent guidance, replaces ~/.claude memory) (#7) * docs: add CLAUDE.md capturing the post-pivot agent guidance Replaces the former personal memory system under ~/.claude/projects/**/memory/ with an in-repo, version-controlled file that is auto-loaded by Claude Code (and any agents.md-compatible tool). Covers: project overview, the 4-repo ecosystem post CLI-first pivot (2026-04-16), the 8 non-negotiable conventions (TDD, strict scope, pro standards, English public-facing, gitflow, npm orgs, rulesets checklist), this repo's specifics (lib-only, packages/core, cli moved out, browser-compatible, no HTTP transport), and the standard feature workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(claude.md): fix repo count and clarify English rule exceptions Heading: 3 repos actifs + 1 archivé (table listed 3 not 4). Rule #5 (English public-facing): reframe as from-now-on plus list explicit exceptions (PRD.md and CLAUDE.md stay French, existing docs stay French until substantial rewrite) so the rule no longer contradicts the current state of the repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: configure GitHub Packages for @focusmcp/* packages (#8) * chore: configure GitHub Packages registry for @focusmcp/* packages Add publishConfig with npm.pkg.github.com registry and .npmrc for scoped package resolution. Preparation for dev package publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add SPDX header to .npmrc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: mandatory tool prefix in brick manifest (#10) * feat: add mandatory prefix field to brick manifest Tools are exposed as {prefix}_{toolName} to prevent collisions between bricks and protect internal tools (no prefix). Prefix must be unique per registry, lowercase alphanumeric. Reserved prefixes: focus, focusmcp, mcp, internal, system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add edge case tests for prefix coverage (registry 98%+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Claude Code Review action (#11) * ci: add Claude Code Review action * ci: use Claude Max OAuth instead of API key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for OIDC auth --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(ci): bump GitHub Actions to v5 (Node.js 24) (#14) - actions/checkout v4 → v5 - actions/setup-node v4 → v5 - actions/upload-artifact v4 → v5 - github/codeql-action/init v3 → v4 - github/codeql-action/analyze v3 → v4 Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enforce bare tool names in manifest — prefix applied at runtime (#15) Tool names in mcp-brick.json must now be bare alphanumeric (e.g. "search" not "indexer_search"). The prefix is added by the runtime when exposing tools via MCP (prefix_toolname). - manifest.ts: reject tool names containing non-alphanumeric chars - tool.ts: update JSDoc to reflect new convention - Tests updated to use bare tool names throughout Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(marketplace): add catalog-store, catalog-fetcher, installer modules (#16) * feat(marketplace): add catalog-store, catalog-fetcher, installer modules Pure, browser-compatible marketplace management for FocusMCP core: - catalog-store: manage catalog source URLs (CRUD, multi-marketplace) - catalog-fetcher: fetch + aggregate catalogs from multiple sources - installer: plan + execute brick install/remove via npm - resolver: extend CatalogBrickSource with npm source type All modules use dependency injection (IO interfaces) — no direct node: imports. The CLI provides concrete implementations. 257 tests passing, 0 typecheck errors, 0 lint errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export marketplace modules from core package entry point Add catalog-store, catalog-fetcher, and installer exports to index.ts so they are part of the public API and pass knip unused-export checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: clean up knip config — remove stale entries Remove deprecated packages/cli workspace, redundant entry patterns (vitest.config, playwright.config), and unused playwright binary ignore. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract shared validation helpers to reduce duplication Move requireObject, requireString, optionalString, requireArray, requireStringArray, optionalStringArray, requireBoolean into a shared helpers.ts module. Imported by resolver, catalog-store, and installer. Reduces jscpd duplication from 1.85% to 0.36% (threshold: 1%). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: update default catalog URL to raw.githubusercontent.com (#17) Switch from gh-pages to raw GitHub content serving for the catalog. URL: https://raw.githubusercontent.com/focus-mcp/marketplace/develop/publish/catalog.json Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add GitHub Packages publish workflow for dev channel (#18) - Add .github/workflows/publish-dev.yml: publishes @focusmcp/* to GitHub Packages on push to develop (skips packages with private:true) - Set "private": false in packages/core, sdk, validator package.json - Add direct_prompt to claude-review.yml for inline PR review guidance Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: rename npm scope from @focusmcp to @focus-mcp (#21) Rename all package names, workflow scopes, .npmrc registry bindings, and source/test file references from @focusmcp/* to @focus-mcp/*. Email addresses unchanged. Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add id-token permission for npm provenance (#22) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): dev publish workflow (#24) * feat(ci): add dev publish workflow with auto-versioning - dev-publish.yml: publish @focus-mcp/{core,sdk,validator} to npmjs.org with --tag dev - Auto-computed version: <base>-dev.<N> (N = commits since last tag) - Mark packages/cli stub as private to prevent accidental publish Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: rename scope @focus-mcp → @focusmcp + dev publish workflow - Rename @focus-mcp/{core,sdk,validator} → @focusmcp/{core,sdk,validator} - Add dev-publish.yml for auto-versioned dev releases - Mark packages/cli stub as private - Update all docs, workflows, configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate pnpm-lock.yaml after scope rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub Packages for dev publish (#25) * fix(ci): use GitHub Packages registry instead of npmjs.org - registry-url → npm.pkg.github.com - NODE_AUTH_TOKEN → GITHUB_TOKEN (no separate secret needed) - Add packages: write permission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename scope to @focus-mcp + npmjs.org for dev publish - All packages: @focus-mcp/{core,sdk,validator} - dev-publish.yml: registry.npmjs.org + NPM_TOKEN - Regenerated lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update scope references to @focus-mcp in docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): add id-token:write permission + remove duplicate workflows (#26) - Add id-token:write to dev-publish.yml (required for npm provenance) - Remove publish-dev.yml (duplicate, was targeting GitHub Packages) Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(release): v1.0.0 + stable-publish workflow (#27) * chore(release): bump @focus-mcp/{core,sdk,validator} to 1.0.0 - Bump all 3 public packages from 0.0.0 to 1.0.0 - Add stable-publish.yml workflow (triggers on push to main → npm @latest) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove release.yml — stable-publish.yml handles releases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(ci): remove claude-review.yml (fails on workflow changes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: restore claude-review.yml — will work after main merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: claude <claude@localhost> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: v1 cleanup — README, VISION, ARCHITECTURE (#30) * docs: v1 cleanup — rewrite README, add VISION.md and ARCHITECTURE.md - README.md: English public-facing with install, quick start, architecture - VISION.md: short "why" doc (1 page) - ARCHITECTURE.md: technical doc for contributors - AGENTS.md, CONTRIBUTING.md, ROADMAP.md: updated for v1.0.0 state - Archived pre-v1 PRD (internal French planning doc) - npm …
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.
Summary
core,sdk,validator,cli) from@focusmcp/*to@focus-mcp/*package.json, allpackages/*/package.jsonpublish-dev.ymlworkflow scope to@focus-mcp.npmrcregistry binding:@focusmcp:registry→@focus-mcp:registrydefine-brick.ts,define-brick.test.ts,validate-brick.ts,validate-brick.test.ts,installer.test.ts, tsconfig files)Test plan
pnpm install— lockfile up to datepnpm test— 257 tests passed (13 test files)pnpm typecheck— 0 errorspnpm lint— 67 files checked, no fixes applied🤖 Generated with Claude Code