@mechanai/deepreview 2.4.2 → 2.6.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/.opencode/agents/deepreview-applier.md +24 -5
- package/.opencode/agents/deepreview-compatibility.md +15 -4
- package/.opencode/agents/deepreview-docs.md +9 -1
- package/.opencode/agents/deepreview-security.md +10 -1
- package/.opencode/commands/deepreview-loop.md +14 -2
- package/.opencode/commands/deepreview-spec-loop.md +15 -1
- package/.opencode/commands/deepreview-spec.md +13 -2
- package/.opencode/commands/deepreview.md +29 -2
- package/README.md +29 -0
- package/package.json +1 -1
- package/src/project-context.test.ts +80 -0
- package/src/project-context.ts +215 -0
|
@@ -6,10 +6,17 @@ permission:
|
|
|
6
6
|
edit: allow
|
|
7
7
|
bash:
|
|
8
8
|
"git diff*": allow
|
|
9
|
+
"mise tasks ls*": allow
|
|
9
10
|
"mise run fmt*": allow
|
|
10
11
|
"mise run lint*": allow
|
|
11
12
|
"mise run check*": allow
|
|
12
13
|
"mise run test*": allow
|
|
14
|
+
"npm run format*": allow
|
|
15
|
+
"npm run lint*": allow
|
|
16
|
+
"npm run test*": allow
|
|
17
|
+
"make fmt*": allow
|
|
18
|
+
"make lint*": allow
|
|
19
|
+
"make test*": allow
|
|
13
20
|
"*": deny
|
|
14
21
|
---
|
|
15
22
|
|
|
@@ -44,13 +51,25 @@ If a fix cannot be applied (file doesn't exist, code doesn't match what was expe
|
|
|
44
51
|
|
|
45
52
|
## Verification (after all fixes are applied)
|
|
46
53
|
|
|
47
|
-
After applying all fixes, run verification
|
|
54
|
+
After applying all fixes, run verification using the project's configured tooling.
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
2. Run `mise run lint` or `mise run check` (whichever exists)
|
|
51
|
-
3. Run `mise run test`
|
|
56
|
+
**Determining commands:** Check `AGENTS.md` (or `CLAUDE.md`) in the project root for explicit format/lint/test commands. If found, use those. Otherwise, auto-detect:
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
- `mise.toml` exists: run `mise tasks ls`, then `mise run <task>` for tasks matching `fmt`/`format`, `lint`/`check`, `test`
|
|
59
|
+
- `package.json` exists with matching scripts: `npm run format`, `npm run lint`, `npm run test`
|
|
60
|
+
- `Makefile` exists with matching targets: `make fmt`, `make lint`, `make test`
|
|
61
|
+
|
|
62
|
+
If no commands are found and no config files exist, skip verification and report `VERIFICATION: SKIPPED — no fmt/lint/test commands found`.
|
|
63
|
+
|
|
64
|
+
**Steps:**
|
|
65
|
+
|
|
66
|
+
1. **Format:** Run the format command (expected to modify files)
|
|
67
|
+
2. **Lint:** Run the lint command
|
|
68
|
+
- If lint fails, fix errors **only in files you modified** this session. Do not fix pre-existing lint issues.
|
|
69
|
+
- Re-run format then lint. Attempt up to 2 fix-and-recheck cycles; if lint still fails, stop and report remaining errors.
|
|
70
|
+
3. **Test:** Run the test command
|
|
71
|
+
|
|
72
|
+
If lint/check/test still fails:
|
|
54
73
|
|
|
55
74
|
- Include the error output in your response
|
|
56
75
|
- Mark the relevant fix as FAILED with the error
|
|
@@ -19,7 +19,16 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
|
|
|
19
19
|
|
|
20
20
|
## Prior Context (if provided)
|
|
21
21
|
|
|
22
|
-
Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
22
|
+
Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
23
|
+
|
|
24
|
+
- **Project Context:** If your prompt includes a "Project Context" section (version, deployment model, status), use it to calibrate severity:
|
|
25
|
+
- v0.x.0 projects: breaking changes are expected and acceptable per semver — flag them as **suggestion**, not **critical** or **warning**. The change "API breaking changes are expected" still applies at v0.x.0.
|
|
26
|
+
- v1+ public libraries: breaking changes require major version bump — flag as **critical** or **warning**.
|
|
27
|
+
- Internal tools with no external consumers: breaking changes are **suggestion**-level (internal reorganization risk only).
|
|
28
|
+
- Private/unpublished packages: breaking changes are **suggestion**, unless the project explicitly targets v1+ stability.
|
|
29
|
+
- **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
|
|
30
|
+
- **Prior Findings:** Do NOT re-report prior findings.
|
|
31
|
+
- **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
|
|
23
32
|
|
|
24
33
|
Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
|
|
25
34
|
|
|
@@ -57,9 +66,11 @@ Write your review to the output path provided. Use this format for each finding:
|
|
|
57
66
|
|
|
58
67
|
Severity guide:
|
|
59
68
|
|
|
60
|
-
- **critical:** Public API or data contract broken with no migration path
|
|
61
|
-
- **warning:** Behavior change that may break some consumers silently
|
|
62
|
-
- **suggestion:** Internal change that could become breaking if exposed later
|
|
69
|
+
- **critical:** Public API or data contract broken with no migration path (or v1+ library with breaking change and no major version bump)
|
|
70
|
+
- **warning:** Behavior change that may break some consumers silently (or v1+ breaking change that should have bumped major version)
|
|
71
|
+
- **suggestion:** Internal change that could become breaking if exposed later (or v0.x.0 breaking API change — these are expected per semver)
|
|
72
|
+
|
|
73
|
+
Note: For v0.x.0 projects, apply the semver exemption — breaking changes are expected during pre-1.0 development. Downgrade breaking API changes to **suggestion** unless they create data loss or irreversible corruption.
|
|
63
74
|
|
|
64
75
|
If you find no issues, write: "No compatibility issues found."
|
|
65
76
|
|
|
@@ -19,7 +19,15 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
|
|
|
19
19
|
|
|
20
20
|
## Prior Context (if provided)
|
|
21
21
|
|
|
22
|
-
Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
22
|
+
Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
23
|
+
|
|
24
|
+
- **Project Context:** If your prompt includes a "Project Context" section (version, deployment model), use it to calibrate severity:
|
|
25
|
+
- v0.x.0 projects are in active development: stale comments and docs are **suggestion**-level, not **critical** or **warning**.
|
|
26
|
+
- Published libraries (v1+) must maintain accurate docs: flag stale docs that contradict code as **critical** or **warning**.
|
|
27
|
+
- Internal tools have lower documentation burden than public APIs: cosmetic doc issues are **suggestion**, not **warning**.
|
|
28
|
+
- **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
|
|
29
|
+
- **Prior Findings:** Do NOT re-report prior findings.
|
|
30
|
+
- **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
|
|
23
31
|
|
|
24
32
|
Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
|
|
25
33
|
|
|
@@ -19,7 +19,16 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
|
|
|
19
19
|
|
|
20
20
|
## Prior Context (if provided)
|
|
21
21
|
|
|
22
|
-
Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
22
|
+
Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
|
|
23
|
+
|
|
24
|
+
- **Project Context:** If your prompt includes a "Project Context" section (version, deployment model, threat model), use it to calibrate severity:
|
|
25
|
+
- Localhost-only tools have no network threat model: downgrade auth/network findings and missing security headers to **suggestion**-level, not **warning**.
|
|
26
|
+
- v0.x.0 projects may skip some production security checks: downgrade findings about production hardening to **suggestion**, not **critical** or **warning**.
|
|
27
|
+
- Internal-network tools are lower-threat than public-facing services: downgrade findings about external attack vectors to **suggestion**.
|
|
28
|
+
- Published libraries (v1+) must follow production security standards: flag unvalidated input and auth gaps as **critical** or **warning**.
|
|
29
|
+
- **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
|
|
30
|
+
- **Prior Findings:** Do NOT re-report prior findings.
|
|
31
|
+
- **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
|
|
23
32
|
|
|
24
33
|
Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
|
|
25
34
|
|
|
@@ -21,8 +21,20 @@ Set ALL_SESSION_DIRS=[] (list of all session directories used, in order)
|
|
|
21
21
|
Determine REPO_ROOT — the main repository root (not a worktree root). Run:
|
|
22
22
|
`REPO_ROOT=$(realpath "$(git rev-parse --git-common-dir)" | sed 's|/\.git$||')`
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
|
|
25
|
+
|
|
26
|
+
- Check for package.json or Cargo.toml to detect version and publish status
|
|
27
|
+
- Check for .deepreview.yml to detect explicit deployment model (threatModel field)
|
|
28
|
+
- If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
|
|
29
|
+
- Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
|
|
30
|
+
- If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
|
|
31
|
+
|
|
32
|
+
Build PRIOR_CONTEXT:
|
|
33
|
+
|
|
34
|
+
- Start with PROJECT_CONTEXT (if non-empty)
|
|
35
|
+
- If CONTEXT_FILE exists, append:
|
|
36
|
+
"## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n"
|
|
37
|
+
- If neither PROJECT_CONTEXT nor CONTEXT_FILE exist, set PRIOR_CONTEXT="" (empty string)
|
|
26
38
|
|
|
27
39
|
STEP 2: RUN INITIAL DEEPREVIEW (full pipeline with cross-validation)
|
|
28
40
|
Run the full deepreview pipeline (Stages 1-5 from the deepreview command):
|
|
@@ -15,7 +15,21 @@ STEP 1: DETERMINE INPUT
|
|
|
15
15
|
- Set ALL_SESSION_DIRS=[] (list of all session directories used, in order)
|
|
16
16
|
- Determine REPO_ROOT — the main repository root (not a worktree root). Run:
|
|
17
17
|
`REPO_ROOT=$(realpath "$(git rev-parse --git-common-dir)" | sed 's|/\.git$||')`
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
|
|
20
|
+
|
|
21
|
+
- Check for package.json or Cargo.toml to detect version and publish status
|
|
22
|
+
- Check for .deepreview.yml to detect explicit deployment model (threatModel field)
|
|
23
|
+
- If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
|
|
24
|
+
- Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
|
|
25
|
+
- If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
|
|
26
|
+
|
|
27
|
+
Build PRIOR_CONTEXT:
|
|
28
|
+
|
|
29
|
+
- Start with PROJECT_CONTEXT (if non-empty)
|
|
30
|
+
- If CONTEXT_FILE exists, append:
|
|
31
|
+
"## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n\n"
|
|
32
|
+
- If neither PROJECT_CONTEXT nor CONTEXT_FILE exist, set PRIOR_CONTEXT="" (empty string)
|
|
19
33
|
|
|
20
34
|
STEP 2: RUN INITIAL DEEPREVIEW-SPEC (full pipeline with cross-validation)
|
|
21
35
|
Run the full deepreview-spec pipeline (Stages 1-5 from the deepreview-spec command):
|
|
@@ -23,10 +23,21 @@ STEP 2: PREPARE INPUT
|
|
|
23
23
|
|
|
24
24
|
Set INPUT_DESCRIPTION="the following spec/plan files: $ARGUMENTS"
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
|
|
27
|
+
|
|
28
|
+
- Check for package.json or Cargo.toml to detect version and publish status
|
|
29
|
+
- Check for .deepreview.yml to detect explicit deployment model (threatModel field)
|
|
30
|
+
- If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
|
|
31
|
+
- Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
|
|
32
|
+
- If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
|
|
33
|
+
|
|
34
|
+
Build CONTEXT_PREAMBLE:
|
|
35
|
+
If PROJECT_CONTEXT is not empty, start with: "${PROJECT_CONTEXT}\n"
|
|
36
|
+
|
|
37
|
+
If CONTEXT_FILE exists, append:
|
|
27
38
|
"## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
|
|
28
39
|
|
|
29
|
-
If CONTEXT_FILE
|
|
40
|
+
If both PROJECT_CONTEXT and CONTEXT_FILE are empty, set CONTEXT_PREAMBLE="" (empty string).
|
|
30
41
|
|
|
31
42
|
STEP 3: DISPATCH STAGE 1 — INITIAL REVIEW (5 parallel tasks)
|
|
32
43
|
Dispatch ALL FIVE of these Task tool calls simultaneously in a single message:
|
|
@@ -41,10 +41,37 @@ Set INPUT_DESCRIPTION based on mode:
|
|
|
41
41
|
- MODE=branch: "a branch diff against main"
|
|
42
42
|
- MODE=files: "the following files: <list of filenames>"
|
|
43
43
|
|
|
44
|
+
STEP 2a: EXTRACT PROJECT CONTEXT
|
|
45
|
+
Build PROJECT_CONTEXT by extracting metadata (version, deployment model, publish status) from the repo:
|
|
46
|
+
|
|
47
|
+
- Check for package.json or Cargo.toml to detect version and publish status
|
|
48
|
+
- Check for .deepreview.yml to detect explicit deployment model (threat-model field)
|
|
49
|
+
- If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
|
|
50
|
+
- Format as:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
## Project Context (for severity calibration)
|
|
54
|
+
|
|
55
|
+
**Name:** [project name]
|
|
56
|
+
**Version:** [version] (if v0.x.0, include note: "pre-1.0 — relaxed API stability expectations")
|
|
57
|
+
**Deployment:** [localhost-only|internal-network|public-facing|library] [threat model note]
|
|
58
|
+
**Status:** [Private/internal|Published]
|
|
59
|
+
|
|
60
|
+
Use this context to calibrate finding severity. For example:
|
|
61
|
+
- v0.1.0 projects may have API instability — flag as **suggestion**, not **warning**.
|
|
62
|
+
- Localhost-only tools have no network threat model — downgrade auth/network findings to **suggestion**.
|
|
63
|
+
- Stale docs in pre-1.0 projects are **suggestion**-level, not critical.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string).
|
|
67
|
+
|
|
68
|
+
STEP 2b: BUILD CONTEXT PREAMBLE
|
|
44
69
|
If CONTEXT_FILE exists, set DESIGN_CONTEXT to the contents of that file. Build a CONTEXT_PREAMBLE:
|
|
45
|
-
"## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
|
|
70
|
+
"${PROJECT_CONTEXT}## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
|
|
71
|
+
|
|
72
|
+
If CONTEXT_FILE does not exist and PROJECT_CONTEXT is not empty, set CONTEXT_PREAMBLE to just "${PROJECT_CONTEXT}\n"
|
|
46
73
|
|
|
47
|
-
If
|
|
74
|
+
If both are empty, set CONTEXT_PREAMBLE="" (empty string).
|
|
48
75
|
|
|
49
76
|
STEP 3: DISPATCH STAGE 1 — INITIAL REVIEW (5 parallel tasks)
|
|
50
77
|
Dispatch ALL FIVE of these Task tool calls simultaneously in a single message:
|
package/README.md
CHANGED
|
@@ -83,6 +83,35 @@ its own context, keeping token usage minimal.
|
|
|
83
83
|
- `git`
|
|
84
84
|
- `gh` CLI (only for PR commands)
|
|
85
85
|
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
### Verification (formatting, linting, tests)
|
|
89
|
+
|
|
90
|
+
After applying fixes, the applier agent runs formatting, linting, and tests. It auto-detects
|
|
91
|
+
commands based on what exists in your project root:
|
|
92
|
+
|
|
93
|
+
| File detected | Format | Lint | Test |
|
|
94
|
+
| -------------- | ---------------- | ---------------------------------- | --------------- |
|
|
95
|
+
| `mise.toml` | `mise run fmt` | `mise run lint` / `mise run check` | `mise run test` |
|
|
96
|
+
| `package.json` | `npm run format` | `npm run lint` | `npm run test` |
|
|
97
|
+
| `Makefile` | `make fmt` | `make lint` | `make test` |
|
|
98
|
+
|
|
99
|
+
If your project uses different commands (e.g., `cargo fmt`, `ruff check --fix`),
|
|
100
|
+
specify them in `AGENTS.md`. The applier looks for commands labeled **Format**, **Lint**, and
|
|
101
|
+
**Test** (or similar). For example:
|
|
102
|
+
|
|
103
|
+
```markdown
|
|
104
|
+
- **Format:** `cargo fmt`
|
|
105
|
+
- **Lint:** `cargo clippy -- -D warnings`
|
|
106
|
+
- **Tests:** `cargo test`
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The applier checks `AGENTS.md` (or `CLAUDE.md`) first, falling back to auto-detection.
|
|
110
|
+
If no commands are found and no config files exist, verification is skipped.
|
|
111
|
+
|
|
112
|
+
When lint fails, the applier attempts to fix errors in the files it modified (up to 2 retry
|
|
113
|
+
cycles) before reporting the failure.
|
|
114
|
+
|
|
86
115
|
## Upgrade
|
|
87
116
|
|
|
88
117
|
OpenCode caches plugins on first install and does not automatically check for newer versions.
|
package/package.json
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { getProjectMetadata, formatProjectContextPreamble } from "./project-context";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
describe("project-context: metadata extraction", () => {
|
|
6
|
+
it("should extract metadata from package.json", () => {
|
|
7
|
+
const repoRoot = path.resolve(import.meta.dirname, "..");
|
|
8
|
+
const metadata = getProjectMetadata(repoRoot);
|
|
9
|
+
|
|
10
|
+
expect(metadata).toBeDefined();
|
|
11
|
+
expect(metadata.version).toBe("0.0.0-development");
|
|
12
|
+
expect(metadata.name).toBe("@mechanai/deepreview");
|
|
13
|
+
expect(metadata.deploymentModel).toBeDefined();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("project-context: preamble version handling", () => {
|
|
18
|
+
it("should format project context preamble for v0 projects", () => {
|
|
19
|
+
const metadata = {
|
|
20
|
+
version: "0.1.0",
|
|
21
|
+
name: "test-project",
|
|
22
|
+
isPrivate: true,
|
|
23
|
+
deploymentModel: "internal-network" as const,
|
|
24
|
+
description: "A test project",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const preamble = formatProjectContextPreamble(metadata);
|
|
28
|
+
|
|
29
|
+
expect(preamble).toContain("Project Context");
|
|
30
|
+
expect(preamble).toContain("0.1.0");
|
|
31
|
+
expect(preamble).toContain("pre-1.0");
|
|
32
|
+
expect(preamble).toContain("internal-network");
|
|
33
|
+
expect(preamble).toContain("severity calibration");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should handle v1+ versions without pre-1.0 note", () => {
|
|
37
|
+
const metadata = {
|
|
38
|
+
version: "1.2.3",
|
|
39
|
+
name: "stable-project",
|
|
40
|
+
isPrivate: false,
|
|
41
|
+
deploymentModel: "public-facing" as const,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const preamble = formatProjectContextPreamble(metadata);
|
|
45
|
+
|
|
46
|
+
expect(preamble).toContain("1.2.3");
|
|
47
|
+
expect(preamble).not.toMatch(/1\.2\.3.*pre-1\.0/u);
|
|
48
|
+
expect(preamble).toContain("public-facing");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("project-context: preamble deployment guidance", () => {
|
|
53
|
+
it("should include deployment guidance for localhost tools", () => {
|
|
54
|
+
const metadata = {
|
|
55
|
+
version: "0.1.0",
|
|
56
|
+
name: "dev-tool",
|
|
57
|
+
isPrivate: true,
|
|
58
|
+
deploymentModel: "localhost-only" as const,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const preamble = formatProjectContextPreamble(metadata);
|
|
62
|
+
|
|
63
|
+
expect(preamble).toContain("localhost-only");
|
|
64
|
+
expect(preamble).toContain("network/auth concerns");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should include guidance for published libraries", () => {
|
|
68
|
+
const metadata = {
|
|
69
|
+
version: "2.0.0",
|
|
70
|
+
name: "published-lib",
|
|
71
|
+
isPrivate: false,
|
|
72
|
+
deploymentModel: "library" as const,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const preamble = formatProjectContextPreamble(metadata);
|
|
76
|
+
|
|
77
|
+
expect(preamble).toContain("library");
|
|
78
|
+
expect(preamble).toContain("API stability");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project context detection and formatting for severity calibration in reviewers.
|
|
3
|
+
*
|
|
4
|
+
* Extracts metadata from package.json, Cargo.toml, and .deepreview.yml to build
|
|
5
|
+
* a preamble that helps reviewers calibrate finding severity appropriately.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { load as loadYaml } from "js-yaml";
|
|
11
|
+
|
|
12
|
+
export interface ProjectMetadata {
|
|
13
|
+
/** Semantic version (e.g., "0.1.0", "3.2.1") */
|
|
14
|
+
version?: string;
|
|
15
|
+
/** Whether the project is marked as private/internal */
|
|
16
|
+
isPrivate?: boolean;
|
|
17
|
+
/** Deployment model: "localhost-only" | "internal-network" | "public-facing" | "library" | "unknown" */
|
|
18
|
+
deploymentModel?: string;
|
|
19
|
+
/** True if project has publish = false in Cargo.toml */
|
|
20
|
+
isUnpublished?: boolean;
|
|
21
|
+
/** Description of the project */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Name of the project */
|
|
24
|
+
name?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DeepReviewConfig {
|
|
28
|
+
threatModel?: "localhost-only" | "internal-network" | "public-facing" | "library";
|
|
29
|
+
/** Additional context hints for reviewers */
|
|
30
|
+
context?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PackageJson {
|
|
34
|
+
name?: string;
|
|
35
|
+
version?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
private?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parsePackageJson(filePath: string): Partial<ProjectMetadata> {
|
|
41
|
+
try {
|
|
42
|
+
const content = readFileSync(filePath, "utf-8");
|
|
43
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Why: JSON.parse returns any; fields are all optional strings/booleans
|
|
44
|
+
const pkg = JSON.parse(content) as PackageJson;
|
|
45
|
+
return {
|
|
46
|
+
version: pkg.version,
|
|
47
|
+
isPrivate: pkg.private === true,
|
|
48
|
+
name: pkg.name,
|
|
49
|
+
description: pkg.description,
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseCargoToml(filePath: string): Partial<ProjectMetadata> {
|
|
57
|
+
try {
|
|
58
|
+
const content = readFileSync(filePath, "utf-8");
|
|
59
|
+
const versionMatch = /^version\s*=\s*["']([^"']+)["']/u.exec(content);
|
|
60
|
+
const nameMatch = /^name\s*=\s*["']([^"']+)["']/u.exec(content);
|
|
61
|
+
const publishMatch = /^\[publish\]/u.exec(content);
|
|
62
|
+
const descMatch = /^description\s*=\s*["']([^"']+)["']/u.exec(content);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
version: versionMatch?.[1],
|
|
66
|
+
name: nameMatch?.[1],
|
|
67
|
+
description: descMatch?.[1],
|
|
68
|
+
isUnpublished: publishMatch !== null,
|
|
69
|
+
};
|
|
70
|
+
} catch {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse .deepreview.yml and extract deployment model.
|
|
77
|
+
*/
|
|
78
|
+
function parseDeepReviewConfig(filePath: string): DeepReviewConfig {
|
|
79
|
+
try {
|
|
80
|
+
const content = readFileSync(filePath, "utf-8");
|
|
81
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Why: loadYaml returns unknown; fields are all optional with safe defaults
|
|
82
|
+
const result = loadYaml(content) as DeepReviewConfig | null;
|
|
83
|
+
return result ?? {};
|
|
84
|
+
} catch {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detect deployment model from project structure and metadata.
|
|
91
|
+
*/
|
|
92
|
+
function detectDeploymentModel(metadata: ProjectMetadata, config: DeepReviewConfig): string {
|
|
93
|
+
// Explicit config wins
|
|
94
|
+
if (config.threatModel) {
|
|
95
|
+
return config.threatModel;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Infer from metadata
|
|
99
|
+
if (metadata.isPrivate === false || metadata.isUnpublished === false) {
|
|
100
|
+
return "public-facing";
|
|
101
|
+
}
|
|
102
|
+
if (metadata.isPrivate === true) {
|
|
103
|
+
return "internal-network";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return "unknown";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Extract all project metadata from the repository root.
|
|
111
|
+
*/
|
|
112
|
+
export function getProjectMetadata(
|
|
113
|
+
repoRoot: string,
|
|
114
|
+
): ProjectMetadata & { deploymentModel: string } {
|
|
115
|
+
// Try package.json first (Node.js projects)
|
|
116
|
+
const packageJsonPath = path.join(repoRoot, "package.json");
|
|
117
|
+
let metadata: Partial<ProjectMetadata> = existsSync(packageJsonPath)
|
|
118
|
+
? parsePackageJson(packageJsonPath)
|
|
119
|
+
: {};
|
|
120
|
+
|
|
121
|
+
// Try Cargo.toml (Rust projects) — takes precedence for version/name if both exist
|
|
122
|
+
const cargoTomlPath = path.join(repoRoot, "Cargo.toml");
|
|
123
|
+
if (existsSync(cargoTomlPath)) {
|
|
124
|
+
metadata = { ...metadata, ...parseCargoToml(cargoTomlPath) };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Try .deepreview.yml for explicit configuration
|
|
128
|
+
const deepReviewConfigPath = path.join(repoRoot, ".deepreview.yml");
|
|
129
|
+
const config: DeepReviewConfig = existsSync(deepReviewConfigPath)
|
|
130
|
+
? parseDeepReviewConfig(deepReviewConfigPath)
|
|
131
|
+
: {};
|
|
132
|
+
|
|
133
|
+
const deploymentModel = detectDeploymentModel(metadata as ProjectMetadata, config);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
...(metadata as ProjectMetadata),
|
|
137
|
+
deploymentModel,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get deployment context description for severity calibration.
|
|
143
|
+
*/
|
|
144
|
+
function getDeploymentContext(deploymentModel: string): string {
|
|
145
|
+
switch (deploymentModel) {
|
|
146
|
+
case "localhost-only":
|
|
147
|
+
return "(localhost-only dev tools — network/auth concerns are lower priority)";
|
|
148
|
+
case "internal-network":
|
|
149
|
+
return "(internal tool — auth is important but threat model is limited to internal users)";
|
|
150
|
+
case "public-facing":
|
|
151
|
+
return "(public-facing service — threat model includes untrusted users)";
|
|
152
|
+
case "library":
|
|
153
|
+
return "(published library — API stability and backwards compatibility are critical)";
|
|
154
|
+
default:
|
|
155
|
+
return "";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build calibration guidelines for severity assessment.
|
|
161
|
+
*/
|
|
162
|
+
function getCalibrationGuidelines(): string[] {
|
|
163
|
+
return [
|
|
164
|
+
"- Pre-1.0 projects: breaking API changes are expected — flag as suggestion, not warning",
|
|
165
|
+
"- Localhost-only tools: network/auth concerns are lower priority — downgrade to suggestion",
|
|
166
|
+
"- Published libraries (v1+): API stability and auth are critical — flag violations as warning/critical",
|
|
167
|
+
"- Stale docs in pre-1.0 projects: flag as suggestion, not critical",
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Format project metadata as a context preamble for reviewers.
|
|
173
|
+
* This is prepended to PRIOR_CONTEXT and helps reviewers calibrate severity.
|
|
174
|
+
*/
|
|
175
|
+
export function formatProjectContextPreamble(
|
|
176
|
+
metadata: ProjectMetadata & { deploymentModel: string },
|
|
177
|
+
): string {
|
|
178
|
+
const lines: string[] = ["## Project Context (for severity calibration)"];
|
|
179
|
+
|
|
180
|
+
if (metadata.name !== undefined && metadata.name !== "") {
|
|
181
|
+
lines.push(`**Name:** ${metadata.name}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (metadata.version !== undefined && metadata.version !== "") {
|
|
185
|
+
const versionParts = metadata.version.split(".");
|
|
186
|
+
const majorVersion = Number.parseInt(versionParts[0] ?? "0", 10);
|
|
187
|
+
if (majorVersion === 0) {
|
|
188
|
+
lines.push(
|
|
189
|
+
`**Version:** ${metadata.version} (pre-1.0 — relaxed API stability expectations; breaking changes are expected)`,
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
lines.push(`**Version:** ${metadata.version}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (metadata.deploymentModel !== "") {
|
|
197
|
+
const deploymentContext = getDeploymentContext(metadata.deploymentModel);
|
|
198
|
+
lines.push(`**Deployment:** ${metadata.deploymentModel} ${deploymentContext}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (metadata.isPrivate === true) {
|
|
202
|
+
lines.push("**Status:** Private/internal project (no external consumers)");
|
|
203
|
+
} else if (metadata.isUnpublished === false) {
|
|
204
|
+
lines.push("**Status:** Published (breaking changes require major version bump)");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (metadata.description !== undefined && metadata.description !== "") {
|
|
208
|
+
lines.push(`**Purpose:** ${metadata.description}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
lines.push("\nUse this context to calibrate finding severity. Calibration guidelines:");
|
|
212
|
+
lines.push(...getCalibrationGuidelines());
|
|
213
|
+
|
|
214
|
+
return lines.join("\n") + "\n";
|
|
215
|
+
}
|