feat(analyzer): add OpenCode-native SkillSpector invocation skill and tool - #537
Conversation
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Reviewed current head fc1773a0027f95a92194fe7b9c0c46be2a3bd5d2. The extension is not ready to ship: it allows model-triggered scans of arbitrary host paths and optional LLM egress without an OpenCode permission request, ignores cancellation, mangles supported SCP-style Git targets, documents a nonexistent provider, loses valid exit-2 reports and successful-run warnings, incompletely redacts supported credentials, and its TypeScript tests are not run by required CI. The inline comments give concrete corrections and regressions.
Hosted checks pass, but these required security/correctness/test changes and mergeStateStatus=BEHIND block merging.
d5eafa9 to
5facfac
Compare
…command - Add .opencode/tools/skillspector_scan.ts: tool wrapping skillspector CLI via execFile with binary chain (SKILLSPECTOR_BIN env -> .venv -> PATH), 120s timeout, stdout/stderr truncation, secret redaction - Add .opencode/commands/skillspector.md: slash command forwarding $ARGUMENTS with LLM opt-in via noLlm flag - Default to --no-llm for instant static scans; LLM opt-in via noLlm=false or env passthrough (SKILLSPECTOR_PROVIDER, SKILLSPECTOR_MODEL) Signed-off-by: Yoseph Zuskin <zuskinyoseph@gmail.com> Co-Authored-By: OpenCode Muse Spark 1.3 Free (1M context) <noreply@opencode.ai> Co-Authored-By: OpenCode Nemotron 3 Ultra Free (1M context) <noreply@opencode.ai>
- The host may omit declared arg defaults, so re-apply them in execute(): format falls back to "json" (avoids `--format undefined`, exit 2), noLlm falls back to true (an omitted noLlm would otherwise silently opt into LLM analysis against the static-by-default contract) - Verified headless on opencode 1.18.30: `/skillspector` round-trip green, fixture 0/LOW SAFE Signed-off-by: Yoseph Zuskin <zuskinyoseph@gmail.com> Co-Authored-By: OpenCode Muse Spark 1.3 Free (1M context) <noreply@opencode.ai>
- The host may omit declared arg defaults, so execute() re-applies them: format falls back to "json" (avoids `--format undefined`, exit 2), noLlm falls back to true (an omitted noLlm would otherwise silently opt into LLM analysis against the static-by-default contract) - Extract pure helpers (truncate, redact, resolveBinary, arg building, exit-code mapping) into dependency-free .opencode/tools/skillspector_scan_lib.ts; thin the tool file to wiring - Cover the lib with skillspector_scan_lib.test.ts via stdlib node --test (18/18, zero new deps; Node 22+ type stripping) - Verified: node --test 18/18; headless `/skillspector` round-trips green on opencode 1.18.30, fixture 0/LOW SAFE (twice, including once with the host omitting all defaults) Signed-off-by: Yoseph Zuskin <zuskinyoseph@gmail.com> Co-Authored-By: OpenCode Muse Spark 1.3 Free (1M context) <noreply@opencode.ai>
- Add docs/OPENCODE_EXTENSION.md modeled on docs/PI_EXTENSION.md: requirements, install (copy .opencode/, skillspector on PATH or SKILLSPECTOR_BIN), basic /skillspector scan, tool parameters, env-based LLM opt-in, unit-test invocation, removal - Note the deliberate param differences vs the Pi extension (no provider/model/yaraRulesDir/verbose; LLM via environment) - Link the new doc from the README Documentation section next to the Pi extension entry Signed-off-by: Yoseph Zuskin <zuskinyoseph@gmail.com> Co-Authored-By: OpenCode Muse Spark 1.3 Free (1M context) <noreply@opencode.ai>
Signed-off-by: Narendran Raghavan <nraghavan@nvidia.com>
5facfac to
586a787
Compare
rng1995
left a comment
There was a problem hiding this comment.
Approved at exact head 586a787 after resolving all eight review threads and rebasing onto current main 2e9ae8d. Verified explicit OpenCode read/edit/external-directory/network/LLM permissions and denial-before-launch; cancellation propagation; SCP/Windows/POSIX target classification; credential redaction and ambient LangChain/LangSmith tracing suppression; bounded stdout/stderr evidence on success and exits 1/2; valid provider documentation; pinned Node 22 TypeScript CI; and symlink/junction defenses for targets, outputs, and explicit/auto executable paths. Focused TypeScript suite passes 44/44, and exact-head changes, lint, test-unit, OpenCode TypeScript Tests, DCO, and docker-smoke checks are all green.
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Re-reviewed current head 030937ebf065ca3a2a2cf1d7454d32dbded01818 against all eight prior findings and the later hardening stack. Every prior issue is resolved: capability requests cover local/external reads, report writes, remote targets, explicit LLM egress, executable access, and the subprocess before launch; cancellation reaches the child; supported SCP targets and cross-platform local paths are classified correctly; credential and tracing data are withheld/redacted; stdout and stderr evidence survives success and exits 1/2; documentation names a valid provider; and the pinned Node 22 test lane is green. The additional symlink/junction checks protect local targets, outputs, and explicit/auto executable paths. I found no remaining required code, test, documentation, security, or compatibility changes.
All required checks, including OpenCode TypeScript Tests, are green and all prior threads are resolved. The PR is mergeable but mergeStateStatus=BEHIND after #558, so the current head is not merge-ready; any auto-sync changes the reviewed commit and requires re-review.
Add OpenCode-native SkillSpector invocation (
/skillspector+ tool)Problem
There is no OpenCode-native way to invoke SkillSpector: no slash command,
no agent-callable tool, so scans require manual CLI use outside the
session.
Fixes: #535
Approach
(file-based tools/commands auto-discovery handles registration):
.opencode/tools/skillspector_scan.tsand.opencode/commands/skillspector.md.skillspector_scan(target, format=json, noLlm=true, output?)shells out via stdlib
execFile: binary chainSKILLSPECTOR_BINenv,then
<worktree>/.venv, then bareskillspectoron PATH, with aninstall hint on miss; defaults
scan <target> --format <format> --no-llm. Mirrors the precedent ofextensions/skillspector.ts(Pi agent surface: same tool name, redaction, truncation, binary
chain) — same conventions, different host loader.
chars / stderr ~6k;
sk-ant-*,sk-*,*_API_KEY/*_TOKENredacted.crash); 2 returns stderr as a usage error.
/skillspectorforwards$ARGUMENTSas the target with an LLM opt-in line.--no-llmfor instant static scans; LLM opt-in vianoLlm=falseorSKILLSPECTOR_PROVIDER/MODELenv passthrough.Verification
/skillspectorauto-discovered, agent calledskillspector_scan {"noLlm":true,"target":"./fixture-skill"}, JSONflowed back and was summarized as 0/100 LOW, SAFE, 2 files, 100%
coverage.
returns the install hint; timeout kills at 120s with partial output
and redacted secrets; usage error surfaces stderr; exit-1 risk
fixture (score 100/CRITICAL) returns parseable JSON head with
truncation note.
feat/opencode-pluginon origin; self-scan of.opencode/viaopencode_cli: 0/LOW, 0 issues, 3/3 LLM calls.SKILLSPECTOR_BIN→ venvskillspector.exe):/skillspectorauto-discovered, agent called
skillspector_scan, fixture skillreturned 0/100 LOW, SAFE, 100% coverage. Finding: the host may omit
the
formatdefault, so the tool passed--format undefined(exit 2, surfaced cleanly); fixed via
args.format ?? "json"/args.noLlm ?? truefallbacks (the latter keeps LLM strictly opt-in)..opencode/tools/skillspector_scan_lib.ts, covered bytests/opencode/skillspector_scan_lib.test.tsvia stdlibnode --test(18/18, Node 22+ type stripping, zero new deps); headless
/skillspectorregression re-run green after the reshape.
Sample
Risks
came back truncated); consumers needing full JSON should pass
output.execFile, notBun.$, for timeout/kill support; trivialto swap if maintainers prefer
$.Signed-off-by(maintainer: verify on push).