nexus-agents 2.6.0 → 2.26.1
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 +70 -64
- package/dist/{chunk-U6PLZTD6.js → chunk-6E3NMMEY.js} +10 -71
- package/dist/chunk-6E3NMMEY.js.map +1 -0
- package/dist/chunk-ARNVVQ5W.js +2849 -0
- package/dist/chunk-ARNVVQ5W.js.map +1 -0
- package/dist/chunk-BOWNZMPH.js +8837 -0
- package/dist/chunk-BOWNZMPH.js.map +1 -0
- package/dist/{chunk-7YMTGMHI.js → chunk-L3LQ3RP5.js} +62519 -40425
- package/dist/chunk-L3LQ3RP5.js.map +1 -0
- package/dist/chunk-LCDOP543.js +365 -0
- package/dist/chunk-LCDOP543.js.map +1 -0
- package/dist/chunk-LCHCASB7.js +62 -0
- package/dist/chunk-LCHCASB7.js.map +1 -0
- package/dist/chunk-PGNRXCYY.js +776 -0
- package/dist/chunk-PGNRXCYY.js.map +1 -0
- package/dist/chunk-UP2VWCW5.js +38 -0
- package/dist/chunk-UVQ7R4C4.js +2106 -0
- package/dist/chunk-UVQ7R4C4.js.map +1 -0
- package/dist/chunk-X2M7OF27.js +72 -0
- package/dist/chunk-X2M7OF27.js.map +1 -0
- package/dist/chunk-X33QNBGA.js +111 -0
- package/dist/chunk-X33QNBGA.js.map +1 -0
- package/dist/cli.d.ts +10 -3
- package/dist/cli.js +13391 -13318
- package/dist/cli.js.map +1 -1
- package/dist/dist-Y5F6UM2N.js +45490 -0
- package/dist/dist-Y5F6UM2N.js.map +1 -0
- package/dist/doctor-deep-I2J5CRFG.js +13 -0
- package/dist/index.d.ts +14359 -5359
- package/dist/index.js +1317 -1109
- package/dist/index.js.map +1 -1
- package/dist/model-capabilities-types-CSWO27YN.d.ts +18 -0
- package/dist/{pr-reviewer-helpers-EVFQKF5W.js → pr-reviewer-helpers-XCY7HOPE.js} +4 -3
- package/dist/pr-reviewer-helpers-XCY7HOPE.js.map +1 -0
- package/dist/setup-command-VNF3KTCJ.js +26 -0
- package/dist/setup-command-VNF3KTCJ.js.map +1 -0
- package/dist/setup-config-VQSWWJ5O.js +9 -0
- package/dist/setup-config-VQSWWJ5O.js.map +1 -0
- package/dist/workflows/templates/docs-audit.yaml +108 -0
- package/dist/workflows/templates/infrastructure-audit.yaml +123 -0
- package/dist/workflows/templates/research-review.yaml +84 -0
- package/dist/workflows/templates/standards-review.yaml +131 -0
- package/package.json +42 -13
- package/scripts/postinstall.js +12 -0
- package/src/workflows/templates/docs-audit.yaml +108 -0
- package/src/workflows/templates/infrastructure-audit.yaml +123 -0
- package/src/workflows/templates/research-review.yaml +84 -0
- package/src/workflows/templates/standards-review.yaml +131 -0
- package/dist/chunk-7YMTGMHI.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-U6PLZTD6.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → chunk-UP2VWCW5.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-EVFQKF5W.js.map → doctor-deep-I2J5CRFG.js.map} +0 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
|
-
[](https://www.typescriptlang.org)
|
|
8
8
|
[](https://modelcontextprotocol.io)
|
|
9
9
|
[](https://www.npmjs.com/package/nexus-agents)
|
|
10
10
|
|
|
@@ -12,14 +12,17 @@
|
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
|
-
Nexus Agents is an MCP (Model Context Protocol) server that coordinates multiple AI experts to handle software development tasks. It provides a unified interface for different AI models and enables multi-agent collaboration through
|
|
15
|
+
Nexus Agents is an MCP (Model Context Protocol) server that coordinates multiple AI experts to handle software development tasks. It provides a unified interface for different AI models and enables multi-agent collaboration through an Orchestrator and 10 specialized experts.
|
|
16
16
|
|
|
17
17
|
### Key Capabilities
|
|
18
18
|
|
|
19
|
-
- **Multi-Agent Orchestration** -
|
|
20
|
-
- **Model Diversity** - Support for Claude,
|
|
21
|
-
- **Workflow Automation** - YAML-based templates for repeatable processes
|
|
22
|
-
- **
|
|
19
|
+
- **Multi-Agent Orchestration** - Orchestrator coordinates 10 specialized experts for complex tasks
|
|
20
|
+
- **Model Diversity** - Support for Claude, Gemini, Codex, and OpenCode (with custom OpenAI-compatible endpoints)
|
|
21
|
+
- **Workflow Automation** - 11 YAML-based templates for repeatable processes
|
|
22
|
+
- **Consensus Voting** - Multi-agent voting with higher-order Bayesian aggregation
|
|
23
|
+
- **Memory System** - 5 typed backends (session, belief, agentic, adaptive, typed)
|
|
24
|
+
- **24 MCP Tools** - Full integration with Claude Code, Claude Desktop, and other MCP clients
|
|
25
|
+
- **Security-First Design** - Defense in depth with input validation and untrusted input hardening
|
|
23
26
|
|
|
24
27
|
---
|
|
25
28
|
|
|
@@ -50,29 +53,23 @@ npx nexus-agents
|
|
|
50
53
|
### Programmatic Usage
|
|
51
54
|
|
|
52
55
|
```typescript
|
|
53
|
-
import {
|
|
54
|
-
createServer,
|
|
55
|
-
startStdioServer,
|
|
56
|
-
TechLead,
|
|
57
|
-
createClaudeAdapter,
|
|
58
|
-
ExpertFactory,
|
|
59
|
-
} from 'nexus-agents';
|
|
56
|
+
import { startStdioServer, ExpertFactory, createClaudeAdapter } from 'nexus-agents';
|
|
60
57
|
|
|
61
|
-
// Start MCP server
|
|
58
|
+
// Start MCP server (recommended — used by Claude Code, Claude Desktop, etc.)
|
|
62
59
|
const result = await startStdioServer({
|
|
63
60
|
name: 'my-server',
|
|
64
61
|
version: '1.0.0',
|
|
65
62
|
});
|
|
66
63
|
|
|
67
|
-
// Or use programmatically
|
|
64
|
+
// Or use programmatically with model adapters
|
|
68
65
|
const adapter = createClaudeAdapter({
|
|
69
66
|
model: 'claude-sonnet-4-20250514',
|
|
70
67
|
});
|
|
71
|
-
const techLead = new TechLead({ adapter });
|
|
72
68
|
|
|
73
69
|
// Create experts dynamically
|
|
74
70
|
const factory = new ExpertFactory(adapter);
|
|
75
71
|
const codeExpert = factory.create({ type: 'code' });
|
|
72
|
+
const securityExpert = factory.create({ type: 'security' });
|
|
76
73
|
```
|
|
77
74
|
|
|
78
75
|
### Claude Desktop Integration
|
|
@@ -99,34 +96,35 @@ Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/
|
|
|
99
96
|
|
|
100
97
|
### Multi-Agent Orchestration
|
|
101
98
|
|
|
102
|
-
The
|
|
99
|
+
The Orchestrator agent analyzes incoming tasks and delegates to specialized experts:
|
|
103
100
|
|
|
104
|
-
| Expert
|
|
105
|
-
|
|
|
106
|
-
| **Code Expert**
|
|
107
|
-
| **Architecture Expert**
|
|
108
|
-
| **Security Expert**
|
|
109
|
-
| **Documentation Expert**
|
|
110
|
-
| **Testing Expert**
|
|
101
|
+
| Expert | Specialization |
|
|
102
|
+
| ------------------------- | ------------------------------------------------------ |
|
|
103
|
+
| **Code Expert** | Implementation, debugging, optimization, refactoring |
|
|
104
|
+
| **Architecture Expert** | System design, patterns, trade-offs, scalability |
|
|
105
|
+
| **Security Expert** | Vulnerability analysis, secure coding, threat modeling |
|
|
106
|
+
| **Documentation Expert** | Technical writing, API docs, code comments |
|
|
107
|
+
| **Testing Expert** | Test strategies, coverage analysis, test generation |
|
|
108
|
+
| **DevOps Expert** | CI/CD, deployment, containerization |
|
|
109
|
+
| **Research Expert** | Literature review, state-of-the-art analysis |
|
|
110
|
+
| **PM Expert** | Product management, requirements, priorities |
|
|
111
|
+
| **UX Expert** | User experience, usability, accessibility |
|
|
112
|
+
| **Infrastructure Expert** | Server management, bare metal, networking |
|
|
111
113
|
|
|
112
|
-
Experts can collaborate on complex tasks. The
|
|
114
|
+
Experts can collaborate on complex tasks. The Orchestrator combines their outputs into a single response.
|
|
113
115
|
|
|
114
116
|
### Model Adapters
|
|
115
117
|
|
|
116
118
|
Use different AI models through unified interfaces:
|
|
117
119
|
|
|
118
|
-
| Provider
|
|
119
|
-
|
|
|
120
|
-
| **Claude**
|
|
121
|
-
| **
|
|
122
|
-
| **
|
|
123
|
-
| **
|
|
120
|
+
| Provider | CLI | Best For |
|
|
121
|
+
| ------------ | -------- | ---------------------------------- |
|
|
122
|
+
| **Claude** | claude | Complex reasoning, analysis |
|
|
123
|
+
| **Gemini** | gemini | Long context, multimodal |
|
|
124
|
+
| **Codex** | codex | Code generation, reasoning |
|
|
125
|
+
| **OpenCode** | opencode | Custom OpenAI-compatible endpoints |
|
|
124
126
|
|
|
125
|
-
Model selection uses
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
Fast (quick queries) -> Balanced (most tasks) -> Powerful (complex reasoning)
|
|
129
|
-
```
|
|
127
|
+
Model selection uses composite routing: Budget → Zero-cost → Preference → TOPSIS → LinUCB bandit.
|
|
130
128
|
|
|
131
129
|
### Workflow Engine
|
|
132
130
|
|
|
@@ -156,13 +154,23 @@ steps:
|
|
|
156
154
|
|
|
157
155
|
### MCP Tools
|
|
158
156
|
|
|
159
|
-
The server exposes
|
|
157
|
+
The server exposes 24 MCP tools for integration. Key tools include:
|
|
158
|
+
|
|
159
|
+
| Tool | Description |
|
|
160
|
+
| -------------------- | ---------------------------------------------- |
|
|
161
|
+
| `orchestrate` | Analyze task and coordinate expert execution |
|
|
162
|
+
| `create_expert` | Create a specialized expert agent |
|
|
163
|
+
| `execute_expert` | Execute a task using a created expert |
|
|
164
|
+
| `run_workflow` | Execute a workflow template |
|
|
165
|
+
| `delegate_to_model` | Route task to optimal model |
|
|
166
|
+
| `consensus_vote` | Multi-model consensus voting on proposals |
|
|
167
|
+
| `research_discover` | Discover papers/repos from external sources |
|
|
168
|
+
| `memory_query` | Query across all memory backends |
|
|
169
|
+
| `issue_triage` | Triage GitHub issues with trust classification |
|
|
170
|
+
| `repo_analyze` | Analyze GitHub repository structure |
|
|
171
|
+
| `repo_security_plan` | Generate security scanning pipeline for a repo |
|
|
160
172
|
|
|
161
|
-
|
|
162
|
-
| --------------- | -------------------------------------------- |
|
|
163
|
-
| `orchestrate` | Analyze task and coordinate expert execution |
|
|
164
|
-
| `create_expert` | Dynamically create a specialized expert |
|
|
165
|
-
| `run_workflow` | Execute a predefined workflow template |
|
|
173
|
+
See the root [README](../../README.md) for the complete list of all 24 tools.
|
|
166
174
|
|
|
167
175
|
---
|
|
168
176
|
|
|
@@ -174,14 +182,18 @@ nexus-agents/
|
|
|
174
182
|
│ └── nexus-agents/ # Main package
|
|
175
183
|
│ ├── src/
|
|
176
184
|
│ │ ├── core/ # Shared types, Result<T,E>, errors, logger
|
|
177
|
-
│ │ ├── config/ # Configuration
|
|
185
|
+
│ │ ├── config/ # Configuration, model registry, timeouts
|
|
178
186
|
│ │ ├── adapters/ # Model adapters (Claude, OpenAI, Gemini, Ollama)
|
|
179
|
-
│ │ ├── agents/ # Agent framework (
|
|
180
|
-
│ │ ├── workflows/ # Workflow engine and templates
|
|
181
|
-
│ │ ├── mcp/ # MCP server and tool definitions
|
|
182
|
-
│ │ ├── cli-adapters/ # External CLI integration (Claude/Gemini/Codex
|
|
183
|
-
│ │ ├── context/ # Token counting, work balancing
|
|
184
|
-
│ │ ├── consensus/ # Multi-agent voting
|
|
187
|
+
│ │ ├── agents/ # Agent framework (Orchestrator, Experts)
|
|
188
|
+
│ │ ├── workflows/ # Workflow engine and 11 YAML templates
|
|
189
|
+
│ │ ├── mcp/ # MCP server and 24 tool definitions
|
|
190
|
+
│ │ ├── cli-adapters/ # External CLI integration (Claude/Gemini/Codex/OpenCode)
|
|
191
|
+
│ │ ├── context/ # Token counting, work balancing
|
|
192
|
+
│ │ ├── consensus/ # Multi-agent voting with higher-order aggregation
|
|
193
|
+
│ │ ├── memory/ # 5 typed memory backends (session, belief, agentic, adaptive, typed)
|
|
194
|
+
│ │ ├── security/ # Input sanitization, trust classification, policy gate
|
|
195
|
+
│ │ ├── orchestration/# Graph workflows, AOrchestra, worker dispatch
|
|
196
|
+
│ │ ├── pipeline/ # Task contracts, plugin registry, event bus
|
|
185
197
|
│ │ ├── index.ts # Main exports
|
|
186
198
|
│ │ └── cli.ts # CLI entry point
|
|
187
199
|
│ └── package.json
|
|
@@ -191,18 +203,18 @@ nexus-agents/
|
|
|
191
203
|
└── pnpm-workspace.yaml
|
|
192
204
|
```
|
|
193
205
|
|
|
194
|
-
See [
|
|
206
|
+
See [docs/architecture/README.md](../../docs/architecture/README.md) for detailed module descriptions.
|
|
195
207
|
|
|
196
208
|
### Dependency Flow
|
|
197
209
|
|
|
198
210
|
```
|
|
199
|
-
MCP Server (external boundary)
|
|
211
|
+
MCP Server (external boundary, 24 tools)
|
|
200
212
|
↓
|
|
201
|
-
|
|
213
|
+
Orchestration Layer (workflows, graph execution, worker dispatch)
|
|
202
214
|
↓
|
|
203
|
-
Agents Layer (
|
|
215
|
+
Agents Layer (Orchestrator, 10 Expert types)
|
|
204
216
|
↓
|
|
205
|
-
Adapters Layer (Claude,
|
|
217
|
+
Adapters Layer (Claude, Gemini, Codex, OpenCode CLIs)
|
|
206
218
|
↓
|
|
207
219
|
Core Layer (Types, Result<T,E>, Errors, Logger)
|
|
208
220
|
```
|
|
@@ -278,15 +290,13 @@ const adapter = factory.create({ provider: 'anthropic', model: 'claude-sonnet-4-
|
|
|
278
290
|
### Agents
|
|
279
291
|
|
|
280
292
|
```typescript
|
|
281
|
-
import {
|
|
282
|
-
|
|
283
|
-
// Create TechLead for orchestration
|
|
284
|
-
const techLead = new TechLead({ adapter });
|
|
293
|
+
import { ExpertFactory } from 'nexus-agents';
|
|
285
294
|
|
|
286
|
-
// Create experts
|
|
295
|
+
// Create experts for specific domains
|
|
287
296
|
const factory = new ExpertFactory(adapter);
|
|
288
297
|
const codeExpert = factory.create({ type: 'code' });
|
|
289
298
|
const securityExpert = factory.create({ type: 'security' });
|
|
299
|
+
const infraExpert = factory.create({ type: 'infrastructure' });
|
|
290
300
|
```
|
|
291
301
|
|
|
292
302
|
### MCP Server
|
|
@@ -314,7 +324,7 @@ if (result.ok) {
|
|
|
314
324
|
|
|
315
325
|
- Node.js 22.x LTS
|
|
316
326
|
- pnpm 9.x
|
|
317
|
-
- TypeScript 5.
|
|
327
|
+
- TypeScript 5.9+
|
|
318
328
|
|
|
319
329
|
### Setup
|
|
320
330
|
|
|
@@ -395,10 +405,6 @@ MIT - See [LICENSE](../../LICENSE) for details.
|
|
|
395
405
|
|
|
396
406
|
---
|
|
397
407
|
|
|
398
|
-
## Acknowledgments
|
|
399
|
-
|
|
400
|
-
This project is a clean-room rewrite inspired by [claude-team-mcp](https://github.com/original/claude-team-mcp), with attribution preserved per MIT license.
|
|
401
|
-
|
|
402
408
|
---
|
|
403
409
|
|
|
404
410
|
Built with Claude Code
|
|
@@ -1,71 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
experts: ["security", "code_quality", "testing"],
|
|
8
|
-
maxDebateRounds: 3,
|
|
9
|
-
consensusThreshold: 0.7,
|
|
10
|
-
minSeverity: "low",
|
|
11
|
-
enableInlineComments: true,
|
|
12
|
-
dryRun: false,
|
|
13
|
-
modelConfig: {
|
|
14
|
-
temperature: 0.3,
|
|
15
|
-
maxTokens: 8192
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
var PRReviewConfigSchema = z.object({
|
|
19
|
-
experts: z.array(
|
|
20
|
-
z.enum([
|
|
21
|
-
"security",
|
|
22
|
-
"performance",
|
|
23
|
-
"code_quality",
|
|
24
|
-
"testing",
|
|
25
|
-
"documentation",
|
|
26
|
-
"architecture"
|
|
27
|
-
])
|
|
28
|
-
).default(["security", "code_quality", "testing"]),
|
|
29
|
-
maxDebateRounds: z.number().int().min(1).max(10).default(3),
|
|
30
|
-
consensusThreshold: z.number().min(0).max(1).default(0.7),
|
|
31
|
-
minSeverity: z.enum(["critical", "high", "medium", "low", "info"]).default("low"),
|
|
32
|
-
enableInlineComments: z.boolean().default(true),
|
|
33
|
-
dryRun: z.boolean().default(false),
|
|
34
|
-
githubToken: z.string().optional(),
|
|
35
|
-
modelConfig: z.object({
|
|
36
|
-
temperature: z.number().min(0).max(2).default(0.3),
|
|
37
|
-
maxTokens: z.number().int().positive().default(8192)
|
|
38
|
-
}).optional()
|
|
39
|
-
});
|
|
40
|
-
var SEVERITY_ORDER = {
|
|
41
|
-
critical: 5,
|
|
42
|
-
high: 4,
|
|
43
|
-
medium: 3,
|
|
44
|
-
low: 2,
|
|
45
|
-
info: 1
|
|
46
|
-
};
|
|
47
|
-
var CATEGORY_DISPLAY_NAMES = {
|
|
48
|
-
security: "Security",
|
|
49
|
-
performance: "Performance",
|
|
50
|
-
code_quality: "Code Quality",
|
|
51
|
-
testing: "Testing",
|
|
52
|
-
documentation: "Documentation",
|
|
53
|
-
architecture: "Architecture"
|
|
54
|
-
};
|
|
55
|
-
var SEVERITY_EMOJI = {
|
|
56
|
-
critical: ":rotating_light:",
|
|
57
|
-
high: ":warning:",
|
|
58
|
-
medium: ":yellow_circle:",
|
|
59
|
-
low: ":large_blue_circle:",
|
|
60
|
-
info: ":information_source:"
|
|
61
|
-
};
|
|
62
|
-
var DECISION_EMOJI = {
|
|
63
|
-
approve: ":white_check_mark:",
|
|
64
|
-
request_changes: ":x:",
|
|
65
|
-
comment: ":speech_balloon:"
|
|
66
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
CATEGORY_DISPLAY_NAMES,
|
|
3
|
+
DECISION_EMOJI,
|
|
4
|
+
SEVERITY_EMOJI,
|
|
5
|
+
SEVERITY_ORDER
|
|
6
|
+
} from "./chunk-X2M7OF27.js";
|
|
67
7
|
|
|
68
8
|
// src/dogfooding/pr-reviewer-helpers.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
69
10
|
function parseSeverity(value) {
|
|
70
11
|
if (typeof value === "string") {
|
|
71
12
|
const lower = value.toLowerCase();
|
|
@@ -184,14 +125,14 @@ function generateSummary(pr, reviews, decision) {
|
|
|
184
125
|
const expertSummaries = reviews.map((r) => `- **${CATEGORY_DISPLAY_NAMES[r.expertType]}**: ${r.summary}`).join("\n");
|
|
185
126
|
return `Reviewed PR #${String(pr.number)}: ${pr.title}
|
|
186
127
|
|
|
187
|
-
**Decision:** ${decision.
|
|
128
|
+
**Decision:** ${decision.replaceAll("_", " ")}
|
|
188
129
|
**Experts consulted:** ${String(reviews.length)}
|
|
189
130
|
|
|
190
131
|
${expertSummaries}`;
|
|
191
132
|
}
|
|
192
133
|
function formatReviewComment(result) {
|
|
193
134
|
const emoji = DECISION_EMOJI[result.decision];
|
|
194
|
-
const decisionText = result.decision.
|
|
135
|
+
const decisionText = result.decision.replaceAll("_", " ").toUpperCase();
|
|
195
136
|
const findingsSection = formatFindingsSection(result);
|
|
196
137
|
const statsSection = formatStatsSection(result);
|
|
197
138
|
return `## ${emoji} Nexus Agents Review: ${decisionText}
|
|
@@ -260,8 +201,6 @@ function createFailedReview(expertId, category, durationMs, error) {
|
|
|
260
201
|
}
|
|
261
202
|
|
|
262
203
|
export {
|
|
263
|
-
DEFAULT_PR_REVIEW_CONFIG,
|
|
264
|
-
CATEGORY_DISPLAY_NAMES,
|
|
265
204
|
parseSeverity,
|
|
266
205
|
parseCategory,
|
|
267
206
|
extractSummary,
|
|
@@ -277,4 +216,4 @@ export {
|
|
|
277
216
|
formatReviewComment,
|
|
278
217
|
createFailedReview
|
|
279
218
|
};
|
|
280
|
-
//# sourceMappingURL=chunk-
|
|
219
|
+
//# sourceMappingURL=chunk-6E3NMMEY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dogfooding/pr-reviewer-helpers.ts"],"sourcesContent":["/**\n * nexus-agents/dogfooding - PR Reviewer Helpers\n *\n * Helper functions for PR review formatting and aggregation.\n *\n * @module dogfooding/pr-reviewer-helpers\n * (Source: Issue #161, Alignment Roadmap Phase 3)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type {\n PRMetadata,\n PRReviewResult,\n ExpertReviewResult,\n ReviewFinding,\n ReviewCategory,\n ReviewSeverity,\n ReviewDecision,\n} from './pr-review-types.js';\nimport {\n SEVERITY_ORDER,\n CATEGORY_DISPLAY_NAMES,\n SEVERITY_EMOJI,\n DECISION_EMOJI,\n} from './pr-review-types.js';\n\n// =============================================================================\n// Parsing Helpers\n// =============================================================================\n\nexport function parseSeverity(value: unknown): ReviewSeverity {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in SEVERITY_ORDER) return lower as ReviewSeverity;\n }\n return 'medium';\n}\n\nexport function parseCategory(value: unknown): ReviewCategory {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in CATEGORY_DISPLAY_NAMES) return lower as ReviewCategory;\n }\n return 'code_quality';\n}\n\nexport function extractSummary(output: Record<string, unknown>): string {\n if (typeof output.summary === 'string') return output.summary;\n if (typeof output.content === 'string') return output.content;\n if (typeof output.message === 'string') return output.message;\n return 'Review completed';\n}\n\nexport function extractStringField(\n record: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === 'string') return value;\n }\n return undefined;\n}\n\n// =============================================================================\n// Finding Parsing\n// =============================================================================\n\nexport function parseFindings(\n output: Record<string, unknown>,\n expertId: string,\n minSeverity: ReviewSeverity\n): ReviewFinding[] {\n const minOrder = SEVERITY_ORDER[minSeverity];\n const sources = collectSources(output);\n\n const findings: ReviewFinding[] = [];\n for (const source of sources) {\n if (!Array.isArray(source)) continue;\n for (const item of source) {\n const finding = parseOneFinding(item, expertId, minOrder);\n if (finding !== null) findings.push(finding);\n }\n }\n return findings;\n}\n\nfunction collectSources(output: Record<string, unknown>): unknown[] {\n return [\n output.findings,\n output.vulnerabilities,\n output.issues,\n (output as { content?: { findings?: unknown } }).content,\n ];\n}\n\nfunction parseOneFinding(item: unknown, expertId: string, minOrder: number): ReviewFinding | null {\n if (typeof item !== 'object' || item === null) return null;\n\n const record = item as Record<string, unknown>;\n const severity = parseSeverity(record.severity);\n if (SEVERITY_ORDER[severity] < minOrder) return null;\n\n return {\n id: randomUUID(),\n category: parseCategory(record.category),\n severity,\n title: extractStringField(record, 'title', 'name') ?? 'Finding',\n description: extractStringField(record, 'description', 'message') ?? '',\n file: typeof record.file === 'string' ? record.file : undefined,\n line: typeof record.line === 'number' ? record.line : undefined,\n suggestion: typeof record.suggestion === 'string' ? record.suggestion : undefined,\n expertId,\n confidence: typeof record.confidence === 'number' ? record.confidence : 0.7,\n };\n}\n\n// =============================================================================\n// Decision Helpers\n// =============================================================================\n\nexport function determineApproval(findings: ReviewFinding[]): boolean {\n const hasBlocking = findings.some((f) => f.severity === 'critical' || f.severity === 'high');\n return !hasBlocking;\n}\n\nexport function determineDecision(\n reviews: ExpertReviewResult[],\n findings: ReviewFinding[]\n): ReviewDecision {\n const hasCritical = findings.some((f) => f.severity === 'critical');\n const hasHigh = findings.some((f) => f.severity === 'high');\n const allApproved = reviews.every((r) => r.approved);\n\n if (hasCritical) return 'request_changes';\n if (hasHigh && !allApproved) return 'request_changes';\n if (findings.length > 0) return 'comment';\n return 'approve';\n}\n\nexport function calculateConsensus(reviews: ExpertReviewResult[]): number {\n if (reviews.length === 0) return 1;\n const approvals = reviews.filter((r) => r.approved).length;\n return approvals / reviews.length;\n}\n\n// =============================================================================\n// Counting Helpers\n// =============================================================================\n\nexport function countBySeverity(findings: ReviewFinding[]): Record<ReviewSeverity, number> {\n const counts: Record<ReviewSeverity, number> = {\n critical: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n\n for (const f of findings) {\n counts[f.severity]++;\n }\n\n return counts;\n}\n\nexport function countByCategory(findings: ReviewFinding[]): Record<ReviewCategory, number> {\n const counts: Record<ReviewCategory, number> = {\n security: 0,\n performance: 0,\n code_quality: 0,\n testing: 0,\n documentation: 0,\n architecture: 0,\n };\n\n for (const f of findings) {\n counts[f.category]++;\n }\n\n return counts;\n}\n\nexport function sumFindings(counts: Record<ReviewSeverity, number>): number {\n return Object.values(counts).reduce((a, b) => a + b, 0);\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\nexport function generateSummary(\n pr: PRMetadata,\n reviews: ExpertReviewResult[],\n decision: ReviewDecision\n): string {\n const expertSummaries = reviews\n .map((r) => `- **${CATEGORY_DISPLAY_NAMES[r.expertType as ReviewCategory]}**: ${r.summary}`)\n .join('\\n');\n\n return `Reviewed PR #${String(pr.number)}: ${pr.title}\n\n**Decision:** ${decision.replaceAll('_', ' ')}\n**Experts consulted:** ${String(reviews.length)}\n\n${expertSummaries}`;\n}\n\n// =============================================================================\n// GitHub Comment Formatting\n// =============================================================================\n\n/**\n * Formats the review result as a GitHub comment.\n */\nexport function formatReviewComment(result: PRReviewResult): string {\n const emoji = DECISION_EMOJI[result.decision];\n const decisionText = result.decision.replaceAll('_', ' ').toUpperCase();\n\n const findingsSection = formatFindingsSection(result);\n const statsSection = formatStatsSection(result);\n\n return `## ${emoji} Nexus Agents Review: ${decisionText}\n\n${result.summary}\n\n${findingsSection}\n\n${statsSection}\n\n---\n*Reviewed by [nexus-agents](https://github.com/williamzujkowski/nexus-agents) in ${String(result.totalDurationMs)}ms*`;\n}\n\nfunction formatFindingsSection(result: PRReviewResult): string {\n const allFindings = result.expertReviews.flatMap((r) => r.findings);\n\n if (allFindings.length === 0) {\n return '_No issues found._';\n }\n\n const sorted = [...allFindings].sort(\n (a, b) => SEVERITY_ORDER[b.severity] - SEVERITY_ORDER[a.severity]\n );\n\n const lines = ['### Findings', ''];\n\n for (const f of sorted) {\n const emoji = SEVERITY_EMOJI[f.severity];\n const loc =\n f.file !== undefined\n ? ` (\\`${f.file}${f.line !== undefined ? `:${String(f.line)}` : ''}\\`)`\n : '';\n lines.push(`${emoji} **${f.title}**${loc}`);\n lines.push(`> ${f.description}`);\n if (f.suggestion !== undefined) {\n lines.push(`> 💡 ${f.suggestion}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction formatStatsSection(result: PRReviewResult): string {\n const { findingsBySeverity } = result;\n const total = sumFindings(findingsBySeverity);\n\n const parts: string[] = [];\n for (const severity of ['critical', 'high', 'medium', 'low', 'info'] as ReviewSeverity[]) {\n const count = findingsBySeverity[severity];\n if (count > 0) {\n parts.push(`${SEVERITY_EMOJI[severity]} ${String(count)} ${severity}`);\n }\n }\n\n return `<details>\n<summary>Review Statistics (${String(total)} findings)</summary>\n\n- Experts: ${String(result.expertCount)}\n- Consensus: ${(result.consensusScore * 100).toFixed(0)}%\n- Duration: ${String(result.totalDurationMs)}ms\n- Findings: ${parts.join(', ') || 'none'}\n\n</details>`;\n}\n\n// =============================================================================\n// Failed Review Factory\n// =============================================================================\n\nexport function createFailedReview(\n expertId: string,\n category: ReviewCategory,\n durationMs: number,\n error: string\n): ExpertReviewResult {\n return {\n expertId,\n expertType: category,\n approved: true, // Don't block on failures\n summary: `Review failed: ${error}`,\n findings: [],\n durationMs,\n confidence: 0,\n };\n}\n"],"mappings":";;;;;;;;AASA,SAAS,kBAAkB;AAqBpB,SAAS,cAAc,OAAgC;AAC5D,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,QAAQ,MAAM,YAAY;AAChC,QAAI,SAAS,eAAgB,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEO,SAAS,cAAc,OAAgC;AAC5D,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,QAAQ,MAAM,YAAY;AAChC,QAAI,SAAS,uBAAwB,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAEO,SAAS,eAAe,QAAyC;AACtE,MAAI,OAAO,OAAO,YAAY,SAAU,QAAO,OAAO;AACtD,MAAI,OAAO,OAAO,YAAY,SAAU,QAAO,OAAO;AACtD,MAAI,OAAO,OAAO,YAAY,SAAU,QAAO,OAAO;AACtD,SAAO;AACT;AAEO,SAAS,mBACd,WACG,MACiB;AACpB,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,SAAU,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAMO,SAAS,cACd,QACA,UACA,aACiB;AACjB,QAAM,WAAW,eAAe,WAAW;AAC3C,QAAM,UAAU,eAAe,MAAM;AAErC,QAAM,WAA4B,CAAC;AACnC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,MAAM,QAAQ,MAAM,EAAG;AAC5B,eAAW,QAAQ,QAAQ;AACzB,YAAM,UAAU,gBAAgB,MAAM,UAAU,QAAQ;AACxD,UAAI,YAAY,KAAM,UAAS,KAAK,OAAO;AAAA,IAC7C;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAA4C;AAClE,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACN,OAAgD;AAAA,EACnD;AACF;AAEA,SAAS,gBAAgB,MAAe,UAAkB,UAAwC;AAChG,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AAEtD,QAAM,SAAS;AACf,QAAM,WAAW,cAAc,OAAO,QAAQ;AAC9C,MAAI,eAAe,QAAQ,IAAI,SAAU,QAAO;AAEhD,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,UAAU,cAAc,OAAO,QAAQ;AAAA,IACvC;AAAA,IACA,OAAO,mBAAmB,QAAQ,SAAS,MAAM,KAAK;AAAA,IACtD,aAAa,mBAAmB,QAAQ,eAAe,SAAS,KAAK;AAAA,IACrE,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,IACtD,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,IACtD,YAAY,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAAA,IACxE;AAAA,IACA,YAAY,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAAA,EAC1E;AACF;AAMO,SAAS,kBAAkB,UAAoC;AACpE,QAAM,cAAc,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,cAAc,EAAE,aAAa,MAAM;AAC3F,SAAO,CAAC;AACV;AAEO,SAAS,kBACd,SACA,UACgB;AAChB,QAAM,cAAc,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,UAAU;AAClE,QAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,MAAM;AAC1D,QAAM,cAAc,QAAQ,MAAM,CAAC,MAAM,EAAE,QAAQ;AAEnD,MAAI,YAAa,QAAO;AACxB,MAAI,WAAW,CAAC,YAAa,QAAO;AACpC,MAAI,SAAS,SAAS,EAAG,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,mBAAmB,SAAuC;AACxE,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE;AACpD,SAAO,YAAY,QAAQ;AAC7B;AAMO,SAAS,gBAAgB,UAA2D;AACzF,QAAM,SAAyC;AAAA,IAC7C,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAEA,aAAW,KAAK,UAAU;AACxB,WAAO,EAAE,QAAQ;AAAA,EACnB;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,UAA2D;AACzF,QAAM,SAAyC;AAAA,IAC7C,UAAU;AAAA,IACV,aAAa;AAAA,IACb,cAAc;AAAA,IACd,SAAS;AAAA,IACT,eAAe;AAAA,IACf,cAAc;AAAA,EAChB;AAEA,aAAW,KAAK,UAAU;AACxB,WAAO,EAAE,QAAQ;AAAA,EACnB;AAEA,SAAO;AACT;AAEO,SAAS,YAAY,QAAgD;AAC1E,SAAO,OAAO,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACxD;AAMO,SAAS,gBACd,IACA,SACA,UACQ;AACR,QAAM,kBAAkB,QACrB,IAAI,CAAC,MAAM,OAAO,uBAAuB,EAAE,UAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,EAC1F,KAAK,IAAI;AAEZ,SAAO,gBAAgB,OAAO,GAAG,MAAM,CAAC,KAAK,GAAG,KAAK;AAAA;AAAA,gBAEvC,SAAS,WAAW,KAAK,GAAG,CAAC;AAAA,yBACpB,OAAO,QAAQ,MAAM,CAAC;AAAA;AAAA,EAE7C,eAAe;AACjB;AASO,SAAS,oBAAoB,QAAgC;AAClE,QAAM,QAAQ,eAAe,OAAO,QAAQ;AAC5C,QAAM,eAAe,OAAO,SAAS,WAAW,KAAK,GAAG,EAAE,YAAY;AAEtE,QAAM,kBAAkB,sBAAsB,MAAM;AACpD,QAAM,eAAe,mBAAmB,MAAM;AAE9C,SAAO,MAAM,KAAK,yBAAyB,YAAY;AAAA;AAAA,EAEvD,OAAO,OAAO;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA,EAEf,YAAY;AAAA;AAAA;AAAA,mFAGqE,OAAO,OAAO,eAAe,CAAC;AACjH;AAEA,SAAS,sBAAsB,QAAgC;AAC7D,QAAM,cAAc,OAAO,cAAc,QAAQ,CAAC,MAAM,EAAE,QAAQ;AAElE,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,GAAG,WAAW,EAAE;AAAA,IAC9B,CAAC,GAAG,MAAM,eAAe,EAAE,QAAQ,IAAI,eAAe,EAAE,QAAQ;AAAA,EAClE;AAEA,QAAM,QAAQ,CAAC,gBAAgB,EAAE;AAEjC,aAAW,KAAK,QAAQ;AACtB,UAAM,QAAQ,eAAe,EAAE,QAAQ;AACvC,UAAM,MACJ,EAAE,SAAS,SACP,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS,SAAY,IAAI,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,QAChE;AACN,UAAM,KAAK,GAAG,KAAK,MAAM,EAAE,KAAK,KAAK,GAAG,EAAE;AAC1C,UAAM,KAAK,KAAK,EAAE,WAAW,EAAE;AAC/B,QAAI,EAAE,eAAe,QAAW;AAC9B,YAAM,KAAK,eAAQ,EAAE,UAAU,EAAE;AAAA,IACnC;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,mBAAmB,QAAgC;AAC1D,QAAM,EAAE,mBAAmB,IAAI;AAC/B,QAAM,QAAQ,YAAY,kBAAkB;AAE5C,QAAM,QAAkB,CAAC;AACzB,aAAW,YAAY,CAAC,YAAY,QAAQ,UAAU,OAAO,MAAM,GAAuB;AACxF,UAAM,QAAQ,mBAAmB,QAAQ;AACzC,QAAI,QAAQ,GAAG;AACb,YAAM,KAAK,GAAG,eAAe,QAAQ,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,QAAQ,EAAE;AAAA,IACvE;AAAA,EACF;AAEA,SAAO;AAAA,8BACqB,OAAO,KAAK,CAAC;AAAA;AAAA,aAE9B,OAAO,OAAO,WAAW,CAAC;AAAA,gBACvB,OAAO,iBAAiB,KAAK,QAAQ,CAAC,CAAC;AAAA,cACzC,OAAO,OAAO,eAAe,CAAC;AAAA,cAC9B,MAAM,KAAK,IAAI,KAAK,MAAM;AAAA;AAAA;AAGxC;AAMO,SAAS,mBACd,UACA,UACA,YACA,OACoB;AACpB,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA;AAAA,IACV,SAAS,kBAAkB,KAAK;AAAA,IAChC,UAAU,CAAC;AAAA,IACX;AAAA,IACA,YAAY;AAAA,EACd;AACF;","names":[]}
|