Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 30 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ erDiagram
Project ||--o{ Revision : "has many"
Project ||--o{ Issue : "has many"
Revision ||--o{ Dependency : "contains"
Revision ||--o{ Snippet : "matched in"
Dependency ||--o{ Issue : "flagged by"
Snippet ||--o{ SnippetMatch : "matched at"

Project {
String id "locator: custom+org/project"
Expand Down Expand Up @@ -73,6 +75,22 @@ erDiagram
String cve "CVE identifier (vulns only)"
DateTime created_at
}

Snippet {
String id PK "numeric string, e.g. 1295019"
String locator "matched OSS package: pod+Alamofire$5.11.0"
String purl "pkg:cocoapods/Alamofire@5.11.0"
SnippetKind kind "whole-file or partial match"
f64 highest_match_percentage "0.0-1.0"
u32 match_count "first-party files matched"
}

SnippetMatch {
String path "first-party file path"
f64 match_percentage "0-100 in matchDetails, 0-1 elsewhere"
Vec_Line detected_code "first-party lines + numbers"
Vec_Line reference_code "open-source lines + numbers"
}
```

## Architecture
Expand All @@ -82,7 +100,11 @@ Project (top-level container)
├── latest_revision: LatestRevision
│ └── locator → can fetch full Revision
├── revisions() → Vec<Revision>
│ └── revision.dependencies() → Vec<Dependency>
│ ├── revision.dependencies() → Vec<Dependency>
│ └── get_snippets(revision) → Vec<Snippet> (snippet-scan OSS matches)
│ ├── get_snippet_details(snippet) → matched first-party files
│ ├── get_snippet_match(snippet, path) → side-by-side detected vs reference code
│ └── get_snippet_locations(revision) → flat (snippet, file) report (+ line ranges)
└── get_project_issues() → Vec<Issue>
└── Issues across all revisions/dependencies
```
Expand All @@ -97,6 +119,10 @@ Project (top-level container)
| Dependencies | `GET /v2/revisions/{locator}/dependencies` | For a revision |
| Issues | `GET /v2/issues` | Paginated, filterable by category/project |
| Issue | `GET /v2/issues/{id}` | Single issue with full details |
| Snippets | `GET /revisions/{locator}/snippets` | Paginated; `pageSize` capped at 50 (`list_all` overrides) |
| Snippet paths | `GET /revisions/{locator}/snippets/paths` | File/dir tree, drill in via `path` |
| Snippet details | `GET /revisions/{locator}/snippets/{id}` | Single snippet + its per-file matches |
| Snippet match | `GET /revisions/{locator}/snippets/{id}/matches/{path}` | Side-by-side detected vs reference code |

## Traits

Expand All @@ -112,6 +138,7 @@ Project (top-level container)
- **Revision** - Snapshot at point in time, implements Get/List
- **Dependency** - Package dependency, implements List only (via revision)
- **Issue** - Vulnerability/licensing/quality issue, implements Get/List
- **Snippet** - Third-party (OSS) code matched into first-party files, implements List only (via revision). Read-only; reached through the `get_snippet_*` convenience functions. Quirks: `id` is a string, `matchDetails.matchPercentage` is 0-100 (other percentages are 0-1), and whole-file matches highlight a trailing blank EOF line that is excluded from the reported range.
- **LicenseInfo** - Can be simple string ("MIT") or full object

## Issue Categories
Expand All @@ -127,6 +154,8 @@ Issues come in three categories with different fields:
## Future Work

- **IssueScan** - Issue scans tied to revisions (not yet implemented)
- **Snippet reject/unreject** - Mutating a snippet's rejection status (out of scope for v1)
- **Cross-revision snippet compare** - Diffing snippet matches across revisions (out of scope for v1)

## Nudge

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fossapi"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "Rust client library for the FOSSA REST API"
license = "MIT"
Expand Down
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,36 @@ fossapi list issues --category licensing
fossapi get issue 12345
```

### Snippets

Snippet scanning finds third-party (open-source) code copied into your
first-party files. Each **snippet** is a matched OSS package; its **matches**
are the first-party files where that code was found. The snippet surface is
read-only and scoped to a single revision.

```bash
# List snippets (matched OSS packages) in a revision
fossapi list snippets "custom+1/my-project\$abc123"

# Restrict to a file/directory subtree (defaults to the repo root)
fossapi list snippets "custom+1/my-project\$abc123" --path /src

# Show the file/directory tree where snippets were detected
fossapi list snippet-paths "custom+1/my-project\$abc123"

# Flat report: every match location (first-party file -> matched package)
fossapi list snippet-locations "custom+1/my-project\$abc123"

# ...and resolve the first-party line range for each match (extra API calls)
fossapi list snippet-locations "custom+1/my-project\$abc123" --with-lines

# Get a snippet's details, including its matched first-party files
fossapi get snippet "custom+1/my-project\$abc123" <snippet-id>

# Side-by-side match details (detected vs reference code) at a matched path
fossapi get snippet-match "custom+1/my-project\$abc123" <snippet-id> src/foo.rs
```

### Output Formats

```bash
Expand Down Expand Up @@ -121,8 +151,14 @@ Add to your MCP config:
| Tool | Description |
|------|-------------|
| `get` | Fetch a single project, revision, or issue by ID |
| `list` | List projects, revisions, dependencies, or issues |
| `list` | List projects, revisions, dependencies, issues, or snippet match locations |
| `update` | Update project metadata (title, description, url, public) |
| `snippet_match` | Drill into one snippet match: the matched first-party and reference code |

> **Snippets over MCP:** use `list` with `entity: snippet` and `parent: <revision
> locator>` (optional `path` and `with_lines`) to map third-party matches to
> first-party files, then `snippet_match` to drill into a single match. Snippets
> don't support `get` or `update`.

## Locators

Expand All @@ -131,3 +167,4 @@ FOSSA uses locators to identify entities:
- **Project**: `custom+{org_id}/{project_name}`
- **Revision**: `custom+{org_id}/{project_name}${revision_ref}`
- **Dependency**: `{fetcher}+{package}${version}` (e.g., `npm+lodash$4.17.21`)
- **Snippet**: identified by its parent revision locator plus a snippet ID (a string)
Loading