Skip to content

feat(analyzer): add OpenCode-native SkillSpector invocation skill and tool - #537

Merged
rng1995 merged 7 commits into
NVIDIA:mainfrom
Yoseph-Zuskin:feat/opencode-plugin
Sep 16, 2026
Merged

rng1995 merged 7 commits into
NVIDIA:mainfrom
Yoseph-Zuskin:feat/opencode-plugin

Conversation

@Yoseph-Zuskin

Copy link
Copy Markdown
Contributor

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

  • Two files, zero new dependencies, no package.json, no plugin module
    (file-based tools/commands auto-discovery handles registration):
    .opencode/tools/skillspector_scan.ts and
    .opencode/commands/skillspector.md.
  • Tool skillspector_scan(target, format=json, noLlm=true, output?)
    shells out via stdlib execFile: binary chain SKILLSPECTOR_BIN env,
    then <worktree>/.venv, then bare skillspector on PATH, with an
    install hint on miss; defaults scan <target> --format <format> --no-llm. Mirrors the precedent of extensions/skillspector.ts
    (Pi agent surface: same tool name, redaction, truncation, binary
    chain) — same conventions, different host loader.
  • 120s timeout with kill + partial-output report; stdout truncated ~12k
    chars / stderr ~6k; sk-ant-*, sk-*, *_API_KEY/*_TOKEN redacted.
  • Exit semantics: 1 with stdout returns the JSON report (findings, not a
    crash); 2 returns stderr as a usage error. /skillspector forwards
    $ARGUMENTS as the target with an LLM opt-in line.
  • Defaults to --no-llm for instant static scans; LLM opt-in via
    noLlm=false or SKILLSPECTOR_PROVIDER/MODEL env passthrough.

Verification

  • Headless load test (opencode 1.18.x, one inference call):
    /skillspector auto-discovered, agent called
    skillspector_scan {"noLlm":true,"target":"./fixture-skill"}, JSON
    flowed back and was summarized as 0/100 LOW, SAFE, 2 files, 100%
    coverage.
  • Zero-inference harness against the same tool file: missing binary
    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.
  • Python gates N/A on this branch: no Python files changed or added.
  • Status (2026-09-14): feat/opencode-plugin on origin; self-scan of
    .opencode/ via opencode_cli: 0/LOW, 0 issues, 3/3 LLM calls.
  • Live extension test (2026-09-14, opencode 1.18.30, temp project,
    SKILLSPECTOR_BIN → venv skillspector.exe): /skillspector
    auto-discovered, agent called skillspector_scan, fixture skill
    returned 0/100 LOW, SAFE, 100% coverage. Finding: the host may omit
    the format default, so the tool passed --format undefined
    (exit 2, surfaced cleanly); fixed via args.format ?? "json" /
    args.noLlm ?? true fallbacks (the latter keeps LLM strictly opt-in).
  • Unit tests: pure helpers extracted to dependency-free
    .opencode/tools/skillspector_scan_lib.ts, covered by
    tests/opencode/skillspector_scan_lib.test.ts via stdlib node --test
    (18/18, Node 22+ type stripping, zero new deps); headless /skillspector
    regression re-run green after the reshape.

Sample

> /skillspector ./fixture-skill
skillspector_scan {"noLlm":true,"target":"./fixture-skill"} -> 0/100 LOW, SAFE

Risks

  • Large reports exceed the ~12k truncation cap (a 17.4k finding report
    came back truncated); consumers needing full JSON should pass output.
  • Uses stdlib execFile, not Bun.$, for timeout/kill support; trivial
    to swap if maintainers prefer $.
  • DCO: all commits carry Signed-off-by (maintainer: verify on push).

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Comment thread .opencode/tools/skillspector_scan.ts Outdated
Comment thread .opencode/tools/skillspector_scan.ts Outdated
Comment thread .opencode/tools/skillspector_scan_lib.ts Outdated
Comment thread .opencode/tools/skillspector_scan_lib.ts Outdated
Comment thread .opencode/tools/skillspector_scan_lib.ts Outdated
Comment thread .opencode/tools/skillspector_scan_lib.ts Outdated
Comment thread docs/OPENCODE_EXTENSION.md Outdated
Comment thread tests/opencode/skillspector_scan_lib.test.ts
@rng1995
rng1995 force-pushed the feat/opencode-plugin branch 9 times, most recently from d5eafa9 to 5facfac Compare September 16, 2026 04:22
Yoseph-Zuskin and others added 5 commits September 15, 2026 22:16
…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>
@rng1995
rng1995 force-pushed the feat/opencode-plugin branch from 5facfac to 586a787 Compare September 16, 2026 05:16

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

@rng1995
rng1995 enabled auto-merge (squash) September 16, 2026 19:29
@rng1995
rng1995 disabled auto-merge September 16, 2026 19:49
@rng1995
rng1995 merged commit 9c546dc into NVIDIA:main Sep 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OpenCode Extension (/skillspector + Tool)

2 participants