Skip to content

feat(scan): read @openshell comments in Ruby, Rust, Java and more; report unscanned languages - #21

Merged
ilanh merged 2 commits into
mainfrom
feat/comment-leaders
Nov 3, 2026
Merged

ilanh merged 2 commits into
mainfrom
feat/comment-leaders

Conversation

@ilanh

@ilanh ilanh commented Nov 3, 2026

Copy link
Copy Markdown
Owner

What and why

Inline @openshell comments in Ruby, Rust, Java, Kotlin, C#, PHP and other languages were silently ignored. They are now read, and files in languages we cannot read are reported instead of skipped silently.

  • Generic # and // comment lexers, picked by file extension. Built-in: rb rake gemspec pl pm r (hash) and rs java kt kts cs php swift scala dart c h cc cpp hpp (slash).
  • [scan.comment_leaders] in openshell-annotate.toml adds extensions or switches defaults off (hash, slash, none); a bad value is a config error.
  • [languages] default executables for these languages are keyed by file extension (rb = "/usr/bin/ruby").
  • Known source files that are still not scanned (for example .lua, .ex, .hs, or an extension set to none) produce a note: N .ext files were not scanned ... line on stderr. stdout and --json are unchanged.
  • No call-site lint for these languages (directives only); documented in the README. The .sh sidecar workaround is documented. A first-class sidecar file is tracked separately in First-class sidecar annotations file (openshell.annotations.yaml) #20.

Verified on copies of the real candidates: an inline directive in pocketrb's web_search.rb and in anthropic-proxy-rs's proxy.rs now scans and generates.

Tests: three new CLI tests. TOOLCHAIN=1.94 scripts/ci-local.sh and stable clippy pass.

Independent of the other PRs (touches scan.rs, lexer.rs, config.rs, README); it can merge in any order.

Closes #7

ilanh added 2 commits November 3, 2026 07:59
…port unscanned languages

Adds generic hash and slash comment lexers chosen by extension, built-in defaults for common
languages, [scan.comment_leaders] to extend or disable them, [languages] defaults keyed by
extension, and a stderr note for known source languages that are not scanned.

Signed-off-by: Ilan Harel <[email protected]>
@ilanh
ilanh force-pushed the feat/comment-leaders branch from 3c48328 to 6a7c1b2 Compare November 3, 2026 07:59
@ilanh
ilanh merged commit 54a9d5b into main Nov 3, 2026
2 checks passed
@ilanh
ilanh deleted the feat/comment-leaders branch November 3, 2026 08:03
ilanh added a commit that referenced this pull request Nov 3, 2026
…ns are reported (#25)

## What and why

Reworks how `exclude` behaves so it stops failing silently.

- **Root cause of the "anchor matches nothing" case:** with `scan .` /
`lint .` the walker yields `./packages/cli/a.ts`, and the relative path
kept the leading `.`, so `^packages/cli/` never matched. Relative paths
are now normalised (this also removes the `./` prefix from source paths
in the generated policy comments, a separate small bug seen on
Paperclip).
- **Dead patterns are reported:** in a whole-repo scan, any
`[scan].exclude` or `[lint].exclude` pattern that matches no file prints
`warning: [scan].exclude pattern ... matches no file`, with a hint.
Regex-looking patterns (`^packages/(admin|cli)/`) get "exclude patterns
are not regexes: ... list one entry per directory". Partial scans stay
quiet; stdout and `--json` are unchanged.
- **One exclude list:** files excluded by `[scan].exclude` are never
read, so `lint` already inherits them (checked, including with a `.`
path). `[lint].exclude` stays as lint-only extras. This is now stated in
the config docs and the README, with the literal (not regex) matching
rule and a per-directory example.

Tests: `tests/excludes.rs` (shared exclude with and without a `.` path,
no `./` prefix, regex pattern warning, dead lint pattern, partial scan
quiet). `TOOLCHAIN=1.94 scripts/ci-local.sh` and stable clippy pass.

Ordering: touches `do_scan` in `main.rs` and `scan.rs` like #21 (comment
leaders); whichever merges second needs a trivial rebase.

Closes #9

Signed-off-by: Ilan Harel <[email protected]>
Co-authored-by: Ilan Harel <[email protected]>
ilanh added a commit that referenced this pull request Nov 3, 2026
…ials (#28)

## What and why

Credentials read through dynamic lookups (Ruby `ENV[key.to_s.upcase]`,
Go viper `SetEnvPrefix` + `GetString`, litellm choosing the variable by
model name) cannot be found statically, so lint did not flag them and
provider detection missed them. They cannot be solved by analysis, so
this documents the limit and adds a way to declare them once.

- New `[providers] expect = ["GITHUB_PERSONAL_ACCESS_TOKEN"]` in
`openshell-annotate.toml`. Each entry behaves like an `@openshell
provider ENV` directive: it is mapped to a profile (profile dirs or
`[providers.env]`), shows up in the providers JSON/script (or as
`unmapped`, with source `openshell-annotate.toml: [providers].expect`),
and counts as declared for the `lint` env-read check.
- Entries must look like environment variable names; anything else is a
config error.
- README: new "dynamic lookups" item in Limits (with the three real
examples from github-mcp-server, pocketrb and mini-swe-agent) and the
config block.

This fixes the issue the way it proposed: documentation plus the config
key, no attempt at static resolution.

Tests: `tests/providers_expect.rs` (lint flag disappears, providers
output, invalid entry). `TOOLCHAIN=1.94 scripts/ci-local.sh` and stable
clippy pass.

Ordering: the README Limits bullet sits next to the one #21 edits, so
whichever merges second needs a trivial rebase.

Closes #17

Signed-off-by: Ilan Harel <[email protected]>
Co-authored-by: Ilan Harel <[email protected]>
ilanh added a commit that referenced this pull request Nov 3, 2026
…entials (#30)

## What and why

Three of five tested projects needed a hand-written custom provider
profile, and `providers.sh` only said `# UNMAPPED ... write a custom
profile`.

- **`generate --scaffold-profiles DIR`** writes `DIR/<id>.yaml` for
every unmapped credential (id = env var lower-cased with hyphens): the
credential, plus endpoints, `access` and binaries taken from the
`egress` directives in the file that declares the credential. `tls=skip`
endpoints get `allow_uninspected_credentials: true` (OpenShell's profile
lint requires it for L4-only credentialed endpoints); credentials with
no egress in their file get `endpoints: []` and TODO comments. Existing
files are kept, nothing is written without the flag, no secret values
are involved. The `UNMAPPED` comment in `providers.sh` now points to the
flag.
- **Misleading duplicate warning:** `egress X duplicates an endpoint the
profile adds` is now only shown when the profile's `binaries` overlap
the rule's binaries (profile bound to `gh`/`git`, app is `node`: the
explicit rule is needed, no warning). Profiles that list no binaries
keep the old behaviour.
- README section "Credentials with no provider profile"; CLI.md
regenerated.

Checked with the real tool: skeletons generated from
anthropic-proxy-rs's sidecar (`tls=skip` case) and synthetic cases all
pass `openshell provider profile lint -f` (OpenShell 0.1.6 on the dev
server). CI cannot run that, so the tests reload the skeletons with our
own profile loader.

Tests: `tests/scaffold_profiles.rs` (3 tests) and a unit test.
`TOOLCHAIN=1.94 scripts/ci-local.sh` and stable clippy pass.

Ordering: independent (new module `scaffold.rs`; small edits in
`providers.rs`, `generate.rs`, `main.rs`'s `Generate` arm). Another PR
(#26) edits other parts of `main.rs`, and #21/#25 edit `do_scan`; any of
them may need a trivial rebase.

Closes #13

---------

Signed-off-by: Ilan Harel <[email protected]>
Co-authored-by: Ilan Harel <[email protected]>
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.

Inline directives in Ruby, Rust, Java, C# and other languages are silently ignored

1 participant