@kodrunhq/opencode-autopilot 0.1.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/LICENSE +21 -0
- package/README.md +1 -0
- package/assets/agents/placeholder-agent.md +13 -0
- package/assets/commands/configure.md +17 -0
- package/assets/commands/new-agent.md +16 -0
- package/assets/commands/new-command.md +15 -0
- package/assets/commands/new-skill.md +15 -0
- package/assets/commands/review-pr.md +49 -0
- package/assets/skills/.gitkeep +0 -0
- package/assets/skills/coding-standards/SKILL.md +327 -0
- package/package.json +52 -0
- package/src/agents/autopilot.ts +42 -0
- package/src/agents/documenter.ts +44 -0
- package/src/agents/index.ts +49 -0
- package/src/agents/metaprompter.ts +50 -0
- package/src/agents/pipeline/index.ts +25 -0
- package/src/agents/pipeline/oc-architect.ts +49 -0
- package/src/agents/pipeline/oc-challenger.ts +44 -0
- package/src/agents/pipeline/oc-critic.ts +42 -0
- package/src/agents/pipeline/oc-explorer.ts +46 -0
- package/src/agents/pipeline/oc-implementer.ts +56 -0
- package/src/agents/pipeline/oc-planner.ts +45 -0
- package/src/agents/pipeline/oc-researcher.ts +46 -0
- package/src/agents/pipeline/oc-retrospector.ts +42 -0
- package/src/agents/pipeline/oc-reviewer.ts +44 -0
- package/src/agents/pipeline/oc-shipper.ts +42 -0
- package/src/agents/pr-reviewer.ts +74 -0
- package/src/agents/researcher.ts +43 -0
- package/src/config.ts +168 -0
- package/src/index.ts +152 -0
- package/src/installer.ts +130 -0
- package/src/orchestrator/arena.ts +41 -0
- package/src/orchestrator/artifacts.ts +28 -0
- package/src/orchestrator/confidence.ts +59 -0
- package/src/orchestrator/fallback/chat-message-handler.ts +49 -0
- package/src/orchestrator/fallback/error-classifier.ts +148 -0
- package/src/orchestrator/fallback/event-handler.ts +235 -0
- package/src/orchestrator/fallback/fallback-config.ts +16 -0
- package/src/orchestrator/fallback/fallback-manager.ts +323 -0
- package/src/orchestrator/fallback/fallback-state.ts +120 -0
- package/src/orchestrator/fallback/index.ts +11 -0
- package/src/orchestrator/fallback/message-replay.ts +40 -0
- package/src/orchestrator/fallback/resolve-chain.ts +34 -0
- package/src/orchestrator/fallback/tool-execute-handler.ts +44 -0
- package/src/orchestrator/fallback/types.ts +46 -0
- package/src/orchestrator/handlers/architect.ts +114 -0
- package/src/orchestrator/handlers/build.ts +363 -0
- package/src/orchestrator/handlers/challenge.ts +41 -0
- package/src/orchestrator/handlers/explore.ts +9 -0
- package/src/orchestrator/handlers/index.ts +21 -0
- package/src/orchestrator/handlers/plan.ts +35 -0
- package/src/orchestrator/handlers/recon.ts +40 -0
- package/src/orchestrator/handlers/retrospective.ts +123 -0
- package/src/orchestrator/handlers/ship.ts +38 -0
- package/src/orchestrator/handlers/types.ts +31 -0
- package/src/orchestrator/lesson-injection.ts +80 -0
- package/src/orchestrator/lesson-memory.ts +110 -0
- package/src/orchestrator/lesson-schemas.ts +24 -0
- package/src/orchestrator/lesson-types.ts +6 -0
- package/src/orchestrator/phase.ts +76 -0
- package/src/orchestrator/plan.ts +43 -0
- package/src/orchestrator/schemas.ts +86 -0
- package/src/orchestrator/skill-injection.ts +52 -0
- package/src/orchestrator/state.ts +80 -0
- package/src/orchestrator/types.ts +20 -0
- package/src/review/agent-catalog.ts +439 -0
- package/src/review/agents/auth-flow-verifier.ts +47 -0
- package/src/review/agents/code-quality-auditor.ts +51 -0
- package/src/review/agents/concurrency-checker.ts +47 -0
- package/src/review/agents/contract-verifier.ts +45 -0
- package/src/review/agents/database-auditor.ts +47 -0
- package/src/review/agents/dead-code-scanner.ts +47 -0
- package/src/review/agents/go-idioms-auditor.ts +46 -0
- package/src/review/agents/index.ts +82 -0
- package/src/review/agents/logic-auditor.ts +47 -0
- package/src/review/agents/product-thinker.ts +49 -0
- package/src/review/agents/python-django-auditor.ts +46 -0
- package/src/review/agents/react-patterns-auditor.ts +46 -0
- package/src/review/agents/red-team.ts +49 -0
- package/src/review/agents/rust-safety-auditor.ts +46 -0
- package/src/review/agents/scope-intent-verifier.ts +45 -0
- package/src/review/agents/security-auditor.ts +47 -0
- package/src/review/agents/silent-failure-hunter.ts +45 -0
- package/src/review/agents/spec-checker.ts +45 -0
- package/src/review/agents/state-mgmt-auditor.ts +46 -0
- package/src/review/agents/test-interrogator.ts +43 -0
- package/src/review/agents/type-soundness.ts +46 -0
- package/src/review/agents/wiring-inspector.ts +46 -0
- package/src/review/cross-verification.ts +71 -0
- package/src/review/finding-builder.ts +74 -0
- package/src/review/fix-cycle.ts +146 -0
- package/src/review/memory.ts +114 -0
- package/src/review/pipeline.ts +258 -0
- package/src/review/report.ts +141 -0
- package/src/review/sanitize.ts +8 -0
- package/src/review/schemas.ts +75 -0
- package/src/review/selection.ts +98 -0
- package/src/review/severity.ts +71 -0
- package/src/review/stack-gate.ts +127 -0
- package/src/review/types.ts +43 -0
- package/src/templates/agent-template.ts +47 -0
- package/src/templates/command-template.ts +29 -0
- package/src/templates/skill-template.ts +42 -0
- package/src/tools/confidence.ts +93 -0
- package/src/tools/create-agent.ts +81 -0
- package/src/tools/create-command.ts +74 -0
- package/src/tools/create-skill.ts +74 -0
- package/src/tools/forensics.ts +88 -0
- package/src/tools/orchestrate.ts +310 -0
- package/src/tools/phase.ts +92 -0
- package/src/tools/placeholder.ts +11 -0
- package/src/tools/plan.ts +56 -0
- package/src/tools/review.ts +295 -0
- package/src/tools/state.ts +112 -0
- package/src/utils/fs-helpers.ts +39 -0
- package/src/utils/gitignore.ts +27 -0
- package/src/utils/paths.ts +17 -0
- package/src/utils/validators.ts +57 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { ensureDir, isEnoentError } from "../utils/fs-helpers";
|
|
4
|
+
import { PHASES, pipelineStateSchema } from "./schemas";
|
|
5
|
+
import type { PipelineState } from "./types";
|
|
6
|
+
|
|
7
|
+
const STATE_FILE = "state.json";
|
|
8
|
+
|
|
9
|
+
export function createInitialState(idea: string): PipelineState {
|
|
10
|
+
const now = new Date().toISOString();
|
|
11
|
+
return pipelineStateSchema.parse({
|
|
12
|
+
schemaVersion: 2,
|
|
13
|
+
status: "IN_PROGRESS",
|
|
14
|
+
idea,
|
|
15
|
+
currentPhase: "RECON",
|
|
16
|
+
startedAt: now,
|
|
17
|
+
lastUpdatedAt: now,
|
|
18
|
+
phases: PHASES.map((name, i) => ({
|
|
19
|
+
name,
|
|
20
|
+
status: i === 0 ? "IN_PROGRESS" : "PENDING",
|
|
21
|
+
})),
|
|
22
|
+
decisions: [],
|
|
23
|
+
confidence: [],
|
|
24
|
+
tasks: [],
|
|
25
|
+
arenaConfidence: null,
|
|
26
|
+
exploreTriggered: false,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function loadState(artifactDir: string): Promise<PipelineState | null> {
|
|
31
|
+
const statePath = join(artifactDir, STATE_FILE);
|
|
32
|
+
try {
|
|
33
|
+
const raw = await readFile(statePath, "utf-8");
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
return pipelineStateSchema.parse(parsed);
|
|
36
|
+
} catch (error: unknown) {
|
|
37
|
+
if (isEnoentError(error)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function saveState(state: PipelineState, artifactDir: string): Promise<void> {
|
|
45
|
+
const validated = pipelineStateSchema.parse(state);
|
|
46
|
+
await ensureDir(artifactDir);
|
|
47
|
+
const statePath = join(artifactDir, STATE_FILE);
|
|
48
|
+
const tmpPath = `${statePath}.tmp.${Date.now()}`;
|
|
49
|
+
await writeFile(tmpPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
50
|
+
await rename(tmpPath, statePath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function patchState(
|
|
54
|
+
current: Readonly<PipelineState>,
|
|
55
|
+
updates: Partial<PipelineState>,
|
|
56
|
+
): PipelineState {
|
|
57
|
+
const merged = {
|
|
58
|
+
...current,
|
|
59
|
+
...updates,
|
|
60
|
+
lastUpdatedAt: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
return pipelineStateSchema.parse(merged);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function appendDecision(
|
|
66
|
+
current: Readonly<PipelineState>,
|
|
67
|
+
decision: { phase: string; agent: string; decision: string; rationale: string },
|
|
68
|
+
): PipelineState {
|
|
69
|
+
return {
|
|
70
|
+
...current,
|
|
71
|
+
decisions: [
|
|
72
|
+
...current.decisions,
|
|
73
|
+
{
|
|
74
|
+
...decision,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
lastUpdatedAt: new Date().toISOString(),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type {
|
|
3
|
+
buildProgressSchema,
|
|
4
|
+
confidenceEntrySchema,
|
|
5
|
+
decisionEntrySchema,
|
|
6
|
+
failureContextSchema,
|
|
7
|
+
phaseSchema,
|
|
8
|
+
phaseStatusSchema,
|
|
9
|
+
pipelineStateSchema,
|
|
10
|
+
taskSchema,
|
|
11
|
+
} from "./schemas";
|
|
12
|
+
|
|
13
|
+
export type PipelineState = z.infer<typeof pipelineStateSchema>;
|
|
14
|
+
export type Phase = z.infer<typeof phaseSchema>;
|
|
15
|
+
export type PhaseStatus = z.infer<typeof phaseStatusSchema>;
|
|
16
|
+
export type DecisionEntry = z.infer<typeof decisionEntrySchema>;
|
|
17
|
+
export type ConfidenceEntry = z.infer<typeof confidenceEntrySchema>;
|
|
18
|
+
export type Task = z.infer<typeof taskSchema>;
|
|
19
|
+
export type BuildProgress = z.infer<typeof buildProgressSchema>;
|
|
20
|
+
export type FailureContext = z.infer<typeof failureContextSchema>;
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import type { AgentCategory, AgentDefinition } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NOTE: This catalog is reference data and is NOT yet wired into the review pipeline.
|
|
5
|
+
* The pipeline uses the agent definitions from src/review/agents/*.ts directly.
|
|
6
|
+
* This file may be integrated in a future milestone to drive dynamic agent selection.
|
|
7
|
+
*
|
|
8
|
+
* Complete registry of review agents ported from the ace review engine.
|
|
9
|
+
* Core squad always runs. Parallel specialists run based on stack gate.
|
|
10
|
+
* Sequenced specialists run after all prior findings are collected.
|
|
11
|
+
*/
|
|
12
|
+
export const AGENT_CATALOG: readonly AgentDefinition[] = Object.freeze([
|
|
13
|
+
// --- Core Squad (always runs) ---
|
|
14
|
+
Object.freeze({
|
|
15
|
+
name: "logic-auditor",
|
|
16
|
+
category: "core" as const,
|
|
17
|
+
domain: "Control flow, null safety, async correctness, boundary conditions",
|
|
18
|
+
catches: Object.freeze([
|
|
19
|
+
"Off-by-one errors",
|
|
20
|
+
"Null dereference",
|
|
21
|
+
"Missing await",
|
|
22
|
+
"Race conditions",
|
|
23
|
+
"Incorrect boundary comparisons",
|
|
24
|
+
]),
|
|
25
|
+
triggerSignals: Object.freeze(["Any code change"]),
|
|
26
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
27
|
+
hardGatesSummary:
|
|
28
|
+
"Traces happy + error path per function, checks loops for termination, verifies null safety",
|
|
29
|
+
}),
|
|
30
|
+
Object.freeze({
|
|
31
|
+
name: "test-interrogator",
|
|
32
|
+
category: "core" as const,
|
|
33
|
+
domain: "Test quality, assertion coverage, edge case gaps",
|
|
34
|
+
catches: Object.freeze([
|
|
35
|
+
"Tests without assertions",
|
|
36
|
+
"Over-broad mocks",
|
|
37
|
+
"Missing edge case coverage",
|
|
38
|
+
"Tests that pass by accident",
|
|
39
|
+
]),
|
|
40
|
+
triggerSignals: Object.freeze(["Any code change"]),
|
|
41
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
42
|
+
hardGatesSummary:
|
|
43
|
+
"Reads each test + tested code, identifies hiding bugs, verifies assertions exist",
|
|
44
|
+
}),
|
|
45
|
+
Object.freeze({
|
|
46
|
+
name: "contract-verifier",
|
|
47
|
+
category: "core" as const,
|
|
48
|
+
domain: "API boundary correctness, request/response shape alignment",
|
|
49
|
+
catches: Object.freeze([
|
|
50
|
+
"Mismatched request/response shapes",
|
|
51
|
+
"Wrong HTTP methods",
|
|
52
|
+
"Missing error handling on boundaries",
|
|
53
|
+
"URL path mismatches",
|
|
54
|
+
]),
|
|
55
|
+
triggerSignals: Object.freeze(["Any code change"]),
|
|
56
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
57
|
+
hardGatesSummary:
|
|
58
|
+
"Reads both sides of every API boundary, compares shapes/methods/URLs/error codes",
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
// --- Parallel Specialists ---
|
|
62
|
+
Object.freeze({
|
|
63
|
+
name: "wiring-inspector",
|
|
64
|
+
category: "parallel" as const,
|
|
65
|
+
domain: "End-to-end connectivity (UI -> API -> DB -> response -> UI)",
|
|
66
|
+
catches: Object.freeze([
|
|
67
|
+
"Disconnected flows",
|
|
68
|
+
"Wrong endpoint URLs",
|
|
69
|
+
"Mismatched request/response shapes",
|
|
70
|
+
"Missing error propagation across layers",
|
|
71
|
+
"Orphaned handlers",
|
|
72
|
+
]),
|
|
73
|
+
triggerSignals: Object.freeze([
|
|
74
|
+
"Changes span 2+ architectural layers",
|
|
75
|
+
"New API endpoints",
|
|
76
|
+
"New UI components that fetch data",
|
|
77
|
+
]),
|
|
78
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
79
|
+
hardGatesSummary:
|
|
80
|
+
"Traces every feature path from UI event to DB write and back, documents each verified link",
|
|
81
|
+
}),
|
|
82
|
+
Object.freeze({
|
|
83
|
+
name: "dead-code-scanner",
|
|
84
|
+
category: "parallel" as const,
|
|
85
|
+
domain: "Unused code, imports, unreachable branches",
|
|
86
|
+
catches: Object.freeze([
|
|
87
|
+
"Unused imports",
|
|
88
|
+
"Orphaned functions",
|
|
89
|
+
"TODO/FIXME",
|
|
90
|
+
"Console.log/debugger",
|
|
91
|
+
"Hardcoded secrets",
|
|
92
|
+
"Commented-out code",
|
|
93
|
+
]),
|
|
94
|
+
triggerSignals: Object.freeze([
|
|
95
|
+
"Refactors",
|
|
96
|
+
"Feature removals",
|
|
97
|
+
"Large diffs",
|
|
98
|
+
"File deletions with remaining references",
|
|
99
|
+
]),
|
|
100
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
101
|
+
hardGatesSummary:
|
|
102
|
+
"Checks all changed files for unused imports, orphaned functions, debug artifacts, hardcoded secrets",
|
|
103
|
+
}),
|
|
104
|
+
Object.freeze({
|
|
105
|
+
name: "spec-checker",
|
|
106
|
+
category: "parallel" as const,
|
|
107
|
+
domain: "Requirements alignment with issue/spec context",
|
|
108
|
+
catches: Object.freeze([
|
|
109
|
+
"Missing requirements",
|
|
110
|
+
"Partial implementations",
|
|
111
|
+
"Ungoverned changes",
|
|
112
|
+
"Extra features not in spec",
|
|
113
|
+
]),
|
|
114
|
+
triggerSignals: Object.freeze([
|
|
115
|
+
"Linked GitHub issue exists",
|
|
116
|
+
"PR description references requirements",
|
|
117
|
+
"Changes touch feature code",
|
|
118
|
+
]),
|
|
119
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
120
|
+
hardGatesSummary: "Maps each requirement to implementation status (done/partial/missing/extra)",
|
|
121
|
+
}),
|
|
122
|
+
Object.freeze({
|
|
123
|
+
name: "database-auditor",
|
|
124
|
+
category: "parallel" as const,
|
|
125
|
+
domain: "Migrations, query performance, schema design, connection management",
|
|
126
|
+
catches: Object.freeze([
|
|
127
|
+
"Destructive migrations without rollback",
|
|
128
|
+
"Missing indexes on FKs",
|
|
129
|
+
"N+1 queries",
|
|
130
|
+
"Raw SQL injection",
|
|
131
|
+
"Wrong column types",
|
|
132
|
+
]),
|
|
133
|
+
triggerSignals: Object.freeze([
|
|
134
|
+
"Migration files in diff",
|
|
135
|
+
"Schema changes",
|
|
136
|
+
"ORM model changes without corresponding migrations",
|
|
137
|
+
]),
|
|
138
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
139
|
+
hardGatesSummary:
|
|
140
|
+
"Verifies rollback path, checks index coverage, detects N+1 patterns, checks for SQL injection",
|
|
141
|
+
}),
|
|
142
|
+
Object.freeze({
|
|
143
|
+
name: "auth-flow-verifier",
|
|
144
|
+
category: "parallel" as const,
|
|
145
|
+
domain: "Auth/authz correctness, middleware guards, token handling",
|
|
146
|
+
catches: Object.freeze([
|
|
147
|
+
"Unprotected routes",
|
|
148
|
+
"Privilege escalation",
|
|
149
|
+
"Token validation gaps",
|
|
150
|
+
"Session fixation",
|
|
151
|
+
"Plaintext password storage",
|
|
152
|
+
]),
|
|
153
|
+
triggerSignals: Object.freeze([
|
|
154
|
+
"Changes to auth middleware",
|
|
155
|
+
"Login/logout handlers",
|
|
156
|
+
"Role/permission checks",
|
|
157
|
+
"JWT/session code",
|
|
158
|
+
]),
|
|
159
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
160
|
+
hardGatesSummary:
|
|
161
|
+
"Traces every protected route to verify guard, checks token validation flow end-to-end",
|
|
162
|
+
}),
|
|
163
|
+
Object.freeze({
|
|
164
|
+
name: "type-soundness",
|
|
165
|
+
category: "parallel" as const,
|
|
166
|
+
domain: "Type correctness, invariant design, encapsulation, generics",
|
|
167
|
+
catches: Object.freeze([
|
|
168
|
+
"Unsafe any usage",
|
|
169
|
+
"Incorrect type narrowing",
|
|
170
|
+
"Meaningless generic constraints",
|
|
171
|
+
"Unsafe type assertions",
|
|
172
|
+
"Violated invariants",
|
|
173
|
+
]),
|
|
174
|
+
triggerSignals: Object.freeze([
|
|
175
|
+
"New type definitions",
|
|
176
|
+
"Complex generics",
|
|
177
|
+
"Type assertion usage",
|
|
178
|
+
"any in diff",
|
|
179
|
+
]),
|
|
180
|
+
stackAffinity: Object.freeze(["typescript", "kotlin", "rust", "go"]),
|
|
181
|
+
hardGatesSummary:
|
|
182
|
+
"Flags every any usage, verifies type narrowing correctness, evaluates invariant enforcement",
|
|
183
|
+
}),
|
|
184
|
+
Object.freeze({
|
|
185
|
+
name: "state-mgmt-auditor",
|
|
186
|
+
category: "parallel" as const,
|
|
187
|
+
domain: "UI state consistency, reactivity bugs, stale state, race conditions in UI",
|
|
188
|
+
catches: Object.freeze([
|
|
189
|
+
"Stale closures",
|
|
190
|
+
"Infinite re-render loops",
|
|
191
|
+
"Derived state stored instead of computed",
|
|
192
|
+
"Missing optimistic update rollback",
|
|
193
|
+
]),
|
|
194
|
+
triggerSignals: Object.freeze([
|
|
195
|
+
"React useState/useReducer",
|
|
196
|
+
"Redux/Zustand/Pinia store changes",
|
|
197
|
+
"Vue reactive state",
|
|
198
|
+
"Svelte stores",
|
|
199
|
+
]),
|
|
200
|
+
stackAffinity: Object.freeze(["react", "vue", "svelte", "angular"]),
|
|
201
|
+
hardGatesSummary:
|
|
202
|
+
"Traces state flow from update to render, verifies no stale closures in hooks",
|
|
203
|
+
}),
|
|
204
|
+
Object.freeze({
|
|
205
|
+
name: "concurrency-checker",
|
|
206
|
+
category: "parallel" as const,
|
|
207
|
+
domain: "Thread/async/goroutine safety, deadlocks, resource leaks",
|
|
208
|
+
catches: Object.freeze([
|
|
209
|
+
"Goroutine leaks",
|
|
210
|
+
"Mutex misuse",
|
|
211
|
+
"Race conditions",
|
|
212
|
+
"Missing context cancellation",
|
|
213
|
+
"Missing await",
|
|
214
|
+
]),
|
|
215
|
+
triggerSignals: Object.freeze([
|
|
216
|
+
"Goroutine creation",
|
|
217
|
+
"Mutex/lock usage",
|
|
218
|
+
"Async/await patterns",
|
|
219
|
+
"Worker threads",
|
|
220
|
+
"Promise.all usage",
|
|
221
|
+
]),
|
|
222
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
223
|
+
hardGatesSummary:
|
|
224
|
+
"Verifies every goroutine/thread has a termination path, checks lock/unlock pairs",
|
|
225
|
+
}),
|
|
226
|
+
Object.freeze({
|
|
227
|
+
name: "code-quality-auditor",
|
|
228
|
+
category: "parallel" as const,
|
|
229
|
+
domain: "Readability, modularity, naming, file organization",
|
|
230
|
+
catches: Object.freeze([
|
|
231
|
+
"Overly long functions (>100 lines)",
|
|
232
|
+
"Overly large files (>800 lines)",
|
|
233
|
+
"Deep nesting (>4 levels)",
|
|
234
|
+
"Poor naming",
|
|
235
|
+
"Code duplication",
|
|
236
|
+
]),
|
|
237
|
+
triggerSignals: Object.freeze([
|
|
238
|
+
"Large diffs",
|
|
239
|
+
"New files",
|
|
240
|
+
"Refactors",
|
|
241
|
+
"Substantial function additions",
|
|
242
|
+
]),
|
|
243
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
244
|
+
hardGatesSummary:
|
|
245
|
+
"Measures function length, file length, nesting depth; checks naming conventions",
|
|
246
|
+
}),
|
|
247
|
+
Object.freeze({
|
|
248
|
+
name: "security-auditor",
|
|
249
|
+
category: "parallel" as const,
|
|
250
|
+
domain: "Systematic OWASP auditing, secrets, injection, crypto",
|
|
251
|
+
catches: Object.freeze([
|
|
252
|
+
"Hardcoded secrets",
|
|
253
|
+
"SQL/NoSQL/command injection",
|
|
254
|
+
"XSS",
|
|
255
|
+
"CSRF gaps",
|
|
256
|
+
"Insecure crypto",
|
|
257
|
+
"SSRF",
|
|
258
|
+
"Sensitive data in logs",
|
|
259
|
+
]),
|
|
260
|
+
triggerSignals: Object.freeze([
|
|
261
|
+
"User input handling",
|
|
262
|
+
"API endpoint changes",
|
|
263
|
+
"Database query construction",
|
|
264
|
+
"Crypto usage",
|
|
265
|
+
]),
|
|
266
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
267
|
+
hardGatesSummary: "Checks OWASP Top 10 categories systematically, scans for hardcoded secrets",
|
|
268
|
+
}),
|
|
269
|
+
Object.freeze({
|
|
270
|
+
name: "scope-intent-verifier",
|
|
271
|
+
category: "parallel" as const,
|
|
272
|
+
domain: "Scope creep, project alignment, feature coherence",
|
|
273
|
+
catches: Object.freeze([
|
|
274
|
+
"Features not in any spec/issue",
|
|
275
|
+
"Changes conflicting with project philosophy",
|
|
276
|
+
"Unnecessary dependencies",
|
|
277
|
+
]),
|
|
278
|
+
triggerSignals: Object.freeze([
|
|
279
|
+
"New capabilities added",
|
|
280
|
+
"New dependencies",
|
|
281
|
+
"Changes to core architecture",
|
|
282
|
+
]),
|
|
283
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
284
|
+
hardGatesSummary:
|
|
285
|
+
"Reads project docs to understand purpose, maps each change to a user need or spec requirement",
|
|
286
|
+
}),
|
|
287
|
+
Object.freeze({
|
|
288
|
+
name: "silent-failure-hunter",
|
|
289
|
+
category: "parallel" as const,
|
|
290
|
+
domain: "Error handling quality, swallowed errors, empty catches, silent fallbacks",
|
|
291
|
+
catches: Object.freeze([
|
|
292
|
+
"Empty catch blocks",
|
|
293
|
+
"Generic error swallowing",
|
|
294
|
+
"Console.log-only error handling",
|
|
295
|
+
"Optional chaining masking nulls",
|
|
296
|
+
"Fallbacks hiding failures",
|
|
297
|
+
]),
|
|
298
|
+
triggerSignals: Object.freeze([
|
|
299
|
+
"New/modified try-catch blocks",
|
|
300
|
+
"Error callbacks",
|
|
301
|
+
"Fallback logic",
|
|
302
|
+
"Default values on failure paths",
|
|
303
|
+
]),
|
|
304
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
305
|
+
hardGatesSummary: "Every catch block must log with context and surface actionable feedback",
|
|
306
|
+
}),
|
|
307
|
+
Object.freeze({
|
|
308
|
+
name: "react-patterns-auditor",
|
|
309
|
+
category: "parallel" as const,
|
|
310
|
+
domain: "React/Next.js specific bug classes",
|
|
311
|
+
catches: Object.freeze([
|
|
312
|
+
"Hooks rules violations",
|
|
313
|
+
"Stale closures",
|
|
314
|
+
"Missing useEffect deps",
|
|
315
|
+
"Server/client boundary violations",
|
|
316
|
+
"Hydration mismatches",
|
|
317
|
+
]),
|
|
318
|
+
triggerSignals: Object.freeze([
|
|
319
|
+
"React component files in diff",
|
|
320
|
+
"Hooks usage",
|
|
321
|
+
"Next.js page/layout files",
|
|
322
|
+
]),
|
|
323
|
+
stackAffinity: Object.freeze(["react", "nextjs"]),
|
|
324
|
+
hardGatesSummary:
|
|
325
|
+
"Checks every hook call for rules compliance, verifies every useEffect deps array",
|
|
326
|
+
}),
|
|
327
|
+
Object.freeze({
|
|
328
|
+
name: "go-idioms-auditor",
|
|
329
|
+
category: "parallel" as const,
|
|
330
|
+
domain: "Go-specific bug classes",
|
|
331
|
+
catches: Object.freeze([
|
|
332
|
+
"defer-in-loop",
|
|
333
|
+
"Goroutine leaks",
|
|
334
|
+
"Nil interface traps",
|
|
335
|
+
"Error shadowing with :=",
|
|
336
|
+
"Context misuse",
|
|
337
|
+
]),
|
|
338
|
+
triggerSignals: Object.freeze([
|
|
339
|
+
"Go files in diff",
|
|
340
|
+
"Goroutine creation",
|
|
341
|
+
"Defer statements",
|
|
342
|
+
"Context.Context usage",
|
|
343
|
+
]),
|
|
344
|
+
stackAffinity: Object.freeze(["go"]),
|
|
345
|
+
hardGatesSummary:
|
|
346
|
+
"Checks every defer for loop placement, every goroutine for cancellation path",
|
|
347
|
+
}),
|
|
348
|
+
Object.freeze({
|
|
349
|
+
name: "python-django-auditor",
|
|
350
|
+
category: "parallel" as const,
|
|
351
|
+
domain: "Python/Django-specific bug classes",
|
|
352
|
+
catches: Object.freeze([
|
|
353
|
+
"N+1 in templates",
|
|
354
|
+
"Unvalidated ModelForms",
|
|
355
|
+
"Missing CSRF",
|
|
356
|
+
"Lazy eval traps",
|
|
357
|
+
"Mutable default args",
|
|
358
|
+
]),
|
|
359
|
+
triggerSignals: Object.freeze([
|
|
360
|
+
"Django view/model/form/template files",
|
|
361
|
+
"Python files with ORM queries",
|
|
362
|
+
"settings.py changes",
|
|
363
|
+
]),
|
|
364
|
+
stackAffinity: Object.freeze(["django", "fastapi"]),
|
|
365
|
+
hardGatesSummary:
|
|
366
|
+
"Checks every queryset for select_related/prefetch_related, every ModelForm for explicit fields",
|
|
367
|
+
}),
|
|
368
|
+
Object.freeze({
|
|
369
|
+
name: "rust-safety-auditor",
|
|
370
|
+
category: "parallel" as const,
|
|
371
|
+
domain: "Rust-specific bug classes",
|
|
372
|
+
catches: Object.freeze([
|
|
373
|
+
"Unjustified unsafe blocks",
|
|
374
|
+
".unwrap() in non-test code",
|
|
375
|
+
"Lifetime correctness issues",
|
|
376
|
+
"Send/Sync violations",
|
|
377
|
+
"mem::forget misuse",
|
|
378
|
+
]),
|
|
379
|
+
triggerSignals: Object.freeze([
|
|
380
|
+
"Rust files in diff",
|
|
381
|
+
"Unsafe blocks",
|
|
382
|
+
".unwrap()/.expect() calls",
|
|
383
|
+
"Lifetime annotations",
|
|
384
|
+
]),
|
|
385
|
+
stackAffinity: Object.freeze(["rust"]),
|
|
386
|
+
hardGatesSummary:
|
|
387
|
+
"Every unsafe block must have a SAFETY comment, every .unwrap() in non-test code is flagged",
|
|
388
|
+
}),
|
|
389
|
+
|
|
390
|
+
// --- Sequenced Specialists (run after all prior findings) ---
|
|
391
|
+
Object.freeze({
|
|
392
|
+
name: "product-thinker",
|
|
393
|
+
category: "sequenced" as const,
|
|
394
|
+
domain: "UX/product impact, feature completeness",
|
|
395
|
+
catches: Object.freeze([
|
|
396
|
+
"Dead-end user flows",
|
|
397
|
+
"Missing CRUD operations",
|
|
398
|
+
"Incomplete features",
|
|
399
|
+
"Poor UX",
|
|
400
|
+
]),
|
|
401
|
+
triggerSignals: Object.freeze([
|
|
402
|
+
"User-facing changes",
|
|
403
|
+
"New UI components",
|
|
404
|
+
"New API endpoints for user features",
|
|
405
|
+
]),
|
|
406
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
407
|
+
hardGatesSummary:
|
|
408
|
+
"Traces complete user journey, checks CRUD completeness, verifies no dead ends. Requires ALL Phase 1-2 findings.",
|
|
409
|
+
}),
|
|
410
|
+
Object.freeze({
|
|
411
|
+
name: "red-team",
|
|
412
|
+
category: "sequenced" as const,
|
|
413
|
+
domain: "Adversarial review, inter-domain gap analysis",
|
|
414
|
+
catches: Object.freeze([
|
|
415
|
+
"Bugs hiding between agent domains",
|
|
416
|
+
"Assumption conflicts",
|
|
417
|
+
"User abuse scenarios",
|
|
418
|
+
"Concurrency issues",
|
|
419
|
+
]),
|
|
420
|
+
triggerSignals: Object.freeze(["Any changes that survived Phase 1-3 review"]),
|
|
421
|
+
stackAffinity: Object.freeze(["universal"]),
|
|
422
|
+
hardGatesSummary:
|
|
423
|
+
"Reads ALL other agents' reports, constructs attack scenarios, checks inter-domain gaps. Requires ALL Phase 1-3 findings.",
|
|
424
|
+
}),
|
|
425
|
+
]);
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The three core squad agents that always run regardless of stack or scoring.
|
|
429
|
+
*/
|
|
430
|
+
export const CORE_SQUAD: readonly AgentDefinition[] = Object.freeze(
|
|
431
|
+
AGENT_CATALOG.filter((a) => a.category === "core"),
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Filter agents by category.
|
|
436
|
+
*/
|
|
437
|
+
export function getAgentsByCategory(category: AgentCategory): readonly AgentDefinition[] {
|
|
438
|
+
return AGENT_CATALOG.filter((a) => a.category === category);
|
|
439
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReviewAgent } from "../types";
|
|
2
|
+
|
|
3
|
+
export const authFlowVerifier: Readonly<ReviewAgent> = Object.freeze({
|
|
4
|
+
name: "auth-flow-verifier",
|
|
5
|
+
description:
|
|
6
|
+
"Verifies authentication and authorization flows including route guards, token validation, privilege escalation prevention, and password storage.",
|
|
7
|
+
relevantStacks: [] as readonly string[],
|
|
8
|
+
severityFocus: ["CRITICAL", "HIGH"] as const,
|
|
9
|
+
prompt: `You are the Auth Flow Verifier. You verify that every protected resource has correct authentication and authorization, and that credential handling follows security best practices. Every finding must describe the specific attack vector.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
Trace every authentication and authorization path in the changed code. Do not assume middleware is correctly applied -- verify it.
|
|
14
|
+
|
|
15
|
+
Check each category systematically:
|
|
16
|
+
|
|
17
|
+
1. **Route Protection** -- For every route or endpoint that accesses user data, modifies state, or returns sensitive information, verify an auth guard (middleware, decorator, or check) is present. Flag any protected resource accessible without authentication.
|
|
18
|
+
2. **Token Validation** -- For every token check (JWT verification, session lookup, API key validation), verify the validation is complete: signature check, expiry check, issuer check, and audience check where applicable. Flag partial validation.
|
|
19
|
+
3. **Privilege Escalation** -- Trace every operation that uses a user ID or role. Verify the ID comes from the authenticated session, not from request parameters. Flag any path where a user could access or modify another user's data by changing an ID in the request.
|
|
20
|
+
4. **Session Fixation** -- Verify that session IDs are regenerated after login. Flag login handlers that reuse existing session tokens.
|
|
21
|
+
5. **Password Storage** -- Verify passwords are hashed with bcrypt, scrypt, or argon2 before storage. Flag any plaintext password storage, MD5/SHA1 hashing, or missing salt.
|
|
22
|
+
6. **Token Expiry** -- Verify that access tokens have a finite TTL and that expired tokens are rejected. Flag missing expiry checks or tokens with no expiration.
|
|
23
|
+
|
|
24
|
+
For each finding, describe the attack: "An attacker could [action] because [vulnerability], resulting in [impact]."
|
|
25
|
+
|
|
26
|
+
Do not comment on code style or business logic -- only auth/authz correctness.
|
|
27
|
+
|
|
28
|
+
## Diff
|
|
29
|
+
|
|
30
|
+
{{DIFF}}
|
|
31
|
+
|
|
32
|
+
## Prior Findings (for cross-verification)
|
|
33
|
+
|
|
34
|
+
{{PRIOR_FINDINGS}}
|
|
35
|
+
|
|
36
|
+
## Project Memory (false positive suppression)
|
|
37
|
+
|
|
38
|
+
{{MEMORY}}
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
|
|
42
|
+
For each finding, output a JSON object:
|
|
43
|
+
{"severity": "CRITICAL|HIGH|MEDIUM|LOW", "domain": "auth", "title": "short title", "file": "path/to/file.ts", "line": 42, "agent": "auth-flow-verifier", "source": "phase1", "evidence": "what was found", "problem": "why it is an issue", "fix": "how to fix it"}
|
|
44
|
+
|
|
45
|
+
If no findings: {"findings": []}
|
|
46
|
+
Wrap all findings in: {"findings": [...]}`,
|
|
47
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ReviewAgent } from "../types";
|
|
2
|
+
|
|
3
|
+
export const codeQualityAuditor: Readonly<ReviewAgent> = Object.freeze({
|
|
4
|
+
name: "code-quality-auditor",
|
|
5
|
+
description:
|
|
6
|
+
"Audits code readability, modularity, and naming conventions including function length, file size, nesting depth, and duplication.",
|
|
7
|
+
relevantStacks: [] as readonly string[],
|
|
8
|
+
severityFocus: ["MEDIUM", "LOW"] as const,
|
|
9
|
+
prompt: `You are the Code Quality Auditor. You review readability, structure, and maintainability of changed code.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
### Quantitative Checks
|
|
14
|
+
1. **Function Length** -- Flag functions longer than 50 lines. Extract logical blocks into well-named helpers.
|
|
15
|
+
2. **File Size** -- Flag files longer than 800 lines. Large files indicate multiple responsibilities.
|
|
16
|
+
3. **Nesting Depth** -- Flag nesting deeper than 4 levels. Use early returns, guard clauses, or extracted functions to flatten.
|
|
17
|
+
4. **Duplication** -- Flag near-identical logic in multiple places. Extract into a shared utility.
|
|
18
|
+
5. **Magic Numbers** -- Flag hardcoded numeric/string literals. Extract to named constants or config.
|
|
19
|
+
|
|
20
|
+
### Abstraction & Design
|
|
21
|
+
6. **Conditional Dispatch** -- Flag if/else-if chains or switch statements dispatching on type strings. Use strategy pattern or registry.
|
|
22
|
+
7. **God Functions** -- Flag functions performing multiple unrelated actions in sequence (validate -> transform -> save -> notify). Each responsibility should be separate.
|
|
23
|
+
8. **Primitive Obsession** -- Flag passing raw strings/numbers for domain concepts (user IDs as strings, amounts as numbers) instead of typed wrappers.
|
|
24
|
+
9. **Dead Code** -- Flag unused variables, unreachable branches, commented-out code blocks.
|
|
25
|
+
|
|
26
|
+
### Naming & Readability
|
|
27
|
+
10. **Self-Documenting Names** -- Function and variable names should describe what they do. Flag cryptic abbreviations.
|
|
28
|
+
11. **Single Responsibility** -- Each function/class/module should have one clear purpose. Flag mixed concerns.
|
|
29
|
+
|
|
30
|
+
Do not comment on business logic correctness -- only readability, structure, and maintainability.
|
|
31
|
+
|
|
32
|
+
## Diff
|
|
33
|
+
|
|
34
|
+
{{DIFF}}
|
|
35
|
+
|
|
36
|
+
## Prior Findings (for cross-verification)
|
|
37
|
+
|
|
38
|
+
{{PRIOR_FINDINGS}}
|
|
39
|
+
|
|
40
|
+
## Project Memory (false positive suppression)
|
|
41
|
+
|
|
42
|
+
{{MEMORY}}
|
|
43
|
+
|
|
44
|
+
## Output
|
|
45
|
+
|
|
46
|
+
For each finding, output a JSON object:
|
|
47
|
+
{"severity": "CRITICAL|HIGH|MEDIUM|LOW", "domain": "quality", "title": "short title", "file": "path/to/file.ts", "line": 42, "agent": "code-quality-auditor", "source": "phase1", "evidence": "what was found", "problem": "why it is an issue", "fix": "how to fix it"}
|
|
48
|
+
|
|
49
|
+
If no findings: {"findings": []}
|
|
50
|
+
Wrap all findings in: {"findings": [...]}`,
|
|
51
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReviewAgent } from "../types";
|
|
2
|
+
|
|
3
|
+
export const concurrencyChecker: Readonly<ReviewAgent> = Object.freeze({
|
|
4
|
+
name: "concurrency-checker",
|
|
5
|
+
description:
|
|
6
|
+
"Audits concurrent code for goroutine/thread leaks, lock safety, shared mutable state, missing context cancellation, and async/await correctness.",
|
|
7
|
+
relevantStacks: [] as readonly string[],
|
|
8
|
+
severityFocus: ["CRITICAL", "HIGH"] as const,
|
|
9
|
+
prompt: `You are the Concurrency Checker. You verify that concurrent code is safe, terminates correctly, and has no race conditions or resource leaks. Every finding must describe the specific concurrency hazard.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
Trace every concurrent operation in the changed code: goroutines, threads, async functions, workers, and promises. Do not assume frameworks handle synchronization automatically.
|
|
14
|
+
|
|
15
|
+
Check each category systematically:
|
|
16
|
+
|
|
17
|
+
1. **Termination Paths** -- For every goroutine, thread, or worker spawned, verify there is a clear termination condition (context cancellation, channel close, signal, or timeout). Flag any concurrent operation that can run indefinitely with no shutdown mechanism.
|
|
18
|
+
2. **Lock Balance** -- For every lock acquisition (mutex.Lock, synchronized, semaphore.acquire), verify a corresponding unlock exists on every code path including error paths. Flag lock acquisitions without guaranteed release (missing defer/finally).
|
|
19
|
+
3. **Shared Mutable State** -- For every variable accessed from multiple concurrent contexts, verify it is protected by a mutex, atomic operation, or channel. Flag raw reads/writes to shared state without synchronization.
|
|
20
|
+
4. **Context Cancellation** -- For every function that receives a context parameter, verify cancellation is checked and propagated. Flag functions that ignore context cancellation or create contexts without cancellation.
|
|
21
|
+
5. **Missing Await** -- For every async function call, verify the returned promise is awaited or explicitly handled (then/catch, Promise.all, void operator with comment). Flag fire-and-forget async calls that silently drop errors.
|
|
22
|
+
6. **Promise.all Error Handling** -- For every Promise.all or Promise.allSettled call, verify error handling covers partial failure. Flag Promise.all without a catch that would lose the other results on any single rejection.
|
|
23
|
+
|
|
24
|
+
Show your traces: "I traced goroutine at line N: spawned with go func() -> reads shared map 'cache' at line M -> no mutex protection. Another goroutine writes to 'cache' at line K -> data race."
|
|
25
|
+
|
|
26
|
+
Do not comment on code style, naming, or architecture -- only concurrency correctness.
|
|
27
|
+
|
|
28
|
+
## Diff
|
|
29
|
+
|
|
30
|
+
{{DIFF}}
|
|
31
|
+
|
|
32
|
+
## Prior Findings (for cross-verification)
|
|
33
|
+
|
|
34
|
+
{{PRIOR_FINDINGS}}
|
|
35
|
+
|
|
36
|
+
## Project Memory (false positive suppression)
|
|
37
|
+
|
|
38
|
+
{{MEMORY}}
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
|
|
42
|
+
For each finding, output a JSON object:
|
|
43
|
+
{"severity": "CRITICAL|HIGH|MEDIUM|LOW", "domain": "concurrency", "title": "short title", "file": "path/to/file.ts", "line": 42, "agent": "concurrency-checker", "source": "phase1", "evidence": "what was found", "problem": "why it is an issue", "fix": "how to fix it"}
|
|
44
|
+
|
|
45
|
+
If no findings: {"findings": []}
|
|
46
|
+
Wrap all findings in: {"findings": [...]}`,
|
|
47
|
+
});
|