Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
97b072b
feat(dommyrock-analyzer-cli): initial support for agentic cli generation
dommyrock Mar 17, 2026
5ae662c
feat(dommyrock-analyzer-cli): context and skill generation
dommyrock Mar 17, 2026
63681bb
feat(dommyrock-analyzer-cli): generate skills files
dommyrock Mar 17, 2026
8774a46
feat(dommyrock-analyzer-cli): multi service api support
dommyrock Mar 17, 2026
894fa7f
feat(dommyrock-analyzer-cli): update shared skill gen code and rename…
dommyrock Mar 17, 2026
56bcaa3
feat(dommyrock-analyzer-cli): add to Limit response fields (context…
dommyrock Mar 17, 2026
109ae11
feat(dommyrock-analyzer-cli): add --paginate-all flag to not overload…
dommyrock Mar 17, 2026
3f4e9b1
feat(dommyrock-analyzer-cli): update test structure and add paginatio…
dommyrock Mar 17, 2026
6663c87
feat(dommyrock-analyzer-cli): cargo fmt & clippy
dommyrock Mar 17, 2026
e60edd4
feat(dommyrock-analyzer-cli): fix render_sub_resources
dommyrock Mar 18, 2026
955d22c
feat(dommyrock-analyzer-cli): make skill generation service agnostic
dommyrock Mar 18, 2026
4021378
feat(dommyrock-analyzer-cli): support table and csv --format and gene…
dommyrock Mar 18, 2026
48a162b
feat(dommyrock-analyzer-cli): add test-cases crate and improve tests …
dommyrock Mar 18, 2026
6f3ecde
feat(dommyrock-analyzer-cli): rm *discovery.json file from git
dommyrock Mar 18, 2026
e8affcd
feat(dommyrock-analyzer-cli): claude skills and context discovery logic
dommyrock Mar 24, 2026
4c1a499
feat(dommyrock-analyzer-cli): discovery file caching fallbacks on Net…
dommyrock Mar 24, 2026
a89b0ee
feat(dommyrock-analyzer-cli): rename cached discovery file and handle…
dommyrock Mar 24, 2026
1d70660
feat(dommyrock-analyzer-cli): simplify analyzer-shared skill generation
dommyrock Mar 24, 2026
b43da54
feat(dommyrock-analyzer-cli): fix --format arg
dommyrock Mar 24, 2026
a5f9682
feat(dommyrock-analyzer-cli): make api-key optional for local testing…
dommyrock Mar 24, 2026
3cf04c2
feat(dommyrock-analyzer-cli): fix pagination results interpretation
dommyrock Mar 24, 2026
6227de2
feat(dommyrock-analyzer-cli): update skill reasoning for scan results…
dommyrock Mar 24, 2026
5c75da2
feat(dommyrock-analyzer-cli): fix results pagination in skills
dommyrock Mar 24, 2026
b96401c
feat(dommyrock-analyzer-cli): fix skill detail generation
dommyrock Mar 24, 2026
5bc652b
feat(dommyrock-analyzer-cli): fix skill detail generation for analyze…
dommyrock Mar 24, 2026
e75fadf
feat(dommyrock-analyzer-cli): fix skill detail generation and fix par…
dommyrock Mar 25, 2026
d24752f
feat(dommyrock-analyzer-cli): regenerate skills
dommyrock Mar 25, 2026
f9c0ef1
feat(dommyrock-analyzer-cli): update skill generation for front matte…
dommyrock Mar 25, 2026
5104143
feat(dommyrock-analyzer-cli): update cache when --discovery is run to…
dommyrock Mar 25, 2026
b7761e2
feat(dommyrock-analyzer-cli): use openapi-to-discovery crate from ssh…
dommyrock Mar 25, 2026
78737c6
feat(dommyrock-analyzer-cli): rename mod to agent_api
dommyrock Mar 25, 2026
1a5dff1
feat(dommyrock-analyzer-cli): append bearer token when calling openap…
dommyrock Mar 27, 2026
6a4476e
feat(dommyrock-analyzer-cli): env config for common env's like dev,st…
dommyrock Mar 27, 2026
dc4027a
feat(dommyrock-analyzer-cli): query param pagination skill hints
dommyrock Mar 27, 2026
93c7707
feat(dommyrock-analyzer-cli): update ci/cd to be able to pull private…
dommyrock Mar 27, 2026
9fd72a6
feat(dommyrock-analyzer-cli): fix ci/cd build for :finding structs th…
dommyrock Mar 27, 2026
263bb6a
feat(dommyrock-analyzer-cli): rm login fallback for local setup afte…
dommyrock Mar 27, 2026
41c21c8
feat(dommyrock-analyzer-cli): fix test
dommyrock Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[net]
git-fetch-with-cli = true
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.OPENAPI_TO_DISCOVERY_DEPLOY_KEY }}
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo check --all-targets
Expand All @@ -25,6 +28,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.OPENAPI_TO_DISCOVERY_DEPLOY_KEY }}
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test
Expand All @@ -44,6 +50,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.OPENAPI_TO_DISCOVERY_DEPLOY_KEY }}
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.OPENAPI_TO_DISCOVERY_DEPLOY_KEY }}
- uses: dtolnay/rust-toolchain@stable

- uses: taiki-e/setup-cross-toolchain-action@v1
Expand Down
96 changes: 96 additions & 0 deletions .github/workflows/update-discovery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Update Discovery Documents

on:
schedule:
- cron: '0 * * * *' # hourly
workflow_dispatch: {} # manual trigger

permissions:
contents: write
pull-requests: write

env:
SERVICES: |
analyzer=https://analyzer.exein.dev/analyzer-discovery.json
# isaac=https://analyzer.exein.dev/isaac-discovery.json
# vuln-tracker=https://analyzer.exein.dev/vuln-tracker-discovery.json

jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Fetch all discovery documents
run: |
mkdir -p discovery
while IFS='=' read -r name url; do
[ -z "$name" ] && continue
[[ "$name" == \#* ]] && continue
echo "Fetching $name from $url"
curl -sf "$url" \
-H "Authorization: Bearer ${{ secrets.ANALYZER_API_KEY }}" \
-o "discovery/${name}.json.new" || echo "WARN: failed to fetch $name"
done <<< "$SERVICES"

- name: Check for changes
id: diff
run: |
changed=false
for f in discovery/*.json.new; do
[ ! -f "$f" ] && continue
base="${f%.new}"
if ! diff -q "$base" "$f" > /dev/null 2>&1; then
mv "$f" "$base"
changed=true
else
rm "$f"
fi
done
echo "changed=$changed" >> "$GITHUB_OUTPUT"

- name: Configure SSH for private dependencies
if: steps.diff.outputs.changed == 'true'
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.OPENAPI_TO_DISCOVERY_DEPLOY_KEY }}

- name: Install Rust toolchain
if: steps.diff.outputs.changed == 'true'
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo
if: steps.diff.outputs.changed == 'true'
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}

- name: Regenerate skills
if: steps.diff.outputs.changed == 'true'
run: |
cargo build --release
for f in discovery/*.json; do
[ ! -f "$f" ] && continue
name="$(basename "$f" .json)"
echo "Generating skills for $name"
./target/release/analyzer --discovery "$f" generate-skills \
|| echo "WARN: failed to generate skills for $name"
done

- name: Create PR
if: steps.diff.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v6
with:
title: "update: Discovery Documents + skills"
body: |
Auto-generated from upstream API changes.

Discovery documents in `discovery/` have changed.
Skill files in `skills/` have been regenerated.
branch: update-discovery
commit-message: "update: discovery documents + regenerated skills"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

note: This was in v1 , i can remove this now if we don't build discovery.json at the c/cd time and pull it on agent-init cmd startup time

delete-branch: true
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/target
Cargo.lock
usage_examples.txt
*-discovery.json
*20*.txt
.env
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When using or contributing to this repository, follow the guidelines in [CONTEXT.md](CONTEXT.md).
145 changes: 145 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Analyzer CLI (`analyzer`) Context

The `analyzer` CLI provides dynamic access to firmware and software security APIs by parsing Discovery Documents at runtime. It supports multiple API services through a compile-time service registry, with each service backed by its own Discovery Document fetched and cached automatically.

## Registered Services

| Alias | Description |
|-------|-------------|
| `analyzer` | Firmware and software image security analysis |

## Agent Integration (Claude Code)

To use the `analyzer` CLI with Claude Code (or other AI agents), run the one-time setup:

```bash
analyzer init-agent
```

This installs skills, usage context, and permissions into `~/.claude/` so that Claude Code automatically discovers the `analyzer` CLI in every project — no source code or manual configuration needed.

What it writes:

| File | Purpose |
|------|---------|
| `~/.claude/skills/` | Per-resource API skill files (generated from discovery documents) |
| `~/.claude/settings.json` | Allowlists the `analyzer` binary for Claude Code |

Skills are loaded on-demand by Claude Code — no global `CLAUDE.md` or `CONTEXT.md` is written, so the analyzer context only appears when relevant.

Re-run `analyzer init-agent` after upgrading to refresh skills.

## Rules of Engagement for Agents

* **Schema First:** *If you don't know the exact JSON payload structure, run `analyzer schema <service>.<resource>.<method>` first to inspect the schema before executing.*
* **Context Window Protection:** *API responses can be large. ALWAYS use `--fields` when listing or getting resources to avoid overwhelming your context window.*
* **Dry-Run Safety:** *Always use the `--dry-run` flag for mutating operations (create, update, delete) to validate your JSON payload before actual execution.*
* **Poll, Don't Guess:** *After scheduling a scan, poll the status endpoint until it completes. Do not assume timing or make further requests against incomplete scans.*

## Core Syntax

```bash
# API commands (service name is first positional arg)
analyzer api <service> <resource> [sub-resource...] <method> [flags]

# Schema introspection (service name is first dotted segment)
analyzer schema <service>.<resource>.<method>

# Navigation
analyzer api analyzer --help
analyzer api analyzer scans --help
```

### Key Flags

| Flag | Purpose |
|------|---------|
| `--params '<JSON>'` | Path and query parameters (e.g., `{"id": "..."}`) |
| `--json '<JSON>'` | Request body for POST/PUT/PATCH methods |
| `--fields '<MASK>'` | Comma-separated response fields to include (context window protection) |
| `--page-all` | Auto-paginate results as NDJSON (one JSON line per page) |
| `--dry-run` | Validate and print the request without executing |
| `--format human\|json\|table` | Output format (default: `human`) |
| `--discovery <path-or-url>` | Override discovery source for dev/testing |

## Schema Introspection

The CLI is self-documenting. Use `analyzer schema` to discover parameters, request/response schemas, and types at runtime — no static docs needed.

```bash
# Browse all resources for a service
analyzer schema analyzer.api

# Inspect a method's params, types, and defaults
analyzer schema analyzer.scans.create
analyzer schema analyzer.objects.get

# Browse a sub-resource tree
analyzer schema analyzer.scans.compliance-check
```

Use `analyzer schema` output to build your `--params` and `--json` flags.

## Usage Patterns

### Reading Data

Always use `--fields` to minimize tokens.

```bash
# List objects (efficient)
analyzer api analyzer objects list --params '{"limit": 10}' --fields "id,name,tags"

# Get scan details
analyzer api analyzer scans get --params '{"id": "SCAN_ID"}' --fields "id,status,score"

# Check service health
analyzer api analyzer health list
```

### Writing Data

Use `--json` for the request body. Always `--dry-run` first.

```bash
analyzer schema analyzer.objects.create
analyzer api analyzer objects create --json '{"name": "Router FW v2.1"}' --dry-run
```

### Pagination (NDJSON)

Use `--page-all` for large result sets. Output is one JSON line per page.

```bash
analyzer api analyzer scans results get \
--params '{"scan_id": "SCAN_ID", "analysis_id": "cve"}' \
--page-all --fields "id,severity,score"
```

### Deleting Data

```bash
analyzer api analyzer objects delete --params '{"id": "OBJ_ID"}' --dry-run
```

## Human-Friendly Commands

Classic CLI subcommands (unchanged by multi-service routing):

```bash
analyzer login
analyzer whoami
analyzer object list
analyzer scan new --object OBJ_ID -f firmware.bin -t linux --wait
analyzer scan score --scan SCAN_ID
```

## Error Handling

All errors are JSON on stderr with a non-zero exit code:

```json
{"error": {"code": 404, "message": "Object not found"}}
```

Exit `0` = success, non-zero = failure. Parse error JSON to decide next steps.
33 changes: 30 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,36 @@ name = "analyzer"
path = "src/main.rs"

[dependencies]
clap = { version = "4", features = ["derive", "env", "color", "help", "usage", "error-context", "suggestions", "wrap_help"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "stream", "rustls-tls"] }

# internal
openapi-to-discovery = { git = "ssh://git@github.com/exein-io/openapi-to-discovery" }

# 3rdparty
clap = { version = "4", features = [
"derive",
"env",
"color",
"help",
"usage",
"error-context",
"suggestions",
"wrap_help",
"string",
] }
reqwest = { version = "0.12", default-features = false, features = [
"json",
"multipart",
"stream",
"rustls-tls",
] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util"] }
tokio = { version = "1", features = [
"rt-multi-thread",
"macros",
"fs",
"io-util",
] }
tokio-util = { version = "0.7", features = ["io"] }
tokio-stream = "0.1"
futures = "0.3"
Expand All @@ -39,8 +64,10 @@ clap_complete = "4"

[dev-dependencies]
assert_cmd = "2"
filetime = "0.2"
predicates = "3"
tempfile = "3"
test-case = "3"
wiremock = "0.6"

[profile.release]
Expand Down
Loading
Loading