All consumers are in the same repo and evolve together — version field
adds ceremony without practical value at this stage.
Keeps schema_version in VerifiedArtifactMetadata (sidecar file format).
* fix: use Strategy.PUBLIC enum in skill-generate test to fix typecheck regression
* feat: add P2 EarlyHint callback channel to generateVerifiedFromUrl
Add optional onEarlyHint callback for internal cost gating before verify stage.
- EarlyHint type: version, stage, continue, reason, confidence, candidate?
- 3 emit points: explore (viable/not), synthesize (candidate/not), cascade (auth/ok)
- candidate only on synthesize/cascade + continue:true (not on stop or explore)
- unsupported-required-args goes directly to P1 terminal, no P2 hint emitted
- 6 new tests covering all hint paths + guardrails
* docs: add opencli-generate skill spec (SKILL.md)
Captures A+B consensus from team discussion:
- Input: url + goal? (natural language intent hint)
- Output: SkillOutput with machine-readable fields + human message
- Decision tree: thin mapping from GenerateOutcome
- Guardrails: no re-orchestration, no auto-escalation, no new taxonomy
- P1/P2 boundary: P1 is single source of truth, P2 transparent to skill
* fix: address review nits on skill spec
- Make path explicitly optional in needs-human-check decision tree
- Add missing non-array-result message template
* feat: add GenerateOutcome → SkillOutput thin wrapper
Implements the skill mapping layer per opencli-generate SKILL.md:
- mapOutcomeToSkillOutput: thin translation from P1 contract to agent-facing output
- executeGenerateSkill: entry point accepting SkillInput (url + goal?)
- Message templates for all StopReason and EscalationReason values
- 8 tests covering all outcome paths and contract shape validation
* fix: prefer outcome.message for richer context in needs-human-check
When GenerateOutcome has a message (e.g. "required args: id"), use it
instead of the generic template, so the specific args info reaches the user.
All 68 browser adapter links and 8 desktop adapter links in
docs/adapters/index.md were missing the .md file extension,
causing broken links when navigating the documentation on GitHub.
- Add getDaemonHealth() returning 'stopped' | 'no-extension' | 'ready'
- Delete discover.ts (thin wrapper with no value)
- Bridge uses getDaemonHealth() + _pollUntilReady() (eliminates duplicate polling)
- Doctor simplified: live check auto-starts daemon; no-live mode does minimal
auto-start only when stopped (avoids misreporting idle-exit as failure)
- CommanderAdapter preserves error message/hint detail (not just generic title)
- All callers use single unified status entry point
* fix(xiaohongshu): scope note interaction selectors to .interact-container
The .like-wrapper / .collect-wrapper / .chat-wrapper class names are
also used by every comment's like/reply buttons in the comment section.
querySelector returned the FIRST match — which on a note with comments
is a comment's count, not the post's. As a result, `xiaohongshu note`
returned wrong like/collect/comment counts for any note that had user
comments.
Scoping each selector to .interact-container (the post's main
interaction bar) returns the correct post-level counts.
Verified on multiple notes:
- Note A: was returning likes=2, now correctly returns likes=74
- Note B: was returning likes=1, now correctly returns likes=796
- Note C: was returning likes=1, now correctly returns likes=269
* test(xiaohongshu): add regression check for .interact-container selector scope
Verify the evaluate script passes scoped selectors so unscoped
versions can't silently regress. Follows reviewer suggestion to
assert on page.evaluate.mock.calls[0][0].
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add GitHub Trending, Binance, and Weather (Open Meteo) adapters
GitHub Trending (2 commands, browser mode):
- repos: trending repositories with stars, forks, language filter
- developers: trending developers with popular repos
Supports --since daily/weekly/monthly and --language filter
Binance (11 commands, public API via data-api.binance.vision):
- top: top trading pairs by 24h volume
- price: single pair 24h price stats
- prices: latest prices for all pairs
- ticker: 24h ticker statistics
- gainers: top gaining pairs by 24h change
- losers: top losing pairs by 24h change
- trades: recent trades for a pair
- depth: order book bid prices
- asks: order book ask prices
- klines: candlestick/kline data
- pairs: list active trading pairs
Weather / Open Meteo (11 commands, free public API, no key needed):
- current: current weather for a city
- forecast: daily forecast up to 16 days
- hourly: hourly forecast
- search: city geocoding lookup
- air: air quality index (simple)
- air-quality: detailed air quality (US/EU AQI, PM2.5, PM10, ozone, NO2, SO2)
- sunrise: sunrise/sunset times with UV index
- wind: detailed wind forecast with gusts and 80m altitude
- precipitation: rain/snow forecast with probability
- history: historical weather up to 92 past days
- compare: side-by-side weather comparison across cities
All 24 commands tested with live data. 258 existing tests pass.
* docs: add missing douban, sinablog, substack adapter documentation
* fix(binance): sort numeric metrics and filter active pairs
* chore: drop non-binance adapters from pr214
* chore: drop binance docs from pr214
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: add Self-Repair protocol for automatic adapter fixing
When an AI agent uses opencli and a command fails, the agent
automatically diagnoses the failure, fixes the adapter, and retries.
- Add CLAUDE.md with Self-Repair protocol (auto-loaded by Claude Code)
- Add designs/self-repair-protocol.md documenting the approach
- Update opencli-repair skill: add Safety Boundaries (AUTH/BROWSER → STOP,
sourcePath-only scope, max 3 rounds), fix AUTH_REQUIRED guidance
- Update opencli-usage skill: add Self-Repair section
Key design decisions:
- Repair target is always RepairContext.adapter.sourcePath (works for both
repo-local clis/ and user-local ~/.opencli/clis/)
- Only adapter files may be modified, never core src/
- Max 3 repair rounds per failure
- AUTH_REQUIRED and BROWSER_CONNECT are hard stops (report, don't modify)
* fix: align auth boundary and scope language across all documents
- Remove "Auth changed (AUTH_REQUIRED)" exploration section from
opencli-repair skill — contradicted the hard stop rule above it
- Update design doc: scope language matches repo-local + explicit skill
delivery model, not universal product behavior
- Update usage skill: reference sourcePath instead of "files under clis/"
* fix: replace remaining repo-relative clis/ paths with sourcePath in design doc
* refactor: rename opencli-repair to opencli-autofix, remove CLAUDE.md
CLAUDE.md was wrong — users don't work inside the opencli repo, and
the protocol shouldn't assume Claude Code. The skill is the portable
delivery mechanism for any AI agent.
- Rename skills/opencli-repair → skills/opencli-autofix
- Remove CLAUDE.md (not the right delivery mechanism)
- Update all references in usage skill and design doc
- Design doc rewritten to reflect skill-first approach
* fix: use sourcePath in example repair session
* feat: emit AutoFix hint on repairable adapter errors
When a command fails with a repairable error (SELECTOR, EMPTY_RESULT,
COMMAND_EXEC, or generic http/not-found), the error output now includes
a hint telling agents to re-run with OPENCLI_DIAGNOSTIC=1 for repair
context. This is the trigger mechanism that bridges the gap between
"command failed" and "agent enters autofix loop".
Non-repairable errors (AUTH_REQUIRED, BROWSER_CONNECT, ARGUMENT) do not
emit the hint — these require user action, not adapter fixes.
* fix: narrow AutoFix hint to adapter-drift errors only
Remove hint from CommandExecutionError (covers env/launcher/runtime
issues, not adapter drift) and generic http errors (often temporary
site issues). Keep hint only for SelectorError, EmptyResultError,
and generic not-found — clear adapter-drift signals.
When the Browser Bridge extension is older than the CLI, sending
'network-capture-start' to the daemon returns 'Unknown action',
causing explore and operate-open to crash with an unhandled error.
Wrap startNetworkCapture calls with .catch() so they degrade
gracefully — explore continues without network capture data, and
operate-open falls back to the JS interceptor injection.
The compose page needs to load Draft.js editor which is heavier than
primaryColumn. 8s is too tight for slow networks and will cause flaky
failures. 15s aligns with the file input timeout (20s) in magnitude.
- engine.ts: replace `git add -A` with scope-aware `execFileSync` to
stage only files matching config.scope globs, and guard against empty
scope degenerating into staging all files
- fix.ts: pass prompt via stdin `input` option instead of shell string
interpolation to prevent $, backtick, and other metacharacter expansion
- generate.ts: update stale comment that claimed unimplemented pipeline
steps (register, verify, Strategy Cascade)
* refactor: remove scoring heuristic, replace with noise filter + metadata
The scoring mechanism was a pre-LLM heuristic that compressed rich endpoint
metadata into a single number. Since this project is designed for AI Agents,
the agent can reason about structured metadata directly.
Changes:
- Remove scoreEndpoint/scoreRequest/scoreWriteRequest and all score fields
- Replace with isNoiseUrl() filter (tracking/beacon/pixel) + isUsefulEndpoint()
- Remove artificial confidence percentages (was score/20)
- Sort by itemCount (transparent, observable) instead of weighted score
- Endpoints now expose full structured metadata for agent consumption
- Net reduction: -43 lines
* fix: widen endpoint filter to keep single-object JSON and stats/metric URLs
- Remove stats/metric from noise pattern — these are often business APIs
- Relax isUsefulEndpoint to keep any JSON endpoint, not just arrays
(preserves /me, /profile, /detail and other single-object APIs)
* fix: add deterministic endpoint ordering for generate/synthesize path
The AI agent path doesn't need ranking, but generate/synthesize still
pick candidates[0] as default — this needs a stable, explainable order.
- Add endpointSortKey() with transparent observable signals: array items,
detected fields, API path patterns, query params
- Update synthesize chooseEndpoint fallback to use itemCount + field count
- Sort key is internal only; not exposed as score to external consumers
* feat(linux-do): split topic content into a dedicated command
Move the old main-post path out of linux-do topic so topic stays a summarized first-page reader while topic-content becomes the Markdown-focused entrypoint for full post bodies.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* feat(linux-do): update topic content handling to include YAML front matter
* fix(linux-do): update default output format to plain for topic-content rendering
* fix(linux-do): replace js-yaml with inline YAML serialization for topic-content
Adapters must only import node builtins, relative modules, or opencli
public APIs. Hand-roll the simple front matter serialization to remove
the third-party js-yaml dependency.
* fix(linux-do): refine YAML quoting to only escape colons followed by space
Colons in URLs (e.g. https://) are valid unquoted YAML values. Only
quote when a colon is followed by a space or appears at end of line.
---------
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* refactor: extract shared scoring logic and consolidate time format utils
- Extract applyUrlScoreAdjustments() and scoreArrayResponse() to analysis.ts,
eliminating duplicated endpoint scoring between explore.ts and record.ts
- Consolidate formatDuration/formatUptime into a single formatDuration(ms)
in download/progress.ts, reused by commands/daemon.ts
* fix: preserve explore scoring semantics and round daemon uptime
- Revert explore.ts scoreEndpoint to original inline /api/ /x/ bonus
without record's tracking/analytics penalty (blocker from review)
- Math.round uptime*1000 to avoid floating-point noise in daemon status
* feat(xueqiu): add kline and groups adapters
Add kline.yaml: fetch candlestick/OHLCV data from Xueqiu v5 chart API.
Supports custom days lookback and outputs date, open, high, low, close,
volume, percent.
Add groups.yaml: list Xueqiu portfolio/group entries.
* fix(xueqiu): correct groups.yaml to use /portfolio/list.json API
The previous implementation used /portfolio/stock/list.json which only
returns stocks in a single group and does not return the group list.
Switch to /portfolio/list.json which returns all portfolio groups
including 实盘, 沪深, 港股, 美股, 模拟(pid=-4), 持仓 etc.
* fix(xueqiu): replace watchlist category param with pid selector
- Remove the unused 'category' parameter (the API ignores it;
all groups live under category=1 regardless)
- Replace with 'pid' parameter to allow fetching any group:
-4=simulated, -5=SH/SZ, -6=US stocks, -7=HK stocks, etc.
- API path still uses category=1 but pid is now user-controllable
---------
* feat(operate): unify network capture + implement CDP consoleMessages
- operate open: start session capture before navigation (catches initial requests)
- operate network: prefer readNetworkCapture() over JS interceptor
- CDPPage: implement consoleMessages() via Runtime.consoleAPICalled
Part of #810
* fix(operate): use correct daemon/CDP entry field names for network capture
Daemon and CDP capture entries use responseStatus/responseContentType/
responsePreview (not status/contentType/responseBody). Fix the
normalization in operate network to match the actual entry shape from
extension/src/cdp.ts.
* fix(cdp): capture Runtime.exceptionThrown in consoleMessages
- Register Runtime.exceptionThrown handler to capture uncaught exceptions
as error-level messages (most valuable diagnostic signal)
- 'error' filter now returns both console.error() and warning/exception
entries, matching typical severity-based logging semantics
* feat(cdp): implement session-level network capture for CDPPage
Implements startNetworkCapture() and readNetworkCapture() on CDPPage using
CDP Network domain events. Updates explore.ts to prefer session capture
over Performance API networkRequests().
Closes part of #810
* fix(cdp): use Network.loadingFinished for reliable body capture
- Move getResponseBody call from responseReceived to loadingFinished,
matching the extension's implementation pattern
- Use extension-compatible entry shape (responseStatus, responseContentType,
responsePreview) instead of custom field names
- Remove unreliable 100ms sleep hack in readNetworkCapture()
- Align with extension/src/cdp.ts:419-437 for consistency
* fix(cdp): drain buffer on readNetworkCapture to match daemon contract
readNetworkCapture() must clear the buffer after reading, matching the
daemon Page's read-and-drain behavior. Without this, repeated reads
would return stale entries.
* fix(cdp): await in-flight body fetches before returning from readNetworkCapture
Track all pending getResponseBody promises and await them in
readNetworkCapture() before draining the buffer. This ensures
explore/diagnostic consumers always get entries with responsePreview
populated, not empty shells where the body fetch hasn't resolved yet.
* fix(explore): handle both legacy and capture entry field names
parseNetworkRequests now maps both shapes:
- Legacy: status, contentType, responseBody
- Capture (extension/CDP): responseStatus, responseContentType, responsePreview
Also clears _pendingBodyFetches on startNetworkCapture reset.
* fix: add safety boundaries to diagnostic output
- Redact sensitive headers (Authorization, Cookie, etc.) from network requests
- Redact sensitive URL query parameters (token, key, secret, etc.)
- Cap individual fields: snapshot (100K chars), adapter source (50K chars),
network requests (50 entries, 4K body each), stack trace (5K chars)
- Enforce 256KB total output budget with graceful degradation:
drops snapshot first, then page state entirely
- Export truncate/redactUrl helpers for testing
* fix: add free-text redaction for all diagnostic string channels
Addresses review feedback: snapshot, consoleErrors, error message/hint/stack
could contain inline secrets (Bearer tokens, JWTs, cookie values, token=value
patterns). All string channels now pass through redactText() before emission.
- Add redactText() with patterns for Bearer tokens, JWTs, cookie values,
and inline key=value secrets
- Apply redactText to: error.message, error.hint, error.stack,
page.snapshot, page.consoleErrors
- Add 6 new test cases for redactText and error message redaction
* fix: resolve adapter source path and add page state collection timeout
Fixes#808 items 1 and 3:
1. adapter.source was missing for all command types because buildRepairContext
only checked cmd._modulePath (set only for manifest lazy-loaded TS).
Now resolveAdapterSourcePath() checks cmd.source first, skips manifest:
pseudo-paths, and maps dist/clis/*.js back to source clis/*.ts.
3. collectPageState() had no timeout — a hung CDP connection would block
error propagation indefinitely. Now wrapped with 5s Promise.race timeout,
falling back to emitting diagnostic without page state.
* fix: track sourceFile in manifest for YAML adapter source resolution
YAML commands inlined in the manifest previously lost their original file
path, causing resolveAdapterSourcePath() to return undefined. Add
sourceFile field to ManifestEntry so discovery can reconstruct the
editable source path for both YAML and TS commands.
* feat: add structured diagnostic output for AI-driven adapter repair
When OPENCLI_DIAGNOSTIC=1 is set, failed commands emit a RepairContext
JSON to stderr containing the error, adapter source, and browser state
(DOM snapshot, network requests, console errors). AI Agents consume
this to diagnose and fix adapters when websites change.
Also adds the opencli-repair skill guide for AI Agents.
* fix: correct e2e test binary path to dist/src/main.js
The e2e helpers pointed to dist/main.js but the actual build output
is at dist/src/main.js (matching package.json "main" field). This
caused all e2e-headed tests to fail with "Cannot find module".
* fix: correct dist/main.js path in autoresearch scripts
* fix: emit diagnostic for pre-session browser failures
When browser connection fails before the session callback runs
(e.g., BrowserConnectError), the inner diagnostic catch never fires.
Use a flag to ensure the outer catch emits diagnostic as a fallback.
* test: tolerate unavailable Bloomberg RSS feeds in e2e
* test: skip flaky bloomberg businessweek e2e test
The Bloomberg Businessweek RSS feed is intermittently unavailable,
causing CI failures unrelated to code changes.
* revert: restore bloomberg businessweek e2e coverage
* 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(discovery): expose runtime deps to user adapters
* fix: route copied adapters through opencli exports
* refactor: route adapter status output through logger
* refactor: migrate adapter imports to package exports
Replace all relative imports (../../src/registry.js, ../../browser/cdp.js, etc.)
with package exports (@jackwener/opencli/registry, @jackwener/opencli/errors, etc.)
across all 484 adapter files.
This decouples adapter import resolution from directory structure:
- User CLIs in ~/.opencli/clis/ resolve via node_modules symlink
- Internal adapters resolve via Node.js self-referencing
- No more shim files needed for import resolution
Changes:
- package.json: add sub-path exports for all public modules
- clis/**: replace relative imports with @jackwener/opencli/...
- discovery.ts: simplify ensureUserCliCompatShims to symlink-only
- registry-api.ts: export CommandArgs type
- Remove root-level shim directories (browser/, download/, pipeline/)
- Remove shim entries from tsconfig.json include and package.json files
* test: add regression tests for package exports
Prevents regressions like #788/#791 by:
1. Scanning all adapter files for forbidden relative imports
(../../src/, ../../browser/, etc.) — fails if any remain
2. Verifying every package.json export maps to an existing source file
18 new test cases.
* fix: use junction on Windows + broaden test patterns
- discovery.ts: use 'junction' symlink type on Windows (no admin required)
- package-exports.test.ts: generalize forbidden patterns to catch any
depth of ../ traversal (not just ../../ and ../../../)
* fix: update stale vi.mock/importActual paths in adapter tests
Test files still used old relative paths for vi.mock() and
vi.importActual() calls. Updated 5 test files to use package exports.
Also broadened regression test patterns to catch mock/importActual paths.
* fix: use rm instead of unlink for symlink cleanup, add warn on failure
Addresses review feedback from Astro-Han:
- rm() handles both symlinks and stale directories (unlink fails on dirs)
- Log a warning when symlink creation fails instead of silent catch
* docs: update import examples to use package exports
Update all documentation, contributing guides, and skills to use
@jackwener/opencli/registry instead of ../../src/registry.js.
Without this, users following the docs would write adapters with
broken imports since the old shim files are no longer created.