Skip to content

Commit 87446ca

Browse files
committed
feat(plugin): ship a PATH shim for the engine and rename skill/ to
plugin/
1 parent e7869dd commit 87446ca

19 files changed

Lines changed: 122 additions & 53 deletions

File tree

.agents/plugins/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"name": "opendoc",
99
"source": {
1010
"source": "local",
11-
"path": "./skill"
11+
"path": "./plugin"
1212
},
1313
"policy": {
1414
"installation": "AVAILABLE",

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"plugins": [
1010
{
1111
"name": "opendoc",
12-
"source": "./skill",
12+
"source": "./plugin",
1313
"description": "opendoc built from this working tree (local development).",
1414
"author": {
1515
"name": "arcships"

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
22

3-
# built skill binary (produced by scripts/build-skill.sh)
4-
skill/bin/opendoc
3+
# dev engine build (produced by scripts/build-skill.sh; the committed
4+
# plugin/bin/opendoc is a thin shim that prefers this when present)
5+
plugin/bin/opendoc-dev

AGENTS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ opendoc is a one-way sync engine that mirrors documents you've authorized in
66
Notion and Feishu into a local, read-only Markdown tree. It never writes
77
back to the source platform. The mirror is meant to be consumed directly by
88
coding agents with Grep/Glob/Read — no API calls, no credentials, no network
9-
access needed at read time. A companion plugin (`skill/`) wraps the `opendoc`
9+
access needed at read time. A companion plugin (`plugin/`) wraps the `opendoc`
1010
binary and exposes it to agents as a skill. The one package is dual-manifested —
1111
`.claude-plugin/plugin.json` for Claude Code, `.codex-plugin/plugin.json` for
1212
Codex — and is distributed only through plugin marketplaces: end users install
1313
from the separate catalog repo `arcships/plugins` (git-subdir entries that
14-
sparse-fetch just `skill/`); the catalogs at THIS repo's root are the dev-only
14+
sparse-fetch just `plugin/`); the catalogs at THIS repo's root are the dev-only
1515
`arcships-dev` marketplace, installing from the local working tree.
1616

1717
## Repo layout
@@ -37,7 +37,7 @@ sparse-fetch just `skill/`); the catalogs at THIS repo's root are the dev-only
3737
```
3838
go build ./...
3939
go test ./...
40-
./scripts/build-skill.sh # builds the engine binary into the plugin package (skill/bin/opendoc)
40+
./scripts/build-skill.sh # builds the dev engine binary (plugin/bin/opendoc-dev)
4141
```
4242

4343
## Key invariants
@@ -57,7 +57,7 @@ go test ./...
5757
`3` = `ExitNotInitialized`) are part of the tool's contract. Don't rename
5858
or renumber them — scripts and docs key off the literal strings.
5959

60-
## `skill/skills/opendoc/SKILL.md`
60+
## `plugin/skills/opendoc/SKILL.md`
6161

6262
This file is runtime instructions read by coding agents, not just
6363
documentation. Its semantics (what it tells an agent to do, and when) must

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Every `.md` carries YAML frontmatter: `id` (platform-stable ID), `source`, `type
7373

7474
## Getting started
7575

76-
opendoc is distributed as an agent plugin — one package, dual-manifested for both agents — via the [arcships/plugins](https://github.com/arcships/plugins) marketplace catalog. Installing sparse-fetches only the plugin package (`skill/`), never this repo's source tree. Then just start using it (e.g. ask the agent to search your notes; it will walk you through first-time setup). The engine binary isn't committed; on first use the skill notices it's missing and, with your OK, downloads the platform build (`opendoc-<os>-<arch>`) from GitHub releases, verified by sha256.
76+
opendoc is distributed as an agent plugin — one package, dual-manifested for both agents — via the [arcships/plugins](https://github.com/arcships/plugins) marketplace catalog. Installing sparse-fetches only the plugin package (`plugin/`), never this repo's source tree. Then just start using it (e.g. ask the agent to search your notes; it will walk you through first-time setup). The engine binary isn't committed; on first use the skill notices it's missing and, with your OK, downloads the platform build (`opendoc-<os>-<arch>`) from GitHub releases, verified by sha256.
7777

7878
**Claude Code** — inside a `claude` session:
7979

@@ -92,7 +92,7 @@ codex plugin add opendoc@arcships
9292
**For development** — build the engine from source and install the plugin from your working tree (this repo carries a dev-only catalog named `arcships-dev` so it can't collide with the real marketplace):
9393

9494
```bash
95-
./scripts/build-skill.sh # builds skill/bin/opendoc
95+
./scripts/build-skill.sh # builds plugin/bin/opendoc-dev
9696
claude plugin marketplace add "$(pwd)" # or: codex plugin marketplace add "$(pwd)"
9797
# then install opendoc@arcships-dev
9898
```
@@ -131,7 +131,7 @@ Exit codes are deterministic, output is structured, and the commands never promp
131131
- [docs/dev/README.md](docs/dev/README.md) — contributor onboarding: build, test, and where to start.
132132
- [docs/dev/testing.md](docs/dev/testing.md) — how tests are organized, mock patterns, fixture red lines.
133133
- [docs/notion-properties-mapping.md](docs/notion-properties-mapping.md) — Notion properties → frontmatter mapping.
134-
- [skill/skills/opendoc/SKILL.md](skill/skills/opendoc/SKILL.md) — the Agent Skill guide that ships in the plugin.
134+
- [plugin/skills/opendoc/SKILL.md](plugin/skills/opendoc/SKILL.md) — the Agent Skill guide that ships in the plugin.
135135

136136
## License
137137

README.zh-CN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ opendoc 是"搬运工 + 图书管理员",不是操作器:只负责把线上
7373

7474
## 快速开始
7575

76-
opendoc 以 agent plugin 的形式分发——同一个包,双 manifest 同时适配两家 agent——通过 [arcships/plugins](https://github.com/arcships/plugins) marketplace 目录仓安装。安装时只会稀疏拉取 plugin 包(`skill/`),本仓库的源码不会落到用户机器上。装好后直接使用(例如让 agent 搜索你的笔记,首次使用它会引导你完成初始化)。引擎二进制不随仓库提交;skill 首次使用时会发现二进制缺失,在征得你同意后从 GitHub releases 下载对应平台的构建(`opendoc-<os>-<arch>`),并做 sha256 校验。
76+
opendoc 以 agent plugin 的形式分发——同一个包,双 manifest 同时适配两家 agent——通过 [arcships/plugins](https://github.com/arcships/plugins) marketplace 目录仓安装。安装时只会稀疏拉取 plugin 包(`plugin/`),本仓库的源码不会落到用户机器上。装好后直接使用(例如让 agent 搜索你的笔记,首次使用它会引导你完成初始化)。引擎二进制不随仓库提交;skill 首次使用时会发现二进制缺失,在征得你同意后从 GitHub releases 下载对应平台的构建(`opendoc-<os>-<arch>`),并做 sha256 校验。
7777

7878
**Claude Code**——在 `claude` 会话内:
7979

@@ -92,7 +92,7 @@ codex plugin add opendoc@arcships
9292
**开发者**——从源码构建引擎,并把本地工作树作为 marketplace 安装(本仓库自带一份名为 `arcships-dev` 的开发目录,不会与正式 marketplace 重名冲突):
9393

9494
```bash
95-
./scripts/build-skill.sh # 构建 skill/bin/opendoc
95+
./scripts/build-skill.sh # 构建 plugin/bin/opendoc-dev
9696
claude plugin marketplace add "$(pwd)" # 或:codex plugin marketplace add "$(pwd)"
9797
# 然后安装 opendoc@arcships-dev
9898
```
@@ -131,7 +131,7 @@ opendoc sync # 首轮全量镜像,之后增量
131131
- [docs/dev/README.md](docs/dev/README.md) — contributor 上手:构建、测试、从哪开始。
132132
- [docs/dev/testing.md](docs/dev/testing.md) — 测试如何组织、mock 模式、fixture 红线。
133133
- [docs/notion-properties-mapping.md](docs/notion-properties-mapping.md) — Notion properties → frontmatter 映射。
134-
- [skill/skills/opendoc/SKILL.md](skill/skills/opendoc/SKILL.md) — plugin 内附带的 Agent Skill 引导。
134+
- [plugin/skills/opendoc/SKILL.md](plugin/skills/opendoc/SKILL.md) — plugin 内附带的 Agent Skill 引导。
135135

136136
## 许可证
137137

docs/dev/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ go build ./...
1212
go test ./... # all green means the environment is ready, ~20 seconds
1313
```
1414

15-
Runtime credentials are only needed if you want to actually run a sync (not required): Feishu authorization (`opendoc init` walks you through it — the embedded engine handles QR-code app creation + login, zero install) and a Notion integration token. See [skill/skills/opendoc/references/setup.md](../../skill/skills/opendoc/references/setup.md) for the configuration flow — that's a script written for an agent to guide a user through, but a human can follow it too.
15+
Runtime credentials are only needed if you want to actually run a sync (not required): Feishu authorization (`opendoc init` walks you through it — the embedded engine handles QR-code app creation + login, zero install) and a Notion integration token. See [plugin/skills/opendoc/references/setup.md](../../plugin/skills/opendoc/references/setup.md) for the configuration flow — that's a script written for an agent to guide a user through, but a human can follow it too.
1616

1717
## Running Locally (Without Polluting Your Real Mirror)
1818

@@ -29,7 +29,7 @@ go run ./cmd/opendoc --root /tmp/opendoc-dev sync
2929
1. Root [README](../../README.md) — why the project exists, the product shape, design principles.
3030
2. [architecture.md](architecture.md) — the layering, the Adapter contract, the full flow of one sync, what each package is responsible for. **Required reading before changing code — this is the spec of record.**
3131
3. [testing.md](testing.md) — how tests are organized, mock patterns, fixture red lines. **Required reading before writing tests.**
32-
4. Reference material (consult as needed): [notion-properties-mapping.md](../notion-properties-mapping.md) — how Notion database properties land in frontmatter; [skill/skills/opendoc/references/degradation-tags.md](../../skill/skills/opendoc/references/degradation-tags.md) — the exact degradation markers the engine emits.
32+
4. Reference material (consult as needed): [notion-properties-mapping.md](../notion-properties-mapping.md) — how Notion database properties land in frontmatter; [plugin/skills/opendoc/references/degradation-tags.md](../../plugin/skills/opendoc/references/degradation-tags.md) — the exact degradation markers the engine emits.
3333

3434
## Repository Map
3535

@@ -47,7 +47,7 @@ internal/
4747
├── frontmatter/ Hand-rolled frontmatter rendering (deterministic key order)
4848
├── config/ config.toml + .internal/env fallback
4949
└── ratelimit/ Token bucket + backoff
50-
skill/ Claude Code plugin package (SKILL.md + bin/opendoc + references/)
50+
plugin/ plugin package for Claude Code + Codex (SKILL.md + bin/ shim + references/)
5151
scripts/ build-skill.sh (build + install the plugin, see the comment header in the file)
5252
docs/ Reference material + this directory (evergreen docs only)
5353
```

docs/dev/architecture.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ The leaf-to-directory conversion that happens the first time a leaf gains a chil
7474
- **content_hash is always computed on the "fetched original"** (before degradation, before rewriting), so degraded output and link rewriting never make a document look "dirty." Link rewriting also deliberately doesn't write back the hash.
7575
- **All disk writes go through `atomicWrite`** (temp + fsync + rename); readers never see a half-written file.
7676
- **Frontmatter is hand-rendered** (no YAML library), guaranteeing deterministic key order and byte-for-byte stability across runs ([frontmatter.go](../../internal/frontmatter/frontmatter.go)).
77-
- **The degradation contract: loss is never silent.** Conversion is inevitably lossy (whiteboards, embedded tables, oversized pages, unsupported blocks), but every degraded resource block must land at least two of three things: **readable degraded content, a drillable ID, and a jumpable online link** — and every degradation increments a counter in the sync report. An unknown block is preserved as its verbatim tag rather than dropped. The exact marker shapes the engine emits, per platform, are catalogued in [skill/skills/opendoc/references/degradation-tags.md](../../skill/skills/opendoc/references/degradation-tags.md) (that file ships in the plugin and must stay in sync with the emitters in `internal/feishu/degrade.go` / `internal/notion/degrade.go`).
77+
- **The degradation contract: loss is never silent.** Conversion is inevitably lossy (whiteboards, embedded tables, oversized pages, unsupported blocks), but every degraded resource block must land at least two of three things: **readable degraded content, a drillable ID, and a jumpable online link** — and every degradation increments a counter in the sync report. An unknown block is preserved as its verbatim tag rather than dropped. The exact marker shapes the engine emits, per platform, are catalogued in [plugin/skills/opendoc/references/degradation-tags.md](../../plugin/skills/opendoc/references/degradation-tags.md) (that file ships in the plugin and must stay in sync with the emitters in `internal/feishu/degrade.go` / `internal/notion/degrade.go`).
7878

7979
## manifest.sqlite (`internal/manifest/`)
8080

@@ -157,7 +157,7 @@ The **doctor probes** are the foundation of onboarding: the structured failure c
157157
## Build and Distribution
158158

159159
```bash
160-
./scripts/build-skill.sh # CGO_ENABLED=0 static build → skill/bin/opendoc
160+
./scripts/build-skill.sh # CGO_ENABLED=0 static build → plugin/bin/opendoc-dev
161161
```
162162

163-
`skill/` is the plugin root, dual-manifested for both supported agents: `.claude-plugin/plugin.json` (Claude Code) + `.codex-plugin/plugin.json` (Codex) + `bin/opendoc` + the skill itself under `skills/opendoc/` (`SKILL.md` + `references/` + `scripts/`). Distribution is marketplace-only, via the separate catalog repo `arcships/plugins`, whose entries use `git-subdir` sources pointing at this repo's `skill/` — installs sparse-fetch only that directory, never the Go source. The catalogs at this repo's root (`.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`) are the dev-only `arcships-dev` marketplace that installs from the local working tree. End users get the engine binary via the bundled `scripts/download-binary.sh`, which installs it at the stable, plugin-independent path `~/.opendoc/bin/opendoc` — plugin directories are unsuitable as the binary's home because hosts variously re-provision them per session (Claude desktop app), mount them read-only (claude.ai cloud), or version-stamp them so each update starts from a clean package (Codex, Claude Code CLI cache). SKILL.md's invocation contract: `command -v opendoc` first (a dev checkout's plugin `bin/` is on the Claude Code CLI's PATH), falling back to `~/.opendoc/bin/opendoc`. Unattended launchd invokes the binary via an absolute path — the stable path keeps the plist valid across plugin updates (see [skill/skills/opendoc/references/launchd/](../../skill/skills/opendoc/references/launchd/)).
163+
`plugin/` is the plugin root, dual-manifested for both supported agents: `.claude-plugin/plugin.json` (Claude Code) + `.codex-plugin/plugin.json` (Codex) + `bin/` + the skill itself under `skills/opendoc/` (`SKILL.md` + `references/` + `scripts/`). Distribution is marketplace-only, via the separate catalog repo `arcships/plugins`, whose entries use `git-subdir` sources pointing at this repo's `plugin/` — installs sparse-fetch only that directory, never the Go source. The catalogs at this repo's root (`.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`) are the dev-only `arcships-dev` marketplace that installs from the local working tree. End users get the engine binary via the bundled `scripts/download-binary.sh`, which installs it at the stable, plugin-independent path `~/.opendoc/bin/opendoc` — plugin directories are unsuitable as the binary's home because hosts variously re-provision them per session (Claude desktop app), mount them read-only (claude.ai cloud), or version-stamp them so each update starts from a clean package (Codex, Claude Code CLI cache). What `bin/` ships instead is a committed shim (`bin/opendoc`) that execs, in order: `$OPENDOC_ENGINE` → `bin/opendoc-dev` (the gitignored dev build) → `~/.opendoc/bin/opendoc`. Claude Code puts an enabled plugin's `bin/` on the Bash tool's PATH, so bare `opendoc` works there; Codex has no PATH mechanism, so SKILL.md's invocation contract resolves the path without ever exiting non-zero: `OPENDOC=$(command -v opendoc || echo "$HOME/.opendoc/bin/opendoc")`. Unattended launchd invokes the binary via an absolute path — the stable path keeps the plist valid across plugin updates (see [plugin/skills/opendoc/references/launchd/](../../plugin/skills/opendoc/references/launchd/)).

internal/cli/init.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818

1919
// feishuAuthScopes is the read-only scope set the mirror needs, verified live on
2020
// the reference app (kept in sync with the scope table in
21-
// skill/references/setup.md). offline_access is required for the refresh token
21+
// plugin/skills/opendoc/references/setup.md). offline_access is required for the
22+
// refresh token
2223
// that keeps unattended syncs alive; everything else is read-only.
2324
const feishuAuthScopes = "offline_access " +
2425
"docx:document:readonly docs:document.content:read docs:document.media:download " +

0 commit comments

Comments
 (0)