* fix: sync package-lock.json with package.json dependencies
package-lock.json was missing @emnapi/core@1.9.2 and
@emnapi/runtime@1.9.2 (transitive deps of @emnapi/wasi-threads),
causing `npm ci` to fail on all CI jobs.
* fix: resolve remaining CI failures after TS-to-JS adapter migration
- vitest.config.ts: update adapter project include/exclude from .test.ts
to .test.{ts,js} to match converted adapter test files
- check-doc-coverage.sh: skip adapter directories containing only utility
files (prefixed with _), fixing false positive for clis/slock/
- linux-do/topic-content.test.js: fix hardcoded reference to topic.ts
(now topic.js after PR #928 migration)
* fix: clean up legacy shim files and stale tmp files on upgrade
Add cleanup steps to fetch-adapters.js that run on every version upgrade:
1. Remove legacy compat shim files from ~/.opencli/ (registry.js,
errors.js, utils.js, etc.) that were created by an older approach
using file:// re-exports. Current approach uses node_modules symlink.
Only deletes files containing "export * from 'file://" to avoid
removing user-created files.
2. Remove legacy compat shim directories (browser/, download/, errors/,
etc.) using the same safety check.
3. Clean up stale .plugins.lock.json.tmp-* files left behind by
crashed processes. These accumulate over time (108 found on one
machine) and clutter ~/.opencli/.
* fix: check every file in legacy shim directories before deleting
Instead of checking only the first file and deleting the entire
directory, now checks each file individually and only deletes files
matching the shim pattern. Directory is removed only if empty after
individual file cleanup.
- Remove mapDistToSource() from diagnostic.ts — mapped dist/clis/
paths back to clis/ but dist/clis/ no longer exists after JS-first
migration. The function always returned null.
- Simplify resolveAdapterSourcePath() to check candidates directly
without the dead dist→source mapping detour.
- Delete scripts/clean-yaml.cjs — walked dist/clis/ to delete YAML
files, but dist/clis/ no longer exists.
- Remove clean-yaml script entry from package.json.
* refactor(adapters): convert adapter layer from TypeScript to JavaScript
Core framework stays TypeScript; adapter layer moves to JS-first.
Adapters are essentially "executable config + browser scripts" that
barely use TS features — this simplifies the build/distribution pipeline
by removing the dist/clis/ intermediate compilation step.
Changes:
- Convert all 753 adapter files in clis/ from .ts to .js
- Update tsconfig to exclude clis/ from compilation
- Simplify build-manifest to scan clis/*.js directly (no dist/clis/)
- Update discovery, main, fetch-adapters to load JS adapters from clis/
- Update generate-verified to output .js artifacts
- Update package.json files field: dist/clis/ → clis/
- Fix all test files for the .ts → .js transition
* fix(main): use findPackageRoot for BUILTIN_CLIS path
The previous relative path (../../clis from __dirname) only worked for
dist/src/main.js but broke dev mode (tsx src/main.ts) where __dirname
is <repo>/src — resolving to /clis instead of <repo>/clis.
Use findPackageRoot() which works for both dev and prod paths.
* fix: avoid inserting completion config inside multi-line shell commands
The postinstall zshrc insertion logic splits backslash-continued blocks
(e.g. zinit stanzas) when it finds a compinit match inside them, which
breaks the user's shell config. Walk backward past continuation lines
so the insertion lands before the entire logical command.
* fix: append zsh completion to end of .zshrc instead of splicing
Replace the fragile compinit-searching splice logic with a simple
append, matching the strategy already used for bash. This avoids
breaking multi-line commands (e.g. zinit blocks with zicompinit).
Still detects existing compinit to avoid adding a duplicate call.
* fix: stop modifying shell rc files in postinstall
Replace the fragile .zshrc/.bashrc modification logic with a safer
approach: only write completion files and print setup instructions.
The previous approach tried to parse and splice into rc files, which
broke multi-line shell commands (e.g. zinit blocks with backslash
continuations matching /compinit/). Instead of attempting to fix the
parser, remove rc modification entirely — this matches the approach
used by rustup, homebrew, and other CLI tools.
Closes#788
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix: review follow-ups — better first-run log, OPENCLI_FETCH=1 skips version check
- Clarify first-run log message: "copying adapters (one-time setup)"
- Add comment explaining why scriptPath uses two levels of ../
- OPENCLI_FETCH=1 now bypasses version-skip to allow forced refresh
* fix: update doc-coverage script path after clis/ move
check-doc-coverage.sh still referenced src/clis/ after PR #782 moved
adapters to root clis/. This caused CI to fail with "0/1 documented".
* fix: resolve package root dynamically for symlink and first-run paths
The symlink at ~/.opencli/node_modules/@jackwener/opencli pointed to
dist/ instead of the package root in prod mode, breaking user TS CLIs
that import from '@jackwener/opencli/registry'.
The first-run scriptPath also resolved incorrectly in dev mode.
Extract findPackageRoot() that walks up to find package.json, fixing
both paths for dev (src/) and prod (dist/src/) layouts.
* refactor: move adapters from src/clis/ to root clis/ for monorepo separation
Separates CLI adapters from the core runtime to prepare for independent
adapter distribution via postinstall fetch.
Key changes:
- Move src/clis/ → clis/ (adapters at repo root)
- Change tsconfig rootDir from "src" to "." so tsc compiles both
- Create root-level shim files (registry.ts, errors.ts, etc.) so adapter
relative imports (../../registry.js) resolve correctly
- Update build-manifest.ts, main.ts paths for new dist/src/ structure
- Expand ensureUserCliCompatShims() to cover all adapter import targets
(types, utils, logger, launcher, browser/*, download/*, pipeline/*)
- Add scripts/fetch-adapters.js postinstall for ~/.opencli/clis/ sync
- Update vitest.config.ts adapter test paths
- Add package.json files field to exclude adapters from npm package
Official adapter files are unconditionally overwritten on update;
user-created files not in the manifest are preserved.
* fix: add dist/clis/ and cli-manifest.json to npm files, harden fetch-adapters
- Add dist/clis/ and dist/cli-manifest.json to package.json files field
so built-in adapters and manifest ship with the npm package
- Replace execSync with execFileSync to prevent command injection
- Add version check to skip redundant adapter fetches
- Track tmpRoot explicitly for reliable cleanup
* fix: address review blockers — manifest-based updates, global-only fetch, first-run fallback
1. Manifest-based update strategy:
- Read old manifest to identify previously-official files
- Clean up files removed upstream (in old manifest but not new)
- User-created files (never in any manifest) remain untouched
2. Only run fetch-adapters on global install (npm_config_global=true)
or explicit OPENCLI_FETCH=1, preventing heavy side effects for
local/dev installs
3. First-run fallback in discovery.ts:
- ensureUserAdapters() checks for adapter-manifest.json
- If missing and ~/.opencli/clis/ is empty, spawns fetch-adapters.js
- Guarantees adapters are available even with --ignore-scripts
* fix: remove OPENCLI_FETCH env var, use internal _OPENCLI_FIRST_RUN instead
* feat: also support OPENCLI_FETCH=1 for explicit adapter fetch trigger
* simplify: replace git clone with local copy from dist/clis/
Adapters already ship in the npm package (dist/clis/), so there's no
need to clone from GitHub. Copy directly from the installed package:
- Eliminates git, curl, tar dependencies
- No network calls in postinstall
- No timeout/offline issues
- Version always matches the installed CLI
- ~65 lines of clone/download code replaced by one cpSync loop
* feat(spotify): add Spotify playback adapter
Adds a new adapter for controlling Spotify via the official Web API.
Uses Strategy.PUBLIC with OAuth2 — no browser session required.
Commands: auth, status, play, pause, next, prev, volume, search, queue, shuffle, repeat.
Credentials are loaded from ~/.opencli/spotify.env or environment variables.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(spotify): rename index.ts → spotify.ts and fix CliError calls
- Renamed src/clis/spotify/index.ts to spotify.ts so the build-manifest
picks it up (index.js is intentionally excluded from manifest scanning)
- Fixed 4 CliError calls: constructor now requires (code, message, hint?)
so each throw now passes an appropriate error code as first argument
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(spotify): fix token refresh corruption, env parse, null guards, validation
- refreshAccessToken: check res.ok before parsing; construct Tokens object
directly instead of mutating loadTokens() result to avoid writing
undefined/NaN on Spotify error responses; preserve existing refresh_token
when Spotify omits it from the response
- loadEnv: split on first '=' only so values containing '=' are preserved
- SCOPES: remove write/library/top scopes not used by any command
- status: guard against data.item being null (active device but no track)
- volume: validate 0-100 range before API call
- auth: check tokenRes.ok on initial token exchange; add server.on('error')
handler for EADDRINUSE; add 5-minute timeout with clearTimeout on close
* feat(postinstall): auto-create ~/.opencli/spotify.env template on install
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(spotify): guard null progress, podcast items, missing tracks data, corrupted tokens, invalid search limit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(spotify): improve missing credentials error with step-by-step guidance
* fix(spotify): harden setup and add docs coverage
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: zero onboarding, extension version check, and update notifier
- Fail-fast guard in execution.ts: when daemon is running but extension
is not connected, immediately surface a setup guide instead of waiting
for the 30s connect timeout
- Extension version handshake: extension sends `hello` with its version
on WebSocket connect; daemon stores it and exposes via /status; CLI
warns on mismatch in both execution path and `opencli doctor`
- `opencli doctor` now shows extension version inline and reports
version mismatch as an actionable issue
- Non-blocking npm update checker: registers a process exit hook so the
update notice appears after command output (same pattern as npm/gh/yarn);
background fetch writes to ~/.opencli/update-check.json for next run
- postinstall: print Browser Bridge setup instructions after shell
completion install for first-time global install users
Bug fixes caught in review:
- discover.ts: add AbortController timeout to checkDaemonStatus() fetch,
move clearTimeout after res.json() to cover body streaming
- daemon.ts: clear extensionVersion and reject pending requests in
ws.on('error') handler, not just ws.on('close')
- update-check.ts: skip update notice when process exits with non-zero
code; read cache once at module load to avoid double disk I/O;
guard isNewer() against NaN from pre-release version strings
* fix: reduce fail-fast timeout to 300ms and guard stderr.write in exit hook
* docs: add missing adapter docs, fix sidebar 404s, add doc-check CI
- Add doc pages for 11 undocumented adapters: arxiv, barchart,
chaoxing, grok, hf, jike, jimeng, linux-do, sinafinance,
stackoverflow, weread, wikipedia
- Update adapters/index.md with all new adapter entries
- Update VitePress sidebar config with 12 new entries
- Remove broken zh/ sidebar refs (troubleshooting, testing)
- Add doc-check CI workflow (adapter coverage + build + link check)
- Add scripts/check-doc-coverage.sh for adapter doc enforcement
- Enhance PR template with adapter doc checklist
* fix(ci): use --root-dir instead of --base for lychee link checker
lychee v0.23 requires --base to be a URL or absolute path.
Use --root-dir for resolving root-relative links in local files.
* fix(ci): remove lychee link-check job, rely on VitePress build
VitePress links use extension-less paths (e.g. /adapters/browser/twitter)
which lychee cannot resolve. The docs-build job already catches all
broken internal links via VitePress dead link detection during build.
* feat(xiaohongshu): add 4 creator analytics commands
Add creator backend support for Xiaohongshu (小红书), enabling
creators to access their analytics data from the command line.
New commands:
- creator-profile: account info (followers, likes, creator level)
- creator-stats: 7-day/30-day overview (views, likes, collects,
comments, shares, new followers) with daily trend data
- creator-notes: note list with per-note metrics from note manager
- creator-note-detail: single note analytics breakdown
(organic vs promoted vs video traffic)
API discovery:
- /api/galaxy/creator/home/personal_info (cookie auth, 200 OK)
- /api/galaxy/creator/data/note_detail_new (cookie auth, 200 OK)
- /api/galaxy/creator/data/note_detail?note_id=xxx (cookie auth, 200 OK)
- Note manager DOM extraction for note list (bypasses v2 signature)
All endpoints verified working with real creator account.
Screenshots (redacted) included in docs/screenshots/.
Requires: Chrome logged into creator.xiaohongshu.com
* chore: remove screenshots from repo (will host externally for PR)
* review: fix creator analytics CLI integration
Co-authored-by: stone16 <stone2paul@gmail.com>
* test: add site-scoped test runner
Co-authored-by: stone16 <stone2paul@gmail.com>
* review: ignore publish timestamps in creator note metrics
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
The postinstall script was appending the fpath line at the end of .zshrc,
but compinit (called earlier by oh-my-zsh or directly) would have already
finished scanning. This caused zsh completion to silently fail for most
users.
Now the script detects the first compinit / oh-my-zsh source line and
inserts the fpath entry before it, ensuring completion works immediately.