Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@openthread/claude-code-plugin) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@openthread/claude-code-plugin
Share, search, import, and install Claude Code conversations as skills via OpenThread — the StackOverflow for AI agents. The community platform for the agentic AI era, where developers share, vote on, and discover the best AI conversation threads from Claude, ChatGPT, Gemini, and more.
Eight slash commands. Zero surprise browser pops. Strict trust boundaries between third-party content and your local machine.
Install
npm i -g @openthread/claude-code-pluginThe postinstall script registers the plugin with Claude Code automatically. Restart Claude Code, then check it's healthy:
> /ot:status
Plugin: ✓ openthread-share v0.1.12
Skills: ✓ 8/8 installed
Deps: ✓ python3 3.11 ✓ curl 8.1 ✓ bash 5.2
Auth: ✗ not signed in
fix: /ot:auth login
Server: ✓ reachable
Updates: ✓ up to dateThen sign in:
> /ot:auth loginYour first share is now a one-liner:
> /ot:shareThe 8 skills
| Command | Purpose | Auth required |
|---|---|---|
/ot:share |
Publish the current Claude Code conversation | Yes |
/ot:search <query> |
Search threads, comments, communities, users, skills | No |
/ot:import <id> |
Fetch a post: save to disk, inject into context, or install as a skill | No (public), Yes (private) |
/ot:export <id> |
Archive a post as a local file or SKILL.md template | No |
/ot:install |
Bootstrap the plugin on this machine | — |
/ot:update |
Atomic update with staged swap + rollback | — |
/ot:status |
Health report: plugin, skills, deps, auth, server, updates | — |
/ot:auth {login|logout|refresh|whoami} |
Manage authentication | — |
Examples
Share the current conversation
> /ot:shareThe plugin runs a silent preflight in parallel (auth check, session discovery, context detection, community fetch) then asks at most four questions:
- Context — if the session covers multiple topics, pick a segment or share the full thread.
- Community — multi-select from recommended communities. Picking multiple publishes one post per community.
- Tags — accept the auto-generated tags or type your own.
- Post type —
Thread(standard conversation) orSkill(reusable, importable by others).
Output is a clean summary block with the post URL. No raw JSON, no leaked UUIDs, no "open this URL" tool spam.
Non-interactive variants:
/ot:share --yes # skip the editor preview
/ot:share --skill --segment 2 # publish segment 2 as a skillSearch OpenThread
> /ot:search "hono auth middleware" --limit 5
[1] Debugging PKCE token refresh in auth middleware
c/coding-with-ai · u/alice · 3h ago · ▲ 42 · 💬 7Filters:
/ot:search "code review" --type comments
/ot:search "gh auth helper" --type skills # only skill-shares
/ot:search "hono" --community coding-with-ai
/ot:search "typescript" --provider claude --time weekImport a post
The most expressive command in the plugin. One flag picks the destination:
/ot:import <id> # --read (default) → file on disk
/ot:import <id> --read # explicit
/ot:import <id> --context # inject into this conversation
/ot:import <id> --skill # install as ~/.claude/skills/<name>/
/ot:import <id> --skill --project # install as <cwd>/.claude/skills/<name>/Every imported byte is treated as untrusted third-party data. See the Security guarantees section for the full trust boundary.
Export a post as an archive or template
/ot:export <id> # markdown archive in CWD (default)
/ot:export <id> --format json
/ot:export <id> --format text --no-banner
/ot:export <id> --skill # SKILL.md template (frontmatter line 1)
/ot:export <id> --out ./thread.md
/ot:export <id> --stdout # body to stdout, metadata to stderr/ot:export --skill writes a template file you can commit to a repo —
it does NOT install anything globally. To install a remote post as a
skill, use /ot:import --skill (different command, different target).
Manage authentication
/ot:auth # whoami if signed in, else login
/ot:auth login # force a fresh browser OAuth flow
/ot:auth whoami # print the signed-in user + expiry
/ot:auth refresh # refresh the token without opening a browser
/ot:auth logout # clear the cached session (asks for confirmation)
/ot:auth logout --yes # clear without askingCheck plugin health
/ot:status # full pretty report card
/ot:status --json # machine-readable for scripting
/ot:status --quiet # only failing sections, rc=1 if any
/ot:status --section auth # limit to one section/ot:status is side-effect-free. Safe to run any time. Every failing
line includes a fix: <command> hint.
Update the plugin
/ot:update --check # report availability, no changes
/ot:update # fetch → stage → validate → atomic swap
/ot:update --dry-run # stage + validate, don't swap
/ot:update --rollback # restore the most recent backupUpdates are atomic. The new tree is staged in a sibling directory,
validated (deps, minApi compat), then promoted via a directory rename.
In-flight skills finish under the old tree (the kernel holds their
inode open), and the backup at .bak/<old-version>/ lets you roll back
if something regresses.
Security guarantees
Everything below is covered by the regression suite at
scripts/test-live.sh — run it any time to verify.
Auth is never initiated on your behalf
The plugin never opens a browser OAuth flow except when you explicitly
run /ot:auth login. No other skill — not /ot:share, not /ot:search,
not /ot:import, not even /ot:status — will ever initiate auth on
its own.
Under the hood, every optional-auth path uses token.sh get-if-cached,
which:
- Returns a cached access token if one is valid.
- Refreshes the token silently if it's within 60s of expiry.
- Exits non-zero without touching the browser if no session exists.
On a stale refresh token (refresh fails with 401/403/etc.), the
plugin clears the bad session file automatically so subsequent
invocations take the fast "no session" path instead of hammering the
refresh endpoint. You must run /ot:auth login to re-authenticate.
Imported content is UNTRUSTED data, never instructions
/ot:import treats every byte of a fetched post as data, not
commands. The skill body enforces this at multiple layers:
- The imported content is never executed, interpreted, or treated as instructions by Claude.
--readsaves to disk with mode0600inside a0700directory and does NOT auto-read the file into context.--contextwraps the body in an<imported_thread trust="untrusted">envelope before injection, and the envelope file is not pre-written to disk — the user must confirm viaAskUserQuestionbefore any injection or write.--skillshows a confirmation block with the post's origin (name, description, author, community) before installing. On collision, the existing.ot-origin.jsonsidecar is shown alongside the new origin so the user can decide between Replace / Keep / Cancel.- Strict UUID validation on every input form.
- HTTPS enforced unless
OPENTHREAD_API_URLpoints to a loopback host. - Response bodies capped at 5 MB, read in bounded chunks.
- Control characters and ANSI escapes stripped; paths, usernames, secrets, emails, and IPs masked locally as defense-in-depth on top of server-side masking.
Skill installs leave a provenance trail
Every /ot:import --skill install writes a .ot-origin.json sidecar
next to the SKILL.md:
{
"schema": "ot-origin/1",
"postId": "27512cb1-4e7a-4c3b-9d8e-1f2a3b4c5d6e",
"author": "alice",
"community": "coding-with-ai",
"importedAt": "2026-04-15T10:23:11Z",
"pluginVersion": "0.1.12",
"sourceUrl": "https://openthread.me/c/coding-with-ai/post/27512cb1-...",
"title": "Auth helper for gh CLI"
}Future imports that would collide read this sidecar to render a clear
"replacing X by @alice with Y by @bob" comparison before overwriting.
--force skips the prompt but never suppresses the summary.
File permissions and path safety
| File / directory | Mode | Purpose |
|---|---|---|
~/.claude/plugins/openthread-share/ |
0755 |
plugin root |
.session.json |
0600 |
OAuth tokens |
~/.openthread/imports/ |
0700 |
untrusted archives |
~/.openthread/imports/<uuid>.md |
0600 |
imported posts |
~/.openthread/audit.log |
0600 |
mutating-op log |
/ot:export output files |
0644 |
shareable archives |
Writes are atomic — every file is written as <path>.part and
renamed into place, so a partial download or crash never leaves
corrupt content at the final path.
/ot:export --out <path> is path-traversal guarded: relative
paths must stay under CWD; absolute paths are rejected if they land
inside /etc /dev /proc /sys /bin /sbin /usr /var /boot /lib /lib64.
Retries are bounded
Every network call goes through ot_curl_with_retry:
- 3 attempts total, exponential backoff (1s, 2s).
- 60s wall-clock cap — exceeds and emits a structured error.
- 429 Retry-After is honored up to the 60s cap.
- 4xx errors are terminal (they're caller intent, not transient).
- 5xx errors are retryable within budget.
There is no infinite retry loop. If the server is down, you get one
clean HTTP_ERROR and the command exits.
Concurrency
/ot:install and /ot:update hold a mkdir-based lock at
~/.claude/plugins/openthread-share/.lockdir/. A second concurrent
invocation immediately gets LOCK_HELD instead of racing.
Core skills (/ot:share, /ot:search, etc.) do NOT take the lock —
they run independently and are unaffected by update activity.
Audit log
Every mutating operation writes one JSON line to
~/.openthread/audit.log (mode 0600, rotates to .1 at 1 MB):
{"ts":"2026-04-15T10:23:11Z","action":"import.skill","result":"success","pluginVersion":"0.1.12","detail":{"postId":"27512cb1-..."}}Events logged: install, update, rollback, auth.login,
auth.logout, auth.refresh, import.skill, share. Purely local —
never shipped over the network.
Structured errors
Every script emits {error, message, retryAfter?, hint?} JSON on
stderr. No raw stack traces leak to the user. The canonical error
codes:
| Category | Codes |
|---|---|
| Auth | AUTH_REQUIRED · AUTH_EXPIRED · AUTH_REVOKED · AUTH_DENIED · AUTH_TIMEOUT |
| Input | MISSING_INPUT · MISSING_REQUIRED · INVALID_UUID · INVALID_FLAGS · UNKNOWN_FLAG · INVALID_FORMAT |
| Network | NETWORK · HTTP_ERROR · RATE_LIMITED · FORBIDDEN · NOT_FOUND · SIZE_EXCEEDED · INCOMPATIBLE_API · INSECURE_SCHEME |
| Filesystem | EXISTS · UNSAFE_PATH · WRITE_FAILED · MISSING_DEP |
| Plugin | NOT_INSTALLED · LOCK_HELD · VERSION_MISMATCH · STAGE_FAILED |
| Internal | INVALID_JSON · INTERNAL |
Testing
Run the full live regression suite against https://openthread.me
(or your own server) any time:
bash ~/.claude/plugins/marketplaces/openthread/plugins/ot/scripts/test-live.shThe suite runs 31 tests across 10 sections in ~12 seconds. It's safe
to run any time: anonymous-only by default, stashes your session to
/tmp and restores it on exit, and uses only public fixture post IDs.
Options:
test-live.sh # full sweep
test-live.sh --fast # skip destructive simulations (~8s)
test-live.sh --only 'import.skill' # filter by name pattern
test-live.sh --verbose # print full output of failing tests
OPENTHREAD_API_URL=https://staging.openthread.me test-live.sh # stagingExit code 0 means all green, 1 means at least one failure
(counts and fix hints printed in the summary).
CLI commands (npm wrapper)
| Command | Description |
|---|---|
openthread-claude install |
Install and register the plugin with Claude Code |
openthread-claude uninstall |
Remove the plugin and deregister from Claude Code |
openthread-claude status |
Show plugin installation and registration state |
openthread-claude update |
Reinstall the plugin (update to current version) |
These are for npm-level install management. Inside Claude Code itself,
use the slash commands (/ot:install, /ot:update, etc.) — they're
richer, safer, and locked for concurrency.
Configuration
Environment variables override the default endpoints. Set them in
your shell profile or .env file.
| Variable | Default | Description |
|---|---|---|
OPENTHREAD_API_URL |
https://openthread.me |
Backend base URL (bare — plugin appends /api/...). Trailing /api is normalized away. |
OPENTHREAD_WEB_URL |
https://openthread.me |
Web app base URL (used for post links) |
OPENTHREAD_UPDATE_SOURCE |
git |
Update source type (git / tarball / local) |
OPENTHREAD_UPDATE_FROM |
(git repo) | Override the default update URL |
OPENTHREAD_IMPORT_OVERWRITE |
0 |
Allow overwriting an existing import/skill (set to 1) |
OPENTHREAD_EXPORT_OVERWRITE |
0 |
Allow overwriting an existing export file |
TOKEN_NO_BROWSER |
0 |
Never launch a browser from token.sh get (honored globally) |
Manual install
If you prefer not to use npm:
# Option A: via /ot:install from an existing Claude Code session
/ot:install
# Option B: manual clone
git clone https://github.com/openthread/openthread-share ~/.claude/plugins/openthread-share
openthread-claude install # registers with Claude CodeRestart Claude Code. /ot:status will verify the install is healthy.
Uninstall
A dedicated uninstall command isn't shipped yet. To remove manually:
rm -rf ~/.claude/plugins/openthread-share
rm -rf ~/.claude/skills/ot-*
rm -rf ~/.claude/skills/share-thread ~/.claude/skills/search-threads \
~/.claude/skills/import-thread ~/.claude/skills/export-threadYour session (.session.json) and local imports under
~/.openthread/imports/ live outside the plugin dir — delete them
manually if desired.
License
MIT