Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "knapper",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"interface": {
"displayName": "Knapper"
},
"plugins": [
{
"name": "knapper",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"source": {
"source": "local",
"path": "./"
Expand Down
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://code.claude.com/schemas/marketplace.json",
"name": "knapper",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"owner": {
"name": "bearfire-dev",
"url": "https://github.com/bearfire-dev"
Expand All @@ -10,7 +10,7 @@
{
"name": "knapper",
"source": "./",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging."
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs."
}
]
}
14 changes: 7 additions & 7 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "knapper",
"version": "0.7.0-beta.1",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"version": "0.7.0",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"author": {
"name": "slate-rehm",
"url": "https://github.com/slate-rehm"
Expand All @@ -12,15 +12,15 @@
"keywords": ["obsidian", "mcp", "plugin-development", "playwright", "cdp", "automation"],
"interface": {
"displayName": "Knapper",
"shortDescription": "Develop and test Obsidian plugins in one managed session.",
"longDescription": "Drive one live Obsidian desktop session through MCP. Build and reload plugins, automate settings and other UI, and inspect console errors.",
"shortDescription": "Develop and test one Obsidian plugin in one private vault.",
"longDescription": "Open a Git-ignored vault, link one plugin, reload it, drive Obsidian windows, and read logs and eval results.",
"developerName": "Bearfire",
"category": "Developer Tools",
"capabilities": ["Obsidian automation", "Plugin development", "UI testing"],
"defaultPrompt": [
"Open an isolated Obsidian session and test this plugin.",
"Reload this Obsidian plugin and check for new errors.",
"Exercise this plugin's settings through the live UI."
"Open a Git-ignored Obsidian vault for this plugin.",
"Reload this plugin and check its logs.",
"Exercise this plugin in the Obsidian UI."
]
}
}
14 changes: 7 additions & 7 deletions .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "knapper",
"version": "0.7.0-beta.1",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"version": "0.7.0",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"author": {
"name": "slate-rehm",
"url": "https://github.com/slate-rehm"
Expand All @@ -12,15 +12,15 @@
"keywords": ["obsidian", "mcp", "plugin-development", "playwright", "cdp", "automation"],
"interface": {
"displayName": "Knapper",
"shortDescription": "Develop and test Obsidian plugins in one managed session.",
"longDescription": "Drive one live Obsidian desktop session through MCP. Build and reload plugins, automate settings and other UI, and inspect console errors.",
"shortDescription": "Develop and test one Obsidian plugin in one private vault.",
"longDescription": "Open a Git-ignored vault, link one plugin, reload it, drive Obsidian windows, and read logs and eval results.",
"developerName": "Bearfire",
"category": "Developer Tools",
"capabilities": ["Obsidian automation", "Plugin development", "UI testing"],
"defaultPrompt": [
"Open an isolated Obsidian session and test this plugin.",
"Reload this Obsidian plugin and check for new errors.",
"Exercise this plugin's settings through the live UI."
"Open a Git-ignored Obsidian vault for this plugin.",
"Reload this plugin and check its logs.",
"Exercise this plugin in the Obsidian UI."
]
}
}
14 changes: 7 additions & 7 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "knapper",
"version": "0.7.0-beta.1",
"description": "MCP server for stateless Obsidian plugin development with one managed session, browser automation, and debugging.",
"version": "0.7.0",
"description": "MCP server for Obsidian plugin development with one private vault, popout-aware UI automation, and logs.",
"author": {
"name": "slate-rehm",
"url": "https://github.com/slate-rehm"
Expand All @@ -12,15 +12,15 @@
"keywords": ["obsidian", "mcp", "plugin-development", "playwright", "cdp", "automation"],
"interface": {
"displayName": "Knapper",
"shortDescription": "Develop and test Obsidian plugins in one managed session.",
"longDescription": "Drive one live Obsidian desktop session through MCP. Build and reload plugins, automate settings and other UI, and inspect console errors.",
"shortDescription": "Develop and test one Obsidian plugin in one private vault.",
"longDescription": "Open a Git-ignored vault, link one plugin, reload it, drive Obsidian windows, and read logs and eval results.",
"developerName": "Bearfire",
"category": "Developer Tools",
"capabilities": ["Obsidian automation", "Plugin development", "UI testing"],
"defaultPrompt": [
"Open an isolated Obsidian session and test this plugin.",
"Reload this Obsidian plugin and check for new errors.",
"Exercise this plugin's settings through the live UI."
"Open a Git-ignored Obsidian vault for this plugin.",
"Reload this plugin and check its logs.",
"Exercise this plugin in the Obsidian UI."
]
}
}
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ permissions:

env:
# Obsidian is a desktop app and cannot run on a CI runner, so the live suites
# (scripts/acceptance.mjs, scripts/e2e.mjs) are deliberately not part of CI.
# (scripts/acceptance.ts, scripts/e2e.ts) are deliberately not part of CI.
# Everything here must pass without a running Obsidian.
CI: "true"

Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
run: npm run check

- name: Typecheck
run: npx tsc --noEmit
run: npm run typecheck

- name: Unit tests
run: npm test
Expand All @@ -61,7 +61,7 @@ jobs:

- name: Manifest versions agree with package.json
if: matrix.node == '22'
run: node scripts/sync-version.mjs --check
run: npm run versions:check

package:
name: package & install
Expand Down Expand Up @@ -138,7 +138,7 @@ jobs:
# contract: the server must still start, hand back a tool list, and
# answer obsidian_status with a diagnosis rather than crashing.
cd /tmp/install-test
node "$GITHUB_WORKSPACE/scripts/ci-smoke.mjs" \
"$GITHUB_WORKSPACE/node_modules/.bin/tsx" "$GITHUB_WORKSPACE/scripts/ci-smoke.ts" \
node_modules/knapper/dist/cli.js

- uses: actions/upload-artifact@v7
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ jobs:
echo "Releasing v$VERSION"

- name: Manifests agree with package.json
run: node scripts/sync-version.mjs --check
run: npm run versions:check

- name: Build
run: npm run build

- name: Degraded-mode smoke test on the build
run: node scripts/ci-smoke.mjs
run: npm run smoke

# The tarball attached below is the only distribution artifact, so packing it
# is a release gate rather than a convenience: if `npm pack` fails, no user
Expand Down
38 changes: 16 additions & 22 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,39 +62,35 @@ Four manifests describe the same plugin and **must be updated together**:

```bash
npm run check # oxfmt + oxlint (pinned vite-plus; no global install needed)
npm run typecheck # tsc --noEmit
npm run typecheck # strict TypeScript 7 check with tsgo
npm test # vitest unit tests
npm run build # tsc -> dist/
npm run build # tsgo -> dist/
npm run smoke # degraded-mode MCP check, no Obsidian required
```

Live suites create a temporary `KNAP_HOME`, a private Obsidian profile, a scratch
vault, and a dynamic CDP port. They must never use the default profile or a caller
vault. The harness proves that each selected vault is disposable before it writes
notes.
The live contract creates a temporary `KNAP_HOME`, one private Obsidian profile,
and a Git-ignored development vault. It uses a dynamic CDP port. It never uses the
default profile.

```bash
npm run acceptance # 23 checks over the critical seams
npm run e2e # 79 checks: vault round-trips, UI, telemetry, dev cycle, errors
npm run acceptance # fixed surface, CLI, UI, popouts, prompts, telemetry, errors
npm run e2e # alias for the same fixed live contract
```

```bash
npm run fence # live checks for authorized and unauthorized private windows
npm run bg-input # 6 live checks: background input without desktop focus theft
npm run workspaces # isolated instances, reconnect, scoped restart, quarantine
npm run fence # alias for the fixed live contract
npm run bg-input # alias for the fixed live contract
npm run workspaces # alias for the fixed live contract
```

Each live suite provisions one private session and tears it down. Run `npm run
workspaces` for changes to `src/session/`, `launch.ts`, the activity guard, or
process-scoping predicates.
Each live command provisions one private profile and tears it down.

`npm run check && npm run typecheck && npm test && npm run acceptance` is the
minimum before proposing a change. Run `npm run e2e` for anything touching the
router, a tool handler, or the CLI argv grammar.

`npm run fence` creates separate authorized and unauthorized private windows when
the platform supports them. `npm run bg-input` is only meaningful when Obsidian is
not the foreground window. Run it without clicking the private Obsidian window.
The contract verifies the exact selected vault, main and popout routing, window
attribution in logs, and real input.

## Conventions that matter

Expand Down Expand Up @@ -132,7 +128,7 @@ registered is not consent. `obsidian_link_plugin` and `obsidian_setup_vault` bot
this: they wrote into `<vault>/.obsidian` of any vault Obsidian happened to know
about, including the user's own. `obsidian_setup_vault` was worse for being fenced
only _by accident_ — a `plugins:restrict` call happened to run first and throw, so
reordering two lines would have reopened it. `fence-live.mjs` now asserts both
reordering two lines would have reopened it. `fence-live.ts` now asserts both
refuse. The two remaining `findVault` callers are deliberate: `vaultAutomationState`
is read-only diagnostics that must be able to inspect an unauthorized vault to
explain the refusal, and `obsidian_remove_vault` is guarded by
Expand Down Expand Up @@ -197,11 +193,9 @@ second copy. Bump both together.
- Delegate independent workstreams to parallel subagents with **strict file
ownership**, since they share one working tree. Overlapping edits corrupt each
other. Follow implementation with an audit subagent that runs `npm run check`.
- Open one isolated session with `obsidian_session_open`. Operational tools use the
active target and do not accept caller-owned handles.
- Open one private profile with `obsidian_open`. Operational tools use the active
target and do not accept caller-owned handles.
- On `KNAPPER_BUSY`, inspect `obsidian_status` and retry after the reported activity
window or after the current owner releases the session.
- `npm run bg-input` stays serialized regardless: it depends on Obsidian not being the
foreground window, which is one global property of the desktop, and it fails open.
- Use mermaid flowcharts to explain architecture in plans.
- Build the big shapes first, then refine. Be specific and precise.
Loading