Commit graph

472 commits

Author SHA1 Message Date
Aaron Bockelie
3c19cf5d2f docs: record single-branch release strategy (ADR-300), retire defunct PR submission guidance
Obsidian moved community-plugin submission from the obsidian-releases PR
process to the community.obsidian.md developer portal. Replace the now-defunct
'I'm Not Dead Yet' PR-refresh procedure in CLAUDE.md with the portal-based
distribution reality (stable Latest release matching manifest, no v-prefix,
versions.json, make promote).

ADR-300 records the decision to keep a single main branch with the existing
prerelease/promote flow rather than add a dedicated Obsidian release branch:
for 'same code, slower cadence' the prerelease/promote gap already provides
the decoupling, and a release branch would only add permanent version-file
divergence. Rejected alternatives documented.
2026-05-16 11:39:27 -05:00
Aaron Bockelie
b5d0c7c67f
Merge pull request #160 from aaronsb/fix/dataview-luxon-and-lint-compat
fix(dataview): Luxon dates, error propagation, and obsidianmd 0.3.0 compat (v0.11.21)
2026-05-15 15:03:14 -05:00
Aaron Bockelie
3608eac37b chore(release): bump version to 0.11.21 and raise minAppVersion to 1.6.6
The plugin code already calls APIs introduced in newer Obsidian versions
(FileManager.trashFile in 1.6.6, Workspace.getLeaf in 0.16.0,
Vault.createFolder in 1.4.0, ToggleComponent.setDisabled in 1.2.3,
AbstractTextComponent.setDisabled in 1.2.3, ButtonComponent.setTooltip
in 1.1.0). Declaring minAppVersion 0.15.0 was misleading — anyone
between 0.15 and 1.5 would have the plugin load and then crash at
runtime. Set the floor to 1.6.6 to match the highest API actually used.
2026-05-15 15:01:29 -05:00
Aaron Bockelie
f618cffcca chore(lint): support eslint-plugin-obsidianmd@0.3.0
The 0.3.0 release adds typed rules (no-plugin-as-component, no-view-references-in-plugin,
no-unsupported-api, prefer-file-manager-trash-file, prefer-instanceof) and wires
them into the hybrid recommended config without a **/*.ts files scope. As a
result they also try to run against package.json — which uses ESLint's JSON
language and has no @typescript-eslint parser services — and ESLint aborts
the whole run with "Error while loading rule 'obsidianmd/no-plugin-as-component'".

- eslint.config.mts: disable those five typed rules on JSON files, and
  ignore the data files the recommended config doesn't wire a JSON parser
  for (tsconfig.json, package-lock.json, manifest.json, versions.json,
  src/config/**/*.json, .claude/**).
- Keep the autofix for `setTimeout` → `window.setTimeout` and
  `globalThis` → `window` in code paths that only run inside Obsidian
  (main.ts, main-complex.ts, worker-manager.ts, connection-pool.ts,
  tools/fetch.ts) — that's the popout-window-compat behaviour the rule
  exists to enforce.
- Revert the autofix in code paths imported by Jest tests where `window`
  is undefined: session-manager.ts and obsidian-api.ts background timers,
  semantic/router.ts active-file timeout, debug.ts console reference.
  Each retains a `// eslint-disable-next-line` with the reason
  (background timer / Node test compat).
2026-05-15 15:01:03 -05:00
Aaron Bockelie
1c6fae133f chore(deps-dev): bump eslint-plugin-obsidianmd to ^0.3.0
Matches the version Dependabot is proposing in #153 so the lint-config
support below lands in main ahead of the broader dev-deps bundle merging.
2026-05-15 15:00:44 -05:00
Aaron Bockelie
b5b1ebdfd2 fix(dataview): serialize Luxon dates and propagate Dataview-internal errors
Closes #115. Closes #123.

The Dataview API returns Luxon DateTime objects (which expose toISO(), not
toISOString()) for file.ctime/mtime, so listPages() and getPageMetadata()
crashed with "toISOString is not a function" the moment any real Dataview
result reached them. The existing tests passed only because the mocks used
native Date, which exposes toISOString().

The query path had a separate envelope bug: executeQuery() hard-coded
success: true whenever dataviewAPI.query() didn't throw, while Dataview
itself returns { successful: false, error: "..." } for malformed queries
without throwing. The formatter then read response.successful (undefined),
hit the failure branch, and rendered every result as " Query failed:
Unknown error" — even when the query had succeeded.

- Add toIsoOptional() that prefers Luxon's toISO() and falls back to
  Date.toISOString(); apply at every page.file.ctime/mtime serialization
  site and inside convertDataviewValue().
- Propagate result.successful and result.error from the inner Dataview
  response to the outer tool envelope.
- Add a dataview.query normalizer case in the formatter dispatcher that
  flattens result.{type,values,headers} to the top level and maps
  success → successful, matching the formatter's contract.
- Regression tests for both bugs, including a Luxon-shaped mock distinct
  from the existing Date-shaped one.
2026-05-15 15:00:11 -05:00
Aaron Bockelie
dfa5affad7 chore: Bump version to 0.11.20 2026-05-15 13:58:25 -05:00
Aaron Bockelie
32bc611118
Merge pull request #159 from aaronsb/fix/router-pagination-param-coercion
fix(router): Numeric pagination params silently dropped (parseInt(paramStr) → paramNum)
2026-05-15 13:58:08 -05:00
Aaron Bockelie
c3ed7f6e7a fix(router): Use paramNum for numeric pagination params
Pagination was silently a no-op for the MCP-facing surface. MCP
clients send numeric arguments (page, pageSize, snippetLength) as
JSON numbers; paramStr returns undefined for non-string values, so
parseInt(paramStr(...) ?? '1') always collapsed to defaults.

Caught while live-verifying the #158 pagination work shipped in
0.11.19: a call with page=2 pageSize=50 returned page 1 pageSize 20.
The router was discarding the user's values before the call ever
reached listFilesPaginated.

Three call sites fixed in src/semantic/router.ts:
- vault.list (line 174-175)  — the immediate verifier
- vault.search (line 286-287)
- vault.search snippet length (line 306)

paramNum already existed at line 44; this commit just wires it in.
2026-05-15 13:56:52 -05:00
Aaron Bockelie
61417387d3 chore: Bump version to 0.11.19 2026-05-15 13:42:45 -05:00
Aaron Bockelie
ebb7f7faec
Merge pull request #158 from aaronsb/feat/vault-list-pagination-hint
feat(vault.list): Coherent pagination + agent next-call hints
2026-05-15 13:42:25 -05:00
Aaron Bockelie
e822d7607f fix(vault.list): Align paginated recursive sort with flat listFiles
Code-reviewer caught a real bug: listFiles (recursive flat) sorts by
full path, but listFilesPaginated in recursive mode was sorting
folders-first-then-by-basename. So the agent's "use page=2 pageSize=50"
hint anchored to a path-sorted universe, but page 2 returned a
basename-sorted universe — overlaps and gaps for any caller trying to
walk the listing.

Recursive paginated mode now sorts by full path, matching listFiles.
Level-only mode (the default for internal callers — cp recursion,
isDirectory probe) keeps the folder-first-then-basename order it
needs for tree navigation.

New test (`paged universe matches the flat listFiles universe`)
concatenates every page and asserts equality with the flat call —
exactly the agent-facing contract the formatter's next-page hint
promises.

Router comment also clarifies why root paginated stays recursive=false
(getAllLoadedFiles is already recursive — the path collapses to the
same answer either way).

Refs PR #158 review.
2026-05-15 13:40:35 -05:00
Aaron Bockelie
d3c025ada3 feat(vault.list): Coherent pagination + agent next-call hints
When the formatter truncated a directory listing with "... and N more",
the agent had no usable next call. \`page=2 pageSize=50\` would have
returned a fundamentally different shape (level-only folder entries
from listFilesPaginated, not the next 50 files from the recursive
listFiles), so any hint to "paginate" would have led the agent
somewhere broken.

This change makes pagination semantically consistent and gives the
agent an explicit, working escape hatch:

- listFilesPaginated gains a recursive flag (default false to
  preserve existing internal callers — cp at router.ts:1049 and the
  isDirectory probe at router.ts:1021 still get level-only).
- vault.list passes recursive=true when paginating with a non-root
  directory. Page N of vault.list('foo') now returns the Nth slice
  of the same flat universe vault.list('foo') (unpaginated) would
  return — no shape switch.
- The flat-list formatter, when it truncates at 50, now tells the
  agent exactly what to call next: page=2 pageSize=50, or raw:true
  for the full list in one response.
- The structured-response formatter shows "Page X of Y" and, when
  there are more pages, surfaces the next-page call with the
  current pageSize baked in.
- normalizeResponse gains a vault.list case translating the API
  response's \`type: 'file'|'folder'\` to the formatter's \`isFolder\`
  boolean. Previously the structured branch's filter on f.isFolder
  always failed silently, lumping every entry into "Files" even when
  half were folders.

Two new tests in tests/list-files-recursive.test.ts:
- recursive paginated mode produces the expected slice with no
  cross-page overlap
- recursive=false (default) preserves the level-only behavior the
  internal callers depend on

Refs #154 (extends the agent-facing fix).
2026-05-15 13:30:45 -05:00
Aaron Bockelie
6e8b6f2a3e chore: Bump version to 0.11.18 2026-05-15 13:20:32 -05:00
Aaron Bockelie
d2b083dd0f
Merge pull request #144 from laplaque/fix/system-info-http-https-ports
fix(system): report both HTTP and HTTPS ports in system info
2026-05-15 13:19:54 -05:00
Aaron Bockelie
6e65ef997b
Merge pull request #145 from laplaque/fix/fetch-web-error-formatter
fix(formatters): extract error text from content array in fetch_web response
2026-05-15 13:19:25 -05:00
Aaron Bockelie
e8798756ed
Merge pull request #157 from aaronsb/fix/vault-list-folders-154
fix(vault): Recurse listFiles into subfolders (#154)
2026-05-15 13:18:43 -05:00
Aaron Bockelie
d636fffe78 fix(vault): Recurse listFiles into subfolders (#154)
vault.list(directory) returned an empty array whenever the target
folder's direct children were all subfolders — listFiles used
folder.children and then filtered to TFile, dropping every
descendant. Meanwhile the root case used vault.getAllLoadedFiles()
which is recursive, so root and directory listings diverged.

listFiles now walks the folder tree when a directory is provided,
matching the recursive semantics of the root case. Callers that
were already iterating result paths get the same shape — full
vault-rooted paths to .md files — they just no longer hit the
empty-list cliff for folder-of-folders inputs.

The synchronous throw on missing directory is preserved; existing
try/catch sites at router.ts:538 and :1030 use it as a "does this
exist as a directory" probe.

Adds tests/list-files-recursive.test.ts with regression coverage:
- recursing through a folder of subfolders returns all descendant files
- recurses through arbitrary nesting depth
- still throws on a missing directory
- root call (no directory) still returns the full vault

Test fixtures: tests/__mocks__/obsidian.ts gained a TFolder class
and a stat field on TFile.

Closes #154.
2026-05-15 13:16:45 -05:00
Aaron Bockelie
b79ca5fdb9 chore(deps): Clear residual audit vulnerabilities + pin yaml
After merging the dependabot batch, npm audit still flagged 5
transitive vulns (4 high, 1 moderate). Running npm audit fix lifts
deep deps to patched ranges and resolves all of them — except it
tries to land yaml@2.9.0 which was published 2026-05-11, inside the
7-day hold window.

Added a package.json override to pin yaml to ~2.8.4 (published
2026-05-02, 13 days aged). 2.8.4 already fixes the CVE-flagged
"Stack Overflow via deeply nested YAML collections" (range
2.0.0-2.8.2), and we get the safety of an aged release.

Final state: 0 vulnerabilities, 127/127 tests still green, all
upgrades respect the 7-day rule.
2026-05-15 13:08:53 -05:00
Aaron Bockelie
30ab1e600b
Merge pull request #151 from aaronsb/dependabot/npm_and_yarn/hono-4.12.18
chore(deps): Bump hono from 4.12.7 to 4.12.18
2026-05-15 13:05:26 -05:00
Aaron Bockelie
3fbee05776
Merge pull request #149 from aaronsb/dependabot/npm_and_yarn/multi-7bdfbe8666
chore(deps): Bump ip-address and express-rate-limit
2026-05-15 13:04:50 -05:00
Aaron Bockelie
8d572ceb1d
Merge pull request #121 from aaronsb/dependabot/npm_and_yarn/hono/node-server-1.19.13
chore(deps): Bump @hono/node-server from 1.19.10 to 1.19.14
2026-05-15 13:04:44 -05:00
dependabot[bot]
9c604fce97
chore(deps): Bump hono from 4.12.7 to 4.12.18
Bumps [hono](https://github.com/honojs/hono) from 4.12.7 to 4.12.18.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.7...v4.12.18)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.12.18
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:03:52 +00:00
dependabot[bot]
aaadb839e2
chore(deps): Bump ip-address and express-rate-limit
Bumps [ip-address](https://github.com/beaugunderson/ip-address) and [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit). These dependencies needed to be updated together.

Updates `ip-address` from 10.1.0 to 10.2.0
- [Commits](https://github.com/beaugunderson/ip-address/commits)

Updates `express-rate-limit` from 8.3.1 to 8.5.2
- [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases)
- [Commits](https://github.com/express-rate-limit/express-rate-limit/compare/v8.3.1...v8.5.2)

---
updated-dependencies:
- dependency-name: express-rate-limit
  dependency-version: 8.5.1
  dependency-type: indirect
- dependency-name: ip-address
  dependency-version: 10.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:03:45 +00:00
dependabot[bot]
f462645b37
chore(deps): Bump @hono/node-server from 1.19.10 to 1.19.14
Bumps [@hono/node-server](https://github.com/honojs/node-server) from 1.19.10 to 1.19.14.
- [Release notes](https://github.com/honojs/node-server/releases)
- [Commits](https://github.com/honojs/node-server/compare/v1.19.10...v1.19.14)

---
updated-dependencies:
- dependency-name: "@hono/node-server"
  dependency-version: 1.19.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:03:42 +00:00
Aaron Bockelie
2695a0a199
Merge pull request #148 from aaronsb/dependabot/npm_and_yarn/production-dependencies-d2df441f24
chore(deps): Bump the production-dependencies group across 1 directory with 5 updates
2026-05-15 13:03:29 -05:00
Aaron Bockelie
190c97addc
Merge pull request #129 from aaronsb/dependabot/github_actions/actions/github-script-9
ci: Bump actions/github-script from 8 to 9
2026-05-15 13:03:16 -05:00
dependabot[bot]
b7217977eb
ci: Bump actions/github-script from 8 to 9
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:03:09 +00:00
Aaron Bockelie
5ed215e41f
Merge pull request #114 from aaronsb/dependabot/npm_and_yarn/brace-expansion-1.1.13
chore(deps-dev): Bump brace-expansion from 1.1.12 to 5.0.6
2026-05-15 13:02:56 -05:00
Aaron Bockelie
b4c5ac9967
Merge pull request #110 from aaronsb/dependabot/npm_and_yarn/flatted-3.4.2
chore(deps-dev): Bump flatted from 3.3.3 to 3.4.2
2026-05-15 13:02:45 -05:00
Aaron Bockelie
6615edecbf
Merge pull request #113 from aaronsb/dependabot/npm_and_yarn/handlebars-4.7.9
chore(deps-dev): Bump handlebars from 4.7.8 to 4.7.9
2026-05-15 13:01:55 -05:00
dependabot[bot]
71f0856c32
chore(deps): Bump the production-dependencies group across 1 directory with 5 updates
Bumps the production-dependencies group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | `1.26.0` | `1.29.0` |
| [minimatch](https://github.com/isaacs/minimatch) | `10.2.2` | `10.2.5` |
| [node-forge](https://github.com/digitalbazaar/forge) | `1.3.3` | `1.4.0` |
| [turndown](https://github.com/mixmark-io/turndown) | `7.2.2` | `7.2.4` |
| [zod](https://github.com/colinhacks/zod) | `4.3.6` | `4.4.3` |



Updates `@modelcontextprotocol/sdk` from 1.26.0 to 1.29.0
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.26.0...v1.29.0)

Updates `minimatch` from 10.2.2 to 10.2.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.2...v10.2.5)

Updates `node-forge` from 1.3.3 to 1.4.0
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.3...v1.4.0)

Updates `turndown` from 7.2.2 to 7.2.4
- [Release notes](https://github.com/mixmark-io/turndown/releases)
- [Commits](https://github.com/mixmark-io/turndown/compare/v7.2.2...v7.2.4)

Updates `zod` from 4.3.6 to 4.4.3
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.3.6...v4.4.3)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: minimatch
  dependency-version: 10.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: node-forge
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: turndown
  dependency-version: 7.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: zod
  dependency-version: 4.4.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:00:34 +00:00
dependabot[bot]
3bf6b6e9f3
chore(deps-dev): Bump handlebars from 4.7.8 to 4.7.9
Bumps [handlebars](https://github.com/handlebars-lang/handlebars.js) from 4.7.8 to 4.7.9.
- [Release notes](https://github.com/handlebars-lang/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/v4.7.9/release-notes.md)
- [Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.8...v4.7.9)

---
updated-dependencies:
- dependency-name: handlebars
  dependency-version: 4.7.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:00:19 +00:00
dependabot[bot]
73a6307d05
chore(deps-dev): Bump brace-expansion from 1.1.12 to 5.0.6
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 5.0.6.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v5.0.6)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:00:08 +00:00
dependabot[bot]
e852a5de44
chore(deps-dev): Bump flatted from 3.3.3 to 3.4.2
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 18:00:03 +00:00
Aaron Bockelie
844351f9d8 docs(claude): Add 7-day hold rule for supply-chain upgrades
npm supply-chain attacks have been spiking — compromised package
versions get published, ingested by automation, then yanked days
later. Adding a guideline that we only land upgrades to versions
≥7 days old to buy time for community discovery before pulling
fresh releases into the dependency tree.

Captured next to the existing pre-push quality checks since it's a
delivery-discipline rule, same shape as "run tests before pushing."
2026-05-15 12:59:56 -05:00
Aaron Bockelie
6705d5aeff build: Add make promote target for prerelease → stable + Latest
The release pipeline always emits prereleases (per the standing
convention in CLAUDE.md). GitHub's /releases/latest/download/<asset>
URL pattern doesn't resolve to prereleases, so the MCPB download
link in plugin Settings would always 404 without an explicit
promotion gesture.

make promote (no args) flips the current package.json version from
prerelease → stable and marks it Latest. make promote TAG=X.Y.Z
targets a specific older release. Documented inline in CLAUDE.md
next to the existing prerelease convention.

First use: just promoted 0.11.17 manually with the same gh command
this target wraps.
2026-05-15 12:46:00 -05:00
Aaron Bockelie
b26dada3e2 chore: Bump version to 0.11.17 2026-05-15 11:53:14 -05:00
Aaron Bockelie
da62e9eec6
Merge pull request #155 from aaronsb/feature/mcpb-bundle-adr102
ADR-102: MCPB bundle for Claude Desktop one-click install
2026-05-15 11:52:52 -05:00
Aaron Bockelie
e5808a3a61 fix(build): Stage mcpb/manifest.json in release commit
Without this, make release-patch leaves mcpb/manifest.json one
version behind on main after each release — sync-version updates it
on disk via the npm version hook but _commit-and-publish never
stages it. The built .mcpb artifact in CI is correct (build runs
sync-version fresh), but main drifts until the next release
self-heals.

Direct consequence of the earlier "build: Sync mcpb/manifest.json
version with package.json" commit in this PR.
2026-05-15 11:51:57 -05:00
Aaron Bockelie
5179b1d8aa feat(settings): Collapse advanced connection options under disclosure
Default view now shows only the two common paths: Claude Desktop
MCPB download and Claude Code CLI string. Everything else moves into
a <details> windowshade ("Advanced — other MCP clients, multi-vault,
custom bundles"), collapsed by default:

- JSON mcpServers block (for Cline, Continue, custom integrations,
  and multi-vault setups)
- Pointer to scripts/make-mcpb.mjs for custom-named bundles per vault

Cleaner default surface, advanced paths still one click away. The
maker-script note moved out of the MCPB section header so the
primary path is just "download, paste, install" with no asterisks.
2026-05-15 11:48:40 -05:00
Aaron Bockelie
b451331989 docs(adr-102): Align with implementation
Three drift points caught in review:

- §4 (CI publishing): drop the promised "second workflow publishes
  latest .mcpb on mcpb/ change" — it isn't implemented and isn't
  needed. Release-time publishing is the actual flow. Also documents
  the versioned + unversioned dual-emit and the stable-URL motivation.
- §2 (server.js behavior): the implementation surfaces JSON-RPC
  -32000 on session expiry and lets the client reinitialize rather
  than attempting transparent replay. ADR previously claimed replay;
  text now matches code with the rationale (replay would require
  re-issuing state-establishing notifications and that path is
  fragile).
- Consequences: acknowledge that the maker script partially exposes
  the bridge to advanced users, which slightly weakens the
  "bridge-is-invisible" framing of ADR-100/102. The audience that
  reads server.js is the same audience that would have written
  custom mcpServers JSON anyway, so the abstraction holds for the
  cohort that benefits from it.

Refs PR #155 review.
2026-05-15 11:46:19 -05:00
Aaron Bockelie
4d46014b8a fix(build): Use stable latest-release URL for MCPB download
The Settings UI was building a versioned releases/download/<version>/
URL that would 404 for any plugin version not yet associated with a
GitHub release — the exact case for users on a fresh BRAT install
running ahead of the release pipeline.

build-mcpb.mjs now emits both the versioned filename (for archival)
and an unversioned obsidian-mcp.mcpb alias. release.yml uploads both.
The Settings UI links to releases/latest/download/obsidian-mcp.mcpb,
which GitHub resolves to the most recent release's asset by name —
stable regardless of plugin version.

Makefile clean target updated to remove the new artifact.

Refs PR #155 review.
2026-05-15 11:46:06 -05:00
Aaron Bockelie
0a123d8072 fix(mcpb): Harden stdio bridge based on code review
- Serialize the first request: messages arriving while initialize is
  in flight now wait on the initialize promise before posting, so
  they don't race past the session-id capture. Latent under Claude
  Desktop (it serializes), but real defense-in-depth for any client
  that doesn't.
- AbortSignal.timeout(30s) on POSTs so an unresponsive server can't
  hang the bridge forever. Notify-stream GET stays unbounded — it's
  long-lived by design.
- DELETE the session on stdin EOF. MCP spec asks clients to retire
  sessions cleanly; previously the bridge just exited and the
  server's session lingered until idle GC.
- SSE frame splitter now tolerates CRLF (\r?\n\r?\n) in addition to
  LF, matching the spec's "newline" wording more broadly.
- startNotifyStream and the 202-Accepted path now drain (cancel)
  non-SSE bodies so the underlying sockets release immediately.

Verifies clean (npm run build + lint + 127 tests pass). Manual
end-to-end install in Claude Desktop continues to work.

Refs PR #155 review.
2026-05-15 11:45:56 -05:00
Aaron Bockelie
1093939901 fix: Correct author name to Aaron Bockelie
package.json had a stale "Aaron Blumenfeld" that propagated into
mcpb/manifest.json when sync-version ran. The top-level manifest.json
already had the right value (Aaron Bockelie, matching git author).
2026-05-15 11:45:43 -05:00
Aaron Bockelie
9ed47c1aad docs(readme): Restructure install section around MCPB
Three onboarding paths now documented in priority order: MCPB
one-click install for Claude Desktop (with cross-platform mime-type
caveat), Claude Code CLI, and the JSON config block for other MCP
clients and multi-vault setups. The maker CLI (scripts/make-mcpb.mjs)
gets its own advanced section for custom-named bundles.

Mirrors the Settings UI ordering so the README and the in-plugin
guidance tell the same story.

Refs ADR-102.
2026-05-15 11:35:10 -05:00
Aaron Bockelie
3a90a403c4 feat(settings): Restructure connection setup — MCPB primary, JSON demoted
Reorders the protocol-information section to surface the easiest
onboarding path first. New section order:

  1. Claude desktop (.mcpb — one-click install)  ← primary
  2. Claude code (CLI)                            ← unchanged
  3. Other mcp clients (JSON config)              ← demoted, renamed

The MCPB section shows a versioned download link that resolves to
the GitHub release asset and a values panel (URL + API key with
copy buttons) for the install prompt. A footnote points multi-vault
users at scripts/make-mcpb.mjs for custom-named bundles.

eslint config now ignores mcpb/ and scripts/ — they ship as zip
content and standalone Node CLIs respectively, not plugin code.

Refs ADR-102.
2026-05-15 11:34:36 -05:00
Aaron Bockelie
7f77b72b6c feat: Add scripts/make-mcpb.mjs interactive maker for advanced users
Power users with multiple Obsidian vaults (or anyone wanting a
custom-named bundle) run `node scripts/make-mcpb.mjs`, answer three
prompts (display name, URL, API key), and get an
obsidian-mcp-<slug>.mcpb with the chosen name and defaults
pre-filled. Drop it into Claude Desktop — one-click install with no
manual entry in the user_config dialog.

Shares the zip builder with scripts/build-mcpb.mjs so there's a
single source of truth for the bundle format. Zero deps; Node ≥18.

This is the path ADR-102 §6 designates for multi-vault setups,
replacing both the rejected N-slot manifest design and the
multi-variant publishing alternative.

Refs ADR-102.
2026-05-15 11:31:57 -05:00
Aaron Bockelie
7ca31c4685 build: Add make mcpb target and attach .mcpb to release assets
scripts/build-mcpb.mjs is a zero-dep pure-Node zip builder
(stored-only container, manual CRC32) that packs mcpb/manifest.json
and mcpb/server.js into obsidian-mcp-<version>.mcpb. Works on any
Node ≥18, doesn't require system zip/7z, runs identically on dev
machines and in CI.

Makefile gains a "mcpb" target so the local workflow is "make mcpb"
when iterating. clean now also removes built .mcpb files.

release.yml builds the bundle as part of the release job and attaches
it to the GitHub release alongside main.js/manifest.json/styles.css,
so BRAT users and the future plugin Settings "download" affordance
both resolve to the canonical release asset.

Refs ADR-102.
2026-05-15 11:30:50 -05:00
Aaron Bockelie
97d346070f build: Sync mcpb/manifest.json version with package.json
Extends sync-version.mjs so a single source of truth (package.json
version) drives manifest.json, mcpb/manifest.json, and version.ts in
lockstep. Future releases bump one file; the MCPB stays current
automatically.

Refs ADR-102.
2026-05-15 11:28:58 -05:00