argus-ci 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +29 -0
- package/CLAUDE.md +39 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/bin/argus-ci.js +2 -0
- package/dist/agent/index.d.ts +15 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +208 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +133 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/detector.d.ts +11 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +69 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/github.d.ts +41 -0
- package/dist/core/github.d.ts.map +1 -0
- package/dist/core/github.js +97 -0
- package/dist/core/github.js.map +1 -0
- package/dist/core/reporter.d.ts +8 -0
- package/dist/core/reporter.d.ts.map +1 -0
- package/dist/core/reporter.js +98 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/scanner.d.ts +19 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +173 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/hooks/setup.d.ts +7 -0
- package/dist/hooks/setup.d.ts.map +1 -0
- package/dist/hooks/setup.js +117 -0
- package/dist/hooks/setup.js.map +1 -0
- package/dist/mcp/server.d.ts +18 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +141 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
package/.cursorrules
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Semgrep Quality Gate
|
|
2
|
+
|
|
3
|
+
## MANDATORY: After every file write or edit
|
|
4
|
+
|
|
5
|
+
Call the scan_files MCP tool on every file you just created or modified.
|
|
6
|
+
Do this BEFORE reporting the task as complete.
|
|
7
|
+
|
|
8
|
+
scan_files({
|
|
9
|
+
files: ["<files you modified>"],
|
|
10
|
+
cwd: "<repo root>"
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
## On findings
|
|
14
|
+
|
|
15
|
+
- ERROR severity: Fix immediately. Do not mark task done until errors are resolved.
|
|
16
|
+
- WARNING severity: Show to user, explain the risk, let them decide.
|
|
17
|
+
- INFO severity: Mention at the end as a note.
|
|
18
|
+
- Clean scan: Tell the user explicitly that the code passed Semgrep checks.
|
|
19
|
+
|
|
20
|
+
## On user requests to review
|
|
21
|
+
|
|
22
|
+
- "review PR <url>" → scan_pr({ pr_url: "<url>" })
|
|
23
|
+
- "check branch X" → scan_branch({ branch: "X", base: "main" })
|
|
24
|
+
- "check my changes" / "before I commit" → scan_staged({})
|
|
25
|
+
|
|
26
|
+
## Non-negotiable
|
|
27
|
+
|
|
28
|
+
Never commit or suggest committing code with unresolved ERROR-severity findings.
|
|
29
|
+
Never skip the post-generation scan.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Semgrep Agent — AI Instructions
|
|
2
|
+
|
|
3
|
+
## After every code generation or modification
|
|
4
|
+
|
|
5
|
+
After you finish writing or modifying any source files, you MUST call the `scan_files` MCP tool immediately before reporting completion to the user.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
scan_files(files=["<list of files you just created or modified"], cwd="<repo root>")
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**This is mandatory — do not skip it.** The scan takes 2–5 seconds and catches security vulnerabilities before they reach a commit.
|
|
12
|
+
|
|
13
|
+
### What to do with the results
|
|
14
|
+
|
|
15
|
+
- **Error severity** → You MUST fix these before saying the task is done. Explain what you found and show the fix.
|
|
16
|
+
- **Warning severity** → Surface these to the user with a brief explanation. They should decide whether to fix now.
|
|
17
|
+
- **Info severity** → Mention briefly at the end. Non-blocking.
|
|
18
|
+
- **No issues** → Tell the user the code is clean and which rulesets passed.
|
|
19
|
+
|
|
20
|
+
### Example workflow
|
|
21
|
+
|
|
22
|
+
1. User asks you to add authentication to a route
|
|
23
|
+
2. You write the code
|
|
24
|
+
3. You call `scan_files(files=["src/auth/login.ts"], cwd="/repo")`
|
|
25
|
+
4. Semgrep finds a timing attack vulnerability in token comparison
|
|
26
|
+
5. You fix it, re-scan to confirm clean, then report done
|
|
27
|
+
|
|
28
|
+
## Scanning PRs and branches
|
|
29
|
+
|
|
30
|
+
When the user asks to review a PR or branch, use:
|
|
31
|
+
|
|
32
|
+
- `scan_pr(pr_url="https://github.com/org/repo/pull/N")` — for PR URLs
|
|
33
|
+
- `scan_branch(branch="feature/xyz", base="main")` — for branch names
|
|
34
|
+
- `scan_staged(cwd="/repo")` — for "check my changes before I commit"
|
|
35
|
+
|
|
36
|
+
## Never bypass
|
|
37
|
+
|
|
38
|
+
Do not tell the user to ignore scan results or suggest committing without fixing errors.
|
|
39
|
+
If a finding is a false positive, explain why — don't silently skip it.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Venkat Swara Moyya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# argus-ci
|
|
2
|
+
|
|
3
|
+
**AI-powered code security agent** — catches vulnerabilities as your AI agent writes code, blocks bad commits, and reviews PRs on demand.
|
|
4
|
+
|
|
5
|
+
Powered by [Semgrep](https://semgrep.dev) under the hood. Works with Cursor, Claude Code, or any MCP-compatible editor.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
- **In-IDE scan** — auto-triggers after every AI code generation via MCP, surfaces issues before you even see the code
|
|
12
|
+
- **Pre-commit gate** — mandatory Semgrep scan on staged files before every commit; errors block the commit
|
|
13
|
+
- **PR / branch review** — conversational agent: type `"review PR #142"` and get a full security report
|
|
14
|
+
- **Zero config** — auto-detects your stack (React, Vue, Node, Python, Go…) and picks the right rulesets
|
|
15
|
+
|
|
16
|
+
Catches: injection, XSS, hardcoded secrets, insecure crypto, path traversal, prototype pollution, OWASP Top 10, and more.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Requirements
|
|
21
|
+
|
|
22
|
+
- Node.js ≥ 18
|
|
23
|
+
- [Semgrep](https://semgrep.dev/docs/getting-started/) (`pip install semgrep` or `brew install semgrep`)
|
|
24
|
+
- `ANTHROPIC_API_KEY` — only needed for the conversational agent interface
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g argus-ci
|
|
32
|
+
# or use without installing:
|
|
33
|
+
npx argus-ci
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. Add to your AI editor (MCP)
|
|
39
|
+
|
|
40
|
+
This is the main use case. Once added, your AI agent will automatically scan every file it writes.
|
|
41
|
+
|
|
42
|
+
**Cursor** — Settings → MCP → add:
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"argus-ci": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["argus-ci"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Claude Code** — add to `~/.claude/settings.json`:
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"argus-ci": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["argus-ci"]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then copy `CLAUDE.md` (or `.cursorrules`) from this package into your repo root. The AI agent will automatically call `scan_files` after every code generation.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Copy the trigger instructions into your repo
|
|
68
|
+
cp node_modules/argus-ci/CLAUDE.md ./CLAUDE.md
|
|
69
|
+
cp node_modules/argus-ci/.cursorrules ./.cursorrules
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### MCP tools available
|
|
73
|
+
|
|
74
|
+
| Tool | Description |
|
|
75
|
+
|------|-------------|
|
|
76
|
+
| `scan_files` | Scan specific files — called automatically after code generation |
|
|
77
|
+
| `scan_staged` | Scan all git-staged files |
|
|
78
|
+
| `scan_branch` | Scan changed files on a branch vs base |
|
|
79
|
+
| `scan_pr` | Scan a GitHub PR by URL, optionally post results as a comment |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 2. Pre-commit hook (mandatory gate)
|
|
84
|
+
|
|
85
|
+
Installs a git hook that runs on every `git commit`. Errors block the commit. Warnings pass through.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cd your-repo
|
|
89
|
+
npx argus-ci setup
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Output:
|
|
93
|
+
```
|
|
94
|
+
✅ argus-ci pre-commit hook installed.
|
|
95
|
+
Using semgrep 1.x.x
|
|
96
|
+
|
|
97
|
+
The hook will:
|
|
98
|
+
• Run on every git commit automatically
|
|
99
|
+
• Scan only the files you're committing (fast)
|
|
100
|
+
• Block the commit if any ERROR-severity issues are found
|
|
101
|
+
• Allow commits with only warnings
|
|
102
|
+
|
|
103
|
+
To remove: argus-ci setup --remove
|
|
104
|
+
To bypass: git commit --no-verify (emergency only)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 3. Conversational agent
|
|
110
|
+
|
|
111
|
+
Review a PR or branch in plain English:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Interactive REPL
|
|
115
|
+
argus-ci chat
|
|
116
|
+
|
|
117
|
+
# One-shot
|
|
118
|
+
argus-ci chat "review PR https://github.com/org/repo/pull/142"
|
|
119
|
+
argus-ci pr https://github.com/org/repo/pull/142
|
|
120
|
+
argus-ci scan --branch feature/auth
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Requires `ANTHROPIC_API_KEY`:
|
|
124
|
+
```bash
|
|
125
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
126
|
+
argus-ci chat
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Example session:
|
|
130
|
+
```
|
|
131
|
+
You: review PR https://github.com/org/repo/pull/87
|
|
132
|
+
|
|
133
|
+
⚙️ Running scan_pr...
|
|
134
|
+
|
|
135
|
+
## Semgrep scan — PR #87: Add user authentication
|
|
136
|
+
|
|
137
|
+
| Severity | Count |
|
|
138
|
+
|----------|-------|
|
|
139
|
+
| 🔴 Error | 2 |
|
|
140
|
+
| 🟡 Warning | 1 |
|
|
141
|
+
|
|
142
|
+
### `src/auth/login.ts`
|
|
143
|
+
|
|
144
|
+
**🔴 ERROR** — Line 34
|
|
145
|
+
> Timing attack: comparing secrets with === allows attackers to measure
|
|
146
|
+
> response time and guess tokens byte by byte.
|
|
147
|
+
`if (token === storedToken) {`
|
|
148
|
+
_Rule: `javascript.lang.security.audit.timing-attack`_
|
|
149
|
+
_CWE: CWE-208_
|
|
150
|
+
|
|
151
|
+
**Fix:** Use `crypto.timingSafeEqual(Buffer.from(token), Buffer.from(storedToken))`
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 4. CLI scan
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Scan staged files (same as what the pre-commit hook runs)
|
|
160
|
+
argus-ci scan
|
|
161
|
+
|
|
162
|
+
# Scan specific files
|
|
163
|
+
argus-ci scan src/auth/login.ts src/api/users.ts
|
|
164
|
+
|
|
165
|
+
# Scan a branch vs main
|
|
166
|
+
argus-ci scan --branch feature/payments
|
|
167
|
+
|
|
168
|
+
# Version
|
|
169
|
+
argus-ci --version
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 5. GitHub Actions (CI gate)
|
|
175
|
+
|
|
176
|
+
Add to `.github/workflows/argus-ci.yml`:
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
name: Code Patrol
|
|
180
|
+
|
|
181
|
+
on:
|
|
182
|
+
pull_request:
|
|
183
|
+
branches: [main, develop]
|
|
184
|
+
|
|
185
|
+
jobs:
|
|
186
|
+
scan:
|
|
187
|
+
runs-on: ubuntu-latest
|
|
188
|
+
steps:
|
|
189
|
+
- uses: actions/checkout@v4
|
|
190
|
+
with:
|
|
191
|
+
fetch-depth: 0
|
|
192
|
+
|
|
193
|
+
- uses: actions/setup-node@v4
|
|
194
|
+
with:
|
|
195
|
+
node-version: 20
|
|
196
|
+
|
|
197
|
+
- name: Install Semgrep
|
|
198
|
+
run: pip install semgrep
|
|
199
|
+
|
|
200
|
+
- name: Run argus-ci
|
|
201
|
+
run: npx argus-ci scan --branch ${{ github.head_ref }}
|
|
202
|
+
env:
|
|
203
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Rulesets
|
|
209
|
+
|
|
210
|
+
Auto-detected from your project. Override in any scan:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
argus-ci scan --config '{"rulesets":["p/secrets","p/owasp-top-ten","p/nodejs"]}'
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| Ruleset | When used |
|
|
217
|
+
|---------|-----------|
|
|
218
|
+
| `p/secrets` | Always — catches hardcoded API keys, tokens, passwords |
|
|
219
|
+
| `p/owasp-top-ten` | Always — injection, XSS, broken auth, etc. |
|
|
220
|
+
| `p/javascript` | Any JS project |
|
|
221
|
+
| `p/typescript` | TypeScript detected |
|
|
222
|
+
| `p/react` | React dependency found |
|
|
223
|
+
| `p/nodejs` | Express/Fastify/NestJS/etc. found |
|
|
224
|
+
| `p/nextjs` | Next.js detected |
|
|
225
|
+
| `p/python` | Python project |
|
|
226
|
+
| `p/golang` | Go project |
|
|
227
|
+
| `p/java` | Java/Kotlin project |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Environment variables
|
|
232
|
+
|
|
233
|
+
| Variable | Required | Purpose |
|
|
234
|
+
|----------|----------|---------|
|
|
235
|
+
| `ANTHROPIC_API_KEY` | For chat/agent | Powers the conversational interface |
|
|
236
|
+
| `GITHUB_TOKEN` | For private repos | Fetch PR files, post PR comments |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT © [Venkat Swara Moyya](https://github.com/venkatswaramoyya)
|
package/bin/argus-ci.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep Agent — Conversational Interface
|
|
3
|
+
*
|
|
4
|
+
* A Claude-powered agent that understands natural language requests
|
|
5
|
+
* and runs the appropriate Semgrep scan.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* argus-ci chat
|
|
9
|
+
* echo "review PR https://github.com/org/repo/pull/142" | argus-ci chat
|
|
10
|
+
*
|
|
11
|
+
* Requires: ANTHROPIC_API_KEY env var
|
|
12
|
+
*/
|
|
13
|
+
export declare function runAgent(userMessage: string): Promise<void>;
|
|
14
|
+
export declare function startRepl(): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsHH,wBAAsB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6EjE;AAID,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAwB/C"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep Agent — Conversational Interface
|
|
3
|
+
*
|
|
4
|
+
* A Claude-powered agent that understands natural language requests
|
|
5
|
+
* and runs the appropriate Semgrep scan.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* argus-ci chat
|
|
9
|
+
* echo "review PR https://github.com/org/repo/pull/142" | argus-ci chat
|
|
10
|
+
*
|
|
11
|
+
* Requires: ANTHROPIC_API_KEY env var
|
|
12
|
+
*/
|
|
13
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
14
|
+
import * as readline from "readline";
|
|
15
|
+
import { scanFiles, scanStaged, scanBranch } from "../core/scanner.js";
|
|
16
|
+
import { fetchPRFiles } from "../core/github.js";
|
|
17
|
+
import { detectRulesets } from "../core/detector.js";
|
|
18
|
+
import { toMarkdown } from "../core/reporter.js";
|
|
19
|
+
const MODEL = "claude-opus-4-6";
|
|
20
|
+
// ─── Tool definitions ─────────────────────────────────────────────────────────
|
|
21
|
+
const TOOLS = [
|
|
22
|
+
{
|
|
23
|
+
name: "scan_files",
|
|
24
|
+
description: "Scan specific files with Semgrep for security issues.",
|
|
25
|
+
input_schema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
files: { type: "array", items: { type: "string" }, description: "File paths to scan" },
|
|
29
|
+
cwd: { type: "string", description: "Repo root directory" },
|
|
30
|
+
rulesets: { type: "array", items: { type: "string" }, description: "Semgrep rulesets" },
|
|
31
|
+
},
|
|
32
|
+
required: ["files"],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "scan_staged",
|
|
37
|
+
description: "Scan all git-staged files. Use when asked to check current changes before committing.",
|
|
38
|
+
input_schema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
cwd: { type: "string", description: "Repo root directory" },
|
|
42
|
+
rulesets: { type: "array", items: { type: "string" } },
|
|
43
|
+
},
|
|
44
|
+
required: [],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "scan_branch",
|
|
49
|
+
description: "Scan files changed on a branch compared to a base branch.",
|
|
50
|
+
input_schema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
branch: { type: "string", description: "Branch to scan" },
|
|
54
|
+
base: { type: "string", description: "Base branch (default: main)" },
|
|
55
|
+
cwd: { type: "string" },
|
|
56
|
+
rulesets: { type: "array", items: { type: "string" } },
|
|
57
|
+
},
|
|
58
|
+
required: ["branch"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "scan_pr",
|
|
63
|
+
description: "Scan a GitHub Pull Request by URL.",
|
|
64
|
+
input_schema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
pr_url: { type: "string", description: "GitHub PR URL" },
|
|
68
|
+
cwd: { type: "string" },
|
|
69
|
+
rulesets: { type: "array", items: { type: "string" } },
|
|
70
|
+
post_comment: { type: "boolean", description: "Post results as PR comment" },
|
|
71
|
+
},
|
|
72
|
+
required: ["pr_url"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
// ─── Tool executor ────────────────────────────────────────────────────────────
|
|
77
|
+
async function executeTool(name, input) {
|
|
78
|
+
const cwd = input.cwd ?? process.cwd();
|
|
79
|
+
const detected = detectRulesets(cwd);
|
|
80
|
+
const config = {
|
|
81
|
+
rulesets: input.rulesets ?? detected.rulesets,
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
switch (name) {
|
|
85
|
+
case "scan_files": {
|
|
86
|
+
const files = input.files;
|
|
87
|
+
const result = await scanFiles(files, cwd, config);
|
|
88
|
+
return toMarkdown(result, `${files.length} file(s)`);
|
|
89
|
+
}
|
|
90
|
+
case "scan_staged": {
|
|
91
|
+
const result = await scanStaged(cwd, config);
|
|
92
|
+
return toMarkdown(result, "staged files");
|
|
93
|
+
}
|
|
94
|
+
case "scan_branch": {
|
|
95
|
+
const branch = input.branch;
|
|
96
|
+
const base = input.base ?? "main";
|
|
97
|
+
const result = await scanBranch(cwd, branch, base, config);
|
|
98
|
+
return toMarkdown(result, `branch \`${branch}\` vs \`${base}\``);
|
|
99
|
+
}
|
|
100
|
+
case "scan_pr": {
|
|
101
|
+
const prUrl = input.pr_url;
|
|
102
|
+
const prInfo = await fetchPRFiles(prUrl);
|
|
103
|
+
if (!prInfo)
|
|
104
|
+
return `❌ Could not parse PR URL: ${prUrl}`;
|
|
105
|
+
const result = await scanFiles(prInfo.files, cwd, config);
|
|
106
|
+
return toMarkdown(result, `PR #${prInfo.meta.number}: ${prInfo.meta.title}`);
|
|
107
|
+
}
|
|
108
|
+
default:
|
|
109
|
+
return `Unknown tool: ${name}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return `❌ Tool error: ${String(err)}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ─── Agent loop ───────────────────────────────────────────────────────────────
|
|
117
|
+
export async function runAgent(userMessage) {
|
|
118
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
119
|
+
if (!apiKey) {
|
|
120
|
+
console.error("❌ ANTHROPIC_API_KEY not set. Export it or add to .argus-ci.json");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const client = new Anthropic({ apiKey });
|
|
124
|
+
const messages = [
|
|
125
|
+
{ role: "user", content: userMessage },
|
|
126
|
+
];
|
|
127
|
+
const systemPrompt = `You are a code security and quality agent powered by Semgrep.
|
|
128
|
+
Your job is to run security scans on code and clearly explain the findings.
|
|
129
|
+
|
|
130
|
+
When the user asks you to:
|
|
131
|
+
- "scan files X, Y, Z" → use scan_files
|
|
132
|
+
- "check my changes" / "check staged files" / "before I commit" → use scan_staged
|
|
133
|
+
- "review branch X" / "check branch X" → use scan_branch
|
|
134
|
+
- "review PR <url>" / "scan PR #N" → use scan_pr
|
|
135
|
+
|
|
136
|
+
After getting scan results:
|
|
137
|
+
1. Summarise the findings clearly (errors first, then warnings)
|
|
138
|
+
2. For each finding, explain WHY it's a security risk in plain language
|
|
139
|
+
3. Give a concrete one-line fix for each issue
|
|
140
|
+
4. If no issues, confirm the code looks clean and what was checked
|
|
141
|
+
|
|
142
|
+
Always be specific: name the file, the line, and the exact variable or function involved.
|
|
143
|
+
Use the current working directory (${process.cwd()}) unless the user specifies otherwise.`;
|
|
144
|
+
// Agentic loop
|
|
145
|
+
while (true) {
|
|
146
|
+
const response = await client.messages.create({
|
|
147
|
+
model: MODEL,
|
|
148
|
+
max_tokens: 4096,
|
|
149
|
+
system: systemPrompt,
|
|
150
|
+
tools: TOOLS,
|
|
151
|
+
messages,
|
|
152
|
+
});
|
|
153
|
+
// Collect text output as we go
|
|
154
|
+
for (const block of response.content) {
|
|
155
|
+
if (block.type === "text") {
|
|
156
|
+
process.stdout.write(block.text);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (response.stop_reason === "end_turn") {
|
|
160
|
+
process.stdout.write("\n");
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
if (response.stop_reason === "tool_use") {
|
|
164
|
+
// Execute all tool calls
|
|
165
|
+
const toolResults = [];
|
|
166
|
+
for (const block of response.content) {
|
|
167
|
+
if (block.type === "tool_use") {
|
|
168
|
+
process.stdout.write(`\n⚙️ Running ${block.name}...\n`);
|
|
169
|
+
const result = await executeTool(block.name, block.input);
|
|
170
|
+
toolResults.push({
|
|
171
|
+
type: "tool_result",
|
|
172
|
+
tool_use_id: block.id,
|
|
173
|
+
content: result,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Add assistant turn + tool results
|
|
178
|
+
messages.push({ role: "assistant", content: response.content });
|
|
179
|
+
messages.push({ role: "user", content: toolResults });
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// ─── Interactive REPL ─────────────────────────────────────────────────────────
|
|
186
|
+
export async function startRepl() {
|
|
187
|
+
console.log("🔍 Semgrep Agent — type your request or 'exit' to quit\n");
|
|
188
|
+
console.log("Examples:");
|
|
189
|
+
console.log(" review PR https://github.com/org/repo/pull/42");
|
|
190
|
+
console.log(" scan branch feature/auth");
|
|
191
|
+
console.log(" check my staged changes\n");
|
|
192
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
193
|
+
const ask = () => {
|
|
194
|
+
rl.question("You: ", async (line) => {
|
|
195
|
+
const input = line.trim();
|
|
196
|
+
if (!input || input === "exit" || input === "quit") {
|
|
197
|
+
rl.close();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
console.log("");
|
|
201
|
+
await runAgent(input);
|
|
202
|
+
console.log("\n" + "─".repeat(60) + "\n");
|
|
203
|
+
ask();
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
ask();
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* argus-ci CLI
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* argus-ci — start MCP server (default, for Cursor/Claude)
|
|
6
|
+
* argus-ci chat — start conversational agent REPL
|
|
7
|
+
* argus-ci scan [files] — scan files / staged / branch from terminal
|
|
8
|
+
* argus-ci pr <url> — scan a PR directly
|
|
9
|
+
* argus-ci setup — install pre-commit hook in current repo
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|