@hstm-labs/forge-reviewer 0.1.11
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 +43 -0
- package/dist/collect-review-files.d.ts +36 -0
- package/dist/collect-review-files.d.ts.map +1 -0
- package/dist/collect-review-files.js +78 -0
- package/dist/collect-review-files.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/review-stage.d.ts +70 -0
- package/dist/review-stage.d.ts.map +1 -0
- package/dist/review-stage.js +364 -0
- package/dist/review-stage.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/validator.d.ts +61 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +221 -0
- package/dist/validator.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @hstm-labs/forge-reviewer
|
|
2
|
+
|
|
3
|
+
Multi-pass AI code review stage for Forge — performs file-level, cross-file, spec-alignment, and synthesis review passes across all generated artifacts, producing structured findings with severity and remediation guidance.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hstm-labs/forge-reviewer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Public API
|
|
12
|
+
|
|
13
|
+
### Types
|
|
14
|
+
|
|
15
|
+
- `CodeReviewReport` — complete review output with findings and summary
|
|
16
|
+
- `CodeReviewFinding` — individual finding with file, line, severity, remediation
|
|
17
|
+
- `ReviewDimension` — review category (correctness, security, performance, etc.)
|
|
18
|
+
- `ReviewPassResult` — output from a single review pass
|
|
19
|
+
- `ReviewFile` — file metadata for review input
|
|
20
|
+
|
|
21
|
+
### Classes
|
|
22
|
+
|
|
23
|
+
- `ReviewStage` — pipeline stage implementing `PipelineStage` interface (4 LLM calls)
|
|
24
|
+
- `ReviewPassValidator` — validates individual review pass output
|
|
25
|
+
- `ReviewSynthesisValidator` — validates final synthesis output
|
|
26
|
+
|
|
27
|
+
### Functions
|
|
28
|
+
|
|
29
|
+
- `collectReviewFiles(artifactsDir)` — gather generated files for review input
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { ReviewStage } from '@hstm-labs/forge-reviewer';
|
|
35
|
+
|
|
36
|
+
const stage = new ReviewStage();
|
|
37
|
+
const result = await stage.execute(input);
|
|
38
|
+
// result.data contains CodeReviewReport with findings from all 4 passes
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File collection helper for the AI code review stage.
|
|
3
|
+
*
|
|
4
|
+
* Extracts generated source code files from prior stage outputs
|
|
5
|
+
* into a flat list for review by the LLM.
|
|
6
|
+
*/
|
|
7
|
+
import type { ApiArtifact } from '@hstm-labs/forge-services-generator';
|
|
8
|
+
import type { UiArtifact } from '@hstm-labs/forge-apps-generator';
|
|
9
|
+
import type { SecurityArtifact } from '@hstm-labs/forge-security-generator';
|
|
10
|
+
import type { InfraArtifact } from '@hstm-labs/forge-infra-generator';
|
|
11
|
+
/** A file collected for review from a prior generation stage. */
|
|
12
|
+
export interface ReviewFile {
|
|
13
|
+
/** File name or relative path. */
|
|
14
|
+
fileName: string;
|
|
15
|
+
/** File content (source code). */
|
|
16
|
+
content: string;
|
|
17
|
+
/** Which generation stage produced this file. */
|
|
18
|
+
stage: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Collect all generated source files from prior stage outputs for review.
|
|
22
|
+
*
|
|
23
|
+
* Source files are extracted from:
|
|
24
|
+
* - API: handlers, types
|
|
25
|
+
* - UI: pages, components, layouts, forms, navigation
|
|
26
|
+
* - Security: authModel, rbac, middleware, inputValidation, securityHeaders
|
|
27
|
+
* - Infra: containers, compose, ciPipeline
|
|
28
|
+
*
|
|
29
|
+
* @param api - API artifact (may be undefined)
|
|
30
|
+
* @param ui - UI artifact (may be undefined)
|
|
31
|
+
* @param security - Security artifact (may be undefined)
|
|
32
|
+
* @param infra - Infra artifact (may be undefined)
|
|
33
|
+
* @returns Array of review files
|
|
34
|
+
*/
|
|
35
|
+
export declare function collectReviewFiles(api: ApiArtifact | undefined, ui: UiArtifact | undefined, security: SecurityArtifact | undefined, infra: InfraArtifact | undefined): ReviewFile[];
|
|
36
|
+
//# sourceMappingURL=collect-review-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-review-files.d.ts","sourceRoot":"","sources":["../src/collect-review-files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAMtE,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,GAAG,SAAS,EAC5B,EAAE,EAAE,UAAU,GAAG,SAAS,EAC1B,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,UAAU,EAAE,CAyDd"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File collection helper for the AI code review stage.
|
|
3
|
+
*
|
|
4
|
+
* Extracts generated source code files from prior stage outputs
|
|
5
|
+
* into a flat list for review by the LLM.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Collector
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Collect all generated source files from prior stage outputs for review.
|
|
12
|
+
*
|
|
13
|
+
* Source files are extracted from:
|
|
14
|
+
* - API: handlers, types
|
|
15
|
+
* - UI: pages, components, layouts, forms, navigation
|
|
16
|
+
* - Security: authModel, rbac, middleware, inputValidation, securityHeaders
|
|
17
|
+
* - Infra: containers, compose, ciPipeline
|
|
18
|
+
*
|
|
19
|
+
* @param api - API artifact (may be undefined)
|
|
20
|
+
* @param ui - UI artifact (may be undefined)
|
|
21
|
+
* @param security - Security artifact (may be undefined)
|
|
22
|
+
* @param infra - Infra artifact (may be undefined)
|
|
23
|
+
* @returns Array of review files
|
|
24
|
+
*/
|
|
25
|
+
export function collectReviewFiles(api, ui, security, infra) {
|
|
26
|
+
const files = [];
|
|
27
|
+
// API: handlers + types
|
|
28
|
+
if (api !== undefined) {
|
|
29
|
+
for (const handler of api.handlers) {
|
|
30
|
+
files.push({ fileName: handler.fileName, content: handler.content, stage: 'services-generate' });
|
|
31
|
+
}
|
|
32
|
+
for (const typeDef of api.types) {
|
|
33
|
+
files.push({ fileName: typeDef.fileName, content: typeDef.content, stage: 'services-generate' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// UI: pages, components, layouts, forms, navigation
|
|
37
|
+
if (ui !== undefined) {
|
|
38
|
+
for (const page of ui.pages) {
|
|
39
|
+
files.push({ fileName: page.fileName, content: page.content, stage: 'apps-generate' });
|
|
40
|
+
}
|
|
41
|
+
for (const component of ui.components) {
|
|
42
|
+
files.push({ fileName: component.fileName, content: component.content, stage: 'apps-generate' });
|
|
43
|
+
}
|
|
44
|
+
for (const layout of ui.layouts) {
|
|
45
|
+
files.push({ fileName: layout.fileName, content: layout.content, stage: 'apps-generate' });
|
|
46
|
+
}
|
|
47
|
+
for (const form of ui.forms) {
|
|
48
|
+
files.push({ fileName: form.fileName, content: form.content, stage: 'apps-generate' });
|
|
49
|
+
}
|
|
50
|
+
files.push({ fileName: ui.navigation.fileName, content: ui.navigation.content, stage: 'apps-generate' });
|
|
51
|
+
}
|
|
52
|
+
// Security: authModel, rbac, middleware, inputValidation, securityHeaders
|
|
53
|
+
if (security !== undefined) {
|
|
54
|
+
files.push({ fileName: security.authModel.fileName, content: security.authModel.content, stage: 'security-generate' });
|
|
55
|
+
files.push({ fileName: security.rbac.fileName, content: security.rbac.content, stage: 'security-generate' });
|
|
56
|
+
for (const mw of security.middleware) {
|
|
57
|
+
files.push({ fileName: mw.fileName, content: mw.content, stage: 'security-generate' });
|
|
58
|
+
}
|
|
59
|
+
for (const rule of security.inputValidation) {
|
|
60
|
+
files.push({ fileName: rule.fileName, content: rule.content, stage: 'security-generate' });
|
|
61
|
+
}
|
|
62
|
+
files.push({ fileName: security.securityHeaders.fileName, content: security.securityHeaders.content, stage: 'security-generate' });
|
|
63
|
+
}
|
|
64
|
+
// Infra: containers, compose, ciPipeline
|
|
65
|
+
if (infra !== undefined) {
|
|
66
|
+
for (const container of infra.containers) {
|
|
67
|
+
files.push({ fileName: container.fileName, content: container.content, stage: 'infra-generate' });
|
|
68
|
+
}
|
|
69
|
+
if (infra.compose !== undefined && infra.compose !== null) {
|
|
70
|
+
files.push({ fileName: infra.compose.fileName, content: infra.compose.content, stage: 'infra-generate' });
|
|
71
|
+
}
|
|
72
|
+
if (infra.ciPipeline !== undefined && infra.ciPipeline !== null) {
|
|
73
|
+
files.push({ fileName: infra.ciPipeline.fileName, content: infra.ciPipeline.content, stage: 'infra-generate' });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return files;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=collect-review-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-review-files.js","sourceRoot":"","sources":["../src/collect-review-files.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hstm-labs/forge-reviewer — Multi-pass AI code review stage for Forge.
|
|
3
|
+
*
|
|
4
|
+
* Provides the {@link ReviewStage} pipeline stage, the
|
|
5
|
+
* {@link ReviewPassValidator} and {@link ReviewSynthesisValidator},
|
|
6
|
+
* and the {@link CodeReviewReport} type family describing review output.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export type { CodeReviewReport, CodeReviewFinding, ReviewDimension, ReviewPassResult, } from './types.js';
|
|
11
|
+
export { ReviewStage } from './review-stage.js';
|
|
12
|
+
export { ReviewPassValidator, ReviewSynthesisValidator } from './validator.js';
|
|
13
|
+
export { collectReviewFiles } from './collect-review-files.js';
|
|
14
|
+
export type { ReviewFile } from './collect-review-files.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hstm-labs/forge-reviewer — Multi-pass AI code review stage for Forge.
|
|
3
|
+
*
|
|
4
|
+
* Provides the {@link ReviewStage} pipeline stage, the
|
|
5
|
+
* {@link ReviewPassValidator} and {@link ReviewSynthesisValidator},
|
|
6
|
+
* and the {@link CodeReviewReport} type family describing review output.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { ReviewStage } from './review-stage.js';
|
|
11
|
+
export { ReviewPassValidator, ReviewSynthesisValidator } from './validator.js';
|
|
12
|
+
export { collectReviewFiles } from './collect-review-files.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-pass AI code review pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Performs 4 LLM passes to analyze all generated artifacts:
|
|
5
|
+
* 1. **File-level pass** — individual file quality, security, best practices
|
|
6
|
+
* 2. **Cross-file pass** — import resolution, type consistency, contract alignment
|
|
7
|
+
* 3. **Spec-alignment pass** — requirements coverage, entity completeness
|
|
8
|
+
* 4. **Synthesis pass** — deduplication, severity validation, summary, pass/fail
|
|
9
|
+
*
|
|
10
|
+
* Each pass uses a shared system template but a distinct user prompt.
|
|
11
|
+
* The synthesis pass aggregates findings from passes 1-3.
|
|
12
|
+
*
|
|
13
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
14
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
15
|
+
*/
|
|
16
|
+
import type { StageName } from '@hstm-labs/forge-common';
|
|
17
|
+
import type { PipelineStage, PipelineStageInput, PipelineStageOutput, PipelineContext, AgentPromptContext } from '@hstm-labs/forge-core';
|
|
18
|
+
/**
|
|
19
|
+
* Pipeline stage that performs a multi-pass AI code review.
|
|
20
|
+
*
|
|
21
|
+
* Produces a {@link CodeReviewReport} with findings from 4 LLM passes.
|
|
22
|
+
* The report is written as `review-report.json` and stored in
|
|
23
|
+
* `data.review` in the pipeline output.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const stage = new ReviewStage();
|
|
28
|
+
* const output = await stage.execute(input, context);
|
|
29
|
+
* const report = output.data?.review as CodeReviewReport;
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class ReviewStage implements PipelineStage {
|
|
33
|
+
readonly name: StageName;
|
|
34
|
+
readonly dependsOn: StageName[];
|
|
35
|
+
readonly requiresLLM = true;
|
|
36
|
+
/**
|
|
37
|
+
* Return rendered agent-mode prompt context using `review-agent.hbs`.
|
|
38
|
+
*
|
|
39
|
+
* @param input - Input from prior stages
|
|
40
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
41
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
42
|
+
*/
|
|
43
|
+
getAgentContext(input: PipelineStageInput, context: PipelineContext): AgentPromptContext;
|
|
44
|
+
/**
|
|
45
|
+
* Execute the multi-pass review stage.
|
|
46
|
+
*
|
|
47
|
+
* @param input - Input from all prior generation stages
|
|
48
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
49
|
+
* @returns Stage output with review report artifact and CodeReviewReport in data
|
|
50
|
+
* @throws {ForgeError} FORGE-PIPE-003 if required dependency stage output is missing
|
|
51
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
52
|
+
*/
|
|
53
|
+
execute(input: PipelineStageInput, context: PipelineContext): Promise<PipelineStageOutput>;
|
|
54
|
+
/**
|
|
55
|
+
* Extract and validate all required dependency outputs.
|
|
56
|
+
*
|
|
57
|
+
* @param input - Pipeline stage input map
|
|
58
|
+
* @returns Structured dependency outputs
|
|
59
|
+
* @throws {ForgeError} if required dependencies are missing
|
|
60
|
+
*/
|
|
61
|
+
private collectDependencies;
|
|
62
|
+
/**
|
|
63
|
+
* Parse findings array from LLM output JSON.
|
|
64
|
+
*
|
|
65
|
+
* @param content - Raw JSON string with `findings` key
|
|
66
|
+
* @returns Array of code review findings
|
|
67
|
+
*/
|
|
68
|
+
private parseFindings;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=review-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-stage.d.ts","sourceRoot":"","sources":["../src/review-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAEf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AA6B/B;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAY,YAAW,aAAa;IAC/C,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAY;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAO7B;IACF,QAAQ,CAAC,WAAW,QAAQ;IAE5B;;;;;;OAMG;IACH,eAAe,CACb,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,kBAAkB;IA+CrB;;;;;;;;OAQG;IACG,OAAO,CACX,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;IA2Q/B;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IA4G3B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;CAItB"}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-pass AI code review pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Performs 4 LLM passes to analyze all generated artifacts:
|
|
5
|
+
* 1. **File-level pass** — individual file quality, security, best practices
|
|
6
|
+
* 2. **Cross-file pass** — import resolution, type consistency, contract alignment
|
|
7
|
+
* 3. **Spec-alignment pass** — requirements coverage, entity completeness
|
|
8
|
+
* 4. **Synthesis pass** — deduplication, severity validation, summary, pass/fail
|
|
9
|
+
*
|
|
10
|
+
* Each pass uses a shared system template but a distinct user prompt.
|
|
11
|
+
* The synthesis pass aggregates findings from passes 1-3.
|
|
12
|
+
*
|
|
13
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
14
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
15
|
+
*/
|
|
16
|
+
import { join, dirname } from 'node:path';
|
|
17
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
18
|
+
import { ForgeError, ErrorCodes, hashContent } from '@hstm-labs/forge-common';
|
|
19
|
+
import { ApiStageExecutor } from '@hstm-labs/forge-core';
|
|
20
|
+
import { loadProfile } from '@hstm-labs/forge-profiles';
|
|
21
|
+
import { loadTemplate, renderTemplate } from '@hstm-labs/forge-templates';
|
|
22
|
+
import { ReviewPassValidator, ReviewSynthesisValidator } from './validator.js';
|
|
23
|
+
import { collectReviewFiles } from './collect-review-files.js';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Constants
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const RETRY_POLICY = {
|
|
28
|
+
maxRetries: 3,
|
|
29
|
+
backoffMs: 1000,
|
|
30
|
+
includeErrorInRetry: true,
|
|
31
|
+
};
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Stage
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Pipeline stage that performs a multi-pass AI code review.
|
|
37
|
+
*
|
|
38
|
+
* Produces a {@link CodeReviewReport} with findings from 4 LLM passes.
|
|
39
|
+
* The report is written as `review-report.json` and stored in
|
|
40
|
+
* `data.review` in the pipeline output.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const stage = new ReviewStage();
|
|
45
|
+
* const output = await stage.execute(input, context);
|
|
46
|
+
* const report = output.data?.review as CodeReviewReport;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export class ReviewStage {
|
|
50
|
+
name = 'review';
|
|
51
|
+
dependsOn = [
|
|
52
|
+
'architect',
|
|
53
|
+
'services-generate',
|
|
54
|
+
'apps-generate',
|
|
55
|
+
'infra-generate',
|
|
56
|
+
'security-generate',
|
|
57
|
+
'seed-data',
|
|
58
|
+
];
|
|
59
|
+
requiresLLM = true;
|
|
60
|
+
/**
|
|
61
|
+
* Return rendered agent-mode prompt context using `review-agent.hbs`.
|
|
62
|
+
*
|
|
63
|
+
* @param input - Input from prior stages
|
|
64
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
65
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
66
|
+
*/
|
|
67
|
+
getAgentContext(input, context) {
|
|
68
|
+
const parsedSpec = input['validate']?.data?.['parsedSpec'];
|
|
69
|
+
const architecture = input['architect']?.data?.['architecture'];
|
|
70
|
+
const api = input['services-generate']?.data?.['api'];
|
|
71
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
72
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'review', 'artifacts');
|
|
73
|
+
const agentTemplate = loadTemplate('review-agent', context.workspace.rootDir);
|
|
74
|
+
const rendered = renderTemplate(agentTemplate, {
|
|
75
|
+
spec: (parsedSpec ?? {}),
|
|
76
|
+
profile: profile,
|
|
77
|
+
architecture: (architecture ?? {}),
|
|
78
|
+
api: (api ?? {}),
|
|
79
|
+
stage: { outputDir },
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
prompt: rendered.content,
|
|
83
|
+
outputSchema: {
|
|
84
|
+
format: 'json',
|
|
85
|
+
requiredFiles: ['review-report.json'],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Execute the multi-pass review stage.
|
|
91
|
+
*
|
|
92
|
+
* @param input - Input from all prior generation stages
|
|
93
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
94
|
+
* @returns Stage output with review report artifact and CodeReviewReport in data
|
|
95
|
+
* @throws {ForgeError} FORGE-PIPE-003 if required dependency stage output is missing
|
|
96
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
97
|
+
*/
|
|
98
|
+
async execute(input, context) {
|
|
99
|
+
// 1. Collect all prior stage outputs
|
|
100
|
+
const { parsedSpec, architecture, api, ui, security, infra } = this.collectDependencies(input);
|
|
101
|
+
// 2. Require adapter
|
|
102
|
+
if (context.adapter === undefined) {
|
|
103
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Review stage requires an LLM adapter in API mode, but none was provided. ' +
|
|
104
|
+
'Configure an LLM provider in forge.config.json or use agent mode.');
|
|
105
|
+
}
|
|
106
|
+
// 3. Load profile
|
|
107
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
108
|
+
// 4. Compute output directory
|
|
109
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'review', 'artifacts');
|
|
110
|
+
// 5. Load shared system template
|
|
111
|
+
const systemTemplate = loadTemplate('review-system', context.workspace.rootDir);
|
|
112
|
+
const userTemplate = loadTemplate('review-user', context.workspace.rootDir);
|
|
113
|
+
const profileCtx = profile;
|
|
114
|
+
const systemPrompt = renderTemplate(systemTemplate, {
|
|
115
|
+
profile: profileCtx,
|
|
116
|
+
});
|
|
117
|
+
// 6. Collect review files
|
|
118
|
+
const reviewFiles = collectReviewFiles(api, ui, security, infra);
|
|
119
|
+
// 7. Shared template context for pass-specific templates
|
|
120
|
+
const specCtx = parsedSpec;
|
|
121
|
+
const archCtx = architecture;
|
|
122
|
+
const apiCtx = api;
|
|
123
|
+
const uiCtx = ui;
|
|
124
|
+
// -----------------------------------------------------------------------
|
|
125
|
+
// Pass 1: File-level review
|
|
126
|
+
// -----------------------------------------------------------------------
|
|
127
|
+
const filePassTemplate = loadTemplate('review-file-pass', context.workspace.rootDir);
|
|
128
|
+
const filePassRendered = renderTemplate(filePassTemplate, {
|
|
129
|
+
profile: profileCtx,
|
|
130
|
+
files: reviewFiles,
|
|
131
|
+
});
|
|
132
|
+
const filePassUserPrompt = renderTemplate(userTemplate, {
|
|
133
|
+
passPrompt: filePassRendered.content,
|
|
134
|
+
});
|
|
135
|
+
const passValidator = new ReviewPassValidator();
|
|
136
|
+
const filePassExecutor = new ApiStageExecutor({
|
|
137
|
+
adapter: context.adapter,
|
|
138
|
+
validator: passValidator,
|
|
139
|
+
retryPolicy: { ...RETRY_POLICY },
|
|
140
|
+
});
|
|
141
|
+
const filePassResult = await filePassExecutor.execute({
|
|
142
|
+
prompt: filePassUserPrompt.content,
|
|
143
|
+
systemPrompt: systemPrompt.content,
|
|
144
|
+
stageName: 'review',
|
|
145
|
+
outputDir,
|
|
146
|
+
runId: context.runId,
|
|
147
|
+
mode: 'api',
|
|
148
|
+
outputSchema: { format: 'json' },
|
|
149
|
+
});
|
|
150
|
+
const filePassFindings = this.parseFindings(filePassResult.artifacts[0]?.content ?? '{"findings":[]}');
|
|
151
|
+
// -----------------------------------------------------------------------
|
|
152
|
+
// Pass 2: Cross-file review
|
|
153
|
+
// -----------------------------------------------------------------------
|
|
154
|
+
const crossFileTemplate = loadTemplate('review-crossfile-pass', context.workspace.rootDir);
|
|
155
|
+
const crossFileRendered = renderTemplate(crossFileTemplate, {
|
|
156
|
+
api: apiCtx,
|
|
157
|
+
ui: uiCtx,
|
|
158
|
+
architecture: archCtx,
|
|
159
|
+
});
|
|
160
|
+
const crossFileUserPrompt = renderTemplate(userTemplate, {
|
|
161
|
+
passPrompt: crossFileRendered.content,
|
|
162
|
+
});
|
|
163
|
+
const crossFileExecutor = new ApiStageExecutor({
|
|
164
|
+
adapter: context.adapter,
|
|
165
|
+
validator: passValidator,
|
|
166
|
+
retryPolicy: { ...RETRY_POLICY },
|
|
167
|
+
});
|
|
168
|
+
const crossFileResult = await crossFileExecutor.execute({
|
|
169
|
+
prompt: crossFileUserPrompt.content,
|
|
170
|
+
systemPrompt: systemPrompt.content,
|
|
171
|
+
stageName: 'review',
|
|
172
|
+
outputDir,
|
|
173
|
+
runId: context.runId,
|
|
174
|
+
mode: 'api',
|
|
175
|
+
outputSchema: { format: 'json' },
|
|
176
|
+
});
|
|
177
|
+
const crossFileFindings = this.parseFindings(crossFileResult.artifacts[0]?.content ?? '{"findings":[]}');
|
|
178
|
+
// -----------------------------------------------------------------------
|
|
179
|
+
// Pass 3: Spec-alignment review
|
|
180
|
+
// -----------------------------------------------------------------------
|
|
181
|
+
const specAlignTemplate = loadTemplate('review-specalign-pass', context.workspace.rootDir);
|
|
182
|
+
const specAlignRendered = renderTemplate(specAlignTemplate, {
|
|
183
|
+
spec: specCtx,
|
|
184
|
+
architecture: archCtx,
|
|
185
|
+
api: apiCtx,
|
|
186
|
+
ui: uiCtx,
|
|
187
|
+
});
|
|
188
|
+
const specAlignUserPrompt = renderTemplate(userTemplate, {
|
|
189
|
+
passPrompt: specAlignRendered.content,
|
|
190
|
+
});
|
|
191
|
+
const specAlignExecutor = new ApiStageExecutor({
|
|
192
|
+
adapter: context.adapter,
|
|
193
|
+
validator: passValidator,
|
|
194
|
+
retryPolicy: { ...RETRY_POLICY },
|
|
195
|
+
});
|
|
196
|
+
const specAlignResult = await specAlignExecutor.execute({
|
|
197
|
+
prompt: specAlignUserPrompt.content,
|
|
198
|
+
systemPrompt: systemPrompt.content,
|
|
199
|
+
stageName: 'review',
|
|
200
|
+
outputDir,
|
|
201
|
+
runId: context.runId,
|
|
202
|
+
mode: 'api',
|
|
203
|
+
outputSchema: { format: 'json' },
|
|
204
|
+
});
|
|
205
|
+
const specAlignFindings = this.parseFindings(specAlignResult.artifacts[0]?.content ?? '{"findings":[]}');
|
|
206
|
+
// -----------------------------------------------------------------------
|
|
207
|
+
// Aggregate and renumber findings from passes 1-3
|
|
208
|
+
// -----------------------------------------------------------------------
|
|
209
|
+
const allPreviousFindings = [
|
|
210
|
+
...filePassFindings,
|
|
211
|
+
...crossFileFindings,
|
|
212
|
+
...specAlignFindings,
|
|
213
|
+
];
|
|
214
|
+
const renumberedFindings = allPreviousFindings.map((f, idx) => ({
|
|
215
|
+
...f,
|
|
216
|
+
id: `REVIEW-${String(idx + 1).padStart(3, '0')}`,
|
|
217
|
+
}));
|
|
218
|
+
// -----------------------------------------------------------------------
|
|
219
|
+
// Pass 4: Synthesis
|
|
220
|
+
// -----------------------------------------------------------------------
|
|
221
|
+
const synthesisTemplate = loadTemplate('review-synthesis-pass', context.workspace.rootDir);
|
|
222
|
+
const synthesisRendered = renderTemplate(synthesisTemplate, {
|
|
223
|
+
previousFindings: renumberedFindings,
|
|
224
|
+
});
|
|
225
|
+
const synthesisUserPrompt = renderTemplate(userTemplate, {
|
|
226
|
+
passPrompt: synthesisRendered.content,
|
|
227
|
+
});
|
|
228
|
+
const synthesisValidator = new ReviewSynthesisValidator();
|
|
229
|
+
const synthesisExecutor = new ApiStageExecutor({
|
|
230
|
+
adapter: context.adapter,
|
|
231
|
+
validator: synthesisValidator,
|
|
232
|
+
retryPolicy: { ...RETRY_POLICY },
|
|
233
|
+
});
|
|
234
|
+
const synthesisResult = await synthesisExecutor.execute({
|
|
235
|
+
prompt: synthesisUserPrompt.content,
|
|
236
|
+
systemPrompt: systemPrompt.content,
|
|
237
|
+
stageName: 'review',
|
|
238
|
+
outputDir,
|
|
239
|
+
runId: context.runId,
|
|
240
|
+
mode: 'api',
|
|
241
|
+
outputSchema: { format: 'json' },
|
|
242
|
+
});
|
|
243
|
+
const synthesisData = JSON.parse(synthesisResult.artifacts[0]?.content ?? '{"findings":[],"summary":"","passOrFail":"pass"}');
|
|
244
|
+
// -----------------------------------------------------------------------
|
|
245
|
+
// Build CodeReviewReport
|
|
246
|
+
// -----------------------------------------------------------------------
|
|
247
|
+
const uniqueFiles = new Set(reviewFiles.map((f) => f.fileName));
|
|
248
|
+
const report = {
|
|
249
|
+
runId: context.runId,
|
|
250
|
+
reviewedAt: new Date().toISOString(),
|
|
251
|
+
totalFiles: uniqueFiles.size,
|
|
252
|
+
totalFindings: synthesisData.findings.length,
|
|
253
|
+
findingsBySeverity: {
|
|
254
|
+
critical: synthesisData.findings.filter((f) => f.severity === 'critical').length,
|
|
255
|
+
warning: synthesisData.findings.filter((f) => f.severity === 'warning').length,
|
|
256
|
+
suggestion: synthesisData.findings.filter((f) => f.severity === 'suggestion').length,
|
|
257
|
+
},
|
|
258
|
+
findings: synthesisData.findings,
|
|
259
|
+
summary: synthesisData.summary,
|
|
260
|
+
passOrFail: synthesisData.passOrFail,
|
|
261
|
+
};
|
|
262
|
+
// -----------------------------------------------------------------------
|
|
263
|
+
// Write report
|
|
264
|
+
// -----------------------------------------------------------------------
|
|
265
|
+
const reportJson = JSON.stringify(report, null, 2);
|
|
266
|
+
const reportPath = 'review-report.json';
|
|
267
|
+
const fullPath = join(outputDir, reportPath);
|
|
268
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
269
|
+
writeFileSync(fullPath, reportJson, 'utf-8');
|
|
270
|
+
const artifacts = [
|
|
271
|
+
{
|
|
272
|
+
filePath: reportPath,
|
|
273
|
+
content: reportJson,
|
|
274
|
+
contentHash: hashContent(reportJson),
|
|
275
|
+
sizeBytes: Buffer.byteLength(reportJson, 'utf-8'),
|
|
276
|
+
},
|
|
277
|
+
];
|
|
278
|
+
return {
|
|
279
|
+
artifacts,
|
|
280
|
+
data: {
|
|
281
|
+
review: report,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
// -------------------------------------------------------------------------
|
|
286
|
+
// Private helpers
|
|
287
|
+
// -------------------------------------------------------------------------
|
|
288
|
+
/**
|
|
289
|
+
* Extract and validate all required dependency outputs.
|
|
290
|
+
*
|
|
291
|
+
* @param input - Pipeline stage input map
|
|
292
|
+
* @returns Structured dependency outputs
|
|
293
|
+
* @throws {ForgeError} if required dependencies are missing
|
|
294
|
+
*/
|
|
295
|
+
collectDependencies(input) {
|
|
296
|
+
// Validate stage
|
|
297
|
+
const validateOutput = input['validate'];
|
|
298
|
+
if (validateOutput === undefined) {
|
|
299
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'validate' stage output, but it was not found. " +
|
|
300
|
+
'Ensure the validate stage runs before the review stage.');
|
|
301
|
+
}
|
|
302
|
+
const parsedSpec = validateOutput.data?.['parsedSpec'];
|
|
303
|
+
if (parsedSpec === undefined) {
|
|
304
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Validate stage did not produce a parsed specification in its output data.');
|
|
305
|
+
}
|
|
306
|
+
// Architect stage
|
|
307
|
+
const architectOutput = input['architect'];
|
|
308
|
+
if (architectOutput === undefined) {
|
|
309
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'architect' stage output, but it was not found.");
|
|
310
|
+
}
|
|
311
|
+
const architecture = architectOutput.data?.['architecture'];
|
|
312
|
+
if (architecture === undefined) {
|
|
313
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Architect stage did not produce an architecture in its output data.');
|
|
314
|
+
}
|
|
315
|
+
// API generate stage
|
|
316
|
+
const apiOutput = input['services-generate'];
|
|
317
|
+
if (apiOutput === undefined) {
|
|
318
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'services-generate' stage output, but it was not found.");
|
|
319
|
+
}
|
|
320
|
+
const api = apiOutput.data?.['api'];
|
|
321
|
+
if (api === undefined) {
|
|
322
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'API generate stage did not produce an API artifact in its output data.');
|
|
323
|
+
}
|
|
324
|
+
// UI generate stage
|
|
325
|
+
const uiOutput = input['apps-generate'];
|
|
326
|
+
if (uiOutput === undefined) {
|
|
327
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'apps-generate' stage output, but it was not found.");
|
|
328
|
+
}
|
|
329
|
+
const ui = uiOutput.data?.['ui'];
|
|
330
|
+
if (ui === undefined) {
|
|
331
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'UI generate stage did not produce a UI artifact in its output data.');
|
|
332
|
+
}
|
|
333
|
+
// Security generate stage
|
|
334
|
+
const securityOutput = input['security-generate'];
|
|
335
|
+
if (securityOutput === undefined) {
|
|
336
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'security-generate' stage output, but it was not found.");
|
|
337
|
+
}
|
|
338
|
+
const security = securityOutput.data?.['security'];
|
|
339
|
+
if (security === undefined) {
|
|
340
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Security generate stage did not produce a security artifact in its output data.');
|
|
341
|
+
}
|
|
342
|
+
// Infra generate stage
|
|
343
|
+
const infraOutput = input['infra-generate'];
|
|
344
|
+
if (infraOutput === undefined) {
|
|
345
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Review stage requires 'infra-generate' stage output, but it was not found.");
|
|
346
|
+
}
|
|
347
|
+
const infra = infraOutput.data?.['infra'];
|
|
348
|
+
if (infra === undefined) {
|
|
349
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Infra generate stage did not produce an infra artifact in its output data.');
|
|
350
|
+
}
|
|
351
|
+
return { parsedSpec, architecture, api, ui, security, infra };
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Parse findings array from LLM output JSON.
|
|
355
|
+
*
|
|
356
|
+
* @param content - Raw JSON string with `findings` key
|
|
357
|
+
* @returns Array of code review findings
|
|
358
|
+
*/
|
|
359
|
+
parseFindings(content) {
|
|
360
|
+
const parsed = JSON.parse(content);
|
|
361
|
+
return parsed.findings;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=review-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-stage.js","sourceRoot":"","sources":["../src/review-stage.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code review artifact type definitions for the Forge review stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the multi-pass review output schema including individual
|
|
5
|
+
* findings, the aggregated report, and per-pass result structure.
|
|
6
|
+
*/
|
|
7
|
+
/** Review dimensions checked by the AI code review stage. */
|
|
8
|
+
export type ReviewDimension = 'code-quality' | 'security' | 'performance' | 'best-practices' | 'consistency' | 'architecture' | 'spec-adherence';
|
|
9
|
+
/** A single code review finding. */
|
|
10
|
+
export interface CodeReviewFinding {
|
|
11
|
+
/** Finding ID: REVIEW-{NNN}. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Severity level. */
|
|
14
|
+
severity: 'critical' | 'warning' | 'suggestion';
|
|
15
|
+
/** Review dimension. */
|
|
16
|
+
dimension: ReviewDimension;
|
|
17
|
+
/** Relative path to reviewed file. */
|
|
18
|
+
file: string;
|
|
19
|
+
/** Line number if applicable. */
|
|
20
|
+
line?: number | undefined;
|
|
21
|
+
/** One-line summary. */
|
|
22
|
+
title: string;
|
|
23
|
+
/** Detailed explanation. */
|
|
24
|
+
description: string;
|
|
25
|
+
/** Code snippet with suggested correction. */
|
|
26
|
+
suggestedFix?: string | undefined;
|
|
27
|
+
/** Requirement ID if spec-related. */
|
|
28
|
+
specReference?: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
/** Complete code review report produced by the review stage. */
|
|
31
|
+
export interface CodeReviewReport {
|
|
32
|
+
/** Run ID. */
|
|
33
|
+
runId: string;
|
|
34
|
+
/** ISO timestamp of review. */
|
|
35
|
+
reviewedAt: string;
|
|
36
|
+
/** Total files reviewed. */
|
|
37
|
+
totalFiles: number;
|
|
38
|
+
/** Total findings. */
|
|
39
|
+
totalFindings: number;
|
|
40
|
+
/** Findings broken down by severity. */
|
|
41
|
+
findingsBySeverity: {
|
|
42
|
+
critical: number;
|
|
43
|
+
warning: number;
|
|
44
|
+
suggestion: number;
|
|
45
|
+
};
|
|
46
|
+
/** All findings. */
|
|
47
|
+
findings: CodeReviewFinding[];
|
|
48
|
+
/** LLM-generated natural language summary. */
|
|
49
|
+
summary: string;
|
|
50
|
+
/** Pass if no critical findings; fail if any critical findings. */
|
|
51
|
+
passOrFail: 'pass' | 'fail';
|
|
52
|
+
}
|
|
53
|
+
/** Raw findings from a single review pass (what the LLM returns). */
|
|
54
|
+
export interface ReviewPassResult {
|
|
55
|
+
/** Pass name. */
|
|
56
|
+
pass: 'file-level' | 'cross-file' | 'spec-alignment' | 'synthesis';
|
|
57
|
+
/** Findings from this pass. */
|
|
58
|
+
findings: CodeReviewFinding[];
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,UAAU,GACV,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,gBAAgB,CAAC;AAMrB,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,YAAY,CAAC;IAChD,wBAAwB;IACxB,SAAS,EAAE,eAAe,CAAC;IAC3B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAMD,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,cAAc;IACd,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,kBAAkB,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,oBAAoB;IACpB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAMD,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB;IACjB,IAAI,EAAE,YAAY,GAAG,YAAY,GAAG,gBAAgB,GAAG,WAAW,CAAC;IACnE,+BAA+B;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code review artifact type definitions for the Forge review stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the multi-pass review output schema including individual
|
|
5
|
+
* findings, the aggregated report, and per-pass result structure.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code review output validators for the Forge review stage.
|
|
3
|
+
*
|
|
4
|
+
* Two validators are provided:
|
|
5
|
+
* - {@link ReviewPassValidator} — validates individual pass output (file-level, cross-file, spec-alignment)
|
|
6
|
+
* - {@link ReviewSynthesisValidator} — validates the synthesis pass output (aggregated report)
|
|
7
|
+
*/
|
|
8
|
+
import type { OutputValidator, OutputSchema, ValidationResult } from '@hstm-labs/forge-core';
|
|
9
|
+
/**
|
|
10
|
+
* Validates individual review pass output from the LLM.
|
|
11
|
+
*
|
|
12
|
+
* Checks:
|
|
13
|
+
* 1. JSON parse validation
|
|
14
|
+
* 2. `findings` key is an array
|
|
15
|
+
* 3. Each finding has required fields: id, severity, dimension, file, title, description
|
|
16
|
+
* 4. `severity` is one of: critical, warning, suggestion
|
|
17
|
+
* 5. `dimension` is a valid ReviewDimension
|
|
18
|
+
* 6. `id` matches REVIEW-{NNN} pattern
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const validator = new ReviewPassValidator();
|
|
23
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class ReviewPassValidator implements OutputValidator {
|
|
27
|
+
/**
|
|
28
|
+
* Validate a single review pass output.
|
|
29
|
+
*
|
|
30
|
+
* @param output - Raw LLM output text
|
|
31
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
32
|
+
* @returns Validation result with descriptive errors
|
|
33
|
+
*/
|
|
34
|
+
validate(output: string, _schema: OutputSchema): ValidationResult;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validates the synthesis pass output from the LLM.
|
|
38
|
+
*
|
|
39
|
+
* Checks:
|
|
40
|
+
* 1. JSON parse validation
|
|
41
|
+
* 2. `findings` array, `summary` string, `passOrFail` string
|
|
42
|
+
* 3. `passOrFail` is 'pass' or 'fail'
|
|
43
|
+
* 4. Consistency: if any finding has severity 'critical', passOrFail must be 'fail'
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const validator = new ReviewSynthesisValidator();
|
|
48
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare class ReviewSynthesisValidator implements OutputValidator {
|
|
52
|
+
/**
|
|
53
|
+
* Validate the synthesis pass output.
|
|
54
|
+
*
|
|
55
|
+
* @param output - Raw LLM output text
|
|
56
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
57
|
+
* @returns Validation result with descriptive errors
|
|
58
|
+
*/
|
|
59
|
+
validate(output: string, _schema: OutputSchema): ValidationResult;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEjB,MAAM,uBAAuB,CAAC;AA0B/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD;;;;;;OAMG;IAEH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,gBAAgB;CA0FlE;AAMD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAC9D;;;;;;OAMG;IAEH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,gBAAgB;CAuElE"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code review output validators for the Forge review stage.
|
|
3
|
+
*
|
|
4
|
+
* Two validators are provided:
|
|
5
|
+
* - {@link ReviewPassValidator} — validates individual pass output (file-level, cross-file, spec-alignment)
|
|
6
|
+
* - {@link ReviewSynthesisValidator} — validates the synthesis pass output (aggregated report)
|
|
7
|
+
*/
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Constants
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const VALID_SEVERITIES = new Set(['critical', 'warning', 'suggestion']);
|
|
12
|
+
const VALID_DIMENSIONS = new Set([
|
|
13
|
+
'code-quality',
|
|
14
|
+
'security',
|
|
15
|
+
'performance',
|
|
16
|
+
'best-practices',
|
|
17
|
+
'consistency',
|
|
18
|
+
'architecture',
|
|
19
|
+
'spec-adherence',
|
|
20
|
+
]);
|
|
21
|
+
const FINDING_ID_PATTERN = /^REVIEW-\d{3,}$/;
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// ReviewPassValidator
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/**
|
|
26
|
+
* Validates individual review pass output from the LLM.
|
|
27
|
+
*
|
|
28
|
+
* Checks:
|
|
29
|
+
* 1. JSON parse validation
|
|
30
|
+
* 2. `findings` key is an array
|
|
31
|
+
* 3. Each finding has required fields: id, severity, dimension, file, title, description
|
|
32
|
+
* 4. `severity` is one of: critical, warning, suggestion
|
|
33
|
+
* 5. `dimension` is a valid ReviewDimension
|
|
34
|
+
* 6. `id` matches REVIEW-{NNN} pattern
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const validator = new ReviewPassValidator();
|
|
39
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export class ReviewPassValidator {
|
|
43
|
+
/**
|
|
44
|
+
* Validate a single review pass output.
|
|
45
|
+
*
|
|
46
|
+
* @param output - Raw LLM output text
|
|
47
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
48
|
+
* @returns Validation result with descriptive errors
|
|
49
|
+
*/
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
51
|
+
validate(output, _schema) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
// 1. Parse as JSON
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(output);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
errors.push({
|
|
60
|
+
message: 'Review pass output is not valid JSON. Ensure the LLM produces only raw JSON without markdown fences or explanatory text.',
|
|
61
|
+
severity: 'error',
|
|
62
|
+
});
|
|
63
|
+
return { valid: false, errors };
|
|
64
|
+
}
|
|
65
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
66
|
+
errors.push({
|
|
67
|
+
message: 'Review pass output must be a JSON object, not an array or primitive.',
|
|
68
|
+
severity: 'error',
|
|
69
|
+
});
|
|
70
|
+
return { valid: false, errors };
|
|
71
|
+
}
|
|
72
|
+
const obj = parsed;
|
|
73
|
+
// 2. Verify findings key is an array
|
|
74
|
+
const findings = obj['findings'];
|
|
75
|
+
if (!Array.isArray(findings)) {
|
|
76
|
+
errors.push({
|
|
77
|
+
message: "Key 'findings' must be an array of finding objects.",
|
|
78
|
+
path: 'findings',
|
|
79
|
+
severity: 'error',
|
|
80
|
+
});
|
|
81
|
+
return { valid: false, errors };
|
|
82
|
+
}
|
|
83
|
+
// 3-6. Validate each finding
|
|
84
|
+
for (let i = 0; i < findings.length; i++) {
|
|
85
|
+
const f = findings[i];
|
|
86
|
+
if (f === undefined || typeof f !== 'object' || f === null) {
|
|
87
|
+
errors.push({
|
|
88
|
+
message: `findings[${String(i)}] is not a valid object.`,
|
|
89
|
+
path: `findings[${String(i)}]`,
|
|
90
|
+
severity: 'error',
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// 3. Required fields
|
|
95
|
+
for (const field of ['id', 'severity', 'dimension', 'file', 'title', 'description']) {
|
|
96
|
+
if (typeof f[field] !== 'string' || f[field].length === 0) {
|
|
97
|
+
errors.push({
|
|
98
|
+
message: `findings[${String(i)}] is missing required field '${field}'.`,
|
|
99
|
+
path: `findings[${String(i)}].${field}`,
|
|
100
|
+
severity: 'error',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 4. Valid severity
|
|
105
|
+
if (typeof f['severity'] === 'string' && !VALID_SEVERITIES.has(f['severity'])) {
|
|
106
|
+
errors.push({
|
|
107
|
+
message: `findings[${String(i)}].severity '${f['severity']}' is invalid. Must be one of: critical, warning, suggestion.`,
|
|
108
|
+
path: `findings[${String(i)}].severity`,
|
|
109
|
+
severity: 'error',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// 5. Valid dimension
|
|
113
|
+
if (typeof f['dimension'] === 'string' && !VALID_DIMENSIONS.has(f['dimension'])) {
|
|
114
|
+
errors.push({
|
|
115
|
+
message: `findings[${String(i)}].dimension '${f['dimension']}' is invalid. Must be a valid ReviewDimension.`,
|
|
116
|
+
path: `findings[${String(i)}].dimension`,
|
|
117
|
+
severity: 'error',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// 6. ID pattern
|
|
121
|
+
if (typeof f['id'] === 'string' && !FINDING_ID_PATTERN.test(f['id'])) {
|
|
122
|
+
errors.push({
|
|
123
|
+
message: `findings[${String(i)}].id '${f['id']}' does not match REVIEW-{NNN} pattern.`,
|
|
124
|
+
path: `findings[${String(i)}].id`,
|
|
125
|
+
severity: 'error',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { valid: errors.length === 0, errors };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// ReviewSynthesisValidator
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
/**
|
|
136
|
+
* Validates the synthesis pass output from the LLM.
|
|
137
|
+
*
|
|
138
|
+
* Checks:
|
|
139
|
+
* 1. JSON parse validation
|
|
140
|
+
* 2. `findings` array, `summary` string, `passOrFail` string
|
|
141
|
+
* 3. `passOrFail` is 'pass' or 'fail'
|
|
142
|
+
* 4. Consistency: if any finding has severity 'critical', passOrFail must be 'fail'
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const validator = new ReviewSynthesisValidator();
|
|
147
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export class ReviewSynthesisValidator {
|
|
151
|
+
/**
|
|
152
|
+
* Validate the synthesis pass output.
|
|
153
|
+
*
|
|
154
|
+
* @param output - Raw LLM output text
|
|
155
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
156
|
+
* @returns Validation result with descriptive errors
|
|
157
|
+
*/
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
159
|
+
validate(output, _schema) {
|
|
160
|
+
const errors = [];
|
|
161
|
+
// 1. Parse as JSON
|
|
162
|
+
let parsed;
|
|
163
|
+
try {
|
|
164
|
+
parsed = JSON.parse(output);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
errors.push({
|
|
168
|
+
message: 'Synthesis pass output is not valid JSON. Ensure the LLM produces only raw JSON without markdown fences or explanatory text.',
|
|
169
|
+
severity: 'error',
|
|
170
|
+
});
|
|
171
|
+
return { valid: false, errors };
|
|
172
|
+
}
|
|
173
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
174
|
+
errors.push({
|
|
175
|
+
message: 'Synthesis pass output must be a JSON object, not an array or primitive.',
|
|
176
|
+
severity: 'error',
|
|
177
|
+
});
|
|
178
|
+
return { valid: false, errors };
|
|
179
|
+
}
|
|
180
|
+
const obj = parsed;
|
|
181
|
+
// 2. Verify findings, summary, passOrFail
|
|
182
|
+
if (!Array.isArray(obj['findings'])) {
|
|
183
|
+
errors.push({
|
|
184
|
+
message: "Key 'findings' must be an array of finding objects.",
|
|
185
|
+
path: 'findings',
|
|
186
|
+
severity: 'error',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (typeof obj['summary'] !== 'string' || obj['summary'].length === 0) {
|
|
190
|
+
errors.push({
|
|
191
|
+
message: "Key 'summary' must be a non-empty string.",
|
|
192
|
+
path: 'summary',
|
|
193
|
+
severity: 'error',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// 3. passOrFail is 'pass' or 'fail'
|
|
197
|
+
if (obj['passOrFail'] !== 'pass' && obj['passOrFail'] !== 'fail') {
|
|
198
|
+
errors.push({
|
|
199
|
+
message: "Key 'passOrFail' must be 'pass' or 'fail'.",
|
|
200
|
+
path: 'passOrFail',
|
|
201
|
+
severity: 'error',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (errors.length > 0) {
|
|
205
|
+
return { valid: false, errors };
|
|
206
|
+
}
|
|
207
|
+
// 4. Consistency check: critical findings → passOrFail must be 'fail'
|
|
208
|
+
const findings = obj['findings'];
|
|
209
|
+
const hasCritical = findings.some((f) => f['severity'] === 'critical');
|
|
210
|
+
if (hasCritical && obj['passOrFail'] === 'pass') {
|
|
211
|
+
errors.push({
|
|
212
|
+
message: "Consistency error: findings contain critical severity issues but passOrFail is 'pass'. " +
|
|
213
|
+
"Any critical finding must result in passOrFail='fail'.",
|
|
214
|
+
path: 'passOrFail',
|
|
215
|
+
severity: 'error',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return { valid: errors.length === 0, errors };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;AAExE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAkB;IAChD,cAAc;IACd,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,gBAAgB;CACjB,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAE7C,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,mBAAmB;IAC9B;;;;;;OAMG;IACH,6DAA6D;IAC7D,QAAQ,CAAC,MAAc,EAAE,OAAqB;QAC5C,MAAM,MAAM,GAAsB,EAAE,CAAC;QAErC,mBAAmB;QACnB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EACL,0HAA0H;gBAC5H,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3E,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,sEAAsE;gBAC/E,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,GAAG,GAAG,MAAiC,CAAC;QAE9C,qCAAqC;QACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,qDAAqD;gBAC9D,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,6BAA6B;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAwC,CAAC;YAC7D,IAAI,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC3D,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,0BAA0B;oBACxD,IAAI,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,GAAG;oBAC9B,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,qBAAqB;YACrB,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAU,EAAE,CAAC;gBAC7F,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAK,CAAC,CAAC,KAAK,CAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACtE,MAAM,CAAC,IAAI,CAAC;wBACV,OAAO,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,gCAAgC,KAAK,IAAI;wBACvE,IAAI,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE;wBACvC,QAAQ,EAAE,OAAO;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,oBAAoB;YACpB,IAAI,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBAC9E,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,UAAU,CAAW,8DAA8D;oBAClI,IAAI,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,YAAY;oBACvC,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;YAED,qBAAqB;YACrB,IAAI,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAoB,CAAC,EAAE,CAAC;gBACnG,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,WAAW,CAAW,gDAAgD;oBACtH,IAAI,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,aAAa;oBACxC,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;YAED,gBAAgB;YAChB,IAAI,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACrE,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAW,wCAAwC;oBAChG,IAAI,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,MAAM;oBACjC,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC;CACF;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,wBAAwB;IACnC;;;;;;OAMG;IACH,6DAA6D;IAC7D,QAAQ,CAAC,MAAc,EAAE,OAAqB;QAC5C,MAAM,MAAM,GAAsB,EAAE,CAAC;QAErC,mBAAmB;QACnB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EACL,6HAA6H;gBAC/H,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3E,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,yEAAyE;gBAClF,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,GAAG,GAAG,MAAiC,CAAC;QAE9C,0CAA0C;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,qDAAqD;gBAC9D,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAK,GAAG,CAAC,SAAS,CAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClF,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,2CAA2C;gBACpD,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;QAED,oCAAoC;QACpC,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,MAAM,EAAE,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,4CAA4C;gBACrD,IAAI,EAAE,YAAY;gBAClB,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAClC,CAAC;QAED,sEAAsE;QACtE,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAmC,CAAC;QACnE,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,UAAU,CAAC,CAAC;QACvE,IAAI,WAAW,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,MAAM,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EACL,yFAAyF;oBACzF,wDAAwD;gBAC1D,IAAI,EAAE,YAAY;gBAClB,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hstm-labs/forge-reviewer",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@hstm-labs/forge-common": "0.1.11",
|
|
20
|
+
"@hstm-labs/forge-core": "0.1.11",
|
|
21
|
+
"@hstm-labs/forge-architect": "0.1.11",
|
|
22
|
+
"@hstm-labs/forge-services-generator": "0.1.11",
|
|
23
|
+
"@hstm-labs/forge-apps-generator": "0.1.11",
|
|
24
|
+
"@hstm-labs/forge-infra-generator": "0.1.11",
|
|
25
|
+
"@hstm-labs/forge-security-generator": "0.1.11",
|
|
26
|
+
"@hstm-labs/forge-seed-data": "0.1.11",
|
|
27
|
+
"@hstm-labs/forge-spec-parser": "0.1.11",
|
|
28
|
+
"@hstm-labs/forge-profiles": "0.1.11",
|
|
29
|
+
"@hstm-labs/forge-templates": "0.1.11"
|
|
30
|
+
}
|
|
31
|
+
}
|