@aionlabsai/aion 0.2.0 → 0.2.2
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/README.md +283 -9
- package/dist/agents/base-agent.d.ts.map +1 -1
- package/dist/agents/base-agent.js +23 -12
- package/dist/agents/base-agent.js.map +1 -1
- package/dist/agents/synthesizer.d.ts +3 -0
- package/dist/agents/synthesizer.d.ts.map +1 -1
- package/dist/agents/synthesizer.js +93 -52
- package/dist/agents/synthesizer.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +115 -121
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/chat.d.ts +3 -0
- package/dist/cli/commands/chat.d.ts.map +1 -0
- package/dist/cli/commands/chat.js +129 -0
- package/dist/cli/commands/chat.js.map +1 -0
- package/dist/cli/commands/context.d.ts +3 -0
- package/dist/cli/commands/context.d.ts.map +1 -0
- package/dist/cli/commands/context.js +72 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/diff.d.ts +3 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/diff.js +144 -0
- package/dist/cli/commands/diff.js.map +1 -0
- package/dist/cli/commands/health.d.ts.map +1 -1
- package/dist/cli/commands/health.js +17 -0
- package/dist/cli/commands/health.js.map +1 -1
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +52 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/next.d.ts +3 -0
- package/dist/cli/commands/next.d.ts.map +1 -0
- package/dist/cli/commands/next.js +49 -0
- package/dist/cli/commands/next.js.map +1 -0
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +52 -392
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/search.d.ts +3 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +50 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/tree.d.ts +3 -0
- package/dist/cli/commands/tree.d.ts.map +1 -0
- package/dist/cli/commands/tree.js +85 -0
- package/dist/cli/commands/tree.js.map +1 -0
- package/dist/cli/menu.d.ts.map +1 -1
- package/dist/cli/menu.js +56 -1
- package/dist/cli/menu.js.map +1 -1
- package/dist/core/cost-tracker.d.ts +10 -0
- package/dist/core/cost-tracker.d.ts.map +1 -1
- package/dist/core/cost-tracker.js +53 -0
- package/dist/core/cost-tracker.js.map +1 -1
- package/dist/core/orchestrator.d.ts +2 -1
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +2 -2
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/pipelines/audit-pipeline.d.ts +13 -2
- package/dist/core/pipelines/audit-pipeline.d.ts.map +1 -1
- package/dist/core/pipelines/audit-pipeline.js +156 -37
- package/dist/core/pipelines/audit-pipeline.js.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/aion-config.d.ts +26 -0
- package/dist/infra/aion-config.d.ts.map +1 -0
- package/dist/infra/aion-config.js +60 -0
- package/dist/infra/aion-config.js.map +1 -0
- package/dist/infra/aion-ignore.d.ts +3 -0
- package/dist/infra/aion-ignore.d.ts.map +1 -0
- package/dist/infra/aion-ignore.js +35 -0
- package/dist/infra/aion-ignore.js.map +1 -0
- package/dist/infra/audit-cache.d.ts +40 -0
- package/dist/infra/audit-cache.d.ts.map +1 -0
- package/dist/infra/audit-cache.js +94 -0
- package/dist/infra/audit-cache.js.map +1 -0
- package/dist/infra/audit-model.d.ts +72 -0
- package/dist/infra/audit-model.d.ts.map +1 -0
- package/dist/infra/audit-model.js +146 -0
- package/dist/infra/audit-model.js.map +1 -0
- package/dist/infra/audit-report-writer.d.ts +4 -0
- package/dist/infra/audit-report-writer.d.ts.map +1 -0
- package/dist/infra/audit-report-writer.js +290 -0
- package/dist/infra/audit-report-writer.js.map +1 -0
- package/dist/infra/audit-trend.d.ts +17 -0
- package/dist/infra/audit-trend.d.ts.map +1 -0
- package/dist/infra/audit-trend.js +113 -0
- package/dist/infra/audit-trend.js.map +1 -0
- package/dist/infra/code-metrics.d.ts +11 -0
- package/dist/infra/code-metrics.d.ts.map +1 -1
- package/dist/infra/code-metrics.js +62 -0
- package/dist/infra/code-metrics.js.map +1 -1
- package/dist/infra/db/store.d.ts.map +1 -1
- package/dist/infra/db/store.js +14 -7
- package/dist/infra/db/store.js.map +1 -1
- package/dist/infra/embeddings.d.ts +2 -0
- package/dist/infra/embeddings.d.ts.map +1 -1
- package/dist/infra/embeddings.js +33 -15
- package/dist/infra/embeddings.js.map +1 -1
- package/dist/infra/project-report.d.ts +67 -0
- package/dist/infra/project-report.d.ts.map +1 -0
- package/dist/infra/project-report.js +159 -0
- package/dist/infra/project-report.js.map +1 -0
- package/dist/infra/repo-vectors.d.ts +31 -0
- package/dist/infra/repo-vectors.d.ts.map +1 -0
- package/dist/infra/repo-vectors.js +83 -0
- package/dist/infra/repo-vectors.js.map +1 -0
- package/dist/infra/update-check.d.ts +3 -0
- package/dist/infra/update-check.d.ts.map +1 -0
- package/dist/infra/update-check.js +84 -0
- package/dist/infra/update-check.js.map +1 -0
- package/dist/prompts/scanner.d.ts +1 -0
- package/dist/prompts/scanner.d.ts.map +1 -1
- package/dist/prompts/scanner.js +5 -1
- package/dist/prompts/scanner.js.map +1 -1
- package/dist/prompts/synthesizer.d.ts.map +1 -1
- package/dist/prompts/synthesizer.js +7 -23
- package/dist/prompts/synthesizer.js.map +1 -1
- package/dist/providers/cli-provider.d.ts +1 -0
- package/dist/providers/cli-provider.d.ts.map +1 -1
- package/dist/providers/cli-provider.js +22 -2
- package/dist/providers/cli-provider.js.map +1 -1
- package/package.json +24 -19
- package/dist/agents/planner.test.d.ts +0 -2
- package/dist/agents/planner.test.d.ts.map +0 -1
- package/dist/agents/planner.test.js +0 -21
- package/dist/agents/planner.test.js.map +0 -1
- package/dist/core/cost-tracker.test.d.ts +0 -2
- package/dist/core/cost-tracker.test.d.ts.map +0 -1
- package/dist/core/cost-tracker.test.js +0 -36
- package/dist/core/cost-tracker.test.js.map +0 -1
- package/dist/core/pipelines/audit-pipeline.test.d.ts +0 -2
- package/dist/core/pipelines/audit-pipeline.test.d.ts.map +0 -1
- package/dist/core/pipelines/audit-pipeline.test.js +0 -135
- package/dist/core/pipelines/audit-pipeline.test.js.map +0 -1
- package/dist/core/repo-context.d.ts +0 -2
- package/dist/core/repo-context.d.ts.map +0 -1
- package/dist/core/repo-context.js +0 -12
- package/dist/core/repo-context.js.map +0 -1
- package/dist/core/repo-context.test.d.ts +0 -2
- package/dist/core/repo-context.test.d.ts.map +0 -1
- package/dist/core/repo-context.test.js +0 -40
- package/dist/core/repo-context.test.js.map +0 -1
- package/dist/core/runtime-policy.test.d.ts +0 -2
- package/dist/core/runtime-policy.test.d.ts.map +0 -1
- package/dist/core/runtime-policy.test.js +0 -27
- package/dist/core/runtime-policy.test.js.map +0 -1
- package/dist/infra/bm25.test.d.ts +0 -2
- package/dist/infra/bm25.test.d.ts.map +0 -1
- package/dist/infra/bm25.test.js +0 -17
- package/dist/infra/bm25.test.js.map +0 -1
- package/dist/infra/chunker.test.d.ts +0 -2
- package/dist/infra/chunker.test.d.ts.map +0 -1
- package/dist/infra/chunker.test.js +0 -33
- package/dist/infra/chunker.test.js.map +0 -1
- package/dist/infra/db/database.d.ts +0 -4
- package/dist/infra/db/database.d.ts.map +0 -1
- package/dist/infra/db/database.js +0 -25
- package/dist/infra/db/database.js.map +0 -1
- package/dist/infra/evidence-gate.test.d.ts +0 -2
- package/dist/infra/evidence-gate.test.d.ts.map +0 -1
- package/dist/infra/evidence-gate.test.js +0 -36
- package/dist/infra/evidence-gate.test.js.map +0 -1
- package/dist/infra/repo-index.test.d.ts +0 -2
- package/dist/infra/repo-index.test.d.ts.map +0 -1
- package/dist/infra/repo-index.test.js +0 -53
- package/dist/infra/repo-index.test.js.map +0 -1
- package/dist/infra/repo-query.test.d.ts +0 -2
- package/dist/infra/repo-query.test.d.ts.map +0 -1
- package/dist/infra/repo-query.test.js +0 -34
- package/dist/infra/repo-query.test.js.map +0 -1
- package/dist/infra/semgrep.test.d.ts +0 -2
- package/dist/infra/semgrep.test.d.ts.map +0 -1
- package/dist/infra/semgrep.test.js +0 -39
- package/dist/infra/semgrep.test.js.map +0 -1
- package/dist/schemas/audit.test.d.ts +0 -2
- package/dist/schemas/audit.test.d.ts.map +0 -1
- package/dist/schemas/audit.test.js +0 -41
- package/dist/schemas/audit.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,40 +2,314 @@
|
|
|
2
2
|
|
|
3
3
|
Multi-agent AI engineering runtime for auditing, analyzing, reviewing, and fixing code from the terminal.
|
|
4
4
|
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Aion is a terminal-first engineering assistant for working inside codebases. It combines quick local scans with multi-agent AI workflows for deeper analysis.
|
|
8
|
+
|
|
9
|
+
Use it to:
|
|
10
|
+
|
|
11
|
+
- Audit a repository across security, architecture, testing, reliability, data, dependencies, and AI prompt risks.
|
|
12
|
+
- Analyze bugs or issue descriptions.
|
|
13
|
+
- Review files, diffs, or risky changes.
|
|
14
|
+
- Run zero-token local scans for secrets, env vars, SBOM, API maps, and cognitive load.
|
|
15
|
+
- Generate dependency graphs, health reports, churn reports, and onboarding guides.
|
|
16
|
+
- Use natural language from the terminal.
|
|
17
|
+
|
|
5
18
|
## Install
|
|
6
19
|
|
|
7
20
|
```bash
|
|
8
21
|
npm install -g @aionlabsai/aion
|
|
9
22
|
```
|
|
10
23
|
|
|
24
|
+
Verify the install:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
aion --version
|
|
28
|
+
aion --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The package also installs the `ai-runtime` command.
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
- Node.js 18 or newer
|
|
36
|
+
- One configured AI provider for AI-powered commands
|
|
37
|
+
- Git for churn and repository history features
|
|
38
|
+
- Optional: Semgrep for deeper static analysis integration
|
|
39
|
+
|
|
40
|
+
## Provider Setup
|
|
41
|
+
|
|
42
|
+
### Claude SDK
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
export ANTHROPIC_API_KEY="your_key_here"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Claude CLI
|
|
49
|
+
|
|
50
|
+
Without `ANTHROPIC_API_KEY`, Aion can use an authenticated `claude` CLI session when available:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude /login
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### OpenRouter
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export OPENROUTER_API_KEY="your_key_here"
|
|
60
|
+
export OPENROUTER_MODEL="moonshotai/kimi-k2"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Codex
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export AI_RUNTIME_CODEX_MODEL="gpt-5-codex"
|
|
67
|
+
```
|
|
68
|
+
|
|
11
69
|
## Usage
|
|
12
70
|
|
|
13
71
|
```bash
|
|
14
72
|
aion --help
|
|
15
73
|
aion menu
|
|
74
|
+
aion next
|
|
16
75
|
aion audit .
|
|
17
76
|
aion analyze .
|
|
18
77
|
aion "review this project and find risky code"
|
|
19
78
|
```
|
|
20
79
|
|
|
21
|
-
|
|
80
|
+
## Interactive Menu
|
|
22
81
|
|
|
23
|
-
|
|
82
|
+
Run:
|
|
24
83
|
|
|
25
|
-
|
|
26
|
-
|
|
84
|
+
```bash
|
|
85
|
+
aion menu
|
|
86
|
+
```
|
|
27
87
|
|
|
28
|
-
|
|
88
|
+
The menu includes:
|
|
89
|
+
|
|
90
|
+
- Audit presets
|
|
91
|
+
- Reports
|
|
92
|
+
- Dependency graph
|
|
93
|
+
- Churn analysis
|
|
94
|
+
- Local scans
|
|
95
|
+
- Pattern detection
|
|
96
|
+
- Health score
|
|
97
|
+
- Explain and onboarding commands
|
|
98
|
+
- Built-in terminal documentation
|
|
99
|
+
- Natural language mode
|
|
100
|
+
|
|
101
|
+
## Common Commands
|
|
102
|
+
|
|
103
|
+
### Audit
|
|
29
104
|
|
|
30
105
|
```bash
|
|
31
|
-
|
|
106
|
+
aion audit . --dry-run
|
|
107
|
+
aion audit . --local-only
|
|
108
|
+
aion audit .
|
|
109
|
+
aion audit . --preset security
|
|
110
|
+
aion audit . --preset ai --budget normal
|
|
111
|
+
aion audit . --domains security,dependencies,compliance
|
|
112
|
+
aion audit . --preset security --max-files 20
|
|
113
|
+
aion audit . --ai-context-budget 6000
|
|
32
114
|
```
|
|
33
115
|
|
|
34
|
-
|
|
116
|
+
Useful presets:
|
|
117
|
+
|
|
118
|
+
- `security`
|
|
119
|
+
- `ai`
|
|
120
|
+
- `backend`
|
|
121
|
+
- `devops`
|
|
122
|
+
- `quality`
|
|
123
|
+
- `saas`
|
|
124
|
+
- `fintech`
|
|
125
|
+
- `full` requires `--force-full` because it can start every AI scanner and spend heavily.
|
|
35
126
|
|
|
36
|
-
|
|
127
|
+
Cost controls:
|
|
37
128
|
|
|
38
129
|
```bash
|
|
39
|
-
|
|
130
|
+
aion audit . --dry-run
|
|
131
|
+
aion audit . --local-only
|
|
132
|
+
aion audit . --preset security --scanners 2
|
|
133
|
+
aion audit . --preset security --max-files 20
|
|
134
|
+
aion audit . --preset full --force-full --budget deep --scanner-timeout 240
|
|
40
135
|
```
|
|
41
136
|
|
|
137
|
+
Local scans still inspect the whole repository. `--max-files` only limits the prioritized file list handed to AI scanners.
|
|
138
|
+
|
|
139
|
+
Recommended low-cost audit flow:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
aion audit . --dry-run --max-files 20
|
|
143
|
+
aion audit . --local-only
|
|
144
|
+
aion audit . --preset security --scanners 2 --max-files 20
|
|
145
|
+
aion context --audit --budget 6000
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This produces compact reports that are safe to send to another AI without pasting raw JSON or all source files.
|
|
149
|
+
|
|
150
|
+
### Analyze, Review, Fix
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
aion analyze "login fails after token refresh"
|
|
154
|
+
aion review src/auth/middleware.ts
|
|
155
|
+
aion fix "users can bypass tenant isolation"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Local Scans
|
|
159
|
+
|
|
160
|
+
These scans do not require model calls:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
aion scan secrets
|
|
164
|
+
aion scan env-audit
|
|
165
|
+
aion scan sbom
|
|
166
|
+
aion scan sbom --unpinned-only
|
|
167
|
+
aion scan api-map
|
|
168
|
+
aion scan cognitive-load
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Reports And Graphs
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
aion health
|
|
175
|
+
aion report
|
|
176
|
+
aion report latest
|
|
177
|
+
aion report --md
|
|
178
|
+
aion context --audit
|
|
179
|
+
aion context "audit report generation" --budget 8000
|
|
180
|
+
aion search "audit report generation" --rebuild
|
|
181
|
+
aion search "where reports are saved" --semantic --rebuild
|
|
182
|
+
aion tree --hotspots --rebuild
|
|
183
|
+
aion graph
|
|
184
|
+
aion churn
|
|
185
|
+
aion patterns
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Explain And Onboard
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
aion explain src/index.ts
|
|
192
|
+
aion impact src/index.ts
|
|
193
|
+
aion onboard
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Natural Language
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
aion "audit this repository for dependency and secret risks"
|
|
200
|
+
aion "explain the auth module"
|
|
201
|
+
aion "find risky code in the payment flow"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Runtime Files
|
|
205
|
+
|
|
206
|
+
Aion writes local runtime data under project-local or user-local folders depending on the command:
|
|
207
|
+
|
|
208
|
+
- `.ai-runtime/` for generated reports and repository indexes
|
|
209
|
+
- `.ai-runtime/reports/latest-audit.json` points to the latest audit run
|
|
210
|
+
- `.ai-runtime/reports/audits/<timestamp>/` stores organized audit output
|
|
211
|
+
- `.ai-memory/` for optional memory/knowledge files
|
|
212
|
+
- `~/.ai-runtime/` for task history unless `AI_RUNTIME_DB_PATH` is set
|
|
213
|
+
|
|
214
|
+
Each organized audit run contains:
|
|
215
|
+
|
|
216
|
+
- `index.html`
|
|
217
|
+
- `summary.md`
|
|
218
|
+
- `digest.md`
|
|
219
|
+
- `ai-context.md`
|
|
220
|
+
- `action-plan.md`
|
|
221
|
+
- `report.json`
|
|
222
|
+
- `action-items.json`
|
|
223
|
+
- `files-hotspots.json`
|
|
224
|
+
- `README.md`
|
|
225
|
+
- `findings-by-persona.json`
|
|
226
|
+
- `findings-by-severity.json`
|
|
227
|
+
- `findings-by-category.json`
|
|
228
|
+
|
|
229
|
+
For human reading, open `digest.md` or `index.html`.
|
|
230
|
+
|
|
231
|
+
For asking another AI to analyze the audit, use `ai-context.md` or generate a fresh compact file:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
aion context --audit --budget 6000
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Low-Token Workflow
|
|
238
|
+
|
|
239
|
+
Run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
aion next
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This prints the recommended sequence for avoiding large token spend. The default flow is:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
aion audit . --dry-run --max-files 20
|
|
249
|
+
aion audit . --local-only
|
|
250
|
+
aion audit . --preset security --scanners 2 --max-files 20
|
|
251
|
+
aion context --audit --budget 6000
|
|
252
|
+
aion report latest
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`aion search --semantic` uses a local deterministic vector index under `.ai-runtime/repo-vectors.json`; it does not call an AI API.
|
|
256
|
+
|
|
257
|
+
## Provider Safety
|
|
258
|
+
|
|
259
|
+
CLI subprocesses receive a minimal environment instead of the full parent shell environment. Codex `--ignore-rules` is disabled by default. To explicitly opt in:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
export AION_CODEX_IGNORE_RULES=1
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Override the task store path:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
export AI_RUNTIME_DB_PATH="/path/to/aion-store"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Security Checks
|
|
272
|
+
|
|
273
|
+
Recommended checks before publishing or releasing:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm test
|
|
277
|
+
npm audit --audit-level=moderate
|
|
278
|
+
aion scan secrets
|
|
279
|
+
aion scan env-audit
|
|
280
|
+
aion scan sbom --unpinned-only
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Development
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
npm install
|
|
287
|
+
npm test
|
|
288
|
+
npm run build
|
|
289
|
+
node dist/index.js --help
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Install the local checkout globally:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
npm link
|
|
296
|
+
aion --version
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Publishing
|
|
300
|
+
|
|
301
|
+
Update the version, validate, then publish:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm version patch
|
|
305
|
+
npm test
|
|
306
|
+
npm audit --audit-level=moderate
|
|
307
|
+
npm pack --dry-run
|
|
308
|
+
npm publish --access public
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Package:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
npm install -g @aionlabsai/aion
|
|
315
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-agent.d.ts","sourceRoot":"","sources":["../../src/agents/base-agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI7E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8BAAsB,SAAS,CAAC,MAAM,EAAE,OAAO;IACjC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW;gBAAnB,MAAM,EAAE,WAAW;IAElD,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAC1D,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IACrD,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS;IAC3D,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAEnD,GAAG,CACP,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,aAAqC,EAC7C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-agent.d.ts","sourceRoot":"","sources":["../../src/agents/base-agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI7E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8BAAsB,SAAS,CAAC,MAAM,EAAE,OAAO;IACjC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW;gBAAnB,MAAM,EAAE,WAAW;IAElD,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAC1D,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IACrD,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS;IAC3D,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAEnD,GAAG,CACP,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,aAAqC,EAC7C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAgC7B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,WAAW;IAiBnB,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;CAsCvD"}
|
|
@@ -11,15 +11,20 @@ export class BaseAgent {
|
|
|
11
11
|
const start = Date.now();
|
|
12
12
|
const worktreePath = this.getWorktreePath(input);
|
|
13
13
|
this.writeClaudeMd(worktreePath);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
agentName: this.config.name,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const attempt = async (charOverride) => {
|
|
15
|
+
const userMessage = this.applyLimits(this.buildUserMessage(input), policy, charOverride);
|
|
16
|
+
return createProvider(this.config.provider, policy).run({ agentName: this.config.name, cwd: worktreePath, systemPrompt: this.config.systemPrompt, userMessage, policy }, onChunk);
|
|
17
|
+
};
|
|
18
|
+
let rawText = await attempt();
|
|
19
|
+
let output;
|
|
20
|
+
try {
|
|
21
|
+
output = this.parseOutput(rawText);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Retry once with 60% context if JSON parsing failed (likely context truncation)
|
|
25
|
+
rawText = await attempt(Math.floor(policy.maxOutputChars * 0.6));
|
|
26
|
+
output = this.parseOutput(rawText);
|
|
27
|
+
}
|
|
23
28
|
return {
|
|
24
29
|
agentName: this.config.name,
|
|
25
30
|
resolvedState: this.resolveState(output),
|
|
@@ -33,15 +38,21 @@ export class BaseAgent {
|
|
|
33
38
|
mkdirSync(claudeDir, { recursive: true });
|
|
34
39
|
writeFileSync(join(claudeDir, 'CLAUDE.md'), this.config.systemPrompt, 'utf8');
|
|
35
40
|
}
|
|
36
|
-
applyLimits(message, policy) {
|
|
41
|
+
applyLimits(message, policy, charOverride) {
|
|
37
42
|
const policyBlock = `Runtime policy:
|
|
38
43
|
- Maximum read per file/context block: ${policy.maxFileLines} lines.
|
|
39
44
|
- Maximum retained CLI output: ${policy.maxOutputChars} chars.
|
|
40
45
|
- Budget: ${policy.budget}.
|
|
41
46
|
|
|
42
47
|
`;
|
|
43
|
-
const
|
|
44
|
-
|
|
48
|
+
const full = policyBlock + message;
|
|
49
|
+
const limitedLines = limitLines(full, policy.maxFileLines * 8);
|
|
50
|
+
const charCap = charOverride ?? policy.maxOutputChars;
|
|
51
|
+
const limited = limitChars(limitedLines, charCap);
|
|
52
|
+
if (limited.length < full.length) {
|
|
53
|
+
return limited + '\n\n[CONTEXT TRUNCATED — respond using only what is visible above; output valid JSON from partial data]';
|
|
54
|
+
}
|
|
55
|
+
return limited;
|
|
45
56
|
}
|
|
46
57
|
parseJson(text, label) {
|
|
47
58
|
const cleaned = text.replace(/^```(?:json)?\n?/m, '').replace(/\n?```$/m, '').trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-agent.js","sourceRoot":"","sources":["../../src/agents/base-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAgB9D,MAAM,OAAgB,SAAS;IACE;IAA/B,YAA+B,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAOtD,KAAK,CAAC,GAAG,CACP,KAAa,EACb,SAAwB,mBAAmB,EAAE,EAC7C,OAAmD;QAEnD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-agent.js","sourceRoot":"","sources":["../../src/agents/base-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,9 @@ export interface SynthesizerInput {
|
|
|
7
7
|
worktreePath: string;
|
|
8
8
|
}
|
|
9
9
|
export declare class SynthesizerAgent extends BaseAgent<SynthesizerInput, AuditReport> {
|
|
10
|
+
private mergedFindings;
|
|
11
|
+
private domainSections;
|
|
12
|
+
private totalFiles;
|
|
10
13
|
constructor();
|
|
11
14
|
protected getWorktreePath(input: SynthesizerInput): string;
|
|
12
15
|
protected buildUserMessage(input: SynthesizerInput): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesizer.d.ts","sourceRoot":"","sources":["../../src/agents/synthesizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"synthesizer.d.ts","sourceRoot":"","sources":["../../src/agents/synthesizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,UAAU,EAAsB,MAAM,qBAAqB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAuGD,qBAAa,gBAAiB,SAAQ,SAAS,CAAC,gBAAgB,EAAE,WAAW,CAAC;IAC5E,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,UAAU,CAAK;;IAUvB,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM;IAI1D,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM;IAkB3D,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAkBhD,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS;CAGxD"}
|
|
@@ -1,46 +1,97 @@
|
|
|
1
1
|
import { BaseAgent } from './base-agent.js';
|
|
2
2
|
import { buildSynthesizerPrompt } from '../prompts/synthesizer.js';
|
|
3
|
-
import {
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low', 'info'];
|
|
5
|
+
const SEVERITY_RANK = { critical: 5, high: 4, medium: 3, low: 2, info: 1 };
|
|
4
6
|
const MIN_PER_DOMAIN = 2;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
// Compact schema — Claude only writes summary + topPriorities
|
|
8
|
+
const SynthOutputSchema = z.object({
|
|
9
|
+
summary: z.string(),
|
|
10
|
+
topPriorities: z.array(z.string()),
|
|
11
|
+
});
|
|
12
|
+
// ── Local helpers ─────────────────────────────────────────────────────────────
|
|
13
|
+
function deduplicateAndRank(scanReports) {
|
|
14
|
+
const seen = new Map();
|
|
7
15
|
for (const report of scanReports) {
|
|
8
|
-
for (const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
for (const f of report.findings) {
|
|
17
|
+
const key = `${f.file}:${f.line ?? ''}:${f.category}`;
|
|
18
|
+
const existing = seen.get(key);
|
|
19
|
+
if (!existing || (SEVERITY_RANK[f.severity] ?? 0) > (SEVERITY_RANK[existing.severity] ?? 0)) {
|
|
20
|
+
seen.set(key, existing
|
|
21
|
+
? { ...f, persona: [existing.persona, f.persona].filter(Boolean).join('+') }
|
|
22
|
+
: f);
|
|
23
|
+
}
|
|
13
24
|
}
|
|
14
25
|
}
|
|
15
|
-
return [...
|
|
16
|
-
domain,
|
|
17
|
-
findings: findings.sort((a, b) => severityRank(b.severity) - severityRank(a.severity)),
|
|
18
|
-
summary: `${findings.length} finding(s) in ${domain}`,
|
|
19
|
-
}));
|
|
26
|
+
return [...seen.values()].sort((a, b) => (SEVERITY_RANK[b.severity] ?? 0) - (SEVERITY_RANK[a.severity] ?? 0));
|
|
20
27
|
}
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
function guaranteePerDomain(reports, merged) {
|
|
25
|
-
const mergedKeys = new Set(merged.map((f) => `${f.file}:${f.line}:${f.finding}`));
|
|
28
|
+
function ensurePerDomainCoverage(deduped, scanReports) {
|
|
29
|
+
const dedupedKeys = new Set(deduped.map((f) => `${f.file}:${f.line ?? ''}:${f.category}`));
|
|
26
30
|
const extras = [];
|
|
27
|
-
for (const report of
|
|
31
|
+
for (const report of scanReports) {
|
|
28
32
|
const domain = report.findings[0]?.category;
|
|
29
33
|
if (!domain)
|
|
30
34
|
continue;
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
35
|
+
const inDeduped = deduped.filter((f) => f.category === domain).length;
|
|
36
|
+
if (inDeduped >= MIN_PER_DOMAIN)
|
|
33
37
|
continue;
|
|
34
|
-
const needed = MIN_PER_DOMAIN -
|
|
38
|
+
const needed = MIN_PER_DOMAIN - inDeduped;
|
|
35
39
|
const candidates = report.findings
|
|
36
|
-
.filter((f) => !
|
|
40
|
+
.filter((f) => !dedupedKeys.has(`${f.file}:${f.line ?? ''}:${f.category}`))
|
|
37
41
|
.slice(0, needed);
|
|
38
42
|
extras.push(...candidates);
|
|
39
|
-
candidates.forEach((f) =>
|
|
43
|
+
candidates.forEach((f) => dedupedKeys.add(`${f.file}:${f.line ?? ''}:${f.category}`));
|
|
40
44
|
}
|
|
41
45
|
return extras;
|
|
42
46
|
}
|
|
47
|
+
function buildDomainSections(findings) {
|
|
48
|
+
const byDomain = new Map();
|
|
49
|
+
for (const f of findings) {
|
|
50
|
+
if (!byDomain.has(f.category))
|
|
51
|
+
byDomain.set(f.category, []);
|
|
52
|
+
byDomain.get(f.category).push(f);
|
|
53
|
+
}
|
|
54
|
+
return [...byDomain.entries()].map(([domain, domainFindings]) => ({
|
|
55
|
+
domain,
|
|
56
|
+
findings: domainFindings.sort((a, b) => (SEVERITY_RANK[b.severity] ?? 0) - (SEVERITY_RANK[a.severity] ?? 0)),
|
|
57
|
+
summary: `${domainFindings.length} finding(s) in ${domain}`,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
// Build a compact text digest — much smaller than raw JSON.
|
|
61
|
+
// Claude only reads this to write summary + topPriorities.
|
|
62
|
+
function buildCompactDigest(findings, totalFiles, domainSections) {
|
|
63
|
+
const lines = [
|
|
64
|
+
`## Audit Digest: ${findings.length} findings across ${domainSections.length} domains (${totalFiles} files scanned)`,
|
|
65
|
+
'',
|
|
66
|
+
];
|
|
67
|
+
for (const sev of SEVERITY_ORDER) {
|
|
68
|
+
const group = findings.filter((f) => f.severity === sev);
|
|
69
|
+
if (group.length === 0)
|
|
70
|
+
continue;
|
|
71
|
+
lines.push(`${sev.toUpperCase()} (${group.length}):`);
|
|
72
|
+
for (const f of group.slice(0, 12)) {
|
|
73
|
+
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
74
|
+
lines.push(`- [${f.category}] ${loc} — ${f.finding.slice(0, 120)}`);
|
|
75
|
+
}
|
|
76
|
+
if (group.length > 12)
|
|
77
|
+
lines.push(` ... ${group.length - 12} more`);
|
|
78
|
+
lines.push('');
|
|
79
|
+
}
|
|
80
|
+
lines.push('Domain coverage:');
|
|
81
|
+
for (const s of domainSections) {
|
|
82
|
+
const bySev = SEVERITY_ORDER.map((sv) => {
|
|
83
|
+
const n = s.findings.filter((f) => f.severity === sv).length;
|
|
84
|
+
return n > 0 ? `${n} ${sv}` : null;
|
|
85
|
+
}).filter(Boolean).join(', ');
|
|
86
|
+
lines.push(`- ${s.domain}: ${s.findings.length} (${bySev || 'none'})`);
|
|
87
|
+
}
|
|
88
|
+
return lines.join('\n');
|
|
89
|
+
}
|
|
90
|
+
// ── Agent ─────────────────────────────────────────────────────────────────────
|
|
43
91
|
export class SynthesizerAgent extends BaseAgent {
|
|
92
|
+
mergedFindings = [];
|
|
93
|
+
domainSections = [];
|
|
94
|
+
totalFiles = 0;
|
|
44
95
|
constructor() {
|
|
45
96
|
super({
|
|
46
97
|
name: 'synthesizer',
|
|
@@ -52,42 +103,32 @@ export class SynthesizerAgent extends BaseAgent {
|
|
|
52
103
|
return input.worktreePath;
|
|
53
104
|
}
|
|
54
105
|
buildUserMessage(input) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Rules:
|
|
65
|
-
- Include AT LEAST ${MIN_PER_DOMAIN} findings per scanner domain regardless of severity
|
|
66
|
-
- Merge duplicates (same file+line), keep the more descriptive finding
|
|
67
|
-
- Rank overall by severity but preserve domain diversity in topPriorities
|
|
68
|
-
- topPriorities must include at least one entry per domain that has critical/high findings
|
|
106
|
+
// All heavy work is done locally here before calling Claude.
|
|
107
|
+
const deduped = deduplicateAndRank(input.scanReports);
|
|
108
|
+
const extras = ensurePerDomainCoverage(deduped, input.scanReports);
|
|
109
|
+
const allFindings = [...deduped, ...extras].sort((a, b) => (SEVERITY_RANK[b.severity] ?? 0) - (SEVERITY_RANK[a.severity] ?? 0));
|
|
110
|
+
this.mergedFindings = allFindings;
|
|
111
|
+
this.domainSections = buildDomainSections(allFindings);
|
|
112
|
+
this.totalFiles = input.totalFiles;
|
|
113
|
+
return buildCompactDigest(allFindings, input.totalFiles, this.domainSections) + `
|
|
69
114
|
|
|
70
|
-
|
|
115
|
+
Write the summary and topPriorities for this audit. Output ONLY valid JSON:
|
|
116
|
+
{ "summary": "...", "topPriorities": ["...", "...", "...", "...", "..."] }`;
|
|
71
117
|
}
|
|
72
118
|
parseOutput(text) {
|
|
73
119
|
const raw = this.parseJson(text, 'SynthesizerAgent');
|
|
74
|
-
const result =
|
|
120
|
+
const result = SynthOutputSchema.safeParse(raw);
|
|
75
121
|
if (!result.success) {
|
|
76
122
|
throw new Error(`SynthesizerAgent schema error: ${result.error.message}`);
|
|
77
123
|
}
|
|
78
|
-
const report = result.data;
|
|
79
|
-
// Post-process: guarantee per-domain representation and add sections
|
|
80
|
-
const extras = guaranteePerDomain([], report.findings);
|
|
81
|
-
const allFindings = [...report.findings, ...extras];
|
|
82
|
-
const sections = buildDomainSections(report.sections
|
|
83
|
-
? report.sections.map((s) => ({ filesScanned: [], findings: s.findings, summary: s.summary }))
|
|
84
|
-
: [{ filesScanned: [], findings: allFindings, summary: '' }]);
|
|
85
124
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
findings: this.mergedFindings,
|
|
126
|
+
criticalCount: this.mergedFindings.filter((f) => f.severity === 'critical').length,
|
|
127
|
+
highCount: this.mergedFindings.filter((f) => f.severity === 'high').length,
|
|
128
|
+
totalFiles: this.totalFiles,
|
|
129
|
+
summary: result.data.summary,
|
|
130
|
+
topPriorities: result.data.topPriorities,
|
|
131
|
+
sections: this.domainSections,
|
|
91
132
|
};
|
|
92
133
|
}
|
|
93
134
|
resolveState(_output) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesizer.js","sourceRoot":"","sources":["../../src/agents/synthesizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"synthesizer.js","sourceRoot":"","sources":["../../src/agents/synthesizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAU,CAAC;AAC9E,MAAM,aAAa,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACnG,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB,8DAA8D;AAC9D,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACnC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,SAAS,kBAAkB,CAAC,WAAyB;IACnD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE7C,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC5F,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ;oBACpB,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;oBAC5E,CAAC,CAAC,CAAC,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC9E,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,OAAuB,EAAE,WAAyB;IACjF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC5C,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QACtE,IAAI,SAAS,IAAI,cAAc;YAAE,SAAS;QAE1C,MAAM,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;QAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ;aAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;aAC1E,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;QAC3B,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAwB;IACnD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5D,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM;QACN,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5G,OAAO,EAAE,GAAG,cAAc,CAAC,MAAM,kBAAkB,MAAM,EAAE;KAC5D,CAAC,CAAC,CAAC;AACN,CAAC;AAED,4DAA4D;AAC5D,2DAA2D;AAC3D,SAAS,kBAAkB,CAAC,QAAwB,EAAE,UAAkB,EAAE,cAA+B;IACvG,MAAM,KAAK,GAAa;QACtB,oBAAoB,QAAQ,CAAC,MAAM,oBAAoB,cAAc,CAAC,MAAM,aAAa,UAAU,iBAAiB;QACpH,EAAE;KACH,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC;QACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QACtD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACpD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;YACtC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACrC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,iFAAiF;AAEjF,MAAM,OAAO,gBAAiB,SAAQ,SAAwC;IACpE,cAAc,GAAmB,EAAE,CAAC;IACpC,cAAc,GAAoB,EAAE,CAAC;IACrC,UAAU,GAAG,CAAC,CAAC;IAEvB;QACE,KAAK,CAAC;YACJ,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,sBAAsB,EAAE;SACvC,CAAC,CAAC;IACL,CAAC;IAES,eAAe,CAAC,KAAuB;QAC/C,OAAO,KAAK,CAAC,YAAY,CAAC;IAC5B,CAAC;IAES,gBAAgB,CAAC,KAAuB;QAChD,6DAA6D;QAC7D,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAC9C,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC9E,CAAC;QAEF,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QAEnC,OAAO,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG;;;2EAGT,CAAC;IAC1E,CAAC;IAES,WAAW,CAAC,IAAY;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAU,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,kCAAkC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,cAAc;YAC7B,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;YAClF,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;YAC1E,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO;YAC5B,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa;YACxC,QAAQ,EAAE,IAAI,CAAC,cAAc;SAC9B,CAAC;IACJ,CAAC;IAES,YAAY,CAAC,OAAoB;QACzC,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+GzC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmGpD"}
|