Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jackwener/OpenCLI
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.5.0
Choose a base ref
...
head repository: jackwener/OpenCLI
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.5.1
Choose a head ref
  • 18 commits
  • 37 files changed
  • 5 contributors

Commits on Mar 27, 2026

  1. fix(doctor): remove unused fix option and add release URL to extensio…

    …n install hint (#498)
    
    * 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
    
    * fix(doctor): remove unused fix option and add release URL to extension install hint
    
    * fix(e2e): update BrowserBridge unavailable detection regex to match current error format
    jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    f9f018d View commit details
    Browse the repository at this point in the history
  2. fix(plugin): remove legacy LOCK_FILE/MONOREPOS_DIR constants (#495)

    Remove the module-level LOCK_FILE and MONOREPOS_DIR constants that were
    computed at load time using os.homedir(). These ignored the HOME
    environment variable, causing path mismatches when tests use HOME for
    isolation.
    
    All usages now go through getLockFilePath() and getMonoreposDir() which
    respect process.env.HOME. Updated plugin.test.ts accordingly.
    ByteYue authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    d742635 View commit details
    Browse the repository at this point in the history
  3. feat(plugin): add 'plugin create <name>' scaffold command (#494)

    * feat(plugin): add 'plugin create <name>' scaffold command
    
    Generate a ready-to-develop plugin directory with all required files:
    - opencli-plugin.json (manifest with name, version, compatibility)
    - package.json (ESM, peer dependency on @jackwener/opencli)
    - hello.yaml (sample YAML command using httpbin)
    - greet.ts (sample TS command using cli() API)
    - README.md (install, usage, and development instructions)
    
    Usage:
      opencli plugin create my-plugin
      opencli plugin create my-plugin --dir /path/to/dir
      opencli plugin create my-plugin --description 'My awesome plugin'
    
    Includes 5 test cases for scaffold generation and error handling.
    
    * fix(plugin): align scaffold with local install flow
    
    ---------
    
    Co-authored-by: jackwener <jakevingoo@gmail.com>
    ByteYue and jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    fa4c44a View commit details
    Browse the repository at this point in the history
  4. feat(plugin): support local path install via file:// and absolute path (

    #491)
    
    Add support for installing plugins from local directories:
      opencli plugin install file:///path/to/my-plugin
      opencli plugin install /path/to/my-plugin
    
    Local plugins are symlinked (not copied) into ~/.opencli/plugins/
    so code changes are reflected immediately without reinstall — ideal
    for plugin development workflows.
    
    Changes:
    - parseSource() now handles file:// URLs and bare absolute paths
    - New installLocalPlugin() creates symlink + installs deps + transpiles
    - Lock file records 'local:<path>' as source for local plugins
    - 6 new test cases for local path parsing and install behavior
    ByteYue authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    384419f View commit details
    Browse the repository at this point in the history
  5. fix(xiaohongshu): adapt publish to new two-step creator center UI (#490)

    * fix(xiaohongshu): adapt publish to new two-step creator center UI (#460)
    
    The creator center now requires image upload before showing the
    title/content editor form. This caused the publish command to fail
    with "Could not find title input".
    
    - Add waitForEditForm() to poll for editor after image upload
    - Extract TITLE_SELECTORS constant shared by waitForEditForm and fillField
    - Add contenteditable title selectors for new UI
    - Make images required (new UI mandates images before editor)
    - Update draft button to match both '暂存离开' and '存草稿'
    - Exclude title placeholder from content fallback selector
    - Update tests to match new flow
    
    * refactor(xiaohongshu): clarify publish surface states
    
    ---------
    
    Co-authored-by: jackwener <jakevingoo@gmail.com>
    Astro-Han and jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    39eec0d View commit details
    Browse the repository at this point in the history
  6. fix(plugin): detect symlinked monorepo sub-plugins in discoverPlugins (

    …#487)
    
    * fix(plugin): detect symlinked monorepo sub-plugins in discoverPlugins
    
    discoverPlugins() used entry.isDirectory() to filter plugin directories,
    but monorepo sub-plugins are installed as symlinks pointing into
    ~/.opencli/monorepos/. On most Node.js versions, isDirectory() returns
    false for symlinks, causing monorepo plugin commands to be silently
    skipped during discovery.
    
    Add entry.isSymbolicLink() check so symlinked plugin directories are
    properly discovered and their commands registered.
    
    * fix(plugin): skip broken symlink discovery
    
    ---------
    
    Co-authored-by: jackwener <jakevingoo@gmail.com>
    ByteYue and jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    5e2e1df View commit details
    Browse the repository at this point in the history
  7. fix(twitter): use DOM-only scraping for trending to match page results (

    #486)
    
    Remove guide.json API path that returned data inconsistent with what
    users see on the page (#463). Use semantic caret button detection
    via data-testid instead of position-based heuristics, and validate
    post count text contains digits before displaying.
    Astro-Han authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    fb5b608 View commit details
    Browse the repository at this point in the history
  8. perf: parallel file discovery, plugin scanning, and external CLI cach…

    …ing (#501)
    
    - Parallelize file scanning in discoverClisFromFs and discoverPluginDir
      using Promise.all(files.map(async ...)) instead of serial for-of with
      await, so isCliModule checks run concurrently
    - Parallelize plugin directory scanning in discoverPlugins
    - Cache loadExternalClis() result to avoid re-parsing YAML on every call
    - Invalidate cache in registerExternalCli after writing to disk
    - Cache strategyLabel() call in list command to avoid redundant computation
    - Add comment explaining why discovery must remain sequential (plugin override semantics)
    jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    31cb229 View commit details
    Browse the repository at this point in the history
  9. feat(xiaohongshu): add published_at to search results (#484) (#485)

    Derive approximate publish date from note IDs, which follow MongoDB
    ObjectID format (first 8 hex chars = Unix timestamp). Exported as a
    pure function with UTC+8 offset for China timezone.
    
    Closes #484
    Astro-Han authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    1a44d8c View commit details
    Browse the repository at this point in the history
  10. fix(plugin): handle EXDEV cross-filesystem rename during install (#488)

    * fix(plugin): handle EXDEV cross-filesystem rename during install
    
    fs.renameSync() fails with EXDEV when source and destination are on
    different filesystem mount points. This commonly happens because plugin
    clones land in os.tmpdir() (often /tmp on a tmpfs) while plugins are
    installed to ~/.opencli/plugins/ (on the root filesystem).
    
    Add a moveDir() helper that catches EXDEV and falls back to
    fs.cpSync() + fs.rmSync(). Applied to both single-plugin and monorepo
    install paths.
    
    * review: clean up failed EXDEV fallback installs
    
    ---------
    
    Co-authored-by: jackwener <jakevingoo@gmail.com>
    ByteYue and jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    55d0473 View commit details
    Browse the repository at this point in the history
  11. feat(bluesky): add Bluesky adapter with 9 commands (#215)

    Bluesky (9 commands, public AT Protocol API, no auth needed):
    - profile: user profile info (followers, following, posts)
    - user: recent posts from a user with engagement stats
    - trending: trending topics on Bluesky
    - search: search users
    - feeds: popular feed generators
    - followers: list user's followers
    - following: list accounts a user follows
    - thread: post thread with replies
    - starter-packs: user's starter packs
    
    All commands use the public Bluesky API, no browser or login required.
    0xsline authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    9a9e078 View commit details
    Browse the repository at this point in the history
  12. fix(execution): apply timeout to non-browser commands (#383)

    Non-browser commands (`browser: false`) ran without any timeout
    protection, even when `timeoutSeconds` was explicitly set. This wraps
    the non-browser execution path with `runWithTimeout()` when the
    adapter defines a positive `timeoutSeconds`.
    
    Also adds an optional `hint` parameter to `TimeoutError` so the
    non-browser path shows a relevant suggestion instead of the
    browser-specific `OPENCLI_BROWSER_COMMAND_TIMEOUT` env var hint.
    Astro-Han authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    ee59750 View commit details
    Browse the repository at this point in the history
  13. fix(plugin): prevent raw .ts import crash when esbuild transpilation …

    …fails (#500) (#503)
    
    When a TS plugin is installed but esbuild is unavailable or transpilation
    fails silently, the plugin discovery would attempt to import() the raw
    .ts file, causing 'Unknown file extension .ts' on production Node.js.
    
    Changes:
    - discovery.ts: Skip raw .ts import when no compiled .js exists; show
      an actionable warning guiding the user to re-transpile or install esbuild
    - plugin.ts: Upgrade esbuild-not-found from debug to warn level; log
      the outer catch error instead of silently swallowing it
    
    Closes #500
    ByteYue authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    2ad1215 View commit details
    Browse the repository at this point in the history
  14. feat(plugin): support multi-source plugin install (ssh, git@, generic…

    … https) (#504)
    
    Extends parseSource() to accept any git-cloneable URL, not just GitHub:
    - ssh://git@host/path/repo.git
    - git@host:user/repo.git (SCP-style)
    - https://any-host.com/path/repo.git
    
    GitHub shorthand (github:user/repo) and local paths continue to work.
    Updated error messages, CLI description, docs, and added 7 new unit tests.
    
    Closes #492
    ByteYue authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    70ad570 View commit details
    Browse the repository at this point in the history
  15. perf: smart pre-navigation — skip redundant nav + remove 2s wait (#507)

    * perf: smart pre-navigation — skip redundant domain nav + remove hardcoded 2s wait
    
    - Add `getCurrentUrl()` to IPage, Page, and CDPPage to check current browser URL
    - Skip pre-navigation entirely if the browser is already on the target domain
    - Remove the hardcoded `page.wait(2)` after pre-navigation — `page.goto()` already
      includes smart DOM-settle detection via `waitForDomStable`, making the fixed
      2-second sleep redundant
    - Saves ~2s per browser command in the common case (consecutive commands on the
      same site), and ~1-2s even on cold navigation
    
    * perf: smart page.wait() — DOM-stable early return for waits >= 1s
    
    For page.wait(N) where N >= 1 second, use DOM MutationObserver-based
    stability detection instead of a fixed sleep. The original wait time
    becomes a hard cap, but the call returns as soon as the DOM stops
    mutating (500ms quiet period).
    
    This benefits ~200 hardcoded sleep calls across ~40 adapters without
    changing any adapter code. A typical page.wait(5) now completes in
    <1s when the page is already stable, instead of always waiting 5s.
    
    Short waits (< 1s) are kept as fixed sleeps — these are typically
    UI animation delays or anti-bot throttling where DOM-ready is irrelevant.
    
    * refactor: getCurrentUrl() uses in-memory tracking instead of round-trip
    
    Replace the sendCommand('exec', 'window.location.href') call with a
    local _lastUrl field set during goto(). This eliminates a daemon HTTP
    round-trip for the domain check, making isAlreadyOnDomain() zero-cost.
    
    On fresh tabs (about:blank), _lastUrl is null so we correctly fall
    through to navigation — no special-casing needed.
    jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    cf99c61 View commit details
    Browse the repository at this point in the history
  16. fix(v2ex): fetch hot topics through browser context (#493)

    * Update hot.yaml
    
    * review: fetch v2ex hot data within browser context
    
    ---------
    
    Co-authored-by: jackwener <jakevingoo@gmail.com>
    huguoliang1314 and jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    a2d1199 View commit details
    Browse the repository at this point in the history
  17. refactor(plugin): make plugin installs transactional (#509)

    * feat(plugin): stage installs before promote
    
    * feat(plugin): make remote updates transactional
    
    * fix(plugin): rollback relink failures
    
    * refactor(plugin): unify transactional publish flow
    
    * refactor(plugin): extract publish pipeline helpers
    
    * refactor(plugin): add structured source model
    
    * refactor(plugin): promote lockfile to structured sources
    
    * fix(plugin): preserve lock reads when migration rewrite fails
    
    * fix(plugin): write lockfiles atomically
    
    * test(plugin): make source helper assertions cross-platform
    jackwener authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    5bd0497 View commit details
    Browse the repository at this point in the history
  18. Configuration menu
    Copy the full SHA
    f9f11e4 View commit details
    Browse the repository at this point in the history
Loading