diff --git a/.github/workflows/publish-one.yml b/.github/workflows/publish-one.yml
index 1ef6b4b3c..5ca6c23d3 100644
--- a/.github/workflows/publish-one.yml
+++ b/.github/workflows/publish-one.yml
@@ -8,7 +8,7 @@ on:
workflow_call:
inputs:
package:
- description: "Workspace member directory (e.g. packages/core, packages/code-review-agent)"
+ description: "Workspace member directory (e.g. packages/core, packages/cli)"
required: true
type: string
version:
diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml
index e40486d91..2818c2e64 100644
--- a/.github/workflows/publish-packages.yml
+++ b/.github/workflows/publish-packages.yml
@@ -30,7 +30,7 @@ jobs:
- name: Validate the manifests declare this version
run: |
VERSION="${{ steps.resolve.outputs.value }}"
- for f in packages/durable-streams/deno.json packages/runtime/deno.json packages/core/deno.json packages/acp/deno.json packages/workflow/deno.json packages/git/deno.json packages/testing/deno.json packages/test-agent/deno.json packages/web/deno.json packages/cli/deno.json packages/code-review-agent/deno.json; do
+ for f in packages/durable-streams/deno.json packages/runtime/deno.json packages/core/deno.json packages/acp/deno.json packages/workflow/deno.json packages/git/deno.json packages/testing/deno.json packages/test-agent/deno.json packages/web/deno.json packages/cli/deno.json; do
declared="$(jq -r .version "$f")"
if [ "$declared" != "$VERSION" ]; then
echo "::error::$f declares $declared, not $VERSION — the tag does not match the manifests"
@@ -131,13 +131,6 @@ jobs:
package: packages/cli
version: ${{ needs.version.outputs.value }}
- code-review-agent:
- needs: [version, core, runtime]
- uses: ./.github/workflows/publish-one.yml
- with:
- package: packages/code-review-agent
- version: ${{ needs.version.outputs.value }}
-
jsr:
needs: [version]
runs-on: ubuntu-latest
diff --git a/.github/workflows/repo-analysis.yml b/.github/workflows/repo-analysis.yml
deleted file mode 100644
index 2ac0b5215..000000000
--- a/.github/workflows/repo-analysis.yml
+++ /dev/null
@@ -1,79 +0,0 @@
-name: Repo Analysis
-
-on:
- workflow_dispatch:
- inputs:
- ref:
- description: Git ref to analyze
- required: true
- default: main
- report_name:
- description: Optional report label prefix
- required: false
- default: repo-analysis
-
-concurrency:
- group: repo-analysis-${{ inputs.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze:
- runs-on: ubuntu-latest
- timeout-minutes: 25
- permissions:
- contents: read
- issues: write
- steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- with:
- ref: ${{ inputs.ref }}
- fetch-depth: 0
-
- - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
-
- - uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
- with:
- deno-version: v2.9.5
-
- - name: Prepare the checked-out xmd binary
- run: deno task setup
-
- - name: Build the checked-out xmd binary
- run: deno task build
-
- - name: Run repo analysis
- env:
- DEEPINFRA_TOKEN: ${{ secrets.DEEPINFRA_TOKEN }}
- GITHUB_TOKEN: ${{ github.token }}
- GITHUB_REPOSITORY: ${{ github.repository }}
- run: |
- ./dist/xmd run .reviews/AnalyzeRepoCI.md \
- --plugin ./packages/code-review-agent/mod.ts \
- -j .reviews/journal.analyze.ci.jsonl > .reviews/analyze-report.md
-
- - name: Write run metadata
- if: always()
- run: |
- mkdir -p .reviews
- cat > .reviews/analyze-run.json <<'JSON'
- {
- "workflow": "repo-analysis.yml",
- "run_id": "${{ github.run_id }}",
- "run_number": "${{ github.run_number }}",
- "run_attempt": "${{ github.run_attempt }}",
- "sha": "${{ github.sha }}",
- "ref": "${{ inputs.ref }}",
- "report_name": "${{ inputs.report_name }}"
- }
- JSON
-
- - name: Upload analysis artifacts
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
- with:
- name: ${{ inputs.report_name }}-${{ github.sha }}
- path: |
- .reviews/analyze-report.md
- .reviews/journal.analyze.ci.jsonl
- .reviews/analyze-run.json
- retention-days: 30
diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml
deleted file mode 100644
index 7c911bb7a..000000000
--- a/.github/workflows/review.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-name: PR Review
-
-on:
- pull_request:
- types: [opened, synchronize, reopened]
-
-jobs:
- review:
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
- contents: read
- steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- with:
- fetch-depth: 0
-
- - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
-
- - uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
- with:
- deno-version: v2.9.5
-
- - name: Prepare the checked-out xmd binary
- run: deno task setup
-
- - name: Build the checked-out xmd binary
- run: deno task build
-
- - name: Run review
- env:
- PR_NUMBER: ${{ github.event.pull_request.number }}
- PR_TITLE: ${{ github.event.pull_request.title }}
- BASE_SHA: ${{ github.event.pull_request.base.sha }}
- HEAD_SHA: ${{ github.event.pull_request.head.sha }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITHUB_REPOSITORY: ${{ github.repository }}
- DEEPINFRA_TOKEN: ${{ secrets.DEEPINFRA_TOKEN }}
- run: |
- ./dist/xmd run .reviews/ReviewPR.md \
- --plugin ./packages/code-review-agent/mod.ts \
- -j .reviews/journal.jsonl \
- --verbose
-
- - name: Upload journal
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
- with:
- name: review-journal-${{ github.event.pull_request.head.sha }}
- path: .reviews/journal.jsonl
- retention-days: 30
diff --git a/.gitignore b/.gitignore
index de5459fd4..15a9c80e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,5 @@
node_modules/
.vscode/
-.reviews/journal*.jsonl
-.reviews/.oxlint/
-.reviews/tsconfig.oxlint.json
-.reviews/analyze-report.md
-.reviews/analyze-run.json
.xmd-eval/
# xmd test failure journals (specs/testing-spec.md)
.xmd/test-artifacts/
diff --git a/.reviews/.oxlintrc.json b/.reviews/.oxlintrc.json
deleted file mode 100644
index 6a9bc310a..000000000
--- a/.reviews/.oxlintrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "$schema": "../node_modules/oxlint/configuration_schema.json",
-
- "extends": ["../oxlint.shared.json"],
-
- "plugins": ["typescript", "unicorn", "import"]
-}
diff --git a/.reviews/AnalyzeRepo.md b/.reviews/AnalyzeRepo.md
deleted file mode 100644
index e10db3939..000000000
--- a/.reviews/AnalyzeRepo.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-title: Repository Analysis
----
-
-
-
-
-
-
-```ts eval
-const fileList = inventory.fileList;
-const fileCount = inventory.fileCount;
-const lineCount = inventory.lineCount;
-const pr = {
- files: [],
- added: [],
- removed: [],
- created: [],
- modified: [],
- deleted: [],
- directories: new Set(),
- addedSource: "",
- diffPreview: "",
- stats: { totalFiles: fileCount, additions: lineCount, deletions: 0, totalChanges: lineCount },
- meta: { title: "Repo Analysis", body: "", number: "" },
-};
-```
-
-
-
-
-
-```ts eval
-import { buildCleanupAnalysis, buildDiagnostics } from "@executablemd/code-review-agent";
-
-const diagnostics = buildDiagnostics(rawDiagnostics, pr, doctor);
-const cleanupAnalysis = buildCleanupAnalysis(diagnostics);
-```
-
-
-
-
-
-
-
-
-
-
diff --git a/.reviews/AnalyzeRepoCI.md b/.reviews/AnalyzeRepoCI.md
deleted file mode 100644
index 4ee99b258..000000000
--- a/.reviews/AnalyzeRepoCI.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-title: Repository Analysis (CI)
----
-
-
diff --git a/.reviews/DispatchRepoAnalysis.md b/.reviews/DispatchRepoAnalysis.md
deleted file mode 100644
index cac429f5e..000000000
--- a/.reviews/DispatchRepoAnalysis.md
+++ /dev/null
@@ -1,127 +0,0 @@
----
-title: Dispatch Repo Analysis Workflow
----
-
-```ts eval
-const requestedRef = process.env.ANALYZE_REF ?? "";
-const reportPrefix = process.env.ANALYZE_REPORT_NAME ?? "repo-analysis";
-```
-
-
-
-```bash exec
-git rev-parse --abbrev-ref HEAD
-```
-
-
-
-
-
-```bash exec
-gh repo view --json nameWithOwner -q .nameWithOwner
-```
-
-
-
-
-
-```bash exec
-date -u +"%Y-%m-%dT%H:%M:%SZ"
-```
-
-
-
-```ts eval
-const targetRef = requestedRef || currentBranch.trim();
-const repo = repoName.trim();
-const dispatchStartIso = dispatchStart.trim();
-```
-
-```bash exec
-gh workflow run repo-analysis.yml --repo {repo} --ref {targetRef} -f ref={targetRef} -f report_name={reportPrefix}
-```
-
-
-
-```bash exec
-for i in $(seq 1 30); do
- RUN_ID=$(gh run list --repo {repo} --workflow repo-analysis.yml --event workflow_dispatch --json databaseId,headBranch,createdAt -q 'map(select(.headBranch == "{targetRef}" and .createdAt >= "{dispatchStartIso}")) | .[0].databaseId // ""')
- if [ -n "$RUN_ID" ]; then
- printf '%s' "$RUN_ID"
- exit 0
- fi
- sleep 2
-done
-echo ""
-```
-
-
-
-```ts eval
-const runIdValue = runId.trim();
-```
-
-```bash exec
-if [ -z "{runIdValue}" ]; then
- echo "Unable to locate workflow run id" >&2
- exit 1
-fi
-gh run watch {runIdValue} --repo {repo} --exit-status
-```
-
-
-
-```bash exec
-gh run view {runIdValue} --repo {repo} --json databaseId,url,status,conclusion,headSha,displayTitle,createdAt,updatedAt
-```
-
-
-
-```bash silent exec
-mkdir -p .reviews/artifacts/{runIdValue}
-gh run download {runIdValue} --repo {repo} --dir .reviews/artifacts/{runIdValue}
-```
-
-```ts eval
-const run = JSON.parse(runJson);
-const artifactDir = `.reviews/artifacts/${runIdValue}`;
-const reportPath = `${artifactDir}/${reportPrefix}-${run.headSha}/analyze-report.md`;
-const metadataPath = `${artifactDir}/${reportPrefix}-${run.headSha}/analyze-run.json`;
-const runUrl = run.url ?? "";
-const runStatus = run.status ?? "";
-const runConclusion = run.conclusion ?? "";
-
-let reportText = "";
-let metadataText = "";
-
-try {
- reportText = Deno.readTextFileSync(reportPath);
-} catch {
- reportText = "(analyze-report.md artifact not found)";
-}
-
-try {
- metadataText = Deno.readTextFileSync(metadataPath);
-} catch {
- metadataText = "{}";
-}
-```
-
-## Repo Analysis Dispatch
-
-- Repository: `{repo}`
-- Ref: `{targetRef}`
-- Run ID: `{runIdValue}`
-- Run URL: {runUrl}
-- Status: `{runStatus}`
-- Conclusion: `{runConclusion}`
-
-### Run Metadata
-
-```json
-{metadataText}
-```
-
-### Report
-
-{reportText}
diff --git a/.reviews/ReviewPR.local.md b/.reviews/ReviewPR.local.md
deleted file mode 100644
index 053485895..000000000
--- a/.reviews/ReviewPR.local.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-title: PR Review (local)
----
-
-
-
-
-
-
-
-```ts eval
-const pr = context.pr;
-const changedFilePaths = context.changedFilePaths;
-const changedTsFiles = pr.files
- .filter((file) => file.language === "typescript" && !file.isTest && !file.isTypeDeclaration)
- .map((file) => file.path)
- .slice(0, 200);
-```
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.reviews/ReviewPR.md b/.reviews/ReviewPR.md
deleted file mode 100644
index 9b5dbe5fc..000000000
--- a/.reviews/ReviewPR.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: PR Review
----
-
-
diff --git a/.reviews/policies/README.md b/.reviews/policies/README.md
deleted file mode 100644
index 5652c36e4..000000000
--- a/.reviews/policies/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Review Policies
-
-The executable policy documents and the reusable review components they compose
-with are installed from `@executablemd/code-review-agent`, not from this
-directory. The package declares them to every `xmd` run, so a review resolves the
-same components whatever checkout it is reviewing and whatever directory it runs
-from — which is the point: a review must not be answerable by its subject.
-
-The policies live at `packages/code-review-agent/src/documents/policies/`:
-
-- `ScopePolicy.md` checks PR size and scoping hygiene.
-- `BloatPolicy.md` surfaces structural bloat patterns.
-- `SlopPolicy.md` surfaces verbosity/slop indicators.
-- `ExtraneousCodePolicy.md` asks for semantic/correctness-focused review.
-- `RepoCleanupPolicy.md` performs repo-wide cleanup policy analysis.
-
-Policies are composed by report components, which the same package declares:
-
-- `PrPolicyReport` composes PR policies.
-- `RepoPolicyReport` composes repo policies.
-
-Reusable workflow steps and primitives are at
-`packages/code-review-agent/src/documents/components/`, and the six TypeScript
-components at `packages/code-review-agent/src/components/`.
-
-What stays here is this repository's own review programs — `ReviewPR.md`,
-`ReviewPR.local.md`, `AnalyzeRepo.md`, `AnalyzeRepoCI.md` and
-`DispatchRepoAnalysis.md` — which compose the installed components, together
-with the sensor configuration and runtime state those runs read and write
-(`.oxlintrc.json`, `.oxlint/`, the generated `tsconfig.oxlint.json`, and the
-journals). None of the review and analysis commands passes a component
-`--include`.
diff --git a/.reviews/retired/agents-review-boundaries.md b/.reviews/retired/agents-review-boundaries.md
deleted file mode 100644
index 889dbd94b..000000000
--- a/.reviews/retired/agents-review-boundaries.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Retired: AGENTS.md review boundaries (parked verbatim pending the review-workflow rewrite)
-
-## Review boundaries
-
-An ordinary implementation verdict reviews one exact feedback-commit SHA against
-the settled contract, the patch, the implementation and the focused evidence
-reported with it. It does not inspect, monitor or wait for CI, and CI status is
-neither positive nor negative evidence for it. A passing verdict is `PASS` —
-never `PASS pending CI`, `mark ready after CI`, or an equivalent condition. CI
-is inspected only when the user explicitly assigns CI troubleshooting.
-
-### Structural consequences
-
-An architecture finding has a **structural consequence** only when it changes
-one or more of:
-
-- what is authorized to execute;
-- which durable identity or retained history is accepted;
-- what durable state is committed, published, or journaled;
-- whether replay can resume the intended run;
-- ownership of a transaction, resource, invocation, or lifecycle;
-- concurrency or cancellation behavior that violates that ownership;
-- which authoritative outcome wins after a fatal failure; or
-- a public persistence or compatibility boundary.
-
-The Architect returns `REQUEST CHANGES` only when all five of these hold:
-
-1. The finding is reproduced or directly traced against the exact reviewed
- commit.
-2. It violates a previously settled structural invariant.
-3. It uses an in-scope supported surface.
-4. It produces a structural consequence from the list above.
-5. Its correction belongs within the current PR's purpose.
-
-The verdict names every one of them: the reviewed SHA, the settled invariant,
-the reproducer or direct trace, the supported surface, the structural
-consequence, and why the correction belongs in this PR. A hypothetical risk, a
-plausible concern, or an adjacent invariant cannot fail architecture review.
-
-The structural checklist is frozen before implementation. A distinct structural
-invariant added afterwards takes an explicit architecture amendment naming its
-consequence, not an implicit review expansion.
-
-### Finite evidence
-
-The Planner owns the evidence sufficient to prove the settled acceptance
-criteria: how much implementation detail each criterion needs, the
-representative scenarios, the focused tests, and when one regression proves a
-criterion. That acceptance and evidence matrix is frozen before implementation.
-
-The Implementor executes the frozen matrix. When implementation evidence shows
-the matrix cannot prove a criterion, the Implementor returns that evidence
-rather than expanding acceptance independently. The Architect may restore a
-structural invariant the plan omits, but does not expand a sufficient matrix
-with permutations that carry no distinct structural consequence.
-
-Once implementation begins, a newly imagined edge case blocks only when it
-proves an existing criterion unmet, or carries a distinct structural consequence
-requiring an explicit architecture amendment.
-
-### Follow-ups and closure
-
-A non-blocking observation does not automatically become an issue. The Planner
-decides whether recurrence likelihood, user impact, or expected remediation
-value makes it worth tracking; otherwise the behavior stays for reactive
-maintenance.
-
-Architect review closes when the frozen structural checklist passes. Planner
-review closes when the frozen acceptance criteria and the selected evidence
-pass. Neither role reopens review for CI, diagnostic hardening, speculative
-permutations, unrelated correctness polish, or an incidental non-structural
-observation. A later correction returns to a role only when it materially
-changes that role's reviewed contract.
diff --git a/README.md b/README.md
index b32222bf5..6d1f57496 100644
--- a/README.md
+++ b/README.md
@@ -57,8 +57,8 @@ Locate the package that owns the behavior you are about to change:
- `packages/testing/` and `packages/test-agent/` — `` and the
deterministic agent; `packages/test-support/` is the one BDD surface all three
runtimes share.
-- `packages/acp/`, `packages/web/`, `packages/code-review-agent/` — the coding
- agent bridge, the web form host, and the review agent.
+- `packages/acp/` and `packages/web/` — the coding agent bridge and the web
+ form host.
- `scripts/` — this repository's own tooling: setup, builds, release targets,
and the checks CI runs.
- `specs/` — the specification each package is held to.
diff --git a/bun.lock b/bun.lock
index 780fb584b..c8f67edbf 100644
--- a/bun.lock
+++ b/bun.lock
@@ -33,7 +33,6 @@
"@durable-streams/server": "^0.3.8",
"@executablemd/acp": "workspace:*",
"@executablemd/cli": "workspace:*",
- "@executablemd/code-review-agent": "workspace:*",
"@executablemd/core": "workspace:*",
"@executablemd/durable-streams": "workspace:*",
"@executablemd/git": "workspace:*",
@@ -84,10 +83,6 @@
"zod": "^4.3.6",
},
},
- "packages/code-review-agent": {
- "name": "@executablemd/code-review-agent",
- "version": "0.14.1",
- },
"packages/core": {
"name": "@executablemd/core",
"version": "0.14.1",
@@ -335,8 +330,6 @@
"@executablemd/cli": ["@executablemd/cli@workspace:packages/cli"],
- "@executablemd/code-review-agent": ["@executablemd/code-review-agent@workspace:packages/code-review-agent"],
-
"@executablemd/core": ["@executablemd/core@workspace:packages/core"],
"@executablemd/durable-streams": ["@executablemd/durable-streams@workspace:packages/durable-streams"],
diff --git a/deno.json b/deno.json
index f64bc1f68..9769f257c 100644
--- a/deno.json
+++ b/deno.json
@@ -74,11 +74,6 @@
"verify": "deno run --allow-all --node-modules-dir=none --cached-only --frozen scripts/preflight.ts scripts/verify.ts",
"vendor:verify": "deno run --allow-read --allow-write=/tmp --allow-env --allow-run --cached-only --frozen scripts/verify-cloudflare-dofs.ts",
"check": "deno check --frozen",
- "check:jsr": "deno publish --dry-run --allow-dirty",
- "review": "deno run --allow-all packages/cli/src/deno.ts run --plugin ./packages/code-review-agent/mod.ts .reviews/ReviewPR.md -j .reviews/journal.jsonl",
- "review:local": "deno run --allow-all packages/cli/src/deno.ts run --plugin ./packages/code-review-agent/mod.ts .reviews/ReviewPR.local.md -j .reviews/journal.local.jsonl",
- "analyze": "deno run --allow-all packages/cli/src/deno.ts run --plugin ./packages/code-review-agent/mod.ts .reviews/AnalyzeRepo.md -j .reviews/journal.analyze.jsonl",
- "analyze:ci": "deno run --allow-all packages/cli/src/deno.ts run --plugin ./packages/code-review-agent/mod.ts .reviews/AnalyzeRepoCI.md -j .reviews/journal.analyze.ci.jsonl",
- "analyze:dispatch": "deno run --allow-all packages/cli/src/deno.ts run .reviews/DispatchRepoAnalysis.md -j .reviews/journal.dispatch.jsonl"
+ "check:jsr": "deno publish --dry-run --allow-dirty"
}
}
diff --git a/deno.lock b/deno.lock
index 850356aa4..2ae9cd884 100644
--- a/deno.lock
+++ b/deno.lock
@@ -4911,15 +4911,6 @@
]
}
},
- "packages/code-review-agent": {
- "packageJson": {
- "dependencies": [
- "npm:@effectionx/fetch@0.2.1",
- "npm:@effectionx/fs@0.3.0",
- "npm:effection@4.1.0"
- ]
- }
- },
"packages/core": {
"dependencies": [
"npm:@babel/standalone@8.0.7",
diff --git a/knip.json b/knip.json
index 1f3732255..af107ae42 100644
--- a/knip.json
+++ b/knip.json
@@ -27,10 +27,6 @@
"@executablemd/acp/embedded-adapters": ["packages/acp/embedded-adapters.ts"],
"@executablemd/cli": ["packages/cli/src/deno.ts"],
"@executablemd/cli/api": ["packages/cli/api.ts"],
- "@executablemd/code-review-agent": ["packages/code-review-agent/mod.ts"],
- "@executablemd/code-review-agent/review-components": [
- "packages/code-review-agent/src/review-components.ts"
- ],
"@executablemd/core": ["packages/core/mod.ts"],
"@executablemd/core/api": ["packages/core/api.ts"],
"@executablemd/core/host/deno": ["packages/core/component-modules/deno.ts"],
@@ -72,10 +68,6 @@
"@executablemd/acp/embedded-adapters": ["../../packages/acp/embedded-adapters.ts"],
"@executablemd/cli": ["../../packages/cli/src/deno.ts"],
"@executablemd/cli/api": ["../../packages/cli/api.ts"],
- "@executablemd/code-review-agent": ["../../packages/code-review-agent/mod.ts"],
- "@executablemd/code-review-agent/review-components": [
- "../../packages/code-review-agent/src/review-components.ts"
- ],
"@executablemd/core": ["../../packages/core/mod.ts"],
"@executablemd/core/api": ["../../packages/core/api.ts"],
"@executablemd/core/host/deno": ["../../packages/core/component-modules/deno.ts"],
diff --git a/package.json b/package.json
index f2224aff2..aa327e29f 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,6 @@
"@durable-streams/server": "^0.3.8",
"@executablemd/acp": "workspace:*",
"@executablemd/cli": "workspace:*",
- "@executablemd/code-review-agent": "workspace:*",
"@executablemd/core": "workspace:*",
"@executablemd/durable-streams": "workspace:*",
"@executablemd/git": "workspace:*",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index c2c5dc578..7cfc98353 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -31,8 +31,5 @@
"marked": "^17.0.4",
"semver": "^7.8.5",
"zod": "^4.3.6"
- },
- "devDependencies": {
- "@executablemd/code-review-agent": "workspace:*"
}
}
diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts
index 8c94340f6..aa2b2a9eb 100755
--- a/packages/cli/src/cli.ts
+++ b/packages/cli/src/cli.ts
@@ -1389,10 +1389,10 @@ export function* installDocumentComponents(mode: DocumentMode, verbose: boolean)
yield* useVerboseComponent();
yield* installTestingComponents({ verbose });
// No package-specific vocabulary is bootstrapped here. A Plugin registers
- // what it claims in the command scope this assembly runs inside, which is
- // what lets `xmd test` claim none of the review graph's names while the
- // nested `host="run"` child it can launch installs the same Plugins again
- // and claims all of them.
+ // its names in the command scope this assembly runs inside, which is what
+ // lets a Plugin contribute nothing to `xmd test` while the nested
+ // `host="run"` child it can launch installs the same Plugins again with
+ // command `run`.
}
// `` for both commands. Registered rather than reserved, so a
diff --git a/packages/cli/tests/fixtures/plugins/declaring.mjs b/packages/cli/tests/fixtures/plugins/declaring.mjs
index 27810290f..16d26e162 100644
--- a/packages/cli/tests/fixtures/plugins/declaring.mjs
+++ b/packages/cli/tests/fixtures/plugins/declaring.mjs
@@ -1,6 +1,8 @@
-// Declares one exact Markdown component. The bytes, their origin and their
-// digest are what the host states about them, exactly as any selected Plugin
-// states its own — a Plugin is trusted code, and this is the same boundary.
+// Declares exact Markdown components and reserves one registration. The bytes,
+// their origin and their digest are what the host states about them, exactly as
+// any selected Plugin states its own — a Plugin is trusted code, and this is the
+// same boundary.
+import { registerComponents } from "@executablemd/core";
import { Plugin } from "@executablemd/core/api";
// A declaration is what a *host* states about bytes it ships, so its
// constructors live on the host boundary rather than on the consumer API.
@@ -11,8 +13,22 @@ const SHADOWED = "the selected declaration, not the checkout's\n";
export default Plugin({
name: "declaring",
- // deno-lint-ignore require-yield
*install() {
+ // The registered tier beside the declared one: a reserved name a
+ // checkout's same-named file cannot replace either.
+ yield* registerComponents([
+ {
+ name: "Stamp",
+ origin: "fixture/declaring",
+ reserved: true,
+ props: { type: "object", properties: {}, additionalProperties: false },
+ description: "Mark the output as the selected Plugin's.",
+ // deno-lint-ignore require-yield
+ *fn() {
+ return "stamped by the selected Plugin";
+ },
+ },
+ ]);
return {
components: [
Markdown({
diff --git a/packages/cli/tests/plugin-cli.test.ts b/packages/cli/tests/plugin-cli.test.ts
index d3931f52b..436162e62 100644
--- a/packages/cli/tests/plugin-cli.test.ts
+++ b/packages/cli/tests/plugin-cli.test.ts
@@ -138,10 +138,11 @@ describe("PC2 — a Plugin declares components, and a checkout cannot answer for
it("resolves a declared component, and leaves the checkout's own components alone", function* () {
yield* useWorkspace(
{
- "doc.md": "\n\n\n\n\n",
- // A repository component of the same name as one the Plugin declares,
- // and one of a name nothing declares.
+ "doc.md": "\n\n\n\n\n\n\n",
+ // Repository components of the names the Plugin declares and reserves,
+ // and one of a name nothing claims.
"components/Shadowed.md": "the checkout's own component\n",
+ "components/Stamp.md": "the checkout's own stamp\n",
"components/Local.md": "an unrelated checkout component\n",
},
function* (dir) {
@@ -151,8 +152,10 @@ describe("PC2 — a Plugin declares components, and a checkout cannot answer for
expect(run.stdout).toContain("hello from the selected Plugin");
expect(run.stdout).toContain("the selected declaration, not the checkout's");
expect(run.stdout).not.toContain("the checkout's own component");
- // Declaring two names claims exactly those two: everything else in the
- // checkout resolves as it always did.
+ expect(run.stdout).toContain("stamped by the selected Plugin");
+ expect(run.stdout).not.toContain("the checkout's own stamp");
+ // Three names belong to the Plugin and no others: everything else in
+ // the checkout resolves as it always did.
expect(run.stdout).toContain("an unrelated checkout component");
},
);
@@ -365,6 +368,7 @@ describe("PC6 — every surface of one command sees one vocabulary", () => {
cwd: dir,
}).expect();
expect(run.stdout).toContain("Greeting");
+ expect(run.stdout).toContain("Stamp");
});
});
@@ -373,6 +377,7 @@ describe("PC6 — every surface of one command sees one vocabulary", () => {
const run = yield* runCli(["syntax"], { cwd: dir }).expect();
// Nothing an operator did not select.
expect(run.stdout).not.toContain("Greeting");
+ expect(run.stdout).not.toContain("Stamp");
// And everything the one bundled Plugin brings, because every syntax
// catalog is built from the profile the command runs with.
for (const name of ["Repository", "Worktree", "Dir", "PullRequest", "Issue"]) {
@@ -409,47 +414,6 @@ describe("PC6 — every surface of one command sees one vocabulary", () => {
});
});
-describe("PC9 — the review graph arrives only when it is selected", () => {
- const REVIEW = "@executablemd/code-review-agent";
- const PACKAGE = fileURLToPath(new URL("../../code-review-agent/mod.ts", import.meta.url));
- const FINDING = '\n';
-
- it("is absent from every command that would run it, until it is named", function* () {
- yield* useWorkspace({ "doc.md": FINDING }, function* (dir) {
- // `xmd syntax` describes the language a run has, so it is where an absent
- // vocabulary shows without running anything.
- const described = yield* runCli(["syntax"], { cwd: dir }).expect();
- expect(described.stdout).not.toContain("Finding");
- expect(described.stdout).not.toContain("ReviewContext");
-
- // And a document that writes one of the forty-one names resolves nothing.
- const run = yield* runCli(["run", "doc.md"], { cwd: dir }).join();
- expect(run.stdout).not.toContain("probe");
- });
- });
-
- it("arrives for run and syntax when it is named", function* () {
- yield* useWorkspace({ "doc.md": FINDING }, function* (dir) {
- const described = yield* runCli(["syntax", `--plugin=${PACKAGE}`], { cwd: dir }).expect();
- expect(described.stdout).toContain("Finding");
- expect(described.stdout).toContain("ReviewContext");
-
- const run = yield* runCli(["run", `--plugin=${PACKAGE}`, "doc.md"], { cwd: dir }).expect();
- expect(run.stdout).toContain("probe");
- });
- });
-
- it("names itself by its own name, however it was reached", function* () {
- yield* useWorkspace({ "doc.md": DOCUMENT }, function* (dir) {
- const run = yield* runCli(
- ["run", `--plugin=${PACKAGE}`, `--plugin=${fixture("active.mjs")}`, "doc.md"],
- { cwd: dir },
- ).expect();
- expect(run.stdout).toContain(`active: ${GIT}, ${REVIEW}, active`);
- });
- });
-});
-
/**
* A package installed where the command runs, as an operator would have it.
*
diff --git a/packages/cli/tests/plugin-host.test.ts b/packages/cli/tests/plugin-host.test.ts
index 0fa516117..80ccc4919 100644
--- a/packages/cli/tests/plugin-host.test.ts
+++ b/packages/cli/tests/plugin-host.test.ts
@@ -7,11 +7,6 @@
* before the first `install()`, so a refused selection installs nothing and
* reads no document. And the whole list has the command's lifetime, so a
* failure part-way through unwinds what came before it.
- *
- * The review Plugin's own rows are here too, because which commands reserve its
- * forty-one names is a property of this assembly rather than of the package —
- * and because nothing installs it until a selection names it. The graph itself
- * is `scripts/tests/review-infrastructure.test.ts`.
*/
import { describe, it } from "@executablemd/test-support/bdd";
import { expect } from "@executablemd/test-support/expect";
@@ -19,10 +14,8 @@ import { ensure, scoped, spawn, suspend, withResolvers } from "effection";
import type { Operation } from "effection";
import { activePlugins, Plugin } from "@executablemd/core/api";
import type { PluginInstallation } from "@executablemd/core/api";
-import { inspectComponent } from "@executablemd/core";
import { Markdown, sourceDigest, Structural } from "@executablemd/core/host";
import { Config, verbose } from "@executablemd/runtime/api";
-import reviewPlugin from "@executablemd/code-review-agent";
import { admitPlugins, installPlugins, NO_PLUGINS } from "../src/plugin-host.ts";
import { syntaxSymbols } from "../src/syntax.ts";
import { structuralValidation } from "../src/plan-component.ts";
@@ -407,79 +400,7 @@ describe("PH5 — a Plugin reads the command's typed configuration", () => {
});
});
-describe("PH6 — the selected review Plugin claims the commands that run a review", () => {
- /** The declared names this command contributes when the Plugin is selected. */
- function* declaredFor(command: string, args: readonly string[]): Operation {
- return yield* scoped(function* () {
- const assembly = yield* installPlugins([reviewPlugin], { command, args });
- return assembly.declarations.map((declaration) => declaration.name);
- });
- }
-
- it("declares the thirty-five Markdown components for run, syntax and plan", function* () {
- for (const command of ["run", "syntax", "plan"]) {
- const declared = yield* declaredFor(command, [command]);
- expect(`${command}: ${declared.length}`).toBe(`${command}: 35`);
- expect(declared).toContain("Finding");
- }
- });
-
- it("declares them for a workflow action that executes a document", function* () {
- for (const action of ["start", "resume", "fork"]) {
- const declared = yield* declaredFor("workflow", ["workflow", action, "flow.md"]);
- expect(`${action}: ${declared.length}`).toBe(`${action}: 35`);
- }
- });
-
- it("declares none for the test root, upgrade, or a workflow management action", function* () {
- expect(yield* declaredFor("test", ["test"])).toEqual([]);
- expect(yield* declaredFor("upgrade", ["upgrade"])).toEqual([]);
- for (const action of ["list", "status", "history", "cancel", "delete", "export", "answer"]) {
- expect(`${action}: ${(yield* declaredFor("workflow", ["workflow", action])).length}`).toBe(
- `${action}: 0`,
- );
- }
- });
-
- it("reads an option's value as a value, never as the action", function* () {
- // Every option the command defines that takes a separated value, each with
- // an executing action's own name as that value and a management action
- // after it. Reading the first recognized word would have installed the
- // review graph for a command that executes no document.
- const valued: readonly (readonly [string, string, readonly string[]])[] = [
- ["--plugin", "start", ["workflow", "--plugin", "start", "list"]],
- ["--output", "start", ["workflow", "--output", "start", "export", "run-1"]],
- ["--status", "resume", ["workflow", "--status", "resume", "list"]],
- ["--id", "fork", ["workflow", "--id", "fork", "list"]],
- ["--at", "start", ["workflow", "--at", "start", "history", "run-1"]],
- ["--artifact", "start", ["workflow", "--artifact", "start", "status"]],
- ["--props", "start", ["workflow", "--props", "start", "list"]],
- ["--props-name", "start", ["workflow", "--props-name", "start", "list"]],
- ];
- for (const [option, value, args] of valued) {
- const declared = yield* declaredFor("workflow", args);
- expect(`${option}=${value}: ${declared.length}`).toBe(`${option}=${value}: 0`);
- }
-
- // The assigned spelling is one token and was never a hazard, but a scan
- // that special-cased the separated form would have found this one.
- expect(
- yield* declaredFor("workflow", ["workflow", "--output=start.xmd", "export", "r"]),
- ).toEqual([]);
-
- // And the same reading still finds a real action written after an option.
- const executing: readonly (readonly string[])[] = [
- ["workflow", "--plugin", "list", "start", "flow.md"],
- ["workflow", "--id", "release-1", "start", "flow.md"],
- ["workflow", "--at", "event-4", "fork", "run-1", "flow.md"],
- ["workflow", "--verbose", "resume", "run-1"],
- ];
- for (const args of executing) {
- const declared = yield* declaredFor("workflow", args);
- expect(`${args.join(" ")}: ${declared.length}`).toBe(`${args.join(" ")}: 35`);
- }
- });
-
+describe("PH6 — an empty selection contributes nothing", () => {
it("contributes nothing at all when it is not in the list", function* () {
// A claim about this primitive, not about the product. `installPlugins()`
// installs the list it is handed and nothing else, so an empty list
@@ -495,50 +416,6 @@ describe("PH6 — the selected review Plugin claims the commands that run a revi
expect(`${command}: ${assembly.installations.length}`).toBe(`${command}: 0`);
}
});
-
- it("registers the six reserved names where it claims the graph", function* () {
- const reserved = [
- "CommentReviewData",
- "CommentReviewState",
- "Doctor",
- "OxlintDiagnostics",
- "RepositoryInventory",
- "ReviewContext",
- ];
- const described = yield* scoped(function* () {
- yield* installPlugins([reviewPlugin], { command: "run", args: ["run"] });
- const found: string[] = [];
- for (const name of reserved) {
- const info = yield* inspectComponent({ name, includes: [] });
- if (
- info.kind === "registered" &&
- info.origin.kind === "registered" &&
- info.origin.origin === "@executablemd/code-review-agent" &&
- info.origin.reserved
- ) {
- found.push(name);
- }
- }
- return found;
- });
- expect(described).toEqual(reserved);
- });
-
- it("registers none of them where no Plugin was selected", function* () {
- const kind = yield* scoped(function* () {
- yield* installPlugins([], { command: "run", args: ["run"] });
- return (yield* inspectComponent({ name: "ReviewContext", includes: [] })).kind;
- });
- expect(kind).not.toBe("registered");
- });
-
- it("registers none of them for the test root", function* () {
- const kind = yield* scoped(function* () {
- yield* installPlugins([reviewPlugin], { command: "test", args: ["test"] });
- return (yield* inspectComponent({ name: "ReviewContext", includes: [] })).kind;
- });
- expect(kind).not.toBe("registered");
- });
});
describe("PH7 — a structural-only Plugin reaches every consumer of the catalog", () => {
diff --git a/packages/cli/tests/support/run-markdown-tier.ts b/packages/cli/tests/support/run-markdown-tier.ts
index 7a9c6d07e..9b9ecf354 100644
--- a/packages/cli/tests/support/run-markdown-tier.ts
+++ b/packages/cli/tests/support/run-markdown-tier.ts
@@ -37,7 +37,6 @@ import { useDenoService } from "../../src/deno-service.ts";
import { useNodeService } from "../../src/node-service.ts";
import { BUNDLED_PLUGIN } from "../../src/run-profile.ts";
-import reviewPlugin from "@executablemd/code-review-agent";
/** The native service adapter the entrypoint for this runtime installs. */
const SERVICES = {
bun: useBunService,
@@ -79,12 +78,9 @@ export function runMarkdownTier(document: string): Operation {
const installService = SERVICES[cliRuntime()];
const testingHost = testingExecutionHost({
includes: ["components", "."],
- // The Plugin a run would have selected, because a `host="run"` child gets
- // what `xmd run` gets: the child installs it again in its own scope, with
- // command `run`. XMD bundles only Git, so this harness names the rest
- // exactly as an
- // operator does.
- plugins: [reviewPlugin],
+ // What `xmd test ` selects with no `--plugin`: a `host="run"`
+ // child gets the bundled Git Plugin and nothing else.
+ plugins: [],
pluginArgs: [],
secretDetection: true,
installService,
diff --git a/packages/cli/tests/testing-execution-host.test.ts b/packages/cli/tests/testing-execution-host.test.ts
index b02d7e47a..5371c9d1d 100644
--- a/packages/cli/tests/testing-execution-host.test.ts
+++ b/packages/cli/tests/testing-execution-host.test.ts
@@ -31,7 +31,6 @@ import { testingExecutionHost } from "../src/testing-host.ts";
import { planComponentDeclaration, planComponentDescription } from "../src/plan-component.ts";
import { BUNDLED_PLUGIN } from "../src/run-profile.ts";
-import reviewPlugin from "@executablemd/code-review-agent";
function doc(...lines: string[]): string {
return `${lines.join("\n")}\n`;
}
@@ -775,11 +774,7 @@ describe("deterministic dependencies declared for a nested run", () => {
const hold = withResolvers();
const host = testingExecutionHost({
includes: [],
- // The Plugin a run would have selected, because a `host="run"` child gets
- // what `xmd run` gets: the child installs it again in its own scope, with
- // command `run`. XMD bundles none, so a harness that wants the review
- // graph names it exactly as an operator does.
- plugins: [reviewPlugin],
+ plugins: [],
pluginArgs: [],
secretDetection: true,
// deno-lint-ignore require-yield
@@ -998,11 +993,7 @@ describe("deterministic dependencies declared for a nested run", () => {
// to say about it, and what it says is why.
const host = testingExecutionHost({
includes: [],
- // The Plugin a run would have selected, because a `host="run"` child gets
- // what `xmd run` gets: the child installs it again in its own scope, with
- // command `run`. XMD bundles none, so a harness that wants the review
- // graph names it exactly as an operator does.
- plugins: [reviewPlugin],
+ plugins: [],
pluginArgs: [],
secretDetection: true,
// deno-lint-ignore require-yield
diff --git a/packages/code-review-agent/deno.json b/packages/code-review-agent/deno.json
deleted file mode 100644
index 4d16e0b98..000000000
--- a/packages/code-review-agent/deno.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "name": "@executablemd/code-review-agent",
- "version": "0.14.1",
- "exports": {
- ".": "./mod.ts",
- "./review-components": "./src/review-components.ts"
- }
-}
diff --git a/packages/code-review-agent/mod.ts b/packages/code-review-agent/mod.ts
deleted file mode 100644
index fd0864297..000000000
--- a/packages/code-review-agent/mod.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * @executablemd/code-review-agent
- *
- * The review component graph, as the Plugin a host installs once an operator
- * has selected this package, and the parsers behind the components in it.
- *
- * The default export is the Plugin. The named exports are the library they
- * always were, and `./review-components` still publishes the graph's own
- * assembly helpers.
- */
-
-/**
- * The review graph as a Plugin — the package's default export, so a host that
- * was pointed at this module by an explicit `--plugin` selection has one
- * unambiguous value to install.
- */
-export { default } from "./src/plugin.ts";
-
-export { parseDiff } from "./src/parse-diff.ts";
-export { buildDiagnostics, parseDiagnostics } from "./src/parse-diagnostics.ts";
-export { parseDoctorResult } from "./src/parse-doctor.ts";
-export { buildDoctorResult, isOxlintCrash, summarizeDoctorProbe } from "./src/doctor.ts";
-export { normalizeDiagnostic, normalizeOxlintOutput } from "./src/parse-oxlint.ts";
-export type { DoctorEnvironment, DoctorProbeInput, DoctorProbeSummary } from "./src/doctor.ts";
-export {
- buildCleanupAnalysis,
- clusterByFile,
- buildPolicyInput,
- buildPolicyReport,
- clusterDiagnostics,
- extractEvidence,
- scoreClusters,
-} from "./src/policy.ts";
-export {
- categorizeRule,
- STRUCTURAL_RULES,
- TYPE_AWARE_RULES,
- VERBOSITY_RULES,
-} from "./src/categories.ts";
-export type {
- CleanupAnalysis,
- CleanupEvidence,
- Diagnostics,
- DiagnosticGroup,
- DiffFile,
- DiffHunk,
- DiffLine,
- DoctorResult,
- FileCluster,
- FileKind,
- OxlintDiagnostic,
- PolicyCategory,
- PolicyCluster,
- PolicyInput,
- PolicyMode,
- PolicyReport,
- PolicyScore,
- PR,
-} from "./src/types.ts";
diff --git a/packages/code-review-agent/package.json b/packages/code-review-agent/package.json
deleted file mode 100644
index b578cf1a4..000000000
--- a/packages/code-review-agent/package.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "name": "@executablemd/code-review-agent",
- "version": "0.14.1",
- "description": "The review component graph and the parsers behind it, for executable.md reviews.",
- "type": "module",
- "exports": {
- ".": "./mod.ts",
- "./review-components": "./src/review-components.ts"
- },
- "dependencies": {
- "@effectionx/fetch": "0.2.1",
- "@effectionx/fs": "0.3.0",
- "@executablemd/core": "workspace:*",
- "@executablemd/runtime": "workspace:*",
- "effection": "4.1.0"
- }
-}
diff --git a/packages/code-review-agent/src/categories.ts b/packages/code-review-agent/src/categories.ts
deleted file mode 100644
index 7c5d42041..000000000
--- a/packages/code-review-agent/src/categories.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Rule categorization for Oxlint diagnostics.
- *
- * Rules are grouped by what kind of code issue they detect.
- * A rule may appear in multiple categories.
- */
-
-export const STRUCTURAL_RULES = [
- "no-unused-vars",
- "no-empty-function",
- "no-empty-object-type",
- "no-static-only-class",
- "no-useless-empty-export",
- "no-unnecessary-type-constraint",
- "no-unnecessary-parameter-property-assignment",
- "no-unnecessary-type-arguments",
- "no-unnecessary-type-assertion",
- "no-redundant-type-constituents",
- "no-unnecessary-boolean-literal-compare",
-] as const;
-
-export const VERBOSITY_RULES = ["no-inferrable-types", "no-console", "no-debugger"] as const;
-
-export const TYPE_AWARE_RULES = [
- "no-unnecessary-type-assertion",
- "no-redundant-type-constituents",
- "no-unnecessary-type-arguments",
- "no-unnecessary-boolean-literal-compare",
-] as const;
-
-/**
- * The curated signal catalog the Oxlint policy enables explicitly, in the order
- * the category lists declare them.
- */
-export const SENSOR_RULES: readonly string[] = [
- ...new Set([...STRUCTURAL_RULES, ...VERBOSITY_RULES, ...TYPE_AWARE_RULES]),
-];
-
-const structuralSet = new Set(STRUCTURAL_RULES);
-const verbositySet = new Set(VERBOSITY_RULES);
-const typeAwareSet = new Set(TYPE_AWARE_RULES);
-
-/**
- * A rule may belong to several categories; unmatched rules belong to `other`.
- */
-export function categorizeRule(
- ruleId: string,
-): ("structural" | "verbosity" | "typeAware" | "other")[] {
- const bare = ruleId.includes("/") ? ruleId.split("/").pop()! : ruleId;
-
- const categories: ("structural" | "verbosity" | "typeAware" | "other")[] = [];
-
- if (structuralSet.has(bare)) {
- categories.push("structural");
- }
- if (verbositySet.has(bare)) {
- categories.push("verbosity");
- }
- if (typeAwareSet.has(bare)) {
- categories.push("typeAware");
- }
- if (categories.length === 0) {
- categories.push("other");
- }
-
- return categories;
-}
diff --git a/packages/code-review-agent/src/comment-review-data.ts b/packages/code-review-agent/src/comment-review-data.ts
deleted file mode 100644
index 9a801e086..000000000
--- a/packages/code-review-agent/src/comment-review-data.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-import type { PR } from "./types.ts";
-
-export interface Pair {
- comment: string;
- code: string;
- file: string;
- lineNumber: number;
-}
-
-export interface Reply {
- file: string;
- lineNumber: number;
- comment?: string;
- botCommentId?: number;
- replyText: string;
- replyId?: number;
- alreadyProcessed?: boolean;
-}
-
-export interface Location {
- file: string;
- lineNumber: number;
- comment: string;
-}
-
-export interface ReviewData {
- pairs: Pair[];
- hasPairs: boolean;
- pairsText: string;
- previousFindings: { file: string; lineNumber: number }[];
- dismissedReplies: Reply[];
- repliesForClassification: Reply[];
- hasRepliesToClassify: boolean;
- repliesText: string;
-}
-
-export function isRecord(value: unknown): value is Record {
- return typeof value === "object" && value !== null;
-}
-
-export function stringValue(record: Record, key: string): string | undefined {
- const value = record[key];
- if (typeof value === "string") {
- return value;
- }
- return undefined;
-}
-
-export function numberValue(record: Record, key: string): number | undefined {
- const value = record[key];
- if (typeof value === "number") {
- return value;
- }
- return undefined;
-}
-
-export function records(value: unknown): Record[] {
- if (!Array.isArray(value)) {
- return [];
- }
- return value.filter((entry) => isRecord(entry));
-}
-
-export function nonEmpty(value: string | undefined): value is string {
- return value !== undefined && value.length > 0;
-}
-
-export function userLogin(record: Record): string | undefined {
- const { user } = record;
- if (isRecord(user)) {
- return stringValue(user, "login");
- }
- return undefined;
-}
-
-export function userType(record: Record): string | undefined {
- const { user } = record;
- if (isRecord(user)) {
- return stringValue(user, "type");
- }
- return undefined;
-}
-
-function isBotReview(record: Record): boolean {
- return (
- userLogin(record) === "github-actions[bot]" &&
- (stringValue(record, "body")?.includes("Redundant comment") ?? false)
- );
-}
-
-export function commentPayload(value: unknown): Record[] {
- if (!Array.isArray(value) || !value.every((entry) => isRecord(entry))) {
- throw new Error("GitHub comments response was not an array");
- }
- return value;
-}
-
-export function pairsFor(pr: PR): Pair[] {
- const pairs: Pair[] = [];
- const lines = pr.added.filter((line) => !line.isTest);
- for (let index = 0; index < lines.length - 1; index++) {
- const current = lines[index].content.trim();
- const next = lines[index + 1].content.trim();
- if (current.startsWith("//") && !next.startsWith("//") && next.length > 0) {
- pairs.push({
- comment: current,
- code: next,
- file: lines[index].file,
- lineNumber: lines[index].lineNumber,
- });
- }
- }
- return pairs;
-}
-
-export function replyLocation(
- reply: Record,
- botCommentMap: Map,
-): Location | undefined {
- const parentId = numberValue(reply, "in_reply_to_id");
- if (parentId === undefined) {
- return undefined;
- }
- return botCommentMap.get(parentId);
-}
-
-interface CommentLocationEntry {
- id: number;
- location: Location;
-}
-
-function commentLine(diffHunk: string): string {
- const addedLine = diffHunk
- .split("\n")
- .filter((line) => line.startsWith("+"))
- .pop();
- return addedLine?.replace(/^\+\s*/u, "").trim() ?? "";
-}
-
-function commentLocation(comment: Record): CommentLocationEntry | undefined {
- if (!isBotReview(comment)) {
- return undefined;
- }
- const id = numberValue(comment, "id");
- const path = stringValue(comment, "path");
- const lineNumber = numberValue(comment, "original_line") ?? numberValue(comment, "line");
- if (id === undefined || !nonEmpty(path) || lineNumber === undefined) {
- return undefined;
- }
- return {
- id,
- location: {
- file: path,
- lineNumber,
- comment: commentLine(stringValue(comment, "diff_hunk") ?? ""),
- },
- };
-}
-
-export function commentLocations(comments: Record[]): {
- botCommentMap: Map;
- previousFindings: { file: string; lineNumber: number }[];
-} {
- const entries = comments
- .map((comment) => commentLocation(comment))
- .filter((entry): entry is CommentLocationEntry => entry !== undefined);
- const botCommentMap = new Map();
- for (const entry of entries) {
- botCommentMap.set(entry.id, entry.location);
- }
- return {
- botCommentMap,
- previousFindings: entries.map(({ location }) => ({
- file: location.file,
- lineNumber: location.lineNumber,
- })),
- };
-}
-
-export function formatPairs(pairs: Pair[], hasPairs: boolean): string {
- if (!hasPairs) {
- return "";
- }
- return pairs
- .map((pair, index) => `[${index}] COMMENT: ${pair.comment}\nCODE: ${pair.code}`)
- .join("\n---\n");
-}
-
-export function formatReplies(replies: Reply[]): string {
- return replies
- .map(
- (reply, index) =>
- `[${index}] FILE: ${reply.file}:${reply.lineNumber}\nREPLY: "${reply.replyText}"`,
- )
- .join("\n---\n");
-}
diff --git a/packages/code-review-agent/src/components.md b/packages/code-review-agent/src/components.md
deleted file mode 100644
index 221e25355..000000000
--- a/packages/code-review-agent/src/components.md
+++ /dev/null
@@ -1,126 +0,0 @@
-Long-form documentation for the code-review agent's TypeScript components.
-
-Seven components gather or group the evidence a review reasons about: the pull
-request and its diff, the checkout's readiness, the Oxlint sensor's diagnostics, the
-repository's source inventory, and the review comments already on the pull
-request. They read or transform evidence without writing a comment, pushing a
-branch or changing a file.
-
-They are reserved: this package claims these seven names, so a repository under
-review cannot supply its own `ReviewContext.ts` and have a review run it. The
-Markdown components that compose them are declared by this package for the same
-reason, and are documented by their own bodies rather than here.
-
-The evidence-gathering components run against the working directory. A review
-of another checkout establishes that checkout first — with `` or
-`` — and these report on whatever is current.
-
-## ReviewContext
-
-Builds the review's subject: which revisions are being compared, and what changed
-between them.
-
-```mdx
-
-```
-
-Reads the local Git checkout for the configured base and head revisions and binds
-the pull-request metadata together with the changed file paths. This is normally
-the first thing a review document does, because almost everything downstream is
-scoped to the files it names.
-
-Where a pull request is available, the metadata comes from it; where one is not,
-the revision range still describes the comparison, so a review of a local branch
-works the same way.
-
-## Doctor
-
-Reports whether this checkout can actually be analyzed, before a review depends
-on it.
-
-```mdx
-
-```
-
-Probes for the pinned Oxlint sensor and its type-aware companion, decides whether
-TypeScript analysis is available, and lists the files that would be analyzed. The
-binding carries actionable setup guidance, so a document can show a person what
-to install rather than failing with an absence.
-
-Use it to branch: a review that finds the sensor unavailable can report that
-plainly instead of reporting a clean result it never measured.
-
-## OxlintDiagnostics
-
-Runs the pinned Oxlint sensor over selected files and returns what it found.
-
-```mdx
-
-```
-
-The diagnostics are normalized, so a document reads one shape regardless of the
-sensor's own output format. `typeAware` selects the type-aware pass, which needs
-the TypeScript support `` reports on.
-
-It fails rather than returning nothing when the sensor crashes or emits output it
-cannot use. A silent empty result would be indistinguishable from a clean run,
-and a review that reported "no findings" because its sensor died is worse than
-one that stopped.
-
-## ReviewDiagnostics
-
-Group normalized Oxlint diagnostics for review policies.
-
-```mdx
-
-```
-
-Groups the sensor results by rule, applies the Doctor's import-noise filtering,
-and computes categories, density and summary through the package's existing
-`buildDiagnostics` helper. This transformation uses only its supplied values.
-
-## RepositoryInventory
-
-Counts what is in the repository, for a review that reasons about the whole tree
-rather than a diff.
-
-```mdx
-
-```
-
-Selects the repository's source paths and binds them with their file and line
-totals. Repository analysis uses it for ratios and thresholds — how much of the
-tree a signal covers, whether a count is large relative to what is there.
-
-## CommentReviewData
-
-Collects the review comments already on the pull request, and what was said back.
-
-```mdx
-
-```
-
-Binds the review-comment pairs, the findings a previous run recorded, and the
-replies grouped by the comment they answer. It is the input to deciding which of
-a previous review's findings still stand — a review that could not see its own
-earlier comments would repeat every one of them on each run.
-
-## CommentReviewState
-
-Decides what the pull request's review comments should become, given the
-classified evidence.
-
-```mdx
-
-```
-
-Takes the collected comment data together with the classification of it and binds
-the checklist text, the findings still pending, and the replies that dismiss the
-ones that are not. It decides; it does not post. What a document does with the
-result — write a comment, render a report, do nothing — is the document's.
diff --git a/packages/code-review-agent/src/components/CommentReviewData.ts b/packages/code-review-agent/src/components/CommentReviewData.ts
deleted file mode 100644
index 738efc3bf..000000000
--- a/packages/code-review-agent/src/components/CommentReviewData.ts
+++ /dev/null
@@ -1,246 +0,0 @@
-import type { Operation } from "effection";
-import { fetch } from "@effectionx/fetch";
-import type { PR } from "../types.ts";
-import { env as runtimeEnv } from "@executablemd/runtime";
-import {
- commentLocations,
- commentPayload,
- formatPairs,
- formatReplies,
- nonEmpty,
- numberValue,
- pairsFor,
- records,
- replyLocation,
- stringValue,
- type Location,
- type Pair,
- type Reply,
- type ReviewData,
- userLogin,
- userType,
-} from "../comment-review-data.ts";
-
-export const props = {
- type: "object",
- properties: { pr: { type: "object" } },
- required: ["pr"],
- additionalProperties: false,
-};
-
-const locationProperties = {
- file: { type: "string" },
- lineNumber: { type: "number" },
-};
-
-const replyProperties = {
- ...locationProperties,
- comment: { type: "string" },
- botCommentId: { type: "number" },
- replyText: { type: "string" },
- replyId: { type: "number" },
- alreadyProcessed: { type: "boolean" },
-};
-
-export const returns = {
- type: "object",
- properties: {
- pairs: {
- type: "array",
- items: {
- type: "object",
- properties: {
- comment: { type: "string" },
- code: { type: "string" },
- ...locationProperties,
- },
- required: ["comment", "code", "file", "lineNumber"],
- additionalProperties: false,
- },
- },
- hasPairs: { type: "boolean" },
- pairsText: { type: "string" },
- previousFindings: {
- type: "array",
- items: {
- type: "object",
- properties: locationProperties,
- required: ["file", "lineNumber"],
- additionalProperties: false,
- },
- },
- dismissedReplies: {
- type: "array",
- items: {
- type: "object",
- properties: replyProperties,
- required: ["file", "lineNumber", "replyText"],
- additionalProperties: false,
- },
- },
- repliesForClassification: {
- type: "array",
- items: {
- type: "object",
- properties: replyProperties,
- required: ["file", "lineNumber", "replyText"],
- additionalProperties: false,
- },
- },
- hasRepliesToClassify: { type: "boolean" },
- repliesText: { type: "string" },
- },
- required: [
- "pairs",
- "hasPairs",
- "pairsText",
- "previousFindings",
- "dismissedReplies",
- "repliesForClassification",
- "hasRepliesToClassify",
- "repliesText",
- ],
- additionalProperties: false,
-};
-
-interface CommentReviewProps {
- pr: PR;
-}
-
-function* githubApi(): Operation {
- const token = yield* runtimeEnv("GITHUB_TOKEN");
- const repository = yield* runtimeEnv("GITHUB_REPOSITORY");
- const number = yield* runtimeEnv("PR_NUMBER");
- if (!nonEmpty(token) || !nonEmpty(repository) || !nonEmpty(number)) {
- return undefined;
- }
- const [owner, name] = repository.split("/");
- if (nonEmpty(owner) && nonEmpty(name)) {
- return `https://api.github.com/repos/${owner}/${name}`;
- }
- return undefined;
-}
-
-function* fetchReplyReactions(api: string, reply: Reply): Operation {
- try {
- const reactions = records(
- yield* fetch(`${api}/pulls/comments/${reply.replyId}/reactions`).expect().json(),
- );
- return reactions.some(
- (reaction) => userLogin(reaction) === "github-actions[bot]" && reaction.content === "+1",
- );
- } catch {
- return false;
- }
-}
-
-function* collectReplies(
- api: string,
- comments: Record[],
- botCommentMap: Map,
-): Operation<{ dismissed: Reply[]; pending: Reply[] }> {
- const dismissed: Reply[] = [];
- const pending: Reply[] = [];
- for (const reply of comments) {
- const entry = yield* replyForClassification(api, reply, botCommentMap);
- if (entry !== undefined) {
- if (entry.alreadyProcessed === true) {
- dismissed.push(entry);
- } else {
- pending.push(entry);
- }
- }
- }
- return { dismissed, pending };
-}
-
-function* replyForClassification(
- api: string,
- reply: Record,
- botCommentMap: Map,
-): Operation {
- const parentId = numberValue(reply, "in_reply_to_id");
- const location = replyLocation(reply, botCommentMap);
- const replyText = stringValue(reply, "body");
- const replyId = numberValue(reply, "id");
- if (
- location === undefined ||
- userType(reply) === "Bot" ||
- replyText === undefined ||
- replyId === undefined
- ) {
- return undefined;
- }
-
- const entry: Reply = { ...location, botCommentId: parentId, replyText, replyId };
- if (yield* fetchReplyReactions(api, entry)) {
- return { ...entry, alreadyProcessed: true };
- }
- return entry;
-}
-
-interface ReviewDataParts {
- pairs: Pair[];
- previousFindings: { file: string; lineNumber: number }[];
- dismissedReplies: Reply[];
- repliesForClassification: Reply[];
-}
-
-function reviewData({
- pairs,
- previousFindings,
- dismissedReplies,
- repliesForClassification,
-}: ReviewDataParts): ReviewData {
- const hasPairs = pairs.length >= 3;
- return {
- pairs,
- hasPairs,
- pairsText: formatPairs(pairs, hasPairs),
- previousFindings,
- dismissedReplies,
- repliesForClassification,
- hasRepliesToClassify: repliesForClassification.length > 0,
- repliesText: formatReplies(repliesForClassification),
- };
-}
-
-function emptyReviewData(pairs: Pair[]): ReviewData {
- return reviewData({
- pairs,
- previousFindings: [],
- dismissedReplies: [],
- repliesForClassification: [],
- });
-}
-
-function* fetchedReviewData(api: string, number: string, pairs: Pair[]): Operation {
- const comments = commentPayload(
- yield* fetch(`${api}/pulls/${number}/comments?per_page=100`).expect().json(),
- );
- const locations = commentLocations(comments);
- const replies = yield* collectReplies(api, comments, locations.botCommentMap);
- return reviewData({
- pairs,
- previousFindings: locations.previousFindings,
- dismissedReplies: replies.dismissed,
- repliesForClassification: replies.pending,
- });
-}
-
-function* reviewDataFor(pr: PR): Operation {
- const pairs = pairsFor(pr);
- const api = yield* githubApi();
- if (api === undefined) {
- return emptyReviewData(pairs);
- }
- const number = yield* runtimeEnv("PR_NUMBER");
- if (!nonEmpty(number)) {
- return emptyReviewData(pairs);
- }
- return yield* fetchedReviewData(api, number, pairs);
-}
-
-export default function* CommentReviewData({ pr }: CommentReviewProps): Operation {
- return yield* reviewDataFor(pr);
-}
diff --git a/packages/code-review-agent/src/components/CommentReviewState.ts b/packages/code-review-agent/src/components/CommentReviewState.ts
deleted file mode 100644
index 08f6a0769..000000000
--- a/packages/code-review-agent/src/components/CommentReviewState.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import type { Operation } from "effection";
-import type { PR } from "../types.ts";
-
-const locationProperties = {
- file: { type: "string" },
- lineNumber: { type: "number" },
-};
-
-const findingProperties = {
- ...locationProperties,
- comment: { type: "string" },
- code: { type: "string" },
-};
-
-const replyProperties = {
- ...locationProperties,
- comment: { type: "string" },
- botCommentId: { type: "number" },
- replyText: { type: "string" },
- replyId: { type: "number" },
- alreadyProcessed: { type: "boolean" },
-};
-
-export const props = {
- type: "object",
- properties: {
- pr: { type: "object" },
- data: { type: "object" },
- classificationResult: { type: "string", default: "" },
- sampleResult: { type: "string", default: "" },
- },
- required: ["pr", "data", "classificationResult", "sampleResult"],
- additionalProperties: false,
-};
-
-export const returns = {
- type: "object",
- properties: {
- hasChecklist: { type: "boolean" },
- checklistMd: { type: "string" },
- hasFindings: { type: "boolean" },
- pendingFindings: {
- type: "array",
- items: {
- type: "object",
- properties: findingProperties,
- required: ["comment", "code", "file", "lineNumber"],
- additionalProperties: false,
- },
- },
- newDismissReplies: {
- type: "array",
- items: {
- type: "object",
- properties: replyProperties,
- required: ["file", "lineNumber", "replyText"],
- additionalProperties: false,
- },
- },
- },
- required: ["hasChecklist", "checklistMd", "hasFindings", "pendingFindings", "newDismissReplies"],
- additionalProperties: false,
-};
-
-interface Pair {
- comment: string;
- code: string;
- file: string;
- lineNumber: number;
-}
-
-interface Reply {
- file: string;
- lineNumber: number;
- comment?: string;
- botCommentId?: number;
- replyText: string;
- replyId?: number;
- alreadyProcessed?: boolean;
-}
-
-interface ReviewData {
- pairs: Pair[];
- previousFindings: { file: string; lineNumber: number }[];
- dismissedReplies: Reply[];
- repliesForClassification: Reply[];
-}
-
-interface CommentReviewStateProps {
- pr: PR;
- data: ReviewData;
- classificationResult: string;
- sampleResult: string;
-}
-
-interface CommentReviewStateValue {
- hasChecklist: boolean;
- checklistMd: string;
- hasFindings: boolean;
- pendingFindings: Pair[];
- newDismissReplies: Reply[];
-}
-
-type ChecklistItem =
- | { status: "applied"; file: string; lineNumber: number; label: string }
- | { status: "dismissed"; file: string; lineNumber: number; comment: string; label: string }
- | { status: "pending"; file: string; lineNumber: number; comment: string };
-
-const CLASSIFICATION_PATTERN = /\[(?\d+)\]\s*(?DISMISS|ACCEPT)/giu;
-const REDUNDANT_PATTERN = /REDUNDANT\[(?\d+)\]/gu;
-
-function key(file: string, lineNumber: number): string {
- return `${file}:${lineNumber}`;
-}
-
-function matchedIndices(input: string, pattern: RegExp): number[] {
- const indices: number[] = [];
- for (const match of input.matchAll(pattern)) {
- const index = Number(match.groups?.index);
- if (Number.isInteger(index)) {
- indices.push(index);
- }
- }
- return indices;
-}
-
-function dismissedIndices(input: string): number[] {
- const indices: number[] = [];
- for (const match of input.matchAll(CLASSIFICATION_PATTERN)) {
- if (match.groups?.action?.toUpperCase() === "DISMISS") {
- const index = Number(match.groups?.index);
- if (Number.isInteger(index)) {
- indices.push(index);
- }
- }
- }
- return indices;
-}
-
-function dismissedRepliesFor(data: ReviewData, classificationResult: string): Reply[] {
- const dismissedReplies = [...data.dismissedReplies];
- for (const index of dismissedIndices(classificationResult)) {
- const reply = data.repliesForClassification[index];
- if (reply !== undefined) {
- dismissedReplies.push(reply);
- }
- }
- return dismissedReplies;
-}
-
-function dismissedSetFor(replies: Reply[]): Set {
- return new Set(replies.map((reply) => key(reply.file, reply.lineNumber)));
-}
-
-function appliedFindingsFor(
- data: ReviewData,
- pr: PR,
- dismissedSet: Set,
-): { file: string; lineNumber: number }[] {
- const addedLineSet = new Set(pr.added.map((line) => key(line.file, line.lineNumber)));
- return data.previousFindings.filter((finding) => {
- const findingKey = key(finding.file, finding.lineNumber);
- return !addedLineSet.has(findingKey) && !dismissedSet.has(findingKey);
- });
-}
-
-function pendingFindingsFor(
- data: ReviewData,
- sampleResult: string,
- dismissedSet: Set,
-): Pair[] {
- return matchedIndices(sampleResult, REDUNDANT_PATTERN)
- .map((index) => data.pairs[index])
- .filter(
- (finding): finding is Pair =>
- finding !== undefined && !dismissedSet.has(key(finding.file, finding.lineNumber)),
- );
-}
-
-function checklistItemsFor(
- appliedFindings: { file: string; lineNumber: number }[],
- dismissedReplies: Reply[],
- pendingFindings: Pair[],
-): ChecklistItem[] {
- return [
- ...appliedFindings.map(
- (finding): ChecklistItem => ({
- status: "applied",
- file: finding.file,
- lineNumber: finding.lineNumber,
- label: "removed",
- }),
- ),
- ...dismissedReplies.map(
- (reply): ChecklistItem => ({
- status: "dismissed",
- file: reply.file,
- lineNumber: reply.lineNumber,
- comment: reply.comment ?? "",
- label: reply.replyText,
- }),
- ),
- ...pendingFindings.map(
- (finding): ChecklistItem => ({
- status: "pending",
- file: finding.file,
- lineNumber: finding.lineNumber,
- comment: finding.comment,
- }),
- ),
- ];
-}
-
-function checklistItemText(item: ChecklistItem): string {
- let checked = "x";
- if (item.status === "pending") {
- checked = " ";
- }
- if (item.status === "applied") {
- return `- [${checked}] \`${item.file}:${item.lineNumber}\` (removed)`;
- }
- if (item.status === "dismissed") {
- return `- [${checked}] \`${item.file}:${item.lineNumber}\` — \`${item.comment}\` (kept: "${item.label}")`;
- }
- return `- [${checked}] \`${item.file}:${item.lineNumber}\` — \`${item.comment}\``;
-}
-
-function checklistMarkdown(items: ChecklistItem[]): string {
- return items.map((item) => checklistItemText(item)).join("\n");
-}
-
-export default function* CommentReviewState({
- pr,
- data,
- classificationResult,
- sampleResult,
-}: CommentReviewStateProps): Operation {
- const dismissedReplies = dismissedRepliesFor(data, classificationResult);
- const dismissedSet = dismissedSetFor(dismissedReplies);
- const appliedFindings = appliedFindingsFor(data, pr, dismissedSet);
- const pendingFindings = pendingFindingsFor(data, sampleResult, dismissedSet);
- const checklistItems = checklistItemsFor(appliedFindings, dismissedReplies, pendingFindings);
-
- return {
- hasChecklist: checklistItems.length > 0,
- checklistMd: checklistMarkdown(checklistItems),
- hasFindings: pendingFindings.length > 0,
- pendingFindings,
- newDismissReplies: dismissedReplies.filter((reply) => reply.alreadyProcessed !== true),
- };
-}
diff --git a/packages/code-review-agent/src/components/Doctor.ts b/packages/code-review-agent/src/components/Doctor.ts
deleted file mode 100644
index e3e9b7427..000000000
--- a/packages/code-review-agent/src/components/Doctor.ts
+++ /dev/null
@@ -1,248 +0,0 @@
-import { type DoctorResult, type OxlintDiagnostic } from "../types.ts";
-import {
- type DoctorEnvironment,
- buildDoctorResult,
- isOxlintCrash,
- summarizeDoctorProbe,
-} from "../doctor.ts";
-import { normalizeOxlintOutput } from "../parse-oxlint.ts";
-import { exec, glob, readTextFile, stat } from "@executablemd/runtime";
-import type { Operation } from "effection";
-
-const OXLINT_PATH = ".reviews/.oxlint/oxlint";
-const TSGOLINT_PATH = ".reviews/.oxlint/tsgolint";
-const OXLINT_CONFIG = ".reviews/.oxlintrc.json";
-const NATIVE_SPECIFIER_PATTERN = /^\s*(?:import|export)\s.*?from\s+["'](?jsr:|npm:)/u;
-
-export const props = {
- type: "object",
- properties: {
- pr: { type: "object" },
- tsconfigPath: {
- type: "string",
- default: ".reviews/tsconfig.oxlint.json",
- },
- },
- required: ["pr"],
- additionalProperties: false,
-};
-
-export const returns = {
- type: "object",
- properties: {
- oxlintInstalled: { type: "boolean" },
- oxlintVersion: { type: "string" },
- tsgolintInstalled: { type: "boolean" },
- tsgolintVersion: { type: "string" },
- tsconfigExists: { type: "boolean" },
- nodeModulesExists: { type: "boolean" },
- typeAwareAvailable: { type: "boolean" },
- filesAnalyzed: { type: "number" },
- filesSkipped: { type: "number" },
- importErrors: { type: "number" },
- availableRuleIds: { type: "array", items: { type: "string" } },
- bloatRulesAvailable: { type: "array", items: { type: "string" } },
- bloatRulesMissing: { type: "array", items: { type: "string" } },
- recommendation: { type: "string" },
- nativeSpecifiers: {
- type: "object",
- properties: {
- count: { type: "number" },
- files: { type: "array", items: { type: "string" } },
- jsr: { type: "number" },
- npm: { type: "number" },
- },
- required: ["count", "files", "jsr", "npm"],
- additionalProperties: false,
- },
- },
- required: [
- "oxlintInstalled",
- "oxlintVersion",
- "tsgolintInstalled",
- "tsgolintVersion",
- "tsconfigExists",
- "nodeModulesExists",
- "typeAwareAvailable",
- "filesAnalyzed",
- "filesSkipped",
- "importErrors",
- "availableRuleIds",
- "bloatRulesAvailable",
- "bloatRulesMissing",
- "recommendation",
- "nativeSpecifiers",
- ],
- additionalProperties: false,
-};
-
-interface DoctorProps {
- pr: object;
- tsconfigPath?: string;
-}
-
-function* version(command: string[]): Operation {
- const result = yield* exec({ command });
- if (result.exitCode === 0) {
- return result.stdout.trim();
- }
- return "";
-}
-
-interface NativeSpecifierCounts {
- jsr: number;
- npm: number;
-}
-
-function nativeSpecifierCounts(source: string): NativeSpecifierCounts {
- const counts = { jsr: 0, npm: 0 };
- for (const line of source.split(/\r?\n/u)) {
- const scheme = NATIVE_SPECIFIER_PATTERN.exec(line)?.groups?.scheme;
- if (scheme === "jsr:") {
- counts.jsr++;
- }
- if (scheme === "npm:") {
- counts.npm++;
- }
- }
- return counts;
-}
-
-interface NativeSpecifierFile {
- path: string;
- counts: NativeSpecifierCounts;
-}
-
-function* nativeSpecifierFile(entry: {
- isFile: boolean;
- path: string;
-}): Operation {
- if (!entry.isFile) {
- return undefined;
- }
- return { path: entry.path, counts: nativeSpecifierCounts(yield* readTextFile(entry.path)) };
-}
-
-function nativeSpecifierValue(files: NativeSpecifierFile[]): DoctorResult["nativeSpecifiers"] {
- let jsr = 0;
- let npm = 0;
- const paths: string[] = [];
- for (const file of files) {
- jsr += file.counts.jsr;
- npm += file.counts.npm;
- if (file.counts.jsr + file.counts.npm > 0) {
- paths.push(file.path);
- }
- }
- return { count: jsr + npm, files: [...new Set(paths)], jsr, npm };
-}
-
-function* nativeSpecifierFiles(
- entries: { isFile: boolean; path: string }[],
-): Operation {
- const files: NativeSpecifierFile[] = [];
- for (const entry of entries) {
- const file = yield* nativeSpecifierFile(entry);
- if (file !== undefined) {
- files.push(file);
- }
- }
- return files;
-}
-
-function* nativeSpecifierSummary(): Operation {
- const entries = yield* glob({
- root: ".",
- patterns: ["packages/**/*.ts", "durable-effects/**/*.ts"],
- exclude: ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"],
- });
- return nativeSpecifierValue(yield* nativeSpecifierFiles(entries));
-}
-
-function probeCommand(tsconfigPath: string): string[] {
- return [
- OXLINT_PATH,
- "--config",
- OXLINT_CONFIG,
- "--type-aware",
- "--tsconfig",
- tsconfigPath,
- "--format",
- "json",
- ];
-}
-
-function probeFailure(exitCode: number, stdout: string, stderr: string): Error | undefined {
- if (exitCode > 1 && !isOxlintCrash(stderr)) {
- return new Error(stderr || `Oxlint probe failed with exit code ${exitCode}`);
- }
- if (stdout.trim().length === 0 && exitCode !== 0 && !isOxlintCrash(stderr)) {
- return new Error(stderr || "Oxlint probe returned no JSON output");
- }
- return undefined;
-}
-
-function probeDiagnostics(stdout: string): OxlintDiagnostic[] {
- if (stdout.trim().length === 0) {
- return [];
- }
- return normalizeOxlintOutput(stdout);
-}
-
-function* probeTypeAware(canProbe: boolean, tsconfigPath: string) {
- if (!canProbe) {
- return summarizeDoctorProbe({ diagnostics: [], stderr: "", exitCode: 2 });
- }
-
- const result = yield* exec({
- command: probeCommand(tsconfigPath),
- env: { OXLINT_TSGOLINT_PATH: TSGOLINT_PATH },
- });
- const failure = probeFailure(result.exitCode, result.stdout, result.stderr);
- if (failure !== undefined) {
- throw failure;
- }
- const diagnostics = probeDiagnostics(result.stdout);
- return summarizeDoctorProbe({
- diagnostics,
- stderr: result.stderr,
- exitCode: result.exitCode,
- });
-}
-
-function* toolVersion(path: string): Operation {
- const result = yield* stat(path);
- if (!result.isFile) {
- return "";
- }
- return yield* version([path, "--version"]);
-}
-
-function* doctorEnvironment(tsconfigPath: string): Operation {
- const oxlint = yield* stat(OXLINT_PATH);
- const tsgolint = yield* stat(TSGOLINT_PATH);
- const tsconfig = yield* stat(tsconfigPath);
- const nodeModules = yield* stat("node_modules");
- return {
- oxlintInstalled: oxlint.isFile,
- oxlintVersion: yield* toolVersion(OXLINT_PATH),
- tsgolintInstalled: tsgolint.isFile,
- tsgolintVersion: yield* toolVersion(TSGOLINT_PATH),
- tsconfigExists: tsconfig.isFile,
- nodeModulesExists: nodeModules.isDirectory,
- nativeSpecifiers: yield* nativeSpecifierSummary(),
- };
-}
-
-export default function* Doctor({
- tsconfigPath = ".reviews/tsconfig.oxlint.json",
-}: DoctorProps): Operation {
- const environment = yield* doctorEnvironment(tsconfigPath);
- const canProbe =
- environment.oxlintInstalled &&
- environment.tsgolintInstalled &&
- environment.tsconfigExists &&
- environment.nodeModulesExists;
- const probe = yield* probeTypeAware(canProbe, tsconfigPath);
- return buildDoctorResult(environment, probe);
-}
diff --git a/packages/code-review-agent/src/components/OxlintDiagnostics.ts b/packages/code-review-agent/src/components/OxlintDiagnostics.ts
deleted file mode 100644
index eb11abc64..000000000
--- a/packages/code-review-agent/src/components/OxlintDiagnostics.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { type OxlintDiagnostic } from "../types.ts";
-import { normalizeOxlintOutput } from "../parse-oxlint.ts";
-import { exec } from "@executablemd/runtime";
-import type { Operation } from "effection";
-
-const OXLINT_PATH = ".reviews/.oxlint/oxlint";
-const OXLINT_CONFIG = ".reviews/.oxlintrc.json";
-const OXLINT_CRASH_PATTERN = /panic|oom|out of memory|fatal|segmentation fault/iu;
-
-export const props = {
- type: "object",
- properties: {
- files: { type: "array", items: { type: "string" } },
- typeAware: { type: "boolean", default: false },
- tsconfigPath: { type: "string", default: ".reviews/tsconfig.oxlint.json" },
- },
- required: ["files"],
- additionalProperties: false,
-};
-
-export const returns = {
- type: "array",
- items: {
- type: "object",
- properties: {
- message: { type: "string" },
- ruleId: { type: "string" },
- severity: { type: "string" },
- file: { type: "string" },
- line: { type: "number" },
- column: { type: "number" },
- },
- required: ["message", "ruleId", "severity", "file", "line", "column"],
- additionalProperties: false,
- },
-};
-
-interface OxlintDiagnosticsProps {
- files: string[];
- typeAware?: boolean;
- tsconfigPath?: string;
-}
-
-interface OxlintCommand {
- command: string[];
- environment: Record;
-}
-
-function oxlintCommand(files: string[], typeAware: boolean, tsconfigPath: string): OxlintCommand {
- const command = [OXLINT_PATH, "--config", OXLINT_CONFIG, "--format", "json"];
- const environment: Record = {};
- if (typeAware) {
- command.push("--type-aware", "--tsconfig", tsconfigPath);
- environment.OXLINT_TSGOLINT_PATH = ".reviews/.oxlint/tsgolint";
- }
- command.push(...files);
- return { command, environment };
-}
-
-function oxlintFailure(exitCode: number, stdout: string, stderr: string): Error | undefined {
- if (exitCode > 1) {
- return new Error(stderr || `Oxlint failed with exit code ${exitCode}`);
- }
- if (OXLINT_CRASH_PATTERN.test(stderr)) {
- return new Error(stderr);
- }
- if (stdout.trim().length === 0) {
- return new Error(stderr || "Oxlint returned no JSON output");
- }
- return undefined;
-}
-
-function* runOxlint(
- files: string[],
- typeAware: boolean,
- tsconfigPath: string,
-): Operation {
- if (files.length === 0) {
- return [];
- }
-
- const { command, environment } = oxlintCommand(files, typeAware, tsconfigPath);
- const result = yield* exec({ command, env: environment });
- const failure = oxlintFailure(result.exitCode, result.stdout, result.stderr);
- if (failure !== undefined) {
- throw failure;
- }
- return normalizeOxlintOutput(result.stdout, files);
-}
-
-export default function* OxlintDiagnostics({
- files,
- typeAware = false,
- tsconfigPath = ".reviews/tsconfig.oxlint.json",
-}: OxlintDiagnosticsProps): Operation {
- return yield* runOxlint(files, typeAware, tsconfigPath);
-}
diff --git a/packages/code-review-agent/src/components/RepositoryInventory.ts b/packages/code-review-agent/src/components/RepositoryInventory.ts
deleted file mode 100644
index c60d7448f..000000000
--- a/packages/code-review-agent/src/components/RepositoryInventory.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { glob, readTextFile } from "@executablemd/runtime";
-import type { Operation } from "effection";
-
-export const props = {
- type: "object",
- properties: {},
- additionalProperties: false,
-};
-
-export const returns = {
- type: "object",
- properties: {
- fileList: { type: "array", items: { type: "string" } },
- fileCount: { type: "number" },
- lineCount: { type: "number" },
- },
- required: ["fileList", "fileCount", "lineCount"],
- additionalProperties: false,
-};
-
-interface RepositoryInventoryValue {
- fileList: string[];
- fileCount: number;
- lineCount: number;
-}
-
-function lineCount(source: string): number {
- if (source.length === 0) {
- return 0;
- }
- const lines = source.split(/\r?\n/u);
- if (source.endsWith("\n")) {
- return lines.length - 1;
- }
- return lines.length;
-}
-
-function filePaths(entries: { isFile: boolean; path: string }[]): string[] {
- const paths = entries.filter((entry) => entry.isFile).map((entry) => entry.path);
- const ordered: string[] = [];
- for (const path of paths) {
- const index = ordered.findIndex((candidate) => candidate > path);
- if (index === -1) {
- ordered.push(path);
- } else {
- ordered.splice(index, 0, path);
- }
- }
- return ordered;
-}
-
-function* totalLineCount(paths: string[]): Operation {
- let total = 0;
- for (const path of paths) {
- total += lineCount(yield* readTextFile(path));
- }
- return total;
-}
-
-export default function* RepositoryInventory(
- _props: Record,
-): Operation {
- const entries = yield* glob({
- root: ".",
- patterns: ["durable-effects/**/*.ts", "packages/**/*.ts"],
- exclude: ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"],
- });
- const fileList = filePaths(entries);
- const totalLines = yield* totalLineCount(fileList);
- return { fileList, fileCount: fileList.length, lineCount: totalLines };
-}
diff --git a/packages/code-review-agent/src/components/ReviewContext.ts b/packages/code-review-agent/src/components/ReviewContext.ts
deleted file mode 100644
index 3b9dcaabc..000000000
--- a/packages/code-review-agent/src/components/ReviewContext.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import type { PR } from "../types.ts";
-import { parseDiff } from "../parse-diff.ts";
-import { exec, env as runtimeEnv } from "@executablemd/runtime";
-import { fetch } from "@effectionx/fetch";
-import type { Operation } from "effection";
-
-export const props = {
- type: "object",
- properties: {},
- additionalProperties: false,
-};
-
-export const returns = {
- type: "object",
- properties: {
- pr: { type: "object" },
- changedFilePaths: { type: "array", items: { type: "string" } },
- },
- required: ["pr", "changedFilePaths"],
- additionalProperties: false,
-};
-
-interface PullRequestResponse {
- body?: unknown;
-}
-
-interface ReviewContextValue {
- pr: Omit & { directories: string[] };
- changedFilePaths: string[];
-}
-
-interface ReviewInputs {
- base: string;
- head: string;
- title: string;
- number: string;
- repository: string;
- localBody: string;
-}
-
-function isPullRequestResponse(value: unknown): value is PullRequestResponse {
- return typeof value === "object" && value !== null;
-}
-
-function* reviewInputs(): Operation {
- return {
- base: (yield* runtimeEnv("BASE_SHA")) ?? "HEAD~1",
- head: (yield* runtimeEnv("HEAD_SHA")) ?? "HEAD",
- title: (yield* runtimeEnv("PR_TITLE")) ?? "",
- number: (yield* runtimeEnv("PR_NUMBER")) ?? "",
- repository: (yield* runtimeEnv("GITHUB_REPOSITORY")) ?? "",
- localBody: (yield* runtimeEnv("PR_BODY")) ?? "",
- };
-}
-
-function* gitOutput(command: string[], description: string): Operation {
- const result = yield* exec({ command });
- if (result.exitCode !== 0) {
- throw new Error(result.stderr || `${description} failed with exit code ${result.exitCode}`);
- }
- return result.stdout;
-}
-
-function* pullBody(repository: string, number: string, fallback: string): Operation {
- if (fallback || !repository || !number) {
- return fallback;
- }
-
- const payload: unknown = yield* fetch(
- `https://api.github.com/repos/${repository}/pulls/${number}`,
- )
- .expect()
- .json();
- if (!isPullRequestResponse(payload)) {
- return fallback;
- }
- if (typeof payload.body === "string") {
- return payload.body;
- }
- return fallback;
-}
-
-export default function* ReviewContext(
- _props: Record,
-): Operation {
- const inputs = yield* reviewInputs();
- const range = `${inputs.base}...${inputs.head}`;
- const diff = yield* gitOutput(["git", "diff", range], "git diff");
- const names = yield* gitOutput(["git", "diff", "--name-status", range], "git diff --name-status");
- const body = yield* pullBody(inputs.repository, inputs.number, inputs.localBody);
- const parsed = parseDiff(diff, names, {
- title: inputs.title,
- body,
- number: inputs.number,
- });
- const pr = { ...parsed, directories: [...parsed.directories] };
- return { pr, changedFilePaths: pr.files.map((file) => file.path) };
-}
diff --git a/packages/code-review-agent/src/components/ReviewDiagnostics.ts b/packages/code-review-agent/src/components/ReviewDiagnostics.ts
deleted file mode 100644
index 84c33b425..000000000
--- a/packages/code-review-agent/src/components/ReviewDiagnostics.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { Operation } from "effection";
-import type { Diagnostics, DoctorResult, OxlintDiagnostic, PR } from "../types.ts";
-import { buildDiagnostics } from "../parse-diagnostics.ts";
-import { returns as rawDiagnosticsSchema } from "./OxlintDiagnostics.ts";
-import { returns as doctorSchema } from "./Doctor.ts";
-
-export const props = {
- type: "object",
- properties: {
- raw: rawDiagnosticsSchema,
- pr: { type: "object" },
- doctor: doctorSchema,
- },
- required: ["raw", "pr", "doctor"],
- additionalProperties: false,
-};
-
-const groupSchema = {
- type: "object",
- properties: {
- ruleId: { type: "string" },
- count: { type: "number" },
- files: { type: "array", items: { type: "string" } },
- instances: rawDiagnosticsSchema,
- },
- required: ["ruleId", "count", "files", "instances"],
- additionalProperties: false,
-};
-const groupsSchema = { type: "array", items: groupSchema };
-
-export const returns = {
- type: "object",
- properties: {
- groups: groupsSchema,
- total: { type: "number" },
- fileCount: { type: "number" },
- ruleCount: { type: "number" },
- byCategory: {
- type: "object",
- properties: {
- structural: groupsSchema,
- verbosity: groupsSchema,
- typeAware: groupsSchema,
- other: groupsSchema,
- },
- required: ["structural", "verbosity", "typeAware", "other"],
- additionalProperties: false,
- },
- summary: { type: "string" },
- density: { type: "number" },
- },
- required: ["groups", "total", "fileCount", "ruleCount", "byCategory", "summary", "density"],
- additionalProperties: false,
-};
-
-interface ReviewDiagnosticsProps {
- raw: OxlintDiagnostic[];
- pr: PR;
- doctor: DoctorResult;
-}
-
-export default function* ReviewDiagnostics({
- raw,
- pr,
- doctor,
-}: ReviewDiagnosticsProps): Operation {
- return buildDiagnostics(raw, pr, doctor);
-}
diff --git a/packages/code-review-agent/src/doctor.ts b/packages/code-review-agent/src/doctor.ts
deleted file mode 100644
index b9e351639..000000000
--- a/packages/code-review-agent/src/doctor.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import type { DoctorResult, OxlintDiagnostic } from "./types.ts";
-import { SENSOR_RULES, TYPE_AWARE_RULES } from "./categories.ts";
-
-const TYPE_AWARE_RULE_SET = new Set(TYPE_AWARE_RULES);
-const IMPORT_NOISE_RATIO = 0.3;
-const OXLINT_CRASH_PATTERN = /panic|oom|out of memory|fatal|segmentation fault/iu;
-
-export interface DoctorProbeSummary {
- typeAwareAvailable: boolean;
- filesAnalyzed: number;
- filesSkipped: number;
- importErrors: number;
- availableRuleIds: string[];
- bloatRulesAvailable: string[];
- bloatRulesMissing: string[];
- recommendation: DoctorResult["recommendation"];
-}
-
-export interface DoctorProbeInput {
- diagnostics: readonly OxlintDiagnostic[];
- stderr: string;
- exitCode: number;
-}
-
-function bloatRulesFor(typeAwareAvailable: boolean): {
- available: string[];
- missing: string[];
-} {
- if (typeAwareAvailable) {
- return { available: [...SENSOR_RULES], missing: [] };
- }
- return {
- available: SENSOR_RULES.filter((rule) => !TYPE_AWARE_RULE_SET.has(rule)),
- missing: [...TYPE_AWARE_RULES],
- };
-}
-
-function recommendationFor(
- typeAwareAvailable: boolean,
- ratio: number,
-): DoctorResult["recommendation"] {
- if (!typeAwareAvailable) {
- return "syntax-only";
- }
- if (ratio < IMPORT_NOISE_RATIO) {
- return "type-aware";
- }
- return "type-aware-filtered";
-}
-
-function noiseRatio(total: number, importNoise: number): number {
- if (total === 0) {
- return 0;
- }
- return importNoise / total;
-}
-
-function availableRuleIds(diagnostics: readonly OxlintDiagnostic[]): string[] {
- return [...new Set(diagnostics.map((diagnostic) => diagnostic.ruleId).filter(Boolean))].sort();
-}
-
-function isImportNoise(diagnostic: OxlintDiagnostic): boolean {
- return (
- diagnostic.message.includes("Cannot find module") ||
- diagnostic.message.includes("cannot find") ||
- diagnostic.ruleId.includes("import")
- );
-}
-
-export function isOxlintCrash(stderr: string): boolean {
- return OXLINT_CRASH_PATTERN.test(stderr);
-}
-
-/** Summarize a bounded type-aware probe without retaining its raw output. */
-export function summarizeDoctorProbe(input: DoctorProbeInput): DoctorProbeSummary {
- const importNoise = input.diagnostics.filter(isImportNoise);
- const files = new Set(input.diagnostics.map((diagnostic) => diagnostic.file).filter(Boolean));
- const skippedFiles = new Set(importNoise.map((diagnostic) => diagnostic.file).filter(Boolean));
- const available = input.exitCode <= 1 && !isOxlintCrash(input.stderr);
- const ratio = noiseRatio(input.diagnostics.length, importNoise.length);
- const rules = bloatRulesFor(available);
-
- return {
- typeAwareAvailable: available,
- filesAnalyzed: files.size,
- filesSkipped: skippedFiles.size,
- importErrors: importNoise.length,
- availableRuleIds: availableRuleIds(input.diagnostics),
- bloatRulesAvailable: rules.available,
- bloatRulesMissing: rules.missing,
- recommendation: recommendationFor(available, ratio),
- };
-}
-
-export interface DoctorEnvironment {
- oxlintInstalled: boolean;
- oxlintVersion: string;
- tsgolintInstalled: boolean;
- tsgolintVersion: string;
- tsconfigExists: boolean;
- nodeModulesExists: boolean;
- nativeSpecifiers: DoctorResult["nativeSpecifiers"];
-}
-
-/** Construct the durable Doctor value from bounded environment observations. */
-export function buildDoctorResult(
- environment: DoctorEnvironment,
- probe: DoctorProbeSummary,
-): DoctorResult {
- return {
- ...environment,
- ...probe,
- nativeSpecifiers: {
- count: environment.nativeSpecifiers.count,
- files: [...environment.nativeSpecifiers.files],
- jsr: environment.nativeSpecifiers.jsr,
- npm: environment.nativeSpecifiers.npm,
- },
- };
-}
diff --git a/packages/code-review-agent/src/documents/components/AbstractionNames.md b/packages/code-review-agent/src/documents/components/AbstractionNames.md
deleted file mode 100644
index ddd47703c..000000000
--- a/packages/code-review-agent/src/documents/components/AbstractionNames.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- pattern:
- type: string
- default: "factory|abstract|base|provider|strategy|adapter|helper|util"
- severity:
- type: string
- default: warning
- message:
- type: string
- default: "New abstraction files: {names}. Verify 3+ consumers."
- required: [pr]
- additionalProperties: false
-description: >-
- Flag new files whose names suggest a speculative abstraction. `` reports created TypeScript files named like factories, adapters or
- helpers, so a reviewer can ask what consumes them.
----
-
-```ts eval
-const re = new RegExp(props.pattern, "i");
-const suspicious = props.pr.created
- .filter(f => f.path.endsWith(".ts") && !f.isTest && !f.isTypeDeclaration)
- .filter(f => re.test(f.path));
-const triggered = suspicious.length > 0;
-const resolvedMessage = props.message.replace(
- "{names}", suspicious.map(f => f.path).join(", ")
-);
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/CleanupIssues.md b/packages/code-review-agent/src/documents/components/CleanupIssues.md
deleted file mode 100644
index 8f0290820..000000000
--- a/packages/code-review-agent/src/documents/components/CleanupIssues.md
+++ /dev/null
@@ -1,159 +0,0 @@
----
-props:
- type: object
- properties:
- cleanupAnalysis:
- type: object
- diagnostics:
- type: object
- required: [cleanupAnalysis, diagnostics]
- additionalProperties: false
-description: >-
- File and retire GitHub issues for the repository's worst cleanup clusters.
- `` keeps one
- labelled issue per file in the top five and closes the ones that dropped out. It
- does nothing without `GITHUB_TOKEN` and `GITHUB_REPOSITORY`.
----
-
-```ts persist eval
-import { env as runtimeEnv } from "@executablemd/runtime";
-
-function* githubConfiguration() {
- const token = yield* runtimeEnv("GITHUB_TOKEN");
- const repo = yield* runtimeEnv("GITHUB_REPOSITORY");
- if (!token || !repo) {
- return undefined;
- }
- const [owner, repoName] = repo.split("/");
- return { api: `https://api.github.com/repos/${owner}/${repoName}` };
-}
-
-const github = yield* githubConfiguration();
-if (!github) {
- return "";
-}
-
-const { api } = github;
-
-const LABEL = "cleanup";
-const TOP_N = 5;
-
-// 1. Ensure label exists
-const labelResponse = yield* fetch(`${api}/labels/${LABEL}`);
-if (labelResponse.status === 404) {
- yield* fetch(`${api}/labels`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- name: LABEL,
- description: "Auto-generated cleanup finding from repo analysis",
- color: "d4c5f9",
- }),
- }).expect();
-}
-
-// 2. Fetch all open issues with cleanup label
-const existingIssues = [];
-let page = 1;
-while (true) {
- const batch = yield* fetch(
- `${api}/issues?labels=${LABEL}&state=open&per_page=100&page=${page}`,
- ).expect().json();
-
- if (!Array.isArray(batch) || batch.length === 0) break;
- existingIssues.push(...batch);
- if (batch.length < 100) break;
- page++;
-}
-
-// 3. Build marker → issue map
-const markerRe = //;
-const issuesByFile = new Map();
-for (const issue of existingIssues) {
- const match = issue.body ? issue.body.match(markerRe) : null;
- if (match) {
- issuesByFile.set(match[1], issue);
- }
-}
-
-// 4. Process top 5 clusters
-const topClusters = props.cleanupAnalysis.fileClusters.slice(0, TOP_N);
-const topFiles = new Set(topClusters.map(c => c.file));
-
-let created = 0;
-let updated = 0;
-let closed = 0;
-
-for (const cluster of topClusters) {
- const marker = ``;
- const title = `cleanup(${cluster.kind}): ${cluster.file} — ${cluster.totalViolations} violations, ${cluster.coOccurrence} rules`;
-
- const bodyLines = [
- marker,
- "",
- "## Cleanup: `" + cluster.file + "`",
- "",
- "| Metric | Value |",
- "|---|---|",
- "| Score | " + cluster.score + " |",
- "| Kind | " + cluster.kind + " |",
- "| Violations | " + cluster.totalViolations + " |",
- "| Co-occurring rules | " + cluster.coOccurrence + " |",
- "| Structural | " + cluster.categories.structural + " |",
- "| Verbosity | " + cluster.categories.verbosity + " |",
- "| Type-aware | " + cluster.categories.typeAware + " |",
- "| Other | " + cluster.categories.other + " |",
- "",
- "**Top rules:** " + cluster.ruleIds.slice(0, 15).join(", ") + (cluster.ruleIds.length > 15 ? " (+" + (cluster.ruleIds.length - 15) + " more)" : ""),
- "",
- "---",
- "Generated by executable.md repo analysis. Last updated: " + new Date().toISOString() + "",
- ];
- const body = bodyLines.join("\n");
-
- const existing = issuesByFile.get(cluster.file);
-
- if (existing) {
- yield* fetch(api + "/issues/" + existing.number, {
- method: "PATCH",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ title, body }),
- }).expect();
- updated++;
- } else {
- yield* fetch(api + "/issues", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- title,
- body,
- labels: [LABEL],
- }),
- }).expect();
- created++;
- }
-}
-
-// 5. Close issues whose files are no longer in top 5
-for (const [file, issue] of issuesByFile.entries()) {
- if (!topFiles.has(file)) {
- yield* fetch(api + "/issues/" + issue.number + "/comments", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- body: "Resolved — file no longer in top-5 cleanup targets. Closing automatically.",
- }),
- }).expect();
-
- yield* fetch(api + "/issues/" + issue.number, {
- method: "PATCH",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ state: "closed" }),
- }).expect();
- closed++;
- }
-}
-
-const summary = "Cleanup issues: created " + created + ", updated " + updated + ", closed " + closed;
-return summary;
-```
diff --git a/packages/code-review-agent/src/documents/components/CommentReview.md b/packages/code-review-agent/src/documents/components/CommentReview.md
deleted file mode 100644
index 682146bb2..000000000
--- a/packages/code-review-agent/src/documents/components/CommentReview.md
+++ /dev/null
@@ -1,77 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- required: [pr]
- additionalProperties: false
-description: >-
- Review the pull request's code comments for ones that only restate the code.
- `` asks the model in scope which are redundant, reads
- replies to earlier suggestions, and renders what remains outstanding. When findings
- remain and the required GitHub environment is present, it also reconciles inline
- removal suggestions on the pull request.
----
-
-
-
-
-
-
-
-
-For each reply to an automated code review suggestion, classify the
-user's intent. They are replying to a suggestion to remove a redundant
-code comment.
-
-DISMISS — the user wants to keep the comment (any reason)
-ACCEPT — the user agrees the comment should be removed
-
-Format: [index] DISMISS or [index] ACCEPT
-
-{reviewData.repliesText}
-
-
-
-
-
-
-
-
-
-
-
-Review these comment/code pairs. List ONLY obvious/redundant ones
-where the comment restates what the code does.
-
-Format each finding as: REDUNDANT[index]: comment text
-
-If none are obvious: "No obvious comments found."
-
-{reviewData.pairsText}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{state.checklistMd}
-
-
diff --git a/packages/code-review-agent/src/documents/components/ConfigSourceMix.md b/packages/code-review-agent/src/documents/components/ConfigSourceMix.md
deleted file mode 100644
index d043afb69..000000000
--- a/packages/code-review-agent/src/documents/components/ConfigSourceMix.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- minFiles:
- type: number
- default: 5
- severity:
- type: string
- default: warning
- message:
- type: string
- default: "PR mixes config and source changes."
- required: [pr]
- additionalProperties: false
-description: >-
- Flag a change that mixes configuration and source edits. `` reports when both appear in a pull request larger than `minFiles`.
----
-
-```ts eval
-const hasConfig = props.pr.files.some(f => f.isConfig);
-const hasSource = props.pr.files.some(f =>
- !f.isConfig && !f.isTest && !f.isTypeDeclaration
-);
-const triggered = hasConfig && hasSource && props.pr.stats.totalFiles > props.minFiles;
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/DeepInfraProvider.md b/packages/code-review-agent/src/documents/components/DeepInfraProvider.md
deleted file mode 100644
index 870855799..000000000
--- a/packages/code-review-agent/src/documents/components/DeepInfraProvider.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-props:
- type: object
- properties:
- model:
- type: string
- required: [model]
- additionalProperties: false
-description: >-
- Answer this document's `` calls from DeepInfra. `…` handles samples that name that model
- and samples that name none, and passes the rest outward. It requires
- `DEEPINFRA_TOKEN`.
-context: >-
- The Markdown whose samples this provider answers.
----
-
-```ts persist eval
-import { env as runtimeEnv } from "@executablemd/runtime";
-
-yield* Sample.around({
- *sample([context], next) {
- if (context.model !== undefined && context.model !== props.model) {
- return yield* next(context);
- }
-
- const messages = [];
- if (context.system) {
- messages.push({ role: "system", content: context.system });
- }
- messages.push({ role: "user", content: context.content });
- const token = yield* runtimeEnv("DEEPINFRA_TOKEN");
- if (!token) {
- throw new Error("DeepInfraProvider requires DEEPINFRA_TOKEN");
- }
-
- const result = yield* fetch("https://api.deepinfra.com/v1/openai/chat/completions", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${token}`,
- },
- body: JSON.stringify({ model: props.model, messages, temperature: 0, max_tokens: 4096 }),
- })
- .expect()
- .json();
-
- const content = result.choices?.[0]?.message?.content;
- if (typeof content !== "string") {
- throw new Error("DeepInfra response did not contain model content");
- }
- return content;
- },
-}, { at: 'min' });
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/DescriptionCheck.md b/packages/code-review-agent/src/documents/components/DescriptionCheck.md
deleted file mode 100644
index 0398a8867..000000000
--- a/packages/code-review-agent/src/documents/components/DescriptionCheck.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- minLength:
- type: number
- default: 50
- severity:
- type: string
- default: error
- message:
- type: string
- default: "PR description must explain what and why."
- required: [pr]
- additionalProperties: false
-description: >-
- Require a pull-request description that explains the change. `` reports a body shorter than `minLength`.
----
-
-
diff --git a/packages/code-review-agent/src/documents/components/EnsureOxlint.md b/packages/code-review-agent/src/documents/components/EnsureOxlint.md
deleted file mode 100644
index 84cef645f..000000000
--- a/packages/code-review-agent/src/documents/components/EnsureOxlint.md
+++ /dev/null
@@ -1,99 +0,0 @@
----
-props:
- type: object
- properties:
- dir:
- type: string
- default: ".reviews/.oxlint"
- additionalProperties: false
-description: >-
- Install the review's pinned Oxlint and tsgolint binaries. ``
- downloads them into the sensor directory and verifies each against a known checksum,
- so a review measures the version it expects. It fails on a platform it ships no
- build for.
----
-
-```ts eval
-import { platform } from "@executablemd/runtime";
-
-const OXLINT_TAG = "apps_v1.74.0";
-const TSGOLINT_VERSION = "0.25.0";
-const host = yield* platform();
-const target = {
- oxlintOs: host.os === "darwin" ? "apple-darwin" : host.os === "linux" ? "unknown-linux-gnu" : "",
- tsgolintOs: host.os === "darwin" ? "darwin" : host.os === "linux" ? "linux" : "",
- oxlintArch: host.arch === "arm64" || host.arch === "aarch64"
- ? "aarch64"
- : host.arch === "x86_64" || host.arch === "amd64" || host.arch === "x64"
- ? "x86_64"
- : "",
- tsgolintArch: host.arch === "arm64" || host.arch === "aarch64"
- ? "arm64"
- : host.arch === "x86_64" || host.arch === "amd64" || host.arch === "x64"
- ? "x64"
- : "",
-};
-if (!target.oxlintOs || !target.oxlintArch) {
- throw new Error(`EnsureOxlint: unsupported platform ${host.os}/${host.arch}`);
-}
-
-const oxlintHashes = {
- "aarch64-apple-darwin": "768a2d00e7e0a95cbf89837086f475d25dc1a1ba605b8831fb5a1db6d590a643",
- "x86_64-apple-darwin": "04ae38d56ae4990ac96320c03f05f38cf1103b5fe64b08cd7203b87e767b45b4",
- "aarch64-unknown-linux-gnu": "ced0d2433bda2b4295e1ab93b40c3f24224713c32a44e13abcda656590dba1cb",
- "x86_64-unknown-linux-gnu": "fd3ed5d2dc55ab6f7a243583c69dd5da4ac97cd1f6e10321225ca6343c9451a9",
-};
-const tsgolintHashes = {
- "darwin-arm64": "3ad51d1b88070b491b81a4f5c6169148914127b9da65f8087823b25568431e1e",
- "darwin-x64": "6b78caa20db383c055cead96724aeafab3cd277b00b1f95c6f56b4bfcf22fd60",
- "linux-arm64": "20bcbab4bb37dd396102566740ee39e67d1e3d16d06096802180426c022bc414",
- "linux-x64": "f6ea083842395d7439eadbbbf380f23793a1fa890fbda92013ee0f6033e75630",
-};
-const oxlintKey = `${target.oxlintArch}-${target.oxlintOs}`;
-const tsgolintKey = `${target.tsgolintOs}-${target.tsgolintArch}`;
-const oxlintUrl = `https://github.com/oxc-project/oxc/releases/download/${OXLINT_TAG}/oxlint-${target.oxlintArch}-${target.oxlintOs}.tar.gz`;
-const tsgolintUrl = `https://registry.npmjs.org/@oxlint-tsgolint/${target.tsgolintOs}-${target.tsgolintArch}/-/${target.tsgolintOs}-${target.tsgolintArch}-${TSGOLINT_VERSION}.tgz`;
-const oxlintSha = oxlintHashes[oxlintKey];
-const tsgolintSha = tsgolintHashes[tsgolintKey];
-if (!oxlintSha || !tsgolintSha) {
- throw new Error(`EnsureOxlint: no checksum for ${oxlintKey}/${tsgolintKey}`);
-}
-```
-
-```bash silent exec
-set -euo pipefail
-DIR="{props.dir}"
-mkdir -p "$DIR"
-
-verify() {
- expected="$2"
- actual="$(sha256sum "$1" 2>/dev/null || shasum -a 256 "$1")"
- actual="${actual%% *}"
- if [ "$actual" != "$expected" ]; then
- echo "EnsureOxlint: checksum mismatch for $1" >&2
- exit 1
- fi
-}
-
-if [ ! -x "$DIR/oxlint" ]; then
- tmp="$(mktemp -d)"
- trap 'rm -rf "$tmp"' EXIT
- curl -fsSL -o "$tmp/oxlint.tar.gz" "{oxlintUrl}"
- verify "$tmp/oxlint.tar.gz" "{oxlintSha}"
- tar xz -C "$tmp" -f "$tmp/oxlint.tar.gz"
- mv "$tmp/oxlint-{target.oxlintArch}-{target.oxlintOs}" "$DIR/oxlint"
- chmod +x "$DIR/oxlint"
- rm -rf "$tmp"
-fi
-
-if [ ! -x "$DIR/tsgolint" ]; then
- tmp="$(mktemp -d)"
- trap 'rm -rf "$tmp"' EXIT
- curl -fsSL -o "$tmp/tsgolint.tgz" "{tsgolintUrl}"
- verify "$tmp/tsgolint.tgz" "{tsgolintSha}"
- tar xz -C "$tmp" -f "$tmp/tsgolint.tgz"
- mv "$tmp/package/tsgolint" "$DIR/tsgolint"
- chmod +x "$DIR/tsgolint"
- rm -rf "$tmp"
-fi
-```
diff --git a/packages/code-review-agent/src/documents/components/Finding.md b/packages/code-review-agent/src/documents/components/Finding.md
deleted file mode 100644
index bf32165b7..000000000
--- a/packages/code-review-agent/src/documents/components/Finding.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-props:
- type: object
- properties:
- when:
- type: boolean
- severity:
- type: string
- default: warning
- message:
- type: string
- required: [when, message]
- additionalProperties: false
-description: >-
- Report one review finding. `` renders a severity icon and the message when `when` holds, and nothing
- when it does not — so a rule can be written unconditionally and stay quiet.
----
-
-```ts eval
-const icon = props.severity === "error" ? "\ud83d\udd34" : "\ud83d\udfe1";
-```
-
-
-
-{icon} {props.message}
-
-
diff --git a/packages/code-review-agent/src/documents/components/Format.md b/packages/code-review-agent/src/documents/components/Format.md
deleted file mode 100644
index 79b22ee09..000000000
--- a/packages/code-review-agent/src/documents/components/Format.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-props:
- type: object
- properties: {}
- additionalProperties: false
-description: >-
- Reformat its content as Markdown. `…` renders the content,
- normalizes it through remark and trims the result.
-context: >-
- The Markdown to reformat.
----
-
-```ts eval
-import { remark } from "npm:remark@15";
-
-const content = yield* renderChildren();
-const file = yield* call(() => remark().process(content));
-return String(file).trim();
-```
diff --git a/packages/code-review-agent/src/documents/components/GitHubAuth.md b/packages/code-review-agent/src/documents/components/GitHubAuth.md
deleted file mode 100644
index 4acd147aa..000000000
--- a/packages/code-review-agent/src/documents/components/GitHubAuth.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-props:
- type: object
- properties: {}
- additionalProperties: false
-description: >-
- Authorize the GitHub requests made inside it. `…` adds a
- bearer token to requests to `api.github.com` over HTTPS and leaves every other host
- untouched. Without `GITHUB_TOKEN` it adds nothing and the requests still run.
-context: >-
- The Markdown whose GitHub requests are authorized.
----
-
-```ts persist eval
-import { FetchApi } from "@effectionx/fetch";
-import { env as runtimeEnv } from "@executablemd/runtime";
-
-function* installGitHubAuth() {
- const token = yield* runtimeEnv("GITHUB_TOKEN");
- if (!token) {
- return;
- }
- yield* FetchApi.around({
- *fetch([input, init, shouldExpect], next) {
- let url;
- try {
- url = input instanceof Request ? new URL(input.url) : new URL(input);
- } catch {
- return yield* next(input, init, shouldExpect);
- }
-
- if (url.protocol !== "https:" || url.hostname !== "api.github.com") {
- return yield* next(input, init, shouldExpect);
- }
-
- const headers = new Headers(input instanceof Request ? input.headers : undefined);
- const requestHeaders = new Headers(init?.headers);
- requestHeaders.forEach((value, key) => headers.set(key, value));
- headers.set("Authorization", `Bearer ${token}`);
- if (!headers.has("Accept")) {
- headers.set("Accept", "application/vnd.github+json");
- }
-
- return yield* next(input, { ...init, headers }, shouldExpect);
- },
- });
-}
-
-yield* installGitHubAuth();
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/GitHubComment.md b/packages/code-review-agent/src/documents/components/GitHubComment.md
deleted file mode 100644
index a78015f82..000000000
--- a/packages/code-review-agent/src/documents/components/GitHubComment.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-props:
- type: object
- properties:
- marker:
- type: string
- default: ""
- additionalProperties: false
-description: >-
- Publish the review as one comment on the pull request.
- `…` posts its content the first time and edits that
- same comment afterwards, so repeated runs leave one comment rather than a thread. It
- requires `GITHUB_REPOSITORY` and `PR_NUMBER`.
-context: >-
- The comment body.
----
-
-```ts eval
-import { env as runtimeEnv } from "@executablemd/runtime";
-
-function* reviewConfiguration() {
- const repository = yield* runtimeEnv("GITHUB_REPOSITORY");
- const number = yield* runtimeEnv("PR_NUMBER");
- if (!repository || !number || !repository.includes("/")) {
- throw new Error("GitHubComment requires GITHUB_REPOSITORY and PR_NUMBER");
- }
- const [owner, name] = repository.split("/");
- return { api: `https://api.github.com/repos/${owner}/${name}`, number };
-}
-
-const content = yield* renderChildren();
-const body = props.marker + "\n" + content.trim();
-const { api, number } = yield* reviewConfiguration();
-const comments = yield* fetch(`${api}/issues/${number}/comments`).expect().json();
-if (!Array.isArray(comments)) {
- throw new Error("GitHub comments response was not an array");
-}
-
-const existing = comments.find((comment) =>
- comment.user?.type === "Bot" && typeof comment.body === "string" && comment.body.includes(props.marker)
-);
-if (existing) {
- yield* fetch(`${api}/issues/comments/${existing.id}`, {
- method: "PATCH",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ body }),
- }).expect();
-} else {
- yield* fetch(`${api}/issues/${number}/comments`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ body }),
- }).expect();
-}
-
-return content;
-```
diff --git a/packages/code-review-agent/src/documents/components/Instruction.md b/packages/code-review-agent/src/documents/components/Instruction.md
deleted file mode 100644
index c1bee0a3a..000000000
--- a/packages/code-review-agent/src/documents/components/Instruction.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-meta:
- componentName: Instruction
- description: >-
- Add a system prompt to the `` calls inside it. `…` appends to whatever
- instruction is already in scope, so an enclosing instruction is read first and
- nesting narrows rather than replaces.
- context: >-
- The Markdown whose samples carry this instruction.
-
-props:
- type: object
- properties:
- system:
- type: string
- description: >
- System prompt text. When Sample calls are made within this
- component's children, the text is included in the
- SampleContext.system field, which providers use as the system
- prompt. Multiple Instruction components accumulate — instructions
- from enclosing scopes appear first, inner instructions are appended.
- required: [system]
- additionalProperties: false
----
-
-```js persist eval
-yield *
- Sample.around(
- {
- *sample([context], next) {
- const existing = context.system || "";
- return yield* next({
- ...context,
- system: existing ? existing + "\n" + props.system : props.system,
- });
- },
- },
- { at: "min" },
- );
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/LinkedIssue.md b/packages/code-review-agent/src/documents/components/LinkedIssue.md
deleted file mode 100644
index 6ea5c3649..000000000
--- a/packages/code-review-agent/src/documents/components/LinkedIssue.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- whenLinesExceed:
- type: number
- default: 0
- severity:
- type: string
- default: warning
- message:
- type: string
- default: "Large PR with no linked issue."
- required: [pr]
- additionalProperties: false
-description: >-
- Ask a large change to link an issue. ``
- reports when the description names no issue and the change is bigger than that.
----
-
-```ts eval
-const hasIssue = /(?:#\d+|https:\/\/github\.com\/.*\/issues\/\d+)/.test(props.pr.meta.body);
-```
-
- props.whenLinesExceed}
- severity={props.severity} message={props.message} />
diff --git a/packages/code-review-agent/src/documents/components/NewDependencies.md b/packages/code-review-agent/src/documents/components/NewDependencies.md
deleted file mode 100644
index 591e5f4ff..000000000
--- a/packages/code-review-agent/src/documents/components/NewDependencies.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- severity:
- type: string
- default: warning
- message:
- type: string
- default: "package.json changed without dependency justification."
- required: [pr]
- additionalProperties: false
-description: >-
- Ask a change that edits `package.json` to account for its dependencies.
- `` reports when the manifest moved and the description
- never mentions dependencies.
----
-
-```ts eval
-const touchesPkg = props.pr.files.some(f =>
- f.path === "package.json" || f.path.endsWith("/package.json")
-);
-const mentionsDeps = props.pr.meta.body.toLowerCase().includes("dependenc");
-const triggered = touchesPkg && !mentionsDeps;
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/OllamaProvider.md b/packages/code-review-agent/src/documents/components/OllamaProvider.md
deleted file mode 100644
index b62eba5a2..000000000
--- a/packages/code-review-agent/src/documents/components/OllamaProvider.md
+++ /dev/null
@@ -1,50 +0,0 @@
----
-props:
- type: object
- properties:
- model:
- type: string
- baseUrl:
- type: string
- default: "http://localhost:11434"
- required: [model]
- additionalProperties: false
-description: >-
- Answer this document's `` calls from a local Ollama server. `…` handles samples that name that model and
- samples that name none, and passes the rest outward.
-context: >-
- The Markdown whose samples this provider answers.
----
-
-```ts persist eval
-yield* Sample.around({
- *sample([context], next) {
- if (context.model !== undefined && context.model !== props.model) {
- return yield* next(context);
- }
-
- const messages = [];
- if (context.system) {
- messages.push({ role: "system", content: context.system });
- }
- messages.push({ role: "user", content: context.content });
-
- const result = yield* fetch(`${props.baseUrl}/v1/chat/completions`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ model: props.model, messages, temperature: 0 }),
- })
- .expect()
- .json();
-
- const content = result.choices?.[0]?.message?.content;
- if (typeof content !== "string") {
- throw new Error("Ollama response did not contain model content");
- }
- return content;
- },
-}, { at: 'min' });
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/OxlintConfig.md b/packages/code-review-agent/src/documents/components/OxlintConfig.md
deleted file mode 100644
index 97a7113cc..000000000
--- a/packages/code-review-agent/src/documents/components/OxlintConfig.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-props:
- type: object
- properties:
- path:
- type: string
- default: ".reviews/tsconfig.oxlint.json"
- additionalProperties: false
-description: >-
- Write the TypeScript configuration the Oxlint sensor's type-aware pass reads.
- `` generates it in the checkout, so a type-aware run has a project
- to resolve against.
----
-
-```ts eval
-import { ensureDir, writeTextFile } from "@executablemd/runtime";
-
-yield* ensureDir(".reviews");
-yield* writeTextFile(props.path, JSON.stringify({
- compilerOptions: {
- target: "ESNext",
- module: "ESNext",
- moduleResolution: "bundler",
- strict: true,
- noEmit: true,
- skipLibCheck: true,
- resolveJsonModule: true,
- lib: ["ESNext", "DOM"],
- types: [],
- },
- include: [
- "packages/*/src/**/*.ts",
- "packages/*/*.ts",
- "durable-effects/**/*.ts",
- ],
- exclude: ["node_modules", "dist", ".vendor", "**/*.test.ts"],
-}, null, 2));
-```
diff --git a/packages/code-review-agent/src/documents/components/OxlintSignals.md b/packages/code-review-agent/src/documents/components/OxlintSignals.md
deleted file mode 100644
index 46a420d9f..000000000
--- a/packages/code-review-agent/src/documents/components/OxlintSignals.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-props:
- type: object
- properties:
- groups:
- type: array
- label:
- type: string
- required: [groups, label]
- additionalProperties: false
-description: >-
- List one category of Oxlint diagnostics. `` names each rule
- with how often it fired and the first few files, and renders nothing when the
- category is empty.
----
-
-```ts eval
-if (props.groups.length === 0) return;
-
-const lines = props.groups.map(g =>
- `- \`${g.ruleId}\` ×${g.count}: ${g.files.slice(0, 3).join(", ")}${g.files.length > 3 ? ` (+${g.files.length - 3})` : ""}`
-);
-
-return `**Oxlint ${props.label}:**\n${lines.join("\n")}`;
-```
diff --git a/packages/code-review-agent/src/documents/components/OxlintSummary.md b/packages/code-review-agent/src/documents/components/OxlintSummary.md
deleted file mode 100644
index 334b918fd..000000000
--- a/packages/code-review-agent/src/documents/components/OxlintSummary.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-props:
- type: object
- properties:
- diagnostics:
- type: object
- doctor:
- type: object
- required: [diagnostics, doctor]
- additionalProperties: false
-description: >-
- Report what the Oxlint sensor found. `` renders a Static Analysis section that says the sensor was
- unavailable rather than reporting a clean run it never measured, and notes type-
- aware rules that could not run.
----
-
-
-
-
-
-🟡 Oxlint not installed. Static analysis skipped.
-
-
-
- 0}>
-
-{props.diagnostics.summary}
-
-
-
- 0
- && props.doctor.oxlintInstalled}>
-
-*{props.doctor.bloatRulesMissing.length} type-aware rules unavailable
-— install `oxlint-tsgolint` for full coverage.*
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/components/Pattern.md b/packages/code-review-agent/src/documents/components/Pattern.md
deleted file mode 100644
index aeeb021ca..000000000
--- a/packages/code-review-agent/src/documents/components/Pattern.md
+++ /dev/null
@@ -1,42 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- pattern:
- type: string
- min:
- type: number
- default: 1
- excludeTests:
- type: boolean
- default: true
- severity:
- type: string
- default: warning
- message:
- type: string
- required: [pr, pattern, message]
- additionalProperties: false
-description: >-
- Report added lines matching a regular expression. `` renders once at
- least `min` lines match, and substitutes the count into `{count}`. Test files are
- excluded unless `excludeTests` says otherwise.
----
-
-```ts eval
-const re = new RegExp(props.pattern, "g");
-const lines = props.excludeTests
- ? props.pr.added.filter(l => !l.isTest)
- : props.pr.added;
-const matches = lines.filter(l => re.test(l.content));
-re.lastIndex = 0;
-
-if (matches.length >= props.min) {
- const icon = props.severity === "error" ? "\ud83d\udd34" : "\ud83d\udfe1";
- return icon + " " + props.message
- .replace("{count}", String(matches.length));
-}
-```
diff --git a/packages/code-review-agent/src/documents/components/PrPolicyReport.md b/packages/code-review-agent/src/documents/components/PrPolicyReport.md
deleted file mode 100644
index b136120fb..000000000
--- a/packages/code-review-agent/src/documents/components/PrPolicyReport.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- diagnostics:
- type: object
- doctor:
- type: object
- required: [pr, diagnostics, doctor]
- additionalProperties: false
-description: >-
- Compose the whole pull-request review. `` renders the scope, structural, slop,
- static-analysis and correctness sections under one heading. When redundant comments
- remain and the required GitHub environment is present, it also reconciles inline
- removal suggestions on the pull request.
----
-
-## PR #{props.pr.meta.number}: {props.pr.meta.title}
-
-**{props.pr.stats.totalFiles}** files, **+{props.pr.stats.additions}** / **-{props.pr.stats.deletions}**
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/components/Ratio.md b/packages/code-review-agent/src/documents/components/Ratio.md
deleted file mode 100644
index cc96cd441..000000000
--- a/packages/code-review-agent/src/documents/components/Ratio.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- numerator:
- type: string
- denominator:
- type: string
- threshold:
- type: number
- minDenominator:
- type: number
- default: 10
- excludeTests:
- type: boolean
- default: true
- severity:
- type: string
- default: warning
- message:
- type: string
- required: [pr, numerator, denominator, threshold, message]
- additionalProperties: false
-description: >-
- Report when one pattern is too common relative to another. `` stays quiet until the denominator has at least `minDenominator` matches,
- so a small diff cannot trip it on one line.
----
-
-```ts eval
-const numRe = new RegExp(props.numerator, "g");
-const denRe = new RegExp(props.denominator, "g");
-const lines = props.excludeTests
- ? props.pr.added.filter(l => !l.isTest)
- : props.pr.added;
-const source = lines.map(l => l.content).join("\n");
-
-const numCount = (source.match(numRe) ?? []).length;
-const denCount = (source.match(denRe) ?? []).length;
-
-if (denCount >= props.minDenominator && numCount / denCount > props.threshold) {
- const ratio = (numCount / denCount * 100).toFixed(1);
- const icon = props.severity === "error" ? "\ud83d\udd34" : "\ud83d\udfe1";
- return icon + " " + props.message
- .replace("{ratio}", ratio)
- .replace("{numeratorCount}", String(numCount))
- .replace("{denominatorCount}", String(denCount));
-}
-```
diff --git a/packages/code-review-agent/src/documents/components/ReleaseSpecWarning.md b/packages/code-review-agent/src/documents/components/ReleaseSpecWarning.md
deleted file mode 100644
index b2a5b3bf1..000000000
--- a/packages/code-review-agent/src/documents/components/ReleaseSpecWarning.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-props:
- type: object
- properties:
- files:
- type: array
- items:
- type: string
- required: [files]
- additionalProperties: false
-description: >-
- Warn when release configuration changes without its specification.
- `` renders a warning if any release file
- changed and `specs/release-process-spec.md` did not.
----
-
-# Release Config Files
-
-The following is the list of files used in the release process. Update this
-list when a file is added or removed.
-
-
-
-- .github/workflows/draft-release.yml
-- .github/workflows/release.yml
-- .github/workflows/publish-one.yml
-- .github/workflows/publish-packages.yml
-- .github/release-drafter.yml
-- scripts/gen-publish-workflow.md
-- scripts/build-npm.ts
-- scripts/bump-version.ts
-
-
-
-```ts eval
-const releaseChanged = props.files.filter((path) => releaseConfigFiles.includes(`- ${path}`));
-const changedList = releaseChanged.join(", ");
-
-// TODO: evaluate whether the diff's changes are actually reflected in the
-// spec document, not just whether the spec file was touched.
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/RepoPolicyReport.md b/packages/code-review-agent/src/documents/components/RepoPolicyReport.md
deleted file mode 100644
index 7a596309b..000000000
--- a/packages/code-review-agent/src/documents/components/RepoPolicyReport.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-props:
- type: object
- properties:
- diagnostics:
- type: object
- doctor:
- type: object
- fileList:
- type: string
- fileCount:
- type: number
- lineCount:
- type: number
- cleanupAnalysis:
- type: object
- required: [diagnostics, doctor, fileList, fileCount, lineCount]
- additionalProperties: false
-description: >-
- Compose the whole repository analysis. `` renders the
- static-analysis and cleanup sections. With the configured GitHub environment, it
- also creates, updates and closes cleanup issues for the five highest-ranked file
- clusters.
----
-
-## Repository Analysis
-
-**{props.fileCount}** TypeScript files, **{props.lineCount}** total lines
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/components/ReviewSection.md b/packages/code-review-agent/src/documents/components/ReviewSection.md
deleted file mode 100644
index a14e2bbd1..000000000
--- a/packages/code-review-agent/src/documents/components/ReviewSection.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-props:
- type: object
- properties:
- heading:
- type: string
- clean:
- type: string
- default: "\u2705 No issues found."
- required: [heading]
- additionalProperties: false
-description: >-
- Render one section of a review, or say it found nothing. `…` prints the heading with whatever its content
- rendered, and prints the `clean` message when the content rendered nothing — so a
- section of quiet rules reads as a pass rather than as a blank.
-context: >-
- The findings that belong to this section.
----
-
-```ts eval
-const content = yield* renderChildren();
-return content.trim().length > 0
- ? `### ${props.heading}\n\n${content}`
- : `### ${props.heading}\n\n${props.clean}`;
-```
diff --git a/packages/code-review-agent/src/documents/components/ReviewSetup.md b/packages/code-review-agent/src/documents/components/ReviewSetup.md
deleted file mode 100644
index c82358708..000000000
--- a/packages/code-review-agent/src/documents/components/ReviewSetup.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-props:
- type: object
- properties:
- pullModel:
- type: boolean
- default: false
- model:
- type: string
- default: "qwen3:30b-a3b"
- additionalProperties: false
-description: >-
- Prepare the checkout for a review run. `` installs the pinned Oxlint
- sensor and writes its configuration; with `pullModel` it also provisions the local
- Ollama model first.
----
-
-
-
-
-
-```ts eval
-import { exec } from "@executablemd/runtime";
-
-if (props.pullModel) {
- const installed = yield* exec({ command: ["ollama", "show", props.model] });
- if (installed.exitCode !== 0) {
- const pulled = yield* exec({ command: ["ollama", "pull", props.model] });
- if (pulled.exitCode !== 0) {
- throw new Error(pulled.stderr || `Unable to provision Ollama model ${props.model}`);
- }
- }
-}
-```
diff --git a/packages/code-review-agent/src/documents/components/Sample.md b/packages/code-review-agent/src/documents/components/Sample.md
deleted file mode 100644
index 525ca0d37..000000000
--- a/packages/code-review-agent/src/documents/components/Sample.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-meta:
- componentName: Sample
- description: >-
- Ask the model in scope for text. `Summarize what changed.`
- sends its content, or the `prompt` when written self-closing, to whichever
- provider is installed — and `model` picks between providers when more than one
- is.
- as: >-
- The model's reply.
- context: >-
- The prompt.
-
-props:
- type: object
- properties:
- prompt:
- type: string
- default: ""
- description: >
- Text prompt to send to the Sample Api. Used in self-closing mode
- when no children are provided. If both children and prompt are
- present, children output takes precedence.
- model:
- type: string
- default: ""
- description: >
- Model routing key. When set, the SampleContext.model field is
- populated so the Sample Api middleware can route to a specific
- provider. When unset, the innermost active provider handles the call.
- params:
- type: string
- default: ""
- description: >
- Additional instruction params passed to the Sample Api middleware
- as SampleContext.params. Providers can use this for custom behavior
- (e.g., "brief", "json", "classify").
- additionalProperties: false
----
-
-```js persist eval
-const childrenOutput = yield * renderChildren();
-const content = childrenOutput || props.prompt || "";
-
-const sampleResult =
- yield *
- Sample.operations.sample({
- content,
- params: props.params || undefined,
- componentName: "Sample",
- model: props.model || undefined,
- });
-
-return sampleResult;
-```
diff --git a/packages/code-review-agent/src/documents/components/SuggestRemoval.md b/packages/code-review-agent/src/documents/components/SuggestRemoval.md
deleted file mode 100644
index b04f62357..000000000
--- a/packages/code-review-agent/src/documents/components/SuggestRemoval.md
+++ /dev/null
@@ -1,165 +0,0 @@
----
-props:
- type: object
- properties:
- findings:
- type: array
- dismissedReplies:
- type: array
- default: []
- required: [findings]
- additionalProperties: false
-description: >-
- Reconcile the review's removal suggestions on the pull request. ``
- deletes previous bot reviews when GitHub permits it, acknowledges declined
- suggestions and resolves their threads, then posts pending findings as a new review.
- It does nothing without the required GitHub environment.
----
-
-```ts eval
-import { env as runtimeEnv } from "@executablemd/runtime";
-
-function* githubConfiguration() {
- const token = yield* runtimeEnv("GITHUB_TOKEN");
- const repo = yield* runtimeEnv("GITHUB_REPOSITORY");
- const prNumber = yield* runtimeEnv("PR_NUMBER");
- const headSha = yield* runtimeEnv("HEAD_SHA");
- if (!token || !repo || !prNumber || !headSha) {
- return undefined;
- }
- const [owner, name] = repo.split("/");
- return {
- api: `https://api.github.com/repos/${owner}/${name}`,
- graphql: "https://api.github.com/graphql",
- prNumber,
- headSha,
- owner,
- name,
- };
-}
-
-const github = yield* githubConfiguration();
-if (!github) {
- return "";
-}
-
-const { api, graphql, prNumber, headSha, owner, name } = github;
-
-const existingReviews = yield* fetch(
- `${api}/pulls/${prNumber}/reviews`
-).expect().json();
-
-const botReviews = existingReviews.filter(r =>
- r.user.login === "github-actions[bot]" &&
- r.body && r.body.includes("redundant comment")
-);
-
-for (const review of botReviews) {
- try {
- yield* fetch(`${api}/pulls/${prNumber}/reviews/${review.id}`, {
- method: "DELETE",
- }).expect();
- } catch {
- // Review may already be submitted (can't delete submitted reviews).
- }
-}
-
-// 2. React 👍 on dismiss replies and resolve their threads
-if (props.dismissedReplies.length > 0) {
- // Fetch review threads via GraphQL to get thread node IDs
- const threadsQuery = `query($owner: String!, $name: String!, $pr: Int!) {
- repository(owner: $owner, name: $name) {
- pullRequest(number: $pr) {
- reviewThreads(first: 100) {
- nodes {
- id
- isResolved
- comments(first: 1) {
- nodes { databaseId }
- }
- }
- }
- }
- }
- }`;
-
- let threadMap = new Map();
- try {
- const threadsResult = yield* fetch(graphql, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- query: threadsQuery,
- variables: { owner, name, pr: parseInt(prNumber, 10) },
- }),
- }).expect().json();
-
- const threads = threadsResult.data?.repository?.pullRequest?.reviewThreads?.nodes ?? [];
- for (const thread of threads) {
- const commentId = thread.comments?.nodes?.[0]?.databaseId;
- if (commentId) {
- threadMap.set(commentId, { threadId: thread.id, isResolved: thread.isResolved });
- }
- }
- } catch {
- // If GraphQL fails, skip thread resolution — 👍 reaction still works
- }
-
- for (const reply of props.dismissedReplies) {
- // React 👍
- if (reply.replyId) {
- try {
- yield* fetch(`${api}/pulls/comments/${reply.replyId}/reactions`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ content: "+1" }),
- }).expect();
- } catch {}
- }
-
- // Resolve the thread
- if (reply.botCommentId && threadMap.has(reply.botCommentId)) {
- const { threadId, isResolved } = threadMap.get(reply.botCommentId);
- if (!isResolved) {
- try {
- yield* fetch(graphql, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- query: `mutation($threadId: ID!) {
- resolveReviewThread(input: { threadId: $threadId }) {
- thread { isResolved }
- }
- }`,
- variables: { threadId },
- }),
- }).expect();
- } catch {}
- }
- }
- }
-}
-
-// 3. Post new review with pending findings
-if (props.findings.length > 0) {
- const comments = props.findings.map(f => ({
- path: f.file,
- line: f.lineNumber,
- body: `Redundant comment — restates what the code does.\n\`\`\`suggestion\n\`\`\``,
- }));
-
- yield* fetch(`${api}/pulls/${prNumber}/reviews`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- commit_id: headSha,
- event: "COMMENT",
- body: `Found ${props.findings.length} redundant comment${props.findings.length === 1 ? "" : "s"}. Inline suggestions to remove them below.`,
- comments,
- }),
- }).expect();
-}
-
-return "";
-```
diff --git a/packages/code-review-agent/src/documents/components/ThinkFilter.md b/packages/code-review-agent/src/documents/components/ThinkFilter.md
deleted file mode 100644
index 6ffd55d98..000000000
--- a/packages/code-review-agent/src/documents/components/ThinkFilter.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-props:
- type: object
- properties: {}
- additionalProperties: false
-description: >-
- Drop a model's reasoning from its answers. `…` removes
- `` blocks from every `` reply inside it, including an unterminated
- one.
-context: >-
- The Markdown whose samples are filtered.
----
-
-```ts persist eval
-yield* Sample.around({
- *sample([context], next) {
- const result = yield* next(context);
- return result.replace(/[\s\S]*?<\/think>/gi, "").replace(/[\s\S]*$/gi, "").trim();
- },
-});
-```
-
-
diff --git a/packages/code-review-agent/src/documents/components/Threshold.md b/packages/code-review-agent/src/documents/components/Threshold.md
deleted file mode 100644
index 02dc9b5dc..000000000
--- a/packages/code-review-agent/src/documents/components/Threshold.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- metric:
- type: string
- op:
- type: string
- value:
- type: number
- severity:
- type: string
- default: warning
- message:
- type: string
- required: [pr, metric, op, value, message]
- additionalProperties: false
-description: >-
- Report when a pull-request metric crosses a limit. ``
- compares one of `totalChanges`, `totalFiles`, `additions`, `deletions` or
- `directories`, and substitutes `{actual}` and `{value}` into the message.
----
-
-```ts eval
-const metrics = {
- totalChanges: props.pr.stats.totalChanges,
- totalFiles: props.pr.stats.totalFiles,
- additions: props.pr.stats.additions,
- deletions: props.pr.stats.deletions,
- directories: Array.isArray(props.pr.directories) ? props.pr.directories.length : props.pr.directories.size,
-};
-
-const actual = metrics[props.metric];
-const ops = {
- ">": (a, b) => a > b,
- ">=": (a, b) => a >= b,
- "<": (a, b) => a < b,
- "<=": (a, b) => a <= b,
- "==": (a, b) => a == b,
-};
-
-if (ops[props.op](actual, props.value)) {
- const icon = props.severity === "error" ? "\ud83d\udd34" : "\ud83d\udfe1";
- return icon + " " + props.message
- .replace("{actual}", String(actual))
- .replace("{value}", String(props.value));
-}
-```
diff --git a/packages/code-review-agent/src/documents/components/UnusedInDiff.md b/packages/code-review-agent/src/documents/components/UnusedInDiff.md
deleted file mode 100644
index b6b904914..000000000
--- a/packages/code-review-agent/src/documents/components/UnusedInDiff.md
+++ /dev/null
@@ -1,71 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- construct:
- type: string
- severity:
- type: string
- default: warning
- message:
- type: string
- required: [pr, construct, message]
- additionalProperties: false
-description: >-
- Report declarations the change adds and nothing references. `` scans added TypeScript for that
- keyword where a declaration starts, so an import specifier naming the same keyword
- is not mistaken for one.
----
-
-```ts eval
-const lines = props.pr.added.filter(l =>
- l.file.endsWith(".ts") || l.file.endsWith(".tsx")
-);
-const source = lines.map(l => l.content).join("\n");
-
-// Anchoring the keyword to statement position (line start, after an optional
-// export/default/declare prefix) excludes `import { type X }` specifiers,
-// whose `type` keyword sits inside braces rather than at the start of a
-// declaration.
-const declPattern = new RegExp(
- `^\\s*(?:export\\s+)?(?:default\\s+|declare\\s+)?${props.construct}\\s+(\\w+)`
-);
-
-const decls = [];
-for (const line of lines) {
- const match = declPattern.exec(line.content);
- if (match) {
- decls.push({ name: match[1], file: line.file, lineNumber: line.lineNumber });
- }
-}
-
-const unused = decls
- .map(d => ({
- ...d,
- refs: (source.match(new RegExp(`\\b${d.name}\\b`, "g")) ?? []).length,
- }))
- .filter(d => d.refs <= 1);
-
-const hasUnused = unused.length > 0;
-const icon = props.severity === "error" ? "🔴" : "🟡";
-const summary = icon + " " + props.message
- .replace("{names}", unused.map(u => u.name).join(", "))
- .replace("{count}", String(unused.length));
-```
-
-
-
-
-{summary}
-
-| Symbol | Declared at | Refs in diff | Why flagged |
-| --- | --- | --- | --- |
-| `{u.name}` | `{u.file}:{u.lineNumber}` | {u.refs} | referenced ≤1× within the added diff (pre-existing usages not counted) |
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/policies/BloatPolicy.md b/packages/code-review-agent/src/documents/policies/BloatPolicy.md
deleted file mode 100644
index 84b2a5c4c..000000000
--- a/packages/code-review-agent/src/documents/policies/BloatPolicy.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- diagnostics:
- type: object
- required: [pr, diagnostics]
- additionalProperties: false
-description: >-
- Report structural bloat in a pull request. `` renders a Structural section covering unreferenced
- type and interface declarations, `any` density, empty function bodies, console
- statements and the sensor's structural signals.
----
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/policies/ExtraneousCodePolicy.md b/packages/code-review-agent/src/documents/policies/ExtraneousCodePolicy.md
deleted file mode 100644
index 3877c9028..000000000
--- a/packages/code-review-agent/src/documents/policies/ExtraneousCodePolicy.md
+++ /dev/null
@@ -1,71 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- diagnostics:
- type: object
- doctor:
- type: object
- required: [pr, diagnostics, doctor]
- additionalProperties: false
-description: >-
- Review a pull request for code that should not be there. `` asks the model in scope to
- weigh the sensor's violation density against the specific patterns behind it, and
- skips changes of twenty lines or fewer.
----
-
-
-
- 20}>
-
-
-
-You are reviewing a TypeScript PR for EXTRANEOUS code only.
-
-PR: {props.pr.meta.title}
-Description: {props.pr.meta.body}
-
-STATIC ANALYSIS SIGNALS:
-{props.diagnostics.summary}
-Violation density: {props.diagnostics.density} per added line.
-
-DENSITY CALIBRATION:
-- Below 0.020: clean — experienced contributor, reviewed code
-- 0.020–0.080: normal — minor issues, typical development
-- Above 0.100: elevated — likely unreviewed generated code
-Adjust judgment based on specific patterns, not number alone.
-
-INTERPRETATION RULES:
-- A few inferrable-type warnings in a large PR are noise.
-- Clusters of unused-vars + empty-functions + unnecessary-type-
- assertions in the SAME FILES suggest unreviewed generated code.
-- Files with both high comment density AND multiple Oxlint
- structural violations are the strongest slop signal.
-
-Report ONLY:
-1. Scope creep — changes unrelated to stated purpose
-2. Speculative abstractions — fewer than 3 consumers (Rule of
- Three: don't abstract until third use)
-3. Dead constructs — declarations never referenced in diff
-4. Wrapper indirection — functions that only forward calls
-5. Signal clusters — files where multiple Oxlint rules fire
-6. Object literal assertions — `{} as Type` hiding missing props
-
-Do NOT flag test helpers, exported types, or style preferences.
-
-For each finding: FILE, PATTERN, CONCERN, QUESTION for author.
-
-If clean: "No extraneous code patterns detected."
-
-DIFF:
-{props.pr.diffPreview}
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/policies/RepoCleanupPolicy.md b/packages/code-review-agent/src/documents/policies/RepoCleanupPolicy.md
deleted file mode 100644
index b2e91d4d4..000000000
--- a/packages/code-review-agent/src/documents/policies/RepoCleanupPolicy.md
+++ /dev/null
@@ -1,77 +0,0 @@
----
-props:
- type: object
- properties:
- diagnostics:
- type: object
- doctor:
- type: object
- fileList:
- type: string
- cleanupAnalysis:
- type: object
- required: [diagnostics, doctor, fileList]
- additionalProperties: false
-description: >-
- Explain the repository's top cleanup clusters. `` asks the model in scope why each already-ranked
- cluster matters and what to do about it, rather than re-ranking them.
----
-
-
-
- 0 && !!props.cleanupAnalysis}>
-
-
-
-You are reviewing pre-scored cleanup clusters from a TypeScript monorepo.
-Oxlint provided the raw signals. The system ranked files by co-occurrence
-of distinct rule violations, weighted toward production code.
-
-Your job is NOT to re-analyze diagnostics. The ranking is already done.
-Your job IS to explain why each top cluster matters and what specific
-cleanup action to take.
-
-PRINCIPLES:
-- Rule of Three: flag any abstraction with fewer than 3 consumers.
- Single-consumer abstractions should be inlined.
-- YAGNI: flag code that exists "just in case" with no current caller.
-
-{props.cleanupAnalysis.promptContext}
-
-For each of the top 5 clusters above, produce exactly this format:
-
-### [rank]. [file path]
-- **Why**: [1-2 sentences explaining what the co-occurring signals mean together]
-- **Action**: [specific verb: remove, inline, delete, extract, add try/finally, etc.]
-- **Scope**: [mechanical | review-required]
-- **Confidence**: [high | medium | low]
-
-Rules:
-- "mechanical" means a maintainer can fix it without design decisions.
-- "review-required" means the fix depends on intent not obvious from signals.
-- Do NOT restate raw counts only; focus on what to DO.
-- Do NOT add items outside ranked clusters. If fewer than 5 clusters, report fewer.
-- If a file is test/demo, call it lower priority unless the same pattern appears in production.
-
-Example of a good item:
-
-### 1. packages/durable-streams/operations.ts
-- **Why**: 4 distinct rules fire here (type assertions, empty functions, unused vars, floating promises), indicating mechanical scaffolding that was not cleaned up.
-- **Action**: Remove unnecessary type assertions first, then audit empty function stubs for dead code.
-- **Scope**: mechanical
-- **Confidence**: high
-
-
-
-
-
- 0 && !props.cleanupAnalysis}>
-
-{props.diagnostics.summary}
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/policies/ScopePolicy.md b/packages/code-review-agent/src/documents/policies/ScopePolicy.md
deleted file mode 100644
index e55ae57ca..000000000
--- a/packages/code-review-agent/src/documents/policies/ScopePolicy.md
+++ /dev/null
@@ -1,53 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- required: [pr]
- additionalProperties: false
-description: >-
- Report the scope and hygiene of a pull request. `` renders a
- Scope section covering size, file and directory counts, description length, issue
- linkage and config/source mixing.
----
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/documents/policies/SlopPolicy.md b/packages/code-review-agent/src/documents/policies/SlopPolicy.md
deleted file mode 100644
index a47d89aff..000000000
--- a/packages/code-review-agent/src/documents/policies/SlopPolicy.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-props:
- type: object
- properties:
- pr:
- type: object
- diagnostics:
- type: object
- required: [pr, diagnostics]
- additionalProperties: false
-description: >-
- Report verbosity indicators in a pull request. `` renders a Slop section covering comment ratio,
- comments that restate the code, and the sensor's verbosity signals. When redundant
- comments remain and the required GitHub environment is present, it also reconciles
- inline removal suggestions on the pull request.
----
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/code-review-agent/src/parse-diagnostics.ts b/packages/code-review-agent/src/parse-diagnostics.ts
deleted file mode 100644
index 6f23dc438..000000000
--- a/packages/code-review-agent/src/parse-diagnostics.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-import type { DiagnosticGroup, Diagnostics, DoctorResult, OxlintDiagnostic, PR } from "./types.ts";
-import { categorizeRule } from "./categories.ts";
-import { normalizeOxlintOutput } from "./parse-oxlint.ts";
-
-const IMPORT_NOISE_MARKERS = ["Cannot find module", "cannot find"];
-const SUMMARY_FILE_LIMIT = 3;
-const DENSITY_PRECISION = 1000;
-
-function emptyDiagnostics(): Diagnostics {
- return {
- groups: [],
- total: 0,
- fileCount: 0,
- ruleCount: 0,
- byCategory: {
- structural: [],
- verbosity: [],
- typeAware: [],
- other: [],
- },
- summary: "",
- density: 0,
- };
-}
-
-function isImportNoise(diagnostic: OxlintDiagnostic): boolean {
- return (
- IMPORT_NOISE_MARKERS.some((marker) => diagnostic.message.includes(marker)) ||
- diagnostic.ruleId.includes("import")
- );
-}
-
-function filteredDiagnostics(
- raw: readonly OxlintDiagnostic[],
- doctor: DoctorResult,
-): OxlintDiagnostic[] {
- if (doctor.recommendation !== "type-aware-filtered") {
- return [...raw];
- }
- return raw.filter((diagnostic) => !isImportNoise(diagnostic));
-}
-
-function groupedDiagnostics(raw: readonly OxlintDiagnostic[]): Map {
- const groups = new Map();
- for (const diagnostic of raw) {
- const { ruleId } = diagnostic;
- const group = groups.get(ruleId);
- let next = [diagnostic];
- if (group !== undefined) {
- next = [...group, diagnostic];
- }
- groups.set(ruleId, next);
- }
- return groups;
-}
-
-function filesForGroup(instances: readonly OxlintDiagnostic[]): string[] {
- const files = new Set();
- for (const diagnostic of instances) {
- if (diagnostic.file.length > 0) {
- files.add(diagnostic.file);
- }
- }
- return [...files];
-}
-
-function fileCountFor(raw: readonly OxlintDiagnostic[]): number {
- const files = new Set();
- for (const diagnostic of raw) {
- if (diagnostic.file.length > 0) {
- files.add(diagnostic.file);
- }
- }
- return files.size;
-}
-
-function diagnosticGroups(raw: readonly OxlintDiagnostic[]): DiagnosticGroup[] {
- const groups = [...groupedDiagnostics(raw).entries()].map(([ruleId, instances]) => ({
- ruleId,
- count: instances.length,
- files: filesForGroup(instances),
- instances,
- }));
- const ordered: DiagnosticGroup[] = [];
- for (const group of groups) {
- const index = ordered.findIndex((candidate) => candidate.count < group.count);
- if (index === -1) {
- ordered.push(group);
- } else {
- ordered.splice(index, 0, group);
- }
- }
- return ordered;
-}
-
-function categorizedGroups(groups: readonly DiagnosticGroup[]): Diagnostics["byCategory"] {
- const byCategory: Diagnostics["byCategory"] = {
- structural: [],
- verbosity: [],
- typeAware: [],
- other: [],
- };
- for (const group of groups) {
- for (const category of categorizeRule(group.ruleId)) {
- byCategory[category].push(group);
- }
- }
- return byCategory;
-}
-
-function densityFor(pr: PR, total: number): number {
- if (pr.stats.additions === 0) {
- return 0;
- }
- return Math.round((total / pr.stats.additions) * DENSITY_PRECISION) / DENSITY_PRECISION;
-}
-
-function pluralized(count: number, singular: string, plural: string): string {
- if (count === 1) {
- return singular;
- }
- return plural;
-}
-
-interface SummaryInput {
- total: number;
- fileCount: number;
- ruleCount: number;
- density: number;
- groups: readonly DiagnosticGroup[];
- doctor: DoctorResult;
-}
-
-function summaryFor({
- total,
- fileCount,
- ruleCount,
- density,
- groups,
- doctor,
-}: SummaryInput): string {
- const lines = [
- `Oxlint: ${total} ${pluralized(total, "diagnostic", "diagnostics")} across ${fileCount} ${pluralized(fileCount, "file", "files")} (${ruleCount} ${pluralized(ruleCount, "rule", "rules")})`,
- `Density: ${density.toFixed(3)} violations/added-line`,
- "",
- ];
- for (const group of groups) {
- const fileList = group.files.slice(0, SUMMARY_FILE_LIMIT).join(", ");
- const moreFiles = group.files.length - SUMMARY_FILE_LIMIT;
- let more = "";
- if (moreFiles > 0) {
- more = ` (+${moreFiles})`;
- }
- lines.push(` ${group.ruleId} (${group.count}): ${fileList}${more}`);
- }
- appendDoctorNotes(lines, doctor);
- return lines.join("\n");
-}
-
-function appendDoctorNotes(lines: string[], doctor: DoctorResult): void {
- if (doctor.bloatRulesMissing.length > 0) {
- lines.push(
- "",
- `Note: ${doctor.bloatRulesMissing.length} type-aware rules unavailable (${doctor.bloatRulesMissing.join(", ")}). Density may be understated.`,
- );
- }
- if (doctor.nativeSpecifiers.count > 0) {
- lines.push(
- "",
- `Note: ${doctor.nativeSpecifiers.count} source files use scheme specifiers (jsr:, npm:).`,
- "Run `deno lint --fix` with no-scheme-specifiers plugin to migrate.",
- );
- }
-}
-
-/** Group bounded Oxlint diagnostics for review policies. */
-export function buildDiagnostics(
- raw: readonly OxlintDiagnostic[],
- pr: PR,
- doctor: DoctorResult,
-): Diagnostics {
- const filtered = filteredDiagnostics(raw, doctor);
- const groups = diagnosticGroups(filtered);
- const total = filtered.length;
- const fileCount = fileCountFor(filtered);
- const density = densityFor(pr, total);
- return {
- groups,
- total,
- fileCount,
- ruleCount: groups.length,
- byCategory: categorizedGroups(groups),
- summary: summaryFor({
- total,
- fileCount,
- ruleCount: groups.length,
- density,
- groups,
- doctor,
- }),
- density,
- };
-}
-
-/** Parse raw Oxlint JSON into structured diagnostics. */
-export function parseDiagnostics(rawJson: string, pr: PR, doctor: DoctorResult): Diagnostics {
- try {
- return buildDiagnostics(normalizeOxlintOutput(rawJson), pr, doctor);
- } catch {
- return emptyDiagnostics();
- }
-}
diff --git a/packages/code-review-agent/src/parse-diff.ts b/packages/code-review-agent/src/parse-diff.ts
deleted file mode 100644
index 38d156d69..000000000
--- a/packages/code-review-agent/src/parse-diff.ts
+++ /dev/null
@@ -1,277 +0,0 @@
-/**
- * Parse Git unified diffs and name-status output into review data.
- * Binary files are omitted; previews are bounded independently of the full diff.
- */
-
-import type { PR, DiffFile, DiffHunk, DiffLine } from "./types.ts";
-
-const DIFF_PREVIEW_MAX = 40_000;
-
-const EXTENSION_LANGUAGE: Record = {
- ".ts": "typescript",
- ".tsx": "typescript",
- ".js": "javascript",
- ".jsx": "javascript",
- ".mjs": "javascript",
- ".cjs": "javascript",
- ".json": "json",
- ".md": "markdown",
- ".mdx": "markdown",
- ".yml": "yaml",
- ".yaml": "yaml",
- ".toml": "toml",
- ".css": "css",
- ".scss": "scss",
- ".html": "html",
- ".py": "python",
- ".rs": "rust",
- ".go": "go",
- ".sh": "shell",
- ".bash": "shell",
- ".zsh": "shell",
- ".sql": "sql",
- ".graphql": "graphql",
- ".gql": "graphql",
- ".xml": "xml",
- ".svg": "xml",
- ".dockerfile": "docker",
- ".rb": "ruby",
- ".java": "java",
- ".kt": "kotlin",
- ".swift": "swift",
- ".c": "c",
- ".cpp": "c++",
- ".h": "c",
- ".hpp": "c++",
-};
-
-function inferLanguage(filePath: string): string {
- const lower = filePath.toLowerCase();
- if (lower === "dockerfile" || lower.endsWith("/dockerfile")) {
- return "docker";
- }
-
- const lastDot = filePath.lastIndexOf(".");
- if (lastDot === -1) {
- return "unknown";
- }
- const ext = filePath.slice(lastDot).toLowerCase();
- return EXTENSION_LANGUAGE[ext] ?? "unknown";
-}
-
-function isTestFile(filePath: string): boolean {
- const lower = filePath.toLowerCase();
- return (
- lower.includes(".test.") ||
- lower.includes(".spec.") ||
- lower.includes("__tests__/") ||
- lower.includes("/test/") ||
- lower.startsWith("test/")
- );
-}
-
-function isConfigFile(filePath: string): boolean {
- const lower = filePath.toLowerCase();
- const base = lower.split("/").pop() ?? "";
- return (
- base.includes(".config.") ||
- (base.startsWith(".") &&
- (base.endsWith("rc") ||
- base.endsWith("rc.json") ||
- base.endsWith("rc.js") ||
- base.endsWith("rc.yml") ||
- base.endsWith("rc.yaml"))) ||
- base.startsWith("tsconfig") ||
- base === "package.json" ||
- base === "package-lock.json" ||
- base === "deno.json" ||
- base === "deno.lock" ||
- base === ".gitignore" ||
- base === ".eslintignore" ||
- base === ".prettierignore"
- );
-}
-
-function isTypeDeclaration(filePath: string): boolean {
- return filePath.endsWith(".d.ts");
-}
-
-/**
- * Parse the `git diff --name-status` output into a map of path → status.
- * Each line is: `STATUS\tFILE` or `STATUS\tOLD\tNEW` (for renames).
- */
-function parseNameStatus(raw: string): Map {
- const result = new Map();
- for (const line of raw.split("\n")) {
- const trimmed = line.trim();
- if (!trimmed) {
- continue;
- }
- const parts = trimmed.split("\t");
- const statusChar = (parts[0] ?? "")[0] as "A" | "M" | "D" | "R" | "C";
- if (!["A", "M", "D", "R", "C"].includes(statusChar)) {
- continue;
- }
-
- if (statusChar === "R" || statusChar === "C") {
- const newPath = parts[2] ?? parts[1] ?? "";
- result.set(newPath, statusChar);
- } else {
- const filePath = parts[1] ?? "";
- result.set(filePath, statusChar);
- }
- }
- return result;
-}
-
-/**
- * Parse a unified diff (`git diff`) into DiffFile objects.
- */
-function parseDiffContent(
- rawDiff: string,
- statusMap: Map,
-): DiffFile[] {
- const files: DiffFile[] = [];
-
- const fileSections = rawDiff.split(/^diff --git /m).filter(Boolean);
-
- for (const section of fileSections) {
- const lines = section.split("\n");
-
- const headerMatch = lines[0]?.match(/a\/(.+?) b\/(.+)/);
- if (!headerMatch) {
- continue;
- }
- const filePath = headerMatch[2];
-
- if (section.includes("Binary files ")) {
- continue;
- }
-
- const status = statusMap.get(filePath) ?? "M";
- const hunks: DiffHunk[] = [];
- let currentHunk: DiffHunk | null = null;
- let addLineNo = 0;
- let removeLineNo = 0;
-
- for (let i = 1; i < lines.length; i++) {
- const line = lines[i];
-
- // Hunk header: @@ -old,count +new,count @@
- const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)/);
- if (hunkMatch) {
- currentHunk = { header: line, lines: [] };
- hunks.push(currentHunk);
- removeLineNo = parseInt(hunkMatch[1], 10);
- addLineNo = parseInt(hunkMatch[2], 10);
- continue;
- }
-
- if (!currentHunk) {
- continue;
- }
-
- if (line.startsWith("+")) {
- currentHunk.lines.push({
- type: "add",
- content: line.slice(1),
- file: filePath,
- lineNumber: addLineNo,
- isTest: isTestFile(filePath),
- });
- addLineNo++;
- } else if (line.startsWith("-")) {
- currentHunk.lines.push({
- type: "remove",
- content: line.slice(1),
- file: filePath,
- lineNumber: removeLineNo,
- isTest: isTestFile(filePath),
- });
- removeLineNo++;
- } else if (line.startsWith(" ")) {
- currentHunk.lines.push({
- type: "context",
- content: line.slice(1),
- file: filePath,
- lineNumber: addLineNo,
- isTest: isTestFile(filePath),
- });
- addLineNo++;
- removeLineNo++;
- }
- }
-
- files.push({
- path: filePath,
- status,
- hunks,
- language: inferLanguage(filePath),
- isTest: isTestFile(filePath),
- isConfig: isConfigFile(filePath),
- isTypeDeclaration: isTypeDeclaration(filePath),
- });
- }
-
- return files;
-}
-
-/**
- * Compute unique directories at depth 2 (e.g., "src/components").
- */
-function computeDirectories(files: DiffFile[]): Set {
- const dirs = new Set();
- for (const file of files) {
- const parts = file.path.split("/");
- if (parts.length >= 2) {
- dirs.add(parts.slice(0, 2).join("/"));
- } else if (parts.length === 1) {
- dirs.add(".");
- }
- }
- return dirs;
-}
-
-/**
- * Parse raw `git diff` and `git diff --name-status` output into a typed PR object.
- *
- * @param rawDiff - Output of `git diff BASE...HEAD`
- * @param rawFiles - Output of `git diff --name-status BASE...HEAD`
- * @param meta - PR metadata (title, body, number)
- * @returns Typed PR object for use by review components
- */
-export function parseDiff(
- rawDiff: string,
- rawFiles: string,
- meta: { title: string; body: string; number: string },
-): PR {
- const statusMap = parseNameStatus(rawFiles);
- const files = parseDiffContent(rawDiff, statusMap);
-
- const allLines = files.flatMap((f) => f.hunks.flatMap((h) => h.lines));
- const added = allLines.filter((l) => l.type === "add");
- const removed = allLines.filter((l) => l.type === "remove");
-
- const addedSource = added.map((l) => l.content).join("\n");
- const diffPreview =
- addedSource.length > DIFF_PREVIEW_MAX ? addedSource.slice(0, DIFF_PREVIEW_MAX) : addedSource;
-
- return {
- files,
- added,
- removed,
- created: files.filter((f) => f.status === "A"),
- modified: files.filter((f) => f.status === "M"),
- deleted: files.filter((f) => f.status === "D"),
- directories: computeDirectories(files),
- addedSource,
- diffPreview,
- stats: {
- totalFiles: files.length,
- additions: added.length,
- deletions: removed.length,
- totalChanges: added.length + removed.length,
- },
- meta,
- };
-}
diff --git a/packages/code-review-agent/src/parse-doctor.ts b/packages/code-review-agent/src/parse-doctor.ts
deleted file mode 100644
index 449006484..000000000
--- a/packages/code-review-agent/src/parse-doctor.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-import type { DoctorResult } from "./types.ts";
-
-const DEFAULTS: DoctorResult = {
- oxlintInstalled: false,
- oxlintVersion: "",
- tsgolintInstalled: false,
- tsgolintVersion: "",
- tsconfigExists: false,
- nodeModulesExists: false,
- typeAwareAvailable: false,
- filesAnalyzed: 0,
- filesSkipped: 0,
- importErrors: 0,
- availableRuleIds: [],
- bloatRulesAvailable: [],
- bloatRulesMissing: [],
- recommendation: "syntax-only",
- nativeSpecifiers: {
- count: 0,
- files: [],
- jsr: 0,
- npm: 0,
- },
-};
-
-function isRecord(value: unknown): value is Record {
- return typeof value === "object" && value !== null && !Array.isArray(value);
-}
-
-function booleanValue(record: Record, key: string): boolean | undefined {
- const value = record[key];
- if (typeof value === "boolean") {
- return value;
- }
- return undefined;
-}
-
-function numberValue(record: Record, key: string): number | undefined {
- const value = record[key];
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- return undefined;
-}
-
-function stringValue(record: Record, key: string): string | undefined {
- const value = record[key];
- if (typeof value === "string") {
- return value;
- }
- return undefined;
-}
-
-function isStringArray(value: unknown): value is string[] {
- return Array.isArray(value) && value.every((item): item is string => typeof item === "string");
-}
-
-function stringArrayValue(record: Record, key: string): string[] | undefined {
- const value = record[key];
- if (isStringArray(value)) {
- return value;
- }
- return undefined;
-}
-
-function recommendationValue(
- record: Record,
-): DoctorResult["recommendation"] | undefined {
- const value = record.recommendation;
- if (value === "type-aware" || value === "type-aware-filtered" || value === "syntax-only") {
- return value;
- }
- return undefined;
-}
-
-function nativeSpecifierValue(value: unknown): DoctorResult["nativeSpecifiers"] {
- if (!isRecord(value)) {
- return cloneNativeSpecifiers(DEFAULTS.nativeSpecifiers);
- }
- return {
- count: numberValue(value, "count") ?? DEFAULTS.nativeSpecifiers.count,
- files: stringArrayValue(value, "files") ?? [...DEFAULTS.nativeSpecifiers.files],
- jsr: numberValue(value, "jsr") ?? DEFAULTS.nativeSpecifiers.jsr,
- npm: numberValue(value, "npm") ?? DEFAULTS.nativeSpecifiers.npm,
- };
-}
-
-function cloneNativeSpecifiers(
- value: DoctorResult["nativeSpecifiers"],
-): DoctorResult["nativeSpecifiers"] {
- return { ...value, files: [...value.files] };
-}
-
-function defaultDoctorResult(): DoctorResult {
- return {
- ...DEFAULTS,
- bloatRulesAvailable: [...DEFAULTS.bloatRulesAvailable],
- bloatRulesMissing: [...DEFAULTS.bloatRulesMissing],
- nativeSpecifiers: cloneNativeSpecifiers(DEFAULTS.nativeSpecifiers),
- };
-}
-
-function parseDoctorObject(record: Record): DoctorResult {
- return {
- oxlintInstalled: booleanValue(record, "oxlintInstalled") ?? DEFAULTS.oxlintInstalled,
- oxlintVersion: stringValue(record, "oxlintVersion") ?? DEFAULTS.oxlintVersion,
- tsgolintInstalled: booleanValue(record, "tsgolintInstalled") ?? DEFAULTS.tsgolintInstalled,
- tsgolintVersion: stringValue(record, "tsgolintVersion") ?? DEFAULTS.tsgolintVersion,
- tsconfigExists: booleanValue(record, "tsconfigExists") ?? DEFAULTS.tsconfigExists,
- nodeModulesExists: booleanValue(record, "nodeModulesExists") ?? DEFAULTS.nodeModulesExists,
- typeAwareAvailable: booleanValue(record, "typeAwareAvailable") ?? DEFAULTS.typeAwareAvailable,
- filesAnalyzed: numberValue(record, "filesAnalyzed") ?? DEFAULTS.filesAnalyzed,
- filesSkipped: numberValue(record, "filesSkipped") ?? DEFAULTS.filesSkipped,
- importErrors: numberValue(record, "importErrors") ?? DEFAULTS.importErrors,
- availableRuleIds: stringArrayValue(record, "availableRuleIds") ?? [
- ...DEFAULTS.availableRuleIds,
- ],
- bloatRulesAvailable: stringArrayValue(record, "bloatRulesAvailable") ?? [
- ...DEFAULTS.bloatRulesAvailable,
- ],
- bloatRulesMissing: stringArrayValue(record, "bloatRulesMissing") ?? [
- ...DEFAULTS.bloatRulesMissing,
- ],
- recommendation: recommendationValue(record) ?? DEFAULTS.recommendation,
- nativeSpecifiers: nativeSpecifierValue(record.nativeSpecifiers),
- };
-}
-
-/** Parse the published Doctor JSON contract while applying safe defaults. */
-export function parseDoctorResult(json: string): DoctorResult {
- try {
- const parsed: unknown = JSON.parse(json);
- if (isRecord(parsed)) {
- return parseDoctorObject(parsed);
- }
- return defaultDoctorResult();
- } catch {
- return defaultDoctorResult();
- }
-}
diff --git a/packages/code-review-agent/src/parse-oxlint.ts b/packages/code-review-agent/src/parse-oxlint.ts
deleted file mode 100644
index a4b4192be..000000000
--- a/packages/code-review-agent/src/parse-oxlint.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import type { OxlintDiagnostic } from "./types.ts";
-
-const RULE_CODE_PATTERN = /\((?[^)]+)\)/u;
-
-function isRecord(value: unknown): value is Record {
- return typeof value === "object" && value !== null;
-}
-
-function stringValue(record: Record, key: string): string | undefined {
- const value = record[key];
- if (typeof value === "string") {
- return value;
- }
- return undefined;
-}
-
-function numberValue(record: Record, key: string): number | undefined {
- const value = record[key];
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- return undefined;
-}
-
-function firstSpan(record: Record): Record | undefined {
- const { labels } = record;
- if (!Array.isArray(labels)) {
- return undefined;
- }
- const [label] = Array.from(labels, (entry: unknown) => entry);
- if (!isRecord(label) || !isRecord(label.span)) {
- return undefined;
- }
- return label.span;
-}
-
-function spanNumber(span: Record | undefined, key: string): number | undefined {
- if (span === undefined) {
- return undefined;
- }
- return numberValue(span, key);
-}
-
-function ruleIdFromCode(code: string): string {
- const match = RULE_CODE_PATTERN.exec(code);
- return match?.groups?.ruleId ?? code;
-}
-
-function ruleIdFor(value: Record, code: string | undefined): string {
- const explicitRuleId = stringValue(value, "ruleId");
- if (explicitRuleId !== undefined) {
- return explicitRuleId;
- }
- if (code !== undefined) {
- return ruleIdFromCode(code);
- }
- return "unknown";
-}
-
-function severityFor(value: string | undefined): "error" | "warning" {
- if (value === "error") {
- return "error";
- }
- return "warning";
-}
-
-function positionFor(
- value: Record,
- span: Record | undefined,
- key: string,
-): number {
- return numberValue(value, key) ?? spanNumber(span, key) ?? 0;
-}
-
-/** Convert one Oxlint object to the bounded representation used by reviews. */
-export function normalizeDiagnostic(value: unknown): OxlintDiagnostic | undefined {
- if (!isRecord(value)) {
- return undefined;
- }
-
- const code = stringValue(value, "code");
- const span = firstSpan(value);
- const severity = stringValue(value, "severity");
- const file = stringValue(value, "file") ?? stringValue(value, "filename") ?? "";
- return {
- message: stringValue(value, "message") ?? "",
- ruleId: ruleIdFor(value, code),
- severity: severityFor(severity),
- file,
- line: positionFor(value, span, "line"),
- column: positionFor(value, span, "column"),
- };
-}
-
-function diagnosticEntries(value: unknown): unknown[] | undefined {
- if (Array.isArray(value)) {
- return Array.from(value, (entry: unknown) => entry);
- }
- if (!isRecord(value)) {
- return undefined;
- }
-
- const { diagnostics } = value;
- if (Array.isArray(diagnostics)) {
- return Array.from(diagnostics, (entry: unknown) => entry);
- }
- if (isRecord(diagnostics) && Array.isArray(diagnostics.diagnostics)) {
- return Array.from(diagnostics.diagnostics, (entry: unknown) => entry);
- }
- return undefined;
-}
-
-function parseOxlintJson(stdout: string): unknown {
- try {
- const parsed: unknown = JSON.parse(stdout);
- return parsed;
- } catch (error) {
- let message = String(error);
- if (error instanceof Error) {
- const { message: errorMessage } = error;
- message = errorMessage;
- }
- throw new Error(`Oxlint returned malformed JSON: ${message}`, { cause: error });
- }
-}
-
-function changedFiles(files: readonly string[] | undefined): Set | undefined {
- if (files === undefined) {
- return undefined;
- }
- return new Set(files);
-}
-
-/**
- * Parse and bound Oxlint JSON before it enters an executable document value.
- * Unknown fields never cross this boundary.
- */
-export function normalizeOxlintOutput(
- stdout: string,
- files?: readonly string[],
-): OxlintDiagnostic[] {
- const entries = diagnosticEntries(parseOxlintJson(stdout));
- if (entries === undefined) {
- throw new Error("Oxlint JSON did not contain a diagnostics array");
- }
-
- const changed = changedFiles(files);
- return entries.flatMap((entry) => {
- const diagnostic = normalizeDiagnostic(entry);
- if (diagnostic === undefined || (changed !== undefined && !changed.has(diagnostic.file))) {
- return [];
- }
- return [diagnostic];
- });
-}
diff --git a/packages/code-review-agent/src/plugin.ts b/packages/code-review-agent/src/plugin.ts
deleted file mode 100644
index 14865a704..000000000
--- a/packages/code-review-agent/src/plugin.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Install the packaged review graph when an operator selects this Plugin.
- *
- * Run, syntax, plan and executing workflow actions reserve its thirty-five
- * Markdown declarations and seven reserved registrations, so the repository being
- * reviewed cannot replace the review program. Root `xmd test` reserves none;
- * its isolated `` child reinstalls this Plugin with
- * command `run`.
- */
-
-import type { Operation } from "effection";
-
-import { Plugin } from "@executablemd/core/api";
-import type { PluginInstallRequest, PluginInstallation } from "@executablemd/core/api";
-
-import { reviewComponentDeclarations, useReviewComponents } from "./review-components.ts";
-
-/** The `xmd workflow` actions that execute a document, and so run a profile. */
-const EXECUTING_ACTIONS: ReadonlySet = new Set(["start", "resume", "fork"]);
-
-/**
- * Workflow options whose separated values must not be mistaken for actions.
- * Keep this aligned with `workflowConfig` in `packages/cli/src/workflow.ts` and
- * argv-level Plugin/root-property options; the CLI dependency prevents
- * importing its grammar here. For example, `workflow --output start export
- * run-1` exports and must not install the review graph because the output value
- * reads `start`.
- */
-const VALUED_OPTIONS: ReadonlySet = new Set([
- "--plugin",
- "--id",
- "--at",
- "--status",
- "--artifact",
- "--output",
-]);
-
-/** The generated root-property options, which take a separated value too. */
-const PROPERTY_OPTION = "--props";
-
-/** Whether this token is an option that takes the token after it. */
-function takesValue(token: string): boolean {
- return (
- VALUED_OPTIONS.has(token) ||
- token === PROPERTY_OPTION ||
- token.startsWith(`${PROPERTY_OPTION}-`)
- );
-}
-
-/**
- * Read the first positional after `workflow`, skipping options and their
- * values. Stop at `--`; unknown actions remain the command's own grammar error
- * and install no review graph.
- */
-function executesDocument(args: readonly string[]): boolean {
- const start = args.indexOf("workflow");
- if (start === -1) {
- return false;
- }
- const tokens = args.slice(start + 1);
- for (let index = 0; index < tokens.length; index++) {
- const token = tokens[index];
- if (token === undefined || token === "--") {
- return false;
- }
- if (takesValue(token)) {
- index += 1;
- continue;
- }
- if (token.startsWith("-")) {
- continue;
- }
- return EXECUTING_ACTIONS.has(token);
- }
- return false;
-}
-
-/** Whether the review graph belongs to this command. */
-function claimsGraph(request: PluginInstallRequest): boolean {
- switch (request.command) {
- case "run":
- case "plan":
- case "syntax":
- return true;
- case "workflow":
- return executesDocument(request.args);
- default:
- return false;
- }
-}
-
-/**
- * Written as a named, explicitly typed value rather than as a default export
- * expression, because a package publishing to JSR states the type of what it
- * exports rather than leaving it to be inferred.
- */
-const reviewPlugin: Plugin = Plugin({
- name: "@executablemd/code-review-agent",
- *install(request: PluginInstallRequest): Operation {
- if (!claimsGraph(request)) {
- return undefined;
- }
- // The seven reserved registrations and the documentation describing them, in
- // the scope the command runs in. Declarative only: nothing here installs a
- // provider, spawns a process, reads a credential or reaches the network.
- yield* useReviewComponents();
- // The thirty-five Markdown declarations travel by value, read from this
- // build's own assets, exactly as they always have.
- return { components: yield* reviewComponentDeclarations() };
- },
-});
-
-export default reviewPlugin;
diff --git a/packages/code-review-agent/src/policy.ts b/packages/code-review-agent/src/policy.ts
deleted file mode 100644
index 5c64be9d2..000000000
--- a/packages/code-review-agent/src/policy.ts
+++ /dev/null
@@ -1,225 +0,0 @@
-import type {
- CleanupAnalysis,
- CleanupEvidence,
- Diagnostics,
- DoctorResult,
- FileCluster,
- FileKind,
- OxlintDiagnostic,
- PolicyCluster,
- PolicyInput,
- PolicyMode,
- PolicyReport,
- PolicyScore,
- PR,
-} from "./types.ts";
-import { categorizeRule } from "./categories.ts";
-
-export function buildPolicyInput(
- mode: PolicyMode,
- diagnostics: Diagnostics,
- options: {
- pr?: PR;
- doctor?: DoctorResult;
- source?: string;
- } = {},
-): PolicyInput {
- return {
- mode,
- diagnostics,
- pr: options.pr,
- doctor: options.doctor,
- metadata: {
- source: options.source,
- generatedAt: new Date().toISOString(),
- },
- };
-}
-
-export function clusterDiagnostics(input: PolicyInput): PolicyCluster[] {
- const diagnostics = input.diagnostics;
- const clusters: PolicyCluster[] = [];
-
- const push = (
- category: PolicyCluster["category"],
- groups: Diagnostics["byCategory"][keyof Diagnostics["byCategory"]],
- ) => {
- for (const group of groups) {
- clusters.push({
- id: `${category}:${group.ruleId}`,
- category,
- ruleIds: [group.ruleId],
- files: group.files,
- count: group.count,
- summary: `${group.ruleId} appears ${group.count} time${group.count === 1 ? "" : "s"}`,
- });
- }
- };
-
- push("bloat", diagnostics.byCategory.structural);
- push("slop", diagnostics.byCategory.verbosity);
- push("correctness", diagnostics.byCategory.typeAware);
- push("correctness", diagnostics.byCategory.other);
-
- return clusters.sort((a, b) => b.count - a.count);
-}
-
-export function scoreClusters(clusters: PolicyCluster[]): PolicyScore[] {
- return clusters.map((cluster) => {
- const score = Math.min(100, cluster.count * 2);
- const confidence = score >= 30 ? "high" : score >= 12 ? "medium" : "low";
- return {
- clusterId: cluster.id,
- score,
- confidence,
- };
- });
-}
-
-export function buildPolicyReport(input: PolicyInput): PolicyReport {
- const clusters = clusterDiagnostics(input);
- const scores = scoreClusters(clusters);
- const summary = `Policy report: ${clusters.length} cluster${clusters.length === 1 ? "" : "s"} from ${input.diagnostics.total} diagnostics.`;
-
- return {
- summary,
- clusters,
- scores,
- };
-}
-
-function classifyFile(path: string): FileKind {
- if (/\.test\.|\.spec\.|(^|\/)tests\//.test(path)) {
- return "test";
- }
- if (/(^|\/)demo\//.test(path)) {
- return "demo";
- }
- if (/\.json$|\.ya?ml$|deno\.json|tsconfig/.test(path)) {
- return "config";
- }
- return "production";
-}
-
-export function clusterByFile(diagnostics: Diagnostics): FileCluster[] {
- const fileMap = new Map();
-
- for (const group of diagnostics.groups) {
- for (const instance of group.instances) {
- if (!instance.file) {
- continue;
- }
-
- const existing = fileMap.get(instance.file);
- if (existing) {
- existing.push(instance);
- } else {
- fileMap.set(instance.file, [instance]);
- }
- }
- }
-
- const clusters: FileCluster[] = [];
-
- for (const [file, instances] of fileMap.entries()) {
- const kind = classifyFile(file);
- const ruleIds = [...new Set(instances.map((d) => d.ruleId))];
- const categories = {
- structural: 0,
- verbosity: 0,
- typeAware: 0,
- other: 0,
- };
-
- for (const instance of instances) {
- const cats = categorizeRule(instance.ruleId);
- for (const cat of cats) {
- categories[cat]++;
- }
- }
-
- const coOccurrence = ruleIds.length;
- const kindWeight =
- kind === "production" ? 1 : kind === "test" ? 0.3 : kind === "demo" ? 0.2 : 0.1;
- const score = Math.round(coOccurrence * instances.length * kindWeight);
-
- clusters.push({
- file,
- kind,
- totalViolations: instances.length,
- ruleIds,
- categories,
- coOccurrence,
- score,
- });
- }
-
- return clusters.sort((a, b) => b.score - a.score);
-}
-
-export function extractEvidence(
- diagnostics: Diagnostics,
- fileClusters: FileCluster[],
- topN = 10,
-): CleanupEvidence[] {
- const topFiles = new Set(fileClusters.slice(0, topN).map((cluster) => cluster.file));
- const evidence: CleanupEvidence[] = [];
-
- for (const group of diagnostics.groups) {
- for (const instance of group.instances) {
- if (!topFiles.has(instance.file)) {
- continue;
- }
-
- if (evidence.length >= 40) {
- return evidence;
- }
-
- evidence.push({
- file: instance.file,
- ruleId: instance.ruleId,
- line: instance.line,
- message: instance.message,
- });
- }
- }
-
- return evidence;
-}
-
-export function buildCleanupAnalysis(diagnostics: Diagnostics, topN = 10): CleanupAnalysis {
- const fileClusters = clusterByFile(diagnostics);
- const evidence = extractEvidence(diagnostics, fileClusters, topN);
- const top = fileClusters.slice(0, topN);
-
- const lines: string[] = [];
- lines.push(`RANKED FILE CLUSTERS (top ${top.length} of ${fileClusters.length}):`);
- lines.push("");
-
- for (let i = 0; i < top.length; i++) {
- const cluster = top[i];
- lines.push(`${i + 1}. ${cluster.file} [${cluster.kind}]`);
- lines.push(
- ` Score: ${cluster.score} | Violations: ${cluster.totalViolations} | Co-occurring rules: ${cluster.coOccurrence}`,
- );
- lines.push(` Rules: ${cluster.ruleIds.join(", ")}`);
- lines.push(
- ` Breakdown: structural=${cluster.categories.structural} slop=${cluster.categories.verbosity} type-aware=${cluster.categories.typeAware} other=${cluster.categories.other}`,
- );
- }
-
- if (evidence.length > 0) {
- lines.push("");
- lines.push("EVIDENCE (representative diagnostics from top files):");
- lines.push("");
- for (const item of evidence.slice(0, 20)) {
- lines.push(` ${item.file}:${item.line} [${item.ruleId}] ${item.message}`);
- }
- }
-
- return {
- fileClusters,
- evidence,
- promptContext: lines.join("\n"),
- };
-}
diff --git a/packages/code-review-agent/src/review-components.ts b/packages/code-review-agent/src/review-components.ts
deleted file mode 100644
index 446d22eed..000000000
--- a/packages/code-review-agent/src/review-components.ts
+++ /dev/null
@@ -1,342 +0,0 @@
-/**
- * Declare the packaged review graph before a root document is imported.
- *
- * Thirty-five Markdown components cross as {@link MarkdownComponent} values
- * with packaged bytes, stable asset origins and digests. Seven TypeScript
- * components use reserved registrations. Both reserve names so the repository
- * being reviewed cannot replace its reviewers; duplicate host reservations
- * refuse at admission. Ordinary includes and component discovery remain
- * available for unreserved names.
- *
- * Assets resolve from this module's own URL in source, npm and compiled builds.
- * Read them through the Effection filesystem directly: document-facing API.Fs
- * and Files middleware must not substitute the review program. Missing assets
- * fail assembly rather than leaving an empty component reservation.
- */
-
-import { readTextFile } from "@effectionx/fs";
-import type { Operation } from "effection";
-import {
- contributeDocumentation,
- documented,
- packageDocumentation,
- registerComponents,
-} from "@executablemd/core";
-import type {
- ComponentRegistration,
- DocumentationContribution,
- DocumentationReader,
- FunctionComponent,
-} from "@executablemd/core";
-// The declaration constructor and its digest are a *host* concern: they are what
-// a trusted installation hands to an execution, rather than anything a document
-// or a registered component reaches.
-import { Markdown, sourceDigest } from "@executablemd/core/host";
-import type { MarkdownComponent } from "@executablemd/core/host";
-
-import CommentReviewData, {
- props as commentReviewDataProps,
- returns as commentReviewDataReturns,
-} from "./components/CommentReviewData.ts";
-import CommentReviewState, {
- props as commentReviewStateProps,
- returns as commentReviewStateReturns,
-} from "./components/CommentReviewState.ts";
-import Doctor, { props as doctorProps, returns as doctorReturns } from "./components/Doctor.ts";
-import OxlintDiagnostics, {
- props as oxlintDiagnosticsProps,
- returns as oxlintDiagnosticsReturns,
-} from "./components/OxlintDiagnostics.ts";
-import RepositoryInventory, {
- props as repositoryInventoryProps,
- returns as repositoryInventoryReturns,
-} from "./components/RepositoryInventory.ts";
-import ReviewDiagnostics, {
- props as reviewDiagnosticsProps,
- returns as reviewDiagnosticsReturns,
-} from "./components/ReviewDiagnostics.ts";
-import ReviewContext, {
- props as reviewContextProps,
- returns as reviewContextReturns,
-} from "./components/ReviewContext.ts";
-
-/** The package every component in this graph reports as its source. */
-export const REVIEW_ORIGIN = "@executablemd/code-review-agent";
-
-/** Which of the two packaged document trees a component's bytes sit in. */
-export type ReviewDocumentGroup = "components" | "policies";
-
-/** One packaged Markdown component: the name a document writes, and where it lives. */
-export interface ReviewDocument {
- readonly name: string;
- readonly group: ReviewDocumentGroup;
-}
-
-/**
- * Declare the packaged Markdown manifest in stable order.
- * Compiled assets cannot rely on directory enumeration. Groups also determine
- * origins, so adding or relocating a component is an explicit identity edit.
- */
-export const REVIEW_DOCUMENTS: readonly ReviewDocument[] = [
- { name: "AbstractionNames", group: "components" },
- { name: "CleanupIssues", group: "components" },
- { name: "CommentReview", group: "components" },
- { name: "ConfigSourceMix", group: "components" },
- { name: "DeepInfraProvider", group: "components" },
- { name: "DescriptionCheck", group: "components" },
- { name: "EnsureOxlint", group: "components" },
- { name: "Finding", group: "components" },
- { name: "Format", group: "components" },
- { name: "GitHubAuth", group: "components" },
- { name: "GitHubComment", group: "components" },
- { name: "Instruction", group: "components" },
- { name: "LinkedIssue", group: "components" },
- { name: "NewDependencies", group: "components" },
- { name: "OllamaProvider", group: "components" },
- { name: "OxlintConfig", group: "components" },
- { name: "OxlintSignals", group: "components" },
- { name: "OxlintSummary", group: "components" },
- { name: "Pattern", group: "components" },
- { name: "PrPolicyReport", group: "components" },
- { name: "Ratio", group: "components" },
- { name: "ReleaseSpecWarning", group: "components" },
- { name: "RepoPolicyReport", group: "components" },
- { name: "ReviewSection", group: "components" },
- { name: "ReviewSetup", group: "components" },
- { name: "Sample", group: "components" },
- { name: "SuggestRemoval", group: "components" },
- { name: "ThinkFilter", group: "components" },
- { name: "Threshold", group: "components" },
- { name: "UnusedInDiff", group: "components" },
- { name: "BloatPolicy", group: "policies" },
- { name: "ExtraneousCodePolicy", group: "policies" },
- { name: "RepoCleanupPolicy", group: "policies" },
- { name: "ScopePolicy", group: "policies" },
- { name: "SlopPolicy", group: "policies" },
-];
-
-/** Where one packaged component's bytes live, as a URL beside this module. */
-export function reviewDocumentUrl(document: ReviewDocument): URL {
- return new URL(`./documents/${document.group}/${document.name}.md`, import.meta.url);
-}
-
-/**
- * Identify a component by package and asset, never its host filesystem path.
- * Source, npm and compiled distributions therefore report the same origin.
- */
-export function reviewDocumentOrigin(document: ReviewDocument): string {
- return `${REVIEW_ORIGIN}/${document.group}/${document.name}.md`;
-}
-
-/**
- * Read this build's packaged Markdown lazily during trusted host assembly.
- * Each assembly creates a fresh array; declaration identity is name, origin, bytes
- * and digest rather than object identity. Missing assets fail before root import.
- */
-export function* reviewComponentDeclarations(): Operation {
- const declarations: MarkdownComponent[] = [];
- for (const document of REVIEW_DOCUMENTS) {
- const url = reviewDocumentUrl(document);
- let source: string;
- try {
- source = yield* readTextFile(url);
- } catch (error) {
- throw new Error(
- `the packaged review component ${document.group}/${document.name}.md is missing from ` +
- `this build (looked in ${url.href})`,
- { cause: error },
- );
- }
- declarations.push(
- Markdown({
- name: document.name,
- origin: reviewDocumentOrigin(document),
- source,
- // Stated about the bytes this build actually read, and checked against
- // them at admission. A build that shipped different bytes is refused
- // where it is installed rather than wherever a document happens to
- // write the name.
- digest: sourceDigest(source),
- // `forms` is deliberately unstated. These components accept both
- // spellings today, and a declaration that narrowed them would change
- // what the existing review roots may write.
- }),
- );
- }
- return declarations;
-}
-
-/**
- * Adapt typed implementations after the engine validates their declared schemas.
- * TypeScript cannot infer a prop type from its JSON Schema value. Keep each
- * implementation's prop type aligned with that schema; the schema remains the
- * runtime check.
- */
-// deno-lint-ignore no-explicit-any
-function reviewComponent(fn: (props: any) => Operation): FunctionComponent {
- return fn;
-}
-
-/**
- * Reserve the seven TypeScript component names alongside the Markdown declarations.
- * These implementations run processes, read credentials and reach the network;
- * leaving them shadowable would let the review subject replace its own reviewers.
- * Their existing public names and contracts are retained.
- */
-export const REVIEW_REGISTRATIONS: readonly ComponentRegistration[] = [
- {
- name: "CommentReviewData",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: commentReviewDataProps,
- returns: commentReviewDataReturns,
- fn: reviewComponent(CommentReviewData),
- ...documented({
- description:
- `Collect the pull request's existing review comments and replies. ` +
- `\`\` prepares the pairs and prior finding ` +
- `state used by review classification.`,
- as: "The normalized review-comment pairs, previous findings, and reply groups.",
- context: null,
- }),
- },
- {
- name: "CommentReviewState",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: commentReviewStateProps,
- returns: commentReviewStateReturns,
- fn: reviewComponent(CommentReviewState),
- ...documented({
- description:
- `Build the pending review-comment update from classified review data. ` +
- `\`\` decides which findings and dismissals remain.`,
- as: "The checklist text, pending findings, and new dismissal replies.",
- context: null,
- }),
- },
- {
- name: "Doctor",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: doctorProps,
- returns: doctorReturns,
- fn: reviewComponent(Doctor),
- ...documented({
- description:
- `Inspect whether the checkout is ready for review analysis. ` +
- `\`\` reports pinned tool availability, TypeScript ` +
- `support, analyzed files, and actionable setup guidance.`,
- as: "The checkout readiness report used by review and repository analysis.",
- context: null,
- }),
- },
- {
- name: "OxlintDiagnostics",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: oxlintDiagnosticsProps,
- returns: oxlintDiagnosticsReturns,
- fn: reviewComponent(OxlintDiagnostics),
- ...documented({
- description:
- `Run the review's pinned Oxlint sensor over selected files. ` +
- `\`\` returns normalized ` +
- `diagnostics and fails on a sensor crash or unusable output.`,
- as: "The normalized Oxlint diagnostics for the selected files.",
- context: null,
- }),
- },
- {
- name: "ReviewDiagnostics",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: reviewDiagnosticsProps,
- returns: reviewDiagnosticsReturns,
- fn: reviewComponent(ReviewDiagnostics),
- ...documented({
- description:
- `Group normalized Oxlint diagnostics for review policies. ` +
- `\`\`.`,
- as: "The grouped diagnostics, categories, density, and review summary.",
- context: null,
- }),
- },
- {
- name: "RepositoryInventory",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: repositoryInventoryProps,
- returns: repositoryInventoryReturns,
- fn: reviewComponent(RepositoryInventory),
- ...documented({
- description:
- `Inventory the repository source used by repository analysis. ` +
- `\`\` returns the selected paths and their file ` +
- `and line totals.`,
- as: "The selected source paths and their file and line totals.",
- context: null,
- }),
- },
- {
- name: "ReviewContext",
- origin: REVIEW_ORIGIN,
- reserved: true,
- props: reviewContextProps,
- returns: reviewContextReturns,
- fn: reviewComponent(ReviewContext),
- ...documented({
- description:
- `Build review context for the configured base and head revisions. ` +
- `\`\` returns pull-request metadata and changed file paths ` +
- `from the local Git checkout.`,
- as: "The pull-request data and changed file paths for the configured revision range.",
- context: null,
- }),
- },
-];
-
-/**
- * This boundary's long-form documentation, and the components it must cover.
- *
- * The covered set is derived from {@link REVIEW_REGISTRATIONS} rather than
- * written out again, so a registration added here demands its documentation
- * without anyone remembering to update a second list.
- *
- * The thirty-five Markdown declarations are deliberately absent from it. A
- * declared component's documentation is its own frontmatter and body — the
- * documentation index joins on a package origin, and declared Markdown names
- * none — so listing them here would demand headings for components this file
- * could not be the documentation of.
- */
-export function* reviewDocumentation(
- read?: DocumentationReader,
-): Operation {
- return yield* packageDocumentation(
- new URL("./components.md", import.meta.url),
- { owner: REVIEW_ORIGIN, asset: "packages/code-review-agent/src/components.md" },
- REVIEW_REGISTRATIONS.map((registration) => registration.name),
- read,
- );
-}
-
-/**
- * Install the seven reserved registrations and the documentation describing them.
- *
- * One call, both halves, for the reason every other package bootstrap is one
- * call: a scope that has the components has the words that describe them, and
- * two installers kept in step by hand eventually are not.
- *
- * Declarative only. Nothing here installs a provider, spawns a process, reads a
- * credential or reaches the network — which is what lets `xmd syntax` and
- * document validation enter it to describe the profile a run would have.
- *
- * The thirty-five Markdown components are not installed here: a declaration
- * crosses on an `ExecutionInstallation`, by value, rather than being registered
- * into a scope. {@link reviewComponentDeclarations} is that half.
- */
-export function* useReviewComponents(): Operation {
- yield* registerComponents(REVIEW_REGISTRATIONS);
- yield* contributeDocumentation(reviewDocumentation);
-}
diff --git a/packages/code-review-agent/src/types.ts b/packages/code-review-agent/src/types.ts
deleted file mode 100644
index c1a777b2e..000000000
--- a/packages/code-review-agent/src/types.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * Types for the code review agent's parsed PR representation
- * and Oxlint diagnostic structures.
- */
-
-export interface PR {
- files: DiffFile[];
- added: DiffLine[];
- removed: DiffLine[];
- created: DiffFile[];
- modified: DiffFile[];
- deleted: DiffFile[];
- directories: Set;
- /** Concatenated content of all added lines. */
- addedSource: string;
- /** addedSource truncated to 80,000 characters. */
- diffPreview: string;
- stats: {
- totalFiles: number;
- additions: number;
- deletions: number;
- totalChanges: number;
- };
- meta: {
- title: string;
- body: string;
- number: string;
- };
-}
-
-export interface DiffFile {
- path: string;
- status: "A" | "M" | "D" | "R" | "C";
- hunks: DiffHunk[];
- language: string;
- isTest: boolean;
- isConfig: boolean;
- isTypeDeclaration: boolean;
-}
-
-export interface DiffHunk {
- header: string;
- lines: DiffLine[];
-}
-
-export interface DiffLine {
- type: "add" | "remove" | "context";
- content: string;
- file: string;
- lineNumber: number;
- isTest: boolean;
-}
-
-export interface OxlintDiagnostic {
- ruleId: string;
- severity: "error" | "warning";
- message: string;
- file: string;
- line: number;
- column: number;
- endLine?: number;
- endColumn?: number;
-}
-
-export interface DiagnosticGroup {
- ruleId: string;
- count: number;
- files: string[];
- instances: OxlintDiagnostic[];
-}
-
-export interface Diagnostics {
- groups: DiagnosticGroup[];
- total: number;
- fileCount: number;
- ruleCount: number;
- byCategory: {
- structural: DiagnosticGroup[];
- verbosity: DiagnosticGroup[];
- typeAware: DiagnosticGroup[];
- other: DiagnosticGroup[];
- };
- summary: string;
- density: number;
-}
-
-export interface DoctorResult {
- oxlintInstalled: boolean;
- oxlintVersion: string;
- tsgolintInstalled: boolean;
- tsgolintVersion: string;
- tsconfigExists: boolean;
- nodeModulesExists: boolean;
- typeAwareAvailable: boolean;
- filesAnalyzed: number;
- filesSkipped: number;
- importErrors: number;
- availableRuleIds: string[];
- bloatRulesAvailable: string[];
- bloatRulesMissing: string[];
- recommendation: "type-aware" | "type-aware-filtered" | "syntax-only";
- nativeSpecifiers: {
- count: number;
- files: string[];
- jsr: number;
- npm: number;
- };
-}
-
-export type PolicyMode = "pr" | "repo";
-
-export interface PolicyInput {
- mode: PolicyMode;
- diagnostics: Diagnostics;
- pr?: PR;
- doctor?: DoctorResult;
- metadata?: {
- source?: string;
- generatedAt?: string;
- };
-}
-
-export type PolicyCategory = "bloat" | "slop" | "correctness" | "scope";
-
-export interface PolicyCluster {
- id: string;
- category: PolicyCategory;
- ruleIds: string[];
- files: string[];
- count: number;
- summary: string;
-}
-
-export interface PolicyScore {
- clusterId: string;
- score: number;
- confidence: "low" | "medium" | "high";
-}
-
-export interface PolicyReport {
- summary: string;
- clusters: PolicyCluster[];
- scores: PolicyScore[];
-}
-
-export type FileKind = "production" | "test" | "demo" | "config";
-
-export interface FileCluster {
- file: string;
- kind: FileKind;
- totalViolations: number;
- ruleIds: string[];
- categories: {
- structural: number;
- verbosity: number;
- typeAware: number;
- other: number;
- };
- coOccurrence: number;
- score: number;
-}
-
-export interface CleanupEvidence {
- file: string;
- ruleId: string;
- line: number;
- message: string;
-}
-
-export interface CleanupAnalysis {
- fileClusters: FileCluster[];
- evidence: CleanupEvidence[];
- promptContext: string;
-}
diff --git a/packages/code-review-agent/tests/doctor.test.ts b/packages/code-review-agent/tests/doctor.test.ts
deleted file mode 100644
index ca43e1a27..000000000
--- a/packages/code-review-agent/tests/doctor.test.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { buildDoctorResult, summarizeDoctorProbe } from "../src/doctor.ts";
-
-describe("Doctor analysis", () => {
- it("classifies import noise and chooses filtered type-aware mode", function* () {
- const summary = summarizeDoctorProbe({
- exitCode: 1,
- stderr: "",
- diagnostics: [
- {
- message: "Cannot find module x",
- ruleId: "import/no-unresolved",
- severity: "error",
- file: "a.ts",
- line: 1,
- column: 1,
- },
- {
- message: "unused",
- ruleId: "no-unused-vars",
- severity: "warning",
- file: "b.ts",
- line: 2,
- column: 1,
- },
- ],
- });
-
- expect(summary.typeAwareAvailable).toBe(true);
- expect(summary.recommendation).toBe("type-aware-filtered");
- expect(summary.importErrors).toBe(1);
- expect(summary.filesAnalyzed).toBe(2);
- expect(summary.filesSkipped).toBe(1);
- expect(summary.availableRuleIds).toEqual(["import/no-unresolved", "no-unused-vars"]);
- });
-
- it("classifies a type-aware crash without exporting raw process output", function* () {
- const summary = summarizeDoctorProbe({
- exitCode: 1,
- stderr: "tsgolint panic: OOM",
- diagnostics: [],
- });
- const result = buildDoctorResult(
- {
- oxlintInstalled: true,
- oxlintVersion: "oxlint 1",
- tsgolintInstalled: true,
- tsgolintVersion: "tsgolint 1",
- tsconfigExists: true,
- nodeModulesExists: true,
- nativeSpecifiers: { count: 0, files: [], jsr: 0, npm: 0 },
- },
- summary,
- );
-
- expect(result.typeAwareAvailable).toBe(false);
- expect(result.recommendation).toBe("syntax-only");
- expect(JSON.stringify(result)).not.toContain("panic");
- });
-});
diff --git a/packages/code-review-agent/tests/parse-diagnostics.test.ts b/packages/code-review-agent/tests/parse-diagnostics.test.ts
deleted file mode 100644
index 7e4209aad..000000000
--- a/packages/code-review-agent/tests/parse-diagnostics.test.ts
+++ /dev/null
@@ -1,314 +0,0 @@
-/**
- * Tests for parseDiagnostics — Oxlint JSON output parser.
- * Covers PD1-PD8 from the oxlint-sensor-spec.
- */
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { buildDiagnostics, parseDiagnostics } from "../src/parse-diagnostics.ts";
-import type { DoctorResult, OxlintDiagnostic, PR } from "../src/types.ts";
-
-function makePR(additions = 100): PR {
- return {
- files: [],
- added: [],
- removed: [],
- created: [],
- modified: [],
- deleted: [],
- directories: new Set(),
- addedSource: "",
- diffPreview: "",
- stats: { totalFiles: 1, additions, deletions: 0, totalChanges: additions },
- meta: { title: "Test", body: "", number: "1" },
- };
-}
-
-function makeDoctor(overrides: Partial = {}): DoctorResult {
- return {
- oxlintInstalled: true,
- oxlintVersion: "0.16.0",
- tsgolintInstalled: true,
- tsgolintVersion: "0.16.0",
- tsconfigExists: true,
- nodeModulesExists: true,
- typeAwareAvailable: true,
- filesAnalyzed: 10,
- filesSkipped: 0,
- importErrors: 0,
- availableRuleIds: [],
- bloatRulesAvailable: [],
- bloatRulesMissing: [],
- recommendation: "type-aware",
- nativeSpecifiers: { count: 0, files: [], jsr: 0, npm: 0 },
- ...overrides,
- };
-}
-
-function makeDiag(ruleId: string, file: string, line = 1, message = ""): OxlintDiagnostic {
- return {
- ruleId,
- severity: "warning",
- message: message || `${ruleId} violation`,
- file,
- line,
- column: 1,
- };
-}
-
-describe("parseDiagnostics", () => {
- it("builds directly from normalized diagnostics", function* () {
- const result = buildDiagnostics([makeDiag("no-console", "src/a.ts")], makePR(), makeDoctor());
-
- expect(result.total).toBe(1);
- expect(result.groups[0].ruleId).toBe("no-console");
- });
-
- it("PD1: groups diagnostics by ruleId", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("no-unused-vars", "src/b.ts"),
- makeDiag("no-console", "src/a.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.groups).toHaveLength(2);
- const unused = result.groups.find((g) => g.ruleId === "no-unused-vars");
- expect(unused).toBeDefined();
- expect(unused!.count).toBe(2);
- expect(unused!.files).toEqual(["src/a.ts", "src/b.ts"]);
-
- const console = result.groups.find((g) => g.ruleId === "no-console");
- expect(console).toBeDefined();
- expect(console!.count).toBe(1);
- });
-
- it("PD2: computes density as total / additions", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("no-unused-vars", "src/b.ts"),
- makeDiag("no-console", "src/a.ts"),
- makeDiag("no-console", "src/b.ts"),
- makeDiag("no-console", "src/c.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(50), makeDoctor());
-
- expect(result.total).toBe(5);
- expect(result.density).toBe(0.1);
- });
-
- it("PD2b: density is 0 when additions is 0", function* () {
- const raw = JSON.stringify([makeDiag("no-unused-vars", "src/a.ts")]);
-
- const result = parseDiagnostics(raw, makePR(0), makeDoctor());
-
- expect(result.density).toBe(0);
- });
-
- it("PD3: categorizes rules into structural, verbosity, typeAware", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("no-console", "src/a.ts"),
- makeDiag("no-unnecessary-type-assertion", "src/a.ts"),
- makeDiag("no-inferrable-types", "src/b.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.byCategory.structural.map((g) => g.ruleId)).toContain("no-unused-vars");
- expect(result.byCategory.structural.map((g) => g.ruleId)).toContain(
- "no-unnecessary-type-assertion",
- );
-
- expect(result.byCategory.verbosity.map((g) => g.ruleId)).toContain("no-console");
- expect(result.byCategory.verbosity.map((g) => g.ruleId)).toContain("no-inferrable-types");
-
- expect(result.byCategory.typeAware.map((g) => g.ruleId)).toContain(
- "no-unnecessary-type-assertion",
- );
- });
-
- it("PD4: filters import noise in type-aware-filtered mode", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("import/no-unresolved", "src/b.ts", 1, 'Cannot find module "jsr:@std/assert"'),
- makeDiag("import/no-unresolved", "src/c.ts", 1, "cannot find module foo"),
- ]);
-
- const filtered = parseDiagnostics(
- raw,
- makePR(),
- makeDoctor({ recommendation: "type-aware-filtered" }),
- );
-
- expect(filtered.total).toBe(1);
- expect(filtered.groups).toHaveLength(1);
- expect(filtered.groups[0].ruleId).toBe("no-unused-vars");
- });
-
- it("PD4b: does NOT filter noise in type-aware mode", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("import/no-unresolved", "src/b.ts", 1, 'Cannot find module "jsr:@std/assert"'),
- ]);
-
- const unfiltered = parseDiagnostics(
- raw,
- makePR(),
- makeDoctor({ recommendation: "type-aware" }),
- );
-
- expect(unfiltered.total).toBe(2);
- });
-
- it("PD5: annotates missing rules in summary", function* () {
- const raw = JSON.stringify([makeDiag("no-unused-vars", "src/a.ts")]);
-
- const result = parseDiagnostics(
- raw,
- makePR(),
- makeDoctor({
- bloatRulesMissing: ["no-unnecessary-type-assertion", "no-redundant-type-constituents"],
- }),
- );
-
- expect(result.summary).toContain("2 type-aware rules unavailable");
- expect(result.summary).toContain("Density may be understated");
- });
-
- it("PD6: annotates scheme specifiers in summary", function* () {
- const raw = JSON.stringify([makeDiag("no-unused-vars", "src/a.ts")]);
-
- const result = parseDiagnostics(
- raw,
- makePR(),
- makeDoctor({
- nativeSpecifiers: {
- count: 5,
- files: ["src/a.ts", "src/b.ts"],
- jsr: 3,
- npm: 2,
- },
- }),
- );
-
- expect(result.summary).toContain("5 source files use scheme specifiers");
- expect(result.summary).toContain("no-scheme-specifiers plugin");
- });
-
- it("PD7: empty input returns zero diagnostics", function* () {
- const result = parseDiagnostics("[]", makePR(), makeDoctor());
-
- expect(result.total).toBe(0);
- expect(result.density).toBe(0);
- expect(result.groups).toHaveLength(0);
- expect(result.fileCount).toBe(0);
- expect(result.ruleCount).toBe(0);
- });
-
- it("accepts oxlint object shape with diagnostics array", function* () {
- const raw = JSON.stringify({
- diagnostics: [makeDiag("no-unused-vars", "src/a.ts"), makeDiag("no-console", "src/b.ts")],
- });
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.total).toBe(2);
- expect(result.groups.map((g) => g.ruleId)).toEqual(["no-unused-vars", "no-console"]);
- });
-
- it("accepts nested diagnostics wrapper shape", function* () {
- const raw = JSON.stringify({
- diagnostics: {
- diagnostics: [makeDiag("no-unused-vars", "src/a.ts")],
- },
- });
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.total).toBe(1);
- expect(result.groups[0].ruleId).toBe("no-unused-vars");
- });
-
- it("normalizes native oxlint fields", function* () {
- const raw = JSON.stringify({
- diagnostics: [
- {
- message: "Type 'X' is imported but never used.",
- code: "eslint(no-unused-vars)",
- severity: "warning",
- filename: "src/example.ts",
- labels: [{ span: { line: 12, column: 7 } }],
- },
- ],
- });
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.total).toBe(1);
- expect(result.groups).toHaveLength(1);
- expect(result.groups[0].ruleId).toBe("no-unused-vars");
- expect(result.groups[0].files).toEqual(["src/example.ts"]);
- expect(result.byCategory.structural.map((g) => g.ruleId)).toContain("no-unused-vars");
- });
-
- it("preserves tolerant parsing for malformed JSON", function* () {
- const result = parseDiagnostics("not valid json {{{", makePR(), makeDoctor());
-
- expect(result.total).toBe(0);
- expect(result.groups).toHaveLength(0);
- });
-
- it("preserves tolerant parsing for unsupported JSON shapes", function* () {
- const result = parseDiagnostics(JSON.stringify({ output: [] }), makePR(), makeDoctor());
-
- expect(result.total).toBe(0);
- expect(result.groups).toHaveLength(0);
- });
-
- it("sorts groups by count descending", function* () {
- const raw = JSON.stringify([
- makeDiag("no-console", "src/a.ts"),
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("no-unused-vars", "src/b.ts"),
- makeDiag("no-unused-vars", "src/c.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.groups[0].ruleId).toBe("no-unused-vars");
- expect(result.groups[0].count).toBe(3);
- expect(result.groups[1].ruleId).toBe("no-console");
- expect(result.groups[1].count).toBe(1);
- });
-
- it("summary includes density and rule breakdown", function* () {
- const raw = JSON.stringify([
- makeDiag("no-unused-vars", "src/a.ts"),
- makeDiag("no-unused-vars", "src/b.ts"),
- makeDiag("no-console", "src/a.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(100), makeDoctor());
-
- expect(result.summary).toContain("3 diagnostics across 2 files (2 rules)");
- expect(result.summary).toContain("Density: 0.030 violations/added-line");
- expect(result.summary).toContain("no-unused-vars (2)");
- expect(result.summary).toContain("no-console (1)");
- });
-
- it("handles plugin-prefixed rule IDs for categorization", function* () {
- const raw = JSON.stringify([
- makeDiag("eslint/no-unused-vars", "src/a.ts"),
- makeDiag("typescript/no-inferrable-types", "src/b.ts"),
- ]);
-
- const result = parseDiagnostics(raw, makePR(), makeDoctor());
-
- expect(result.byCategory.structural.map((g) => g.ruleId)).toContain("eslint/no-unused-vars");
- expect(result.byCategory.verbosity.map((g) => g.ruleId)).toContain(
- "typescript/no-inferrable-types",
- );
- });
-});
diff --git a/packages/code-review-agent/tests/parse-diff.test.ts b/packages/code-review-agent/tests/parse-diff.test.ts
deleted file mode 100644
index 6d703c185..000000000
--- a/packages/code-review-agent/tests/parse-diff.test.ts
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * Tests for parseDiff — unified diff parser.
- */
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { parseDiff } from "../src/parse-diff.ts";
-
-const META = { title: "Test PR", body: "Test body", number: "42" };
-
-describe("parseDiff", () => {
- it("parses a simple added file", function* () {
- const rawDiff = [
- "diff --git a/src/hello.ts b/src/hello.ts",
- "new file mode 100644",
- "--- /dev/null",
- "+++ b/src/hello.ts",
- "@@ -0,0 +1,3 @@",
- "+export function hello() {",
- '+ return "hello";',
- "+}",
- ].join("\n");
- const rawFiles = "A\tsrc/hello.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files).toHaveLength(1);
- expect(pr.files[0].path).toBe("src/hello.ts");
- expect(pr.files[0].status).toBe("A");
- expect(pr.files[0].language).toBe("typescript");
- expect(pr.files[0].isTest).toBe(false);
- expect(pr.files[0].isConfig).toBe(false);
- expect(pr.created).toHaveLength(1);
- expect(pr.stats.additions).toBe(3);
- expect(pr.stats.deletions).toBe(0);
- expect(pr.stats.totalFiles).toBe(1);
- });
-
- it("parses modified and deleted files", function* () {
- const rawDiff = [
- "diff --git a/src/a.ts b/src/a.ts",
- "--- a/src/a.ts",
- "+++ b/src/a.ts",
- "@@ -1,2 +1,3 @@",
- " const x = 1;",
- "+const y = 2;",
- " export { x };",
- "diff --git a/src/b.ts b/src/b.ts",
- "deleted file mode 100644",
- "--- a/src/b.ts",
- "+++ /dev/null",
- "@@ -1,2 +0,0 @@",
- "-const old = true;",
- "-export { old };",
- ].join("\n");
- const rawFiles = "M\tsrc/a.ts\nD\tsrc/b.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files).toHaveLength(2);
- expect(pr.modified).toHaveLength(1);
- expect(pr.deleted).toHaveLength(1);
- expect(pr.stats.additions).toBe(1);
- expect(pr.stats.deletions).toBe(2);
- expect(pr.stats.totalChanges).toBe(3);
- });
-
- it("handles rename detection", function* () {
- const rawDiff = [
- "diff --git a/old-name.ts b/new-name.ts",
- "similarity index 100%",
- "rename from old-name.ts",
- "rename to new-name.ts",
- ].join("\n");
- const rawFiles = "R100\told-name.ts\tnew-name.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files).toHaveLength(1);
- expect(pr.files[0].path).toBe("new-name.ts");
- expect(pr.files[0].status).toBe("R");
- expect(pr.files[0].hunks).toHaveLength(0);
- expect(pr.stats.totalChanges).toBe(0);
- });
-
- it("skips binary files", function* () {
- const rawDiff = [
- "diff --git a/image.png b/image.png",
- "new file mode 100644",
- "Binary files /dev/null and b/image.png differ",
- ].join("\n");
- const rawFiles = "A\timage.png";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files).toHaveLength(0);
- });
-
- it("classifies test files", function* () {
- const rawDiff = [
- "diff --git a/src/hello.test.ts b/src/hello.test.ts",
- "new file mode 100644",
- "--- /dev/null",
- "+++ b/src/hello.test.ts",
- "@@ -0,0 +1,1 @@",
- '+it("works", () => {});',
- ].join("\n");
- const rawFiles = "A\tsrc/hello.test.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files[0].isTest).toBe(true);
- expect(pr.added[0].isTest).toBe(true);
- });
-
- it("classifies config files", function* () {
- const rawDiff = [
- "diff --git a/package.json b/package.json",
- "--- a/package.json",
- "+++ b/package.json",
- "@@ -1,1 +1,2 @@",
- ' { "name": "test"',
- '+, "version": "1.0.0" }',
- ].join("\n");
- const rawFiles = "M\tpackage.json";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files[0].isConfig).toBe(true);
- });
-
- it("classifies type declarations", function* () {
- const rawDiff = [
- "diff --git a/types/global.d.ts b/types/global.d.ts",
- "new file mode 100644",
- "--- /dev/null",
- "+++ b/types/global.d.ts",
- "@@ -0,0 +1,1 @@",
- "+declare const foo: string;",
- ].join("\n");
- const rawFiles = "A\ttypes/global.d.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.files[0].isTypeDeclaration).toBe(true);
- });
-
- it("truncates diffPreview at 40K chars", function* () {
- const longLine = "x".repeat(100_000);
- const rawDiff = [
- "diff --git a/big.ts b/big.ts",
- "new file mode 100644",
- "--- /dev/null",
- "+++ b/big.ts",
- "@@ -0,0 +1,1 @@",
- "+" + longLine,
- ].join("\n");
- const rawFiles = "A\tbig.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.addedSource.length).toBe(100_000);
- expect(pr.diffPreview.length).toBe(40_000);
- });
-
- it("computes directories at depth 2", function* () {
- const rawDiff = [
- "diff --git a/src/components/a.ts b/src/components/a.ts",
- "--- /dev/null",
- "+++ b/src/components/a.ts",
- "@@ -0,0 +1,1 @@",
- "+const a = 1;",
- "diff --git a/src/utils/b.ts b/src/utils/b.ts",
- "--- /dev/null",
- "+++ b/src/utils/b.ts",
- "@@ -0,0 +1,1 @@",
- "+const b = 2;",
- "diff --git a/root.ts b/root.ts",
- "--- /dev/null",
- "+++ b/root.ts",
- "@@ -0,0 +1,1 @@",
- "+const c = 3;",
- ].join("\n");
- const rawFiles = "A\tsrc/components/a.ts\nA\tsrc/utils/b.ts\nA\troot.ts";
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- expect(pr.directories.size).toBe(3);
- expect(pr.directories.has("src/components")).toBe(true);
- expect(pr.directories.has("src/utils")).toBe(true);
- expect(pr.directories.has(".")).toBe(true);
- });
-
- it("handles empty diff", function* () {
- const pr = parseDiff("", "", META);
-
- expect(pr.files).toHaveLength(0);
- expect(pr.added).toHaveLength(0);
- expect(pr.removed).toHaveLength(0);
- expect(pr.stats.totalChanges).toBe(0);
- expect(pr.meta.title).toBe("Test PR");
- expect(pr.meta.number).toBe("42");
- });
-
- it("preserves meta passthrough", function* () {
- const pr = parseDiff("", "", {
- title: "feat: add feature",
- body: "This PR adds a feature.\n\nFixes #123",
- number: "456",
- });
-
- expect(pr.meta.title).toBe("feat: add feature");
- expect(pr.meta.body).toContain("Fixes #123");
- expect(pr.meta.number).toBe("456");
- });
-
- it("infers languages correctly", function* () {
- const files = [
- "a.ts",
- "b.js",
- "c.py",
- "d.go",
- "e.md",
- "f.json",
- "g.yaml",
- "Dockerfile",
- "h.unknown",
- ];
- const rawDiff = files
- .map((f) =>
- [
- `diff --git a/${f} b/${f}`,
- `--- /dev/null`,
- `+++ b/${f}`,
- `@@ -0,0 +1,1 @@`,
- `+content`,
- ].join("\n"),
- )
- .join("\n");
- const rawFiles = files.map((f) => `A\t${f}`).join("\n");
-
- const pr = parseDiff(rawDiff, rawFiles, META);
-
- const langs = new Map(pr.files.map((f) => [f.path, f.language]));
- expect(langs.get("a.ts")).toBe("typescript");
- expect(langs.get("b.js")).toBe("javascript");
- expect(langs.get("c.py")).toBe("python");
- expect(langs.get("d.go")).toBe("go");
- expect(langs.get("e.md")).toBe("markdown");
- expect(langs.get("f.json")).toBe("json");
- expect(langs.get("g.yaml")).toBe("yaml");
- expect(langs.get("Dockerfile")).toBe("docker");
- expect(langs.get("h.unknown")).toBe("unknown");
- });
-});
diff --git a/packages/code-review-agent/tests/parse-doctor.test.ts b/packages/code-review-agent/tests/parse-doctor.test.ts
deleted file mode 100644
index 7000b20fd..000000000
--- a/packages/code-review-agent/tests/parse-doctor.test.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { parseDoctorResult } from "../mod.ts";
-
-describe("parseDoctorResult", () => {
- it("parses a complete Doctor value", function* () {
- const result = parseDoctorResult(
- JSON.stringify({
- oxlintInstalled: true,
- recommendation: "type-aware",
- nativeSpecifiers: { count: 2, files: ["a.ts"], jsr: 1, npm: 1 },
- }),
- );
-
- expect(result.oxlintInstalled).toBe(true);
- expect(result.recommendation).toBe("type-aware");
- expect(result.nativeSpecifiers.count).toBe(2);
- });
-
- it("returns defaults for malformed input", function* () {
- const result = parseDoctorResult("not json");
-
- expect(result.oxlintInstalled).toBe(false);
- expect(result.recommendation).toBe("syntax-only");
- expect(result.nativeSpecifiers.files).toEqual([]);
- });
-});
diff --git a/packages/code-review-agent/tests/parse-oxlint.test.ts b/packages/code-review-agent/tests/parse-oxlint.test.ts
deleted file mode 100644
index d3e0336f5..000000000
--- a/packages/code-review-agent/tests/parse-oxlint.test.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { normalizeOxlintOutput } from "../src/parse-oxlint.ts";
-
-describe("Oxlint normalization", () => {
- it("keeps only the bounded diagnostic fields and filters files", function* () {
- const result = normalizeOxlintOutput(
- JSON.stringify({
- diagnostics: [
- {
- message: "unused",
- code: "eslint(no-unused-vars)",
- severity: "warning",
- filename: "src/a.ts",
- labels: [{ span: { line: 4, column: 7 } }],
- source: "SECRET_SOURCE_EXCERPT",
- cause: "ARBITRARY_CAUSE",
- url: "https://example.invalid/diagnostic",
- },
- { message: "other", file: "src/b.ts" },
- ],
- stderr: "UNBOUNDED_STDERR",
- }),
- ["src/a.ts"],
- );
-
- expect(result).toEqual([
- {
- message: "unused",
- ruleId: "no-unused-vars",
- severity: "warning",
- file: "src/a.ts",
- line: 4,
- column: 7,
- },
- ]);
- });
-
- it("rejects malformed or unsupported output", function* () {
- let caught: unknown;
- try {
- normalizeOxlintOutput("not json");
- } catch (error) {
- caught = error;
- }
- expect(caught).toBeInstanceOf(Error);
- if (caught instanceof Error) {
- expect(caught.message).toContain("malformed JSON");
- expect(caught.cause).toBeInstanceOf(SyntaxError);
- }
- expect(() => normalizeOxlintOutput(JSON.stringify({ output: [] }))).toThrow(
- "diagnostics array",
- );
- });
-});
diff --git a/packages/code-review-agent/tests/policy.test.ts b/packages/code-review-agent/tests/policy.test.ts
deleted file mode 100644
index f8a14c0d1..000000000
--- a/packages/code-review-agent/tests/policy.test.ts
+++ /dev/null
@@ -1,192 +0,0 @@
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import {
- buildCleanupAnalysis,
- buildPolicyInput,
- buildPolicyReport,
- clusterByFile,
- clusterDiagnostics,
- extractEvidence,
- scoreClusters,
-} from "../src/policy.ts";
-import type { Diagnostics } from "../src/types.ts";
-
-function diagnosticsFixture(): Diagnostics {
- const unusedA = {
- ruleId: "no-unused-vars",
- severity: "warning" as const,
- message: "'x' is defined but never used.",
- file: "src/a.ts",
- line: 10,
- column: 1,
- };
- const unusedATwo = {
- ...unusedA,
- line: 14,
- };
- const emptyA = {
- ruleId: "no-empty-function",
- severity: "warning" as const,
- message: "Unexpected empty function.",
- file: "src/a.ts",
- line: 20,
- column: 1,
- };
- const consoleDemo = {
- ruleId: "no-console",
- severity: "warning" as const,
- message: "Unexpected console statement.",
- file: "demo/b.ts",
- line: 3,
- column: 1,
- };
-
- return {
- groups: [
- {
- ruleId: "no-unused-vars",
- count: 3,
- files: ["src/a.ts", "demo/b.ts"],
- instances: [
- unusedA,
- unusedATwo,
- {
- ...unusedA,
- file: "demo/b.ts",
- line: 9,
- },
- ],
- },
- {
- ruleId: "no-console",
- count: 2,
- files: ["demo/b.ts"],
- instances: [
- consoleDemo,
- {
- ...consoleDemo,
- line: 7,
- },
- ],
- },
- {
- ruleId: "no-empty-function",
- count: 1,
- files: ["src/a.ts"],
- instances: [emptyA],
- },
- ],
- total: 6,
- fileCount: 2,
- ruleCount: 3,
- byCategory: {
- structural: [
- {
- ruleId: "no-unused-vars",
- count: 3,
- files: ["src/a.ts", "demo/b.ts"],
- instances: [
- unusedA,
- unusedATwo,
- {
- ...unusedA,
- file: "demo/b.ts",
- line: 9,
- },
- ],
- },
- {
- ruleId: "no-empty-function",
- count: 1,
- files: ["src/a.ts"],
- instances: [emptyA],
- },
- ],
- verbosity: [
- {
- ruleId: "no-console",
- count: 2,
- files: ["demo/b.ts"],
- instances: [
- consoleDemo,
- {
- ...consoleDemo,
- line: 7,
- },
- ],
- },
- ],
- typeAware: [],
- other: [],
- },
- summary: "fixture",
- density: 0.6,
- };
-}
-
-describe("policy helpers", () => {
- it("builds policy input", function* () {
- const diagnostics = diagnosticsFixture();
- const input = buildPolicyInput("repo", diagnostics, {
- source: "tests",
- });
-
- expect(input.mode).toBe("repo");
- expect(input.diagnostics.total).toBe(6);
- expect(input.metadata?.source).toBe("tests");
- expect(typeof input.metadata?.generatedAt).toBe("string");
- });
-
- it("clusters diagnostics by category", function* () {
- const diagnostics = diagnosticsFixture();
- const clusters = clusterDiagnostics(buildPolicyInput("pr", diagnostics));
-
- expect(clusters).toHaveLength(3);
- expect(clusters[0].category).toBe("bloat");
- expect(clusters[1].category).toBe("slop");
- expect(clusters[2].category).toBe("bloat");
- });
-
- it("scores clusters and produces report", function* () {
- const diagnostics = diagnosticsFixture();
- const input = buildPolicyInput("pr", diagnostics);
- const clusters = clusterDiagnostics(input);
- const scores = scoreClusters(clusters);
- const report = buildPolicyReport(input);
-
- expect(scores).toHaveLength(3);
- expect(report.clusters).toHaveLength(3);
- expect(report.scores).toHaveLength(3);
- expect(report.summary).toContain("3 clusters");
- });
-
- it("builds ranked file clusters", function* () {
- const diagnostics = diagnosticsFixture();
- const fileClusters = clusterByFile(diagnostics);
-
- expect(fileClusters).toHaveLength(2);
- expect(fileClusters[0].file).toBe("src/a.ts");
- expect(fileClusters[0].kind).toBe("production");
- expect(fileClusters[0].coOccurrence).toBe(2);
- expect(fileClusters[0].score).toBeGreaterThan(fileClusters[1].score);
- });
-
- it("extracts evidence from top ranked files", function* () {
- const diagnostics = diagnosticsFixture();
- const fileClusters = clusterByFile(diagnostics);
- const evidence = extractEvidence(diagnostics, fileClusters, 1);
-
- expect(evidence.length).toBeGreaterThan(0);
- expect(evidence.every((entry) => entry.file === "src/a.ts")).toBe(true);
- });
-
- it("builds cleanup analysis prompt context", function* () {
- const diagnostics = diagnosticsFixture();
- const cleanup = buildCleanupAnalysis(diagnostics, 2);
-
- expect(cleanup.fileClusters).toHaveLength(2);
- expect(cleanup.evidence.length).toBeGreaterThan(0);
- expect(cleanup.promptContext).toContain("RANKED FILE CLUSTERS");
- expect(cleanup.promptContext).toContain("EVIDENCE");
- });
-});
diff --git a/packages/core/src/sample-api.ts b/packages/core/src/sample-api.ts
index 57be0f45f..776cc2971 100644
--- a/packages/core/src/sample-api.ts
+++ b/packages/core/src/sample-api.ts
@@ -1,7 +1,7 @@
/**
* The Sample Api — Effection Api for LLM inference.
*
- * Provider components (e.g., DeepInfraProvider, OllamaProvider) install
+ * Provider components (e.g., OllamaProvider) install
* middleware via `yield* Sample.around(...)` to route sample calls to
* their inference server.
*
@@ -47,7 +47,7 @@ export const Sample: Api = createApi("Sample", {
*sample(_context: SampleContext): Operation {
throw new Error(
"Sample Api requires provider middleware — " +
- "install a provider (e.g., DeepInfraProvider, OllamaProvider) or " +
+ "install a provider (e.g., OllamaProvider) or " +
"yield* Sample.around({ *sample([ctx], next) { ... } }) " +
"before using components",
);
diff --git a/packages/core/tests/component-schema-conformance.test.ts b/packages/core/tests/component-schema-conformance.test.ts
index 9fe91b735..11066359d 100644
--- a/packages/core/tests/component-schema-conformance.test.ts
+++ b/packages/core/tests/component-schema-conformance.test.ts
@@ -7,12 +7,7 @@ import { parseFrontmatter } from "../src/frontmatter.ts";
import { compilePropsSchema } from "../src/validate.ts";
const ROOT = new URL("../../../", import.meta.url);
-const COMPONENT_DIRS = [
- "packages/core/components",
- "smoke-test",
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/documents/policies",
-];
+const COMPONENT_DIRS = ["packages/core/components", "smoke-test"];
describe("component schema conformance", () => {
it("every markdown component's props schema parses and compiles", function* () {
diff --git a/packages/core/tests/eval-transform.test.ts b/packages/core/tests/eval-transform.test.ts
index becbbd11f..84ec177d1 100644
--- a/packages/core/tests/eval-transform.test.ts
+++ b/packages/core/tests/eval-transform.test.ts
@@ -136,14 +136,12 @@ describe("User import extraction", () => {
// T17: Single-line import → extracted in userImports, removed from code
it("T17: single-line import extracted", function* () {
const result = transformBlock(
- 'import { parseDiff } from "@executablemd/code-review-agent";\n\nconst pr = parseDiff();',
+ 'import { parseDiff } from "@scope/diff";\n\nconst pr = parseDiff();',
"block-17",
[],
);
expect(result.userImports).toHaveLength(1);
- expect(result.userImports[0]).toBe(
- 'import { parseDiff } from "@executablemd/code-review-agent";',
- );
+ expect(result.userImports[0]).toBe('import { parseDiff } from "@scope/diff";');
expect(result.code).not.toContain("import");
expect(result.code).toContain("const pr = parseDiff();");
expect(result.exports).toContain("pr");
@@ -152,14 +150,12 @@ describe("User import extraction", () => {
// T18: import type → extracted correctly (TypeScript syntax preserved)
it("T18: import type preserved in userImports", function* () {
const result = transformBlock(
- 'import type { PR } from "@executablemd/code-review-agent";\n\nconst x = 1;',
+ 'import type { PR } from "@scope/diff";\n\nconst x = 1;',
"block-18",
[],
);
expect(result.userImports).toHaveLength(1);
- expect(result.userImports[0]).toBe(
- 'import type { PR } from "@executablemd/code-review-agent";',
- );
+ expect(result.userImports[0]).toBe('import type { PR } from "@scope/diff";');
expect(result.code).not.toContain("import");
expect(result.code).toContain("const x = 1;");
});
diff --git a/packages/core/tests/unused-in-diff.test.ts b/packages/core/tests/unused-in-diff.test.ts
deleted file mode 100644
index 5a272c4a5..000000000
--- a/packages/core/tests/unused-in-diff.test.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { describe, it, beforeAll } from "@executablemd/test-support/bdd";
-import { useTempFileCompiler } from "../src/temp-file-compiler.ts";
-import { expect } from "@executablemd/test-support/expect";
-import { readTextFile } from "@effectionx/fs";
-import type { Operation } from "effection";
-import { InMemoryStream } from "@executablemd/durable-streams";
-import { useStubFs } from "@executablemd/runtime/test";
-import { execute } from "../src/execute.ts";
-import { collect } from "../src/collect.ts";
-import { asText } from "./helpers.ts";
-
-interface AddedLine {
- file: string;
- lineNumber: number;
- content: string;
-}
-
-function added(file: string, lineNumber: number, content: string): AddedLine {
- return { file, lineNumber, content };
-}
-
-const WHY = "referenced ≤1× within the added diff";
-
-function doc(lines: AddedLine[], construct: string): string {
- return [
- "```js eval",
- `const pr = ${JSON.stringify({ added: lines })};`,
- "```",
- "",
- ``,
- ].join("\n");
-}
-
-// The real component is read before useStubFs replaces the filesystem, so the
-// tests exercise the packaged review source rather than a test-owned copy. Its
-// `` is a native directive, so the stub filesystem supplies no component for
-// it.
-function* render(lines: AddedLine[], construct: string): Operation {
- const unusedInDiff = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/UnusedInDiff.md",
- );
-
- yield* useStubFs({
- "components/UnusedInDiff.md": unusedInDiff,
- "doc.md": doc(lines, construct),
- });
-
- return asText(yield* collect(yield* execute({ path: "doc.md", stream: new InMemoryStream() })));
-}
-
-describe("UnusedInDiff", () => {
- beforeAll(() => useTempFileCompiler());
- it("renders the disclosure with symbol, location, count and reason", function* () {
- const output = yield* render([added("core/src/a.ts", 24, "type Orphan = string;")], "type");
-
- // Exact so the selected branch's summary line, table header and row
- // whitespace are locked, not merely present somewhere in the output.
- expect(output.trim()).toBe(
- [
- "",
- "🟡 1 with no consumers: Orphan.
",
- "",
- "| Symbol | Declared at | Refs in diff | Why flagged |",
- "| --- | --- | --- | --- |",
- `| \`Orphan\` | \`core/src/a.ts:24\` | 1 | ${WHY} (pre-existing usages not counted) |`,
- "",
- "",
- " ",
- ].join("\n"),
- );
- });
-
- it("does not flag a type named only by an import specifier", function* () {
- const output = yield* render(
- [added("core/src/a.ts", 1, 'import { type Mods } from "./mods.ts";')],
- "type",
- );
-
- expect(output).not.toContain("Mods");
- expect(output).not.toContain("");
- expect(output.trim()).toBe("");
- });
-
- it("excludes a declaration referenced again within the added diff", function* () {
- const output = yield* render(
- [
- added("core/src/a.ts", 30, "export type Used = string;"),
- added("core/src/a.ts", 31, 'const value: Used = "x";'),
- ],
- "type",
- );
-
- expect(output).not.toContain("");
- expect(output.trim()).toBe("");
- });
-
- it("renders nothing when there are no findings", function* () {
- const output = yield* render([added("core/src/a.ts", 5, "const plain = 1;")], "type");
-
- expect(output.trim()).toBe("");
- });
-
- it("detects exported, declared and default interface declarations", function* () {
- const output = yield* render(
- [
- added("core/src/a.ts", 1, "export interface Alpha {"),
- added("core/src/a.ts", 5, "export declare interface Beta {"),
- added("core/src/a.ts", 9, "export default interface Gamma {"),
- ],
- "interface",
- );
-
- expect(output).toContain("| `Alpha` | `core/src/a.ts:1` | 1 |");
- expect(output).toContain("| `Beta` | `core/src/a.ts:5` | 1 |");
- expect(output).toContain("| `Gamma` | `core/src/a.ts:9` | 1 |");
- expect(output).not.toContain("ERROR");
- });
-
- it("detects an exported declared type alias", function* () {
- const output = yield* render(
- [added("core/src/a.ts", 12, "export declare type Delta = string;")],
- "type",
- );
-
- expect(output).toContain("| `Delta` | `core/src/a.ts:12` | 1 |");
- expect(output).not.toContain("ERROR");
- });
-});
diff --git a/packages/git/src/plugin.ts b/packages/git/src/plugin.ts
index 0db203fd4..e0a1828a1 100644
--- a/packages/git/src/plugin.ts
+++ b/packages/git/src/plugin.ts
@@ -63,11 +63,9 @@ const EXECUTING_ACTIONS: ReadonlySet = new Set(["start", "resume", "fork
/**
* The workflow options whose value is the token after them.
*
- * The same list the review Plugin's own scan carries, for the same reason and
- * written out for the same reason: it is every non-boolean field of the
- * command's grammar (`workflowConfig` in `packages/cli/src/workflow.ts`) plus
- * the aggregate and generated root properties, which are read out of argv
- * before that grammar exists. The boolean switches take no value and are
+ * It is every non-boolean field of the command's grammar (`workflowConfig` in
+ * `packages/cli/src/workflow.ts`) plus the aggregate and generated root
+ * properties, which are read out of argv before that grammar exists. The boolean switches take no value and are
* skipped as the options they are. `--plugin` is not here: the host has already
* taken it out of the argv this scans.
*
diff --git a/packages/runtime/test/README.md b/packages/runtime/test/README.md
index eb8eab8cc..ad08f242f 100644
--- a/packages/runtime/test/README.md
+++ b/packages/runtime/test/README.md
@@ -205,9 +205,8 @@ yield *
```
`init.expect` is part of the request, not a check a caller applies afterwards:
-it says the caller treats a non-2xx as a failure, and
-`packages/code-review-agent/src/documents/components/GitHubAuth.md`
-is a real consumer that reads it. A substituted provider that ignores it makes
+it says the caller treats a non-2xx as a failure, and the runtime's own
+provider in `packages/runtime/apis.ts` honors it. A substituted provider that ignores it makes
an authored `fetch(url).expect()` succeed on a 404.
Count what the provider was asked to perform separately from what a document
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c6b4e2664..f5a09a21b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -105,9 +105,6 @@ importers:
'@executablemd/cli':
specifier: workspace:*
version: link:packages/cli
- '@executablemd/code-review-agent':
- specifier: workspace:*
- version: link:packages/code-review-agent
'@executablemd/core':
specifier: workspace:*
version: link:packages/core
@@ -243,28 +240,6 @@ importers:
zod:
specifier: ^4.3.6
version: 4.3.6
- devDependencies:
- '@executablemd/code-review-agent':
- specifier: workspace:*
- version: link:../code-review-agent
-
- packages/code-review-agent:
- dependencies:
- '@effectionx/fetch':
- specifier: 0.2.1
- version: 0.2.1(effection@4.1.0)
- '@effectionx/fs':
- specifier: 0.3.0
- version: 0.3.0(effection@4.1.0)
- '@executablemd/core':
- specifier: workspace:*
- version: link:../core
- '@executablemd/runtime':
- specifier: workspace:*
- version: link:../runtime
- effection:
- specifier: 4.1.0
- version: 4.1.0
packages/core:
dependencies:
diff --git a/scripts/build-npm.ts b/scripts/build-npm.ts
index aafbc9d76..554e89e77 100644
--- a/scripts/build-npm.ts
+++ b/scripts/build-npm.ts
@@ -5,7 +5,7 @@
* deno run -A scripts/build-npm.ts [version]
*
* is a workspace member directory (e.g. "packages/core" or
- * "packages/code-review-agent"). [version] defaults to 0.0.0-dev. Output lands
+ * "packages/cli"). [version] defaults to 0.0.0-dev. Output lands
* in /npm.
*
* Everything published is derived from the member's own deno.json (name,
@@ -96,10 +96,8 @@ function* packagedDocuments(pkgDir: URL): Operation {
const found: string[] = [];
for (const name of names) {
// A recursive listing names the directories as well as what is in them, and
- // each entry here becomes a `copyFile`. Packages whose documents sit flat in
- // `src/documents/` never noticed; the review graph keeps its documents under
- // `components/` and `policies/`, and copying a directory as a file fails the
- // whole npm build with `EINVAL`.
+ // each entry here becomes a `copyFile`. Copying a nested directory as a file
+ // fails the whole npm build with `EINVAL`.
const info = yield* stat(join(root, name));
if (info.isFile()) {
found.push(`src/documents/${name.split(sep).join("/")}`);
diff --git a/scripts/lib/compile.ts b/scripts/lib/compile.ts
index f51191806..28c7d6875 100644
--- a/scripts/lib/compile.ts
+++ b/scripts/lib/compile.ts
@@ -39,18 +39,6 @@ export const COMPILE_FLAGS = [
*/
export const EMBEDDED_PACKAGES: readonly string[] = [];
-/**
- * Packages whose assets the binary deliberately does not carry.
- *
- * `scripts/tests/packaged-document.test.ts` walks the repository for packages
- * that ship documents and holds the lists above to what it finds, which is what
- * keeps a new asset from being forgotten. A package outside the binary would
- * fail that walk for the right reason and the wrong subject, so it is named
- * here — and the same test checks that exclusion rather than trusting it, by proving
- * no CLI production module imports one.
- */
-export const UNEMBEDDED_PACKAGES: readonly string[] = ["packages/code-review-agent"];
-
/**
* Each package's `src/documents/`, embedded whole.
*
diff --git a/scripts/tests/bump-version.test.ts b/scripts/tests/bump-version.test.ts
index 43e5c0d32..d6ece50e7 100644
--- a/scripts/tests/bump-version.test.ts
+++ b/scripts/tests/bump-version.test.ts
@@ -8,11 +8,11 @@ import { pathToFileURL } from "node:url";
import { bumpManifests } from "../lib/bump-version.ts";
/**
- * The install step of the review and analysis workflows, which resolve the
- * latest published release. A bump must leave it alone: pinning it to the
- * version being cut points CI at a release that does not exist yet (#160).
+ * A workflow step that resolves the latest published release. A bump must
+ * leave it alone: pinning it to the version being cut points CI at a release
+ * that does not exist yet (#160).
*/
-const REVIEW_WORKFLOW = ` - name: Install xmd release binary
+const WORKFLOW = ` - name: Install xmd release binary
run: |
curl -fsSL https://executable.md/install.sh | sh
`;
@@ -41,7 +41,7 @@ function* workspace(): Operation {
}
yield* ensureDir(new URL(".github/workflows/", root));
- yield* writeTextFile(new URL(".github/workflows/review.yml", root), REVIEW_WORKFLOW);
+ yield* writeTextFile(new URL(".github/workflows/install.yml", root), WORKFLOW);
return root;
}
@@ -103,8 +103,6 @@ describe("bumpManifests", () => {
yield* bumpManifests("2.0.0", root);
- expect(yield* readTextFile(new URL(".github/workflows/review.yml", root))).toEqual(
- REVIEW_WORKFLOW,
- );
+ expect(yield* readTextFile(new URL(".github/workflows/install.yml", root))).toEqual(WORKFLOW);
});
});
diff --git a/scripts/tests/cli-npm-bin.test.ts b/scripts/tests/cli-npm-bin.test.ts
index b1eaeb84e..c1ac2d49e 100644
--- a/scripts/tests/cli-npm-bin.test.ts
+++ b/scripts/tests/cli-npm-bin.test.ts
@@ -321,15 +321,6 @@ describe("npm CLI package", { sanitizeOps: false, sanitizeResources: false }, ()
yield* readTextFile(path.join(root, PKG_DIR, "src/documents", asset)),
);
}
-
- // The review graph is not part of this package any more. It is a Plugin an
- // operator selects, so its assets ship with *it* — and the emitted CLI
- // neither depends on it nor carries a byte of it. A build that quietly put
- // it back would be a distribution deciding what a run installs.
- const manifest = yield* readEmittedManifest(root);
- expect(Object.keys(manifest.dependencies ?? {})).not.toContain(
- "@executablemd/code-review-agent",
- );
});
/**
diff --git a/scripts/tests/no-sync-filesystem.test.ts b/scripts/tests/no-sync-filesystem.test.ts
index 08baa9e80..0330ee949 100644
--- a/scripts/tests/no-sync-filesystem.test.ts
+++ b/scripts/tests/no-sync-filesystem.test.ts
@@ -157,7 +157,7 @@ describe("local/no-sync-filesystem", () => {
});
it("finds no configuration entry turning the rule off for a path", function* () {
- for (const config of [".oxlintrc.json", "oxlint.shared.json", ".reviews/.oxlintrc.json"]) {
+ for (const config of [".oxlintrc.json", "oxlint.shared.json"]) {
const source = yield* readTextFile(path.join(ROOT, config));
const off = /"local\/no-sync-filesystem":\s*(?:"off"|\["off")/u.test(source);
expect([config, off]).toEqual([config, false]);
diff --git a/scripts/tests/oxlint-policy.test.ts b/scripts/tests/oxlint-policy.test.ts
index 17628c9e4..039d9fb61 100644
--- a/scripts/tests/oxlint-policy.test.ts
+++ b/scripts/tests/oxlint-policy.test.ts
@@ -10,23 +10,12 @@ import { useTempDirectory } from "@executablemd/test-support/temp";
import { ROOT, runOxlint } from "./oxlint.ts";
import { BANNED_WORDS } from "../banned-words.js";
-import {
- categorizeRule,
- SENSOR_RULES,
- TYPE_AWARE_RULES,
-} from "../../packages/code-review-agent/src/categories.ts";
-import { summarizeDoctorProbe } from "../../packages/code-review-agent/src/doctor.ts";
const SHARED = "oxlint.shared.json";
const GATE = ".oxlintrc.json";
-const SENSOR = ".reviews/.oxlintrc.json";
-/** A production module, a test module, and a review component. */
-const SUBJECTS = [
- "packages/code-review-agent/src/doctor.ts",
- "packages/code-review-agent/tests/doctor.test.ts",
- "packages/code-review-agent/src/components/Doctor.ts",
-];
+/** A production module and a test module. */
+const SUBJECTS = ["packages/core/mod.ts", "packages/core/tests/expand.test.ts"];
const PLUGINS = ["typescript", "unicorn", "import"];
@@ -167,13 +156,6 @@ function severities(rules: Record): Map {
return new Map(Object.entries(rules).map(([id, value]) => [bare(id), severity(value)]));
}
-function enabled(rules: Record): string[] {
- return Object.entries(rules)
- .filter(([, value]) => severity(value) !== "off")
- .map(([id]) => id)
- .sort();
-}
-
/** A directory of this test's own. */
function scratch(prefix: string): Operation {
return useTempDirectory(`${prefix}-`);
@@ -213,23 +195,16 @@ describe("Oxlint policy", () => {
expect((yield* runOxlint(".oxlint-prose.json", [file])).code).not.toBe(0);
});
- it("extends one canonical shared policy from both profiles", function* () {
+ it("extends the one shared policy from the gate", function* () {
const gate = yield* config(GATE);
- const sensor = yield* config(SENSOR);
expect(gate.extends).toEqual(["./oxlint.shared.json"]);
- expect(sensor.extends).toEqual(["../oxlint.shared.json"]);
-
- const fromGate = path.resolve(ROOT, gate.extends[0]);
- const fromSensor = path.resolve(ROOT, ".reviews", sensor.extends[0]);
- expect(fromGate).toBe(path.join(ROOT, SHARED));
- expect(fromSensor).toBe(fromGate);
+ expect(path.resolve(ROOT, gate.extends[0])).toBe(path.join(ROOT, SHARED));
});
- it("keeps the built-in catalog in the shared source and out of both profiles", function* () {
+ it("keeps the built-in catalog in the shared source and out of the gate", function* () {
const shared = yield* config(SHARED);
const gate = yield* config(GATE);
- const sensor = yield* config(SENSOR);
expect(shared.plugins).toEqual(PLUGINS);
expect(shared.categories).toEqual({
@@ -239,9 +214,6 @@ describe("Oxlint policy", () => {
style: "off",
});
- const signals = Object.entries(shared.rules).filter(([, value]) => severity(value) === "warn");
- expect(signals.map(([id]) => bare(id)).sort()).toEqual([...SENSOR_RULES].sort());
-
const denials = Object.entries(shared.rules).filter(([, value]) => severity(value) === "off");
expect(denials.map(([id]) => id).sort()).toEqual([...DENIED].sort());
@@ -253,90 +225,24 @@ describe("Oxlint policy", () => {
]);
expect(gate.categories).toEqual({});
- expect(sensor.categories).toEqual({});
- expect(
- Object.keys(gate.rules)
- .map(bare)
- .filter((id) => SENSOR_RULES.includes(id)),
- ).toEqual([]);
- expect(Object.keys(sensor.rules)).toEqual([]);
- });
-
- it("keeps the curated signal options the sensor's density model reads", function* () {
- const shared = yield* config(SHARED);
-
- expect(shared.rules["eslint/no-unused-vars"]).toEqual([
- "warn",
- {
- args: "all",
- argsIgnorePattern: "^_",
- varsIgnorePattern: "^_",
- caughtErrorsIgnorePattern: "^_",
- destructuredArrayIgnorePattern: "^_",
- ignoreRestSiblings: true,
- },
- ]);
- expect(shared.rules["eslint/no-console"]).toEqual(["warn", { allow: ["warn", "error"] }]);
+ const builtIn = new Set(Object.keys(shared.rules));
+ expect(Object.keys(gate.rules).filter((id) => builtIn.has(id))).toEqual([]);
});
- it("resolves both inheritance chains for production, test and review-component paths", function* () {
+ it("resolves the inheritance chain for production and test paths", function* () {
for (const subject of SUBJECTS) {
const gate = yield* profile(GATE, subject);
- const sensor = yield* profile(SENSOR, subject);
expect(gate.plugins.sort()).toEqual([...PLUGINS].sort());
- expect(sensor.plugins.sort()).toEqual([...PLUGINS].sort());
expect(Object.keys(gate.rules).length).toBeGreaterThan(0);
- expect(Object.keys(sensor.rules).length).toBeGreaterThan(0);
- }
- });
-
- it("keeps the sensor's built-in rules a subset of the gate's", function* () {
- for (const subject of SUBJECTS) {
- const gate = new Set(enabled((yield* profile(GATE, subject)).rules));
- const sensor = enabled((yield* profile(SENSOR, subject)).rules);
-
- expect(sensor.filter((rule) => !gate.has(rule))).toEqual([]);
}
});
- it("keeps every curated signal warning-enabled for the sensor", function* () {
- for (const subject of SUBJECTS) {
- const applied = severities((yield* profile(SENSOR, subject)).rules);
+ it("keeps the named conflicts and undecided candidates off", function* () {
+ const applied = severities((yield* profile(GATE, SUBJECTS[0])).rules);
- for (const rule of SENSOR_RULES) {
- expect([rule, applied.get(rule)]).toEqual([rule, "warn"]);
- expect([rule, categorizeRule(rule)]).not.toEqual([rule, ["other"]]);
- }
- }
- });
-
- it("keeps the named conflicts and undecided candidates off in both profiles", function* () {
- for (const file of [GATE, SENSOR]) {
- const applied = severities((yield* profile(file, SUBJECTS[0])).rules);
-
- for (const rule of DENIED) {
- expect([file, rule, applied.get(bare(rule))]).toEqual([file, rule, "off"]);
- }
- }
- });
-
- it("keeps the sensor advisory, with no local plugin, local rule or error severity", function* () {
- const sensor = yield* config(SENSOR);
- expect(sensor.jsPlugins).toEqual([]);
- expect(Object.keys(sensor.rules)).toEqual([]);
- expect(sensor.overrides).toEqual([]);
-
- for (const subject of SUBJECTS) {
- const applied = yield* profile(SENSOR, subject);
- const rules = [
- ...Object.entries(applied.rules),
- ...applied.overrides.flatMap((entry) => Object.entries(entry.rules)),
- ];
-
- expect(rules.filter(([, value]) => severity(value) === "error")).toEqual([]);
- expect(rules.map(([id]) => id).filter((id) => id.startsWith("local/"))).toEqual([]);
- expect(applied.jsPlugins).toEqual([]);
+ for (const rule of DENIED) {
+ expect([rule, applied.get(bare(rule))]).toEqual([rule, "off"]);
}
});
@@ -359,7 +265,6 @@ describe("Oxlint policy", () => {
it("carries both the shared and the gate's test override through inheritance", function* () {
for (const subject of SUBJECTS) {
const gate = yield* profile(GATE, subject);
- const sensor = yield* profile(SENSOR, subject);
expect(gate.overrides.map((entry) => entry.files)).toEqual([TEST_FILES, TEST_FILES]);
expect(gate.overrides.map((entry) => severities(entry.rules).get("no-console"))).toEqual([
@@ -369,70 +274,37 @@ describe("Oxlint policy", () => {
expect(
gate.overrides.map((entry) => severities(entry.rules).get("no-redundant-test-scope")),
).toEqual([undefined, "error"]);
-
- expect(sensor.overrides.map((entry) => entry.files)).toEqual([TEST_FILES]);
- expect(severities(sensor.overrides[0].rules).get("no-console")).toBe("off");
- expect(severities(sensor.overrides[0].rules).get("no-empty-function")).toBe("off");
}
});
- it("applies the shared test override to the files the sensor lints", function* () {
+ it("applies the shared test override to the files the gate lints", function* () {
const directory = yield* scratch("oxlint-policy");
const body = 'export function probe() {\n console.log("probe");\n}\n';
yield* ensureDir(path.join(directory, "tests"));
yield* writeTextFile(path.join(directory, "tests", "probe.ts"), body);
yield* writeTextFile(path.join(directory, "probe.ts"), body);
- for (const file of [GATE, SENSOR]) {
- const run = yield* runOxlint(file, [
- "--format=json",
- path.join(directory, "tests", "probe.ts"),
- path.join(directory, "probe.ts"),
- ]);
- const report = parseReport(run.stdout, `oxlint -c ${file}`);
- const consoles = report.diagnostics.filter((entry) => entry.code === "eslint(no-console)");
-
- expect(consoles.map((entry) => path.relative(directory, entry.filename))).toEqual([
- "probe.ts",
- ]);
- }
- });
-
- it("reports Doctor's catalog as the curated signals", function* () {
- const available = summarizeDoctorProbe({ exitCode: 0, stderr: "", diagnostics: [] });
- expect(available.bloatRulesAvailable).toEqual([...SENSOR_RULES]);
- expect(available.bloatRulesMissing).toEqual([]);
+ const run = yield* runOxlint(GATE, [
+ "--format=json",
+ path.join(directory, "tests", "probe.ts"),
+ path.join(directory, "probe.ts"),
+ ]);
+ const report = parseReport(run.stdout, `oxlint -c ${GATE}`);
+ const consoles = report.diagnostics.filter((entry) => entry.code === "eslint(no-console)");
- const syntaxOnly = summarizeDoctorProbe({
- exitCode: 2,
- stderr: "tsgolint panic",
- diagnostics: [],
- });
- const typeAware = new Set(TYPE_AWARE_RULES);
- expect(syntaxOnly.bloatRulesMissing).toEqual([...TYPE_AWARE_RULES]);
- expect(syntaxOnly.bloatRulesAvailable.sort()).toEqual(
- SENSOR_RULES.filter((rule) => !typeAware.has(rule)).sort(),
- );
+ expect(consoles.map((entry) => path.relative(directory, entry.filename))).toEqual(["probe.ts"]);
});
- it("pins one Oxlint and one tsgolint version across the toolchain", function* () {
- const provisioning = yield* readTextFile(
- path.join(ROOT, "packages/code-review-agent/src/documents/components/EnsureOxlint.md"),
- );
- const oxlint = /const OXLINT_TAG = "apps_v([\d.]+)";/u.exec(provisioning);
- const tsgolint = /const TSGOLINT_VERSION = "([\d.]+)";/u.exec(provisioning);
- if (!oxlint || !tsgolint) {
- throw new Error("EnsureOxlint.md no longer declares its Oxlint and tsgolint versions");
- }
+ it("pins one exact Oxlint version across the toolchain", function* () {
+ const nodeManifest = JSON.parse(yield* readTextFile(path.join(ROOT, "package.json")));
+ const version = nodeManifest.devDependencies.oxlint;
+ expect(version).toMatch(/^\d+\.\d+\.\d+$/u);
const denoConfig = yield* readTextFile(path.join(ROOT, "deno.json"));
- const nodeManifest = yield* readTextFile(path.join(ROOT, "package.json"));
- expect(denoConfig).toContain(`"oxlint": "npm:oxlint@${oxlint[1]}"`);
- expect(denoConfig).toContain(`"oxlint-tsgolint": "npm:oxlint-tsgolint@${tsgolint[1]}"`);
- expect(nodeManifest).toContain(`"oxlint": "${oxlint[1]}"`);
+ expect(denoConfig).toContain(`"oxlint": "npm:oxlint@${version}"`);
const run = yield* runOxlint(GATE, ["--version"]);
- expect(run.stdout.trim()).toContain(oxlint[1]);
+ expect(run.stdout.trim()).toContain(version);
});
it("fails rather than reporting an empty policy when inheritance is broken", function* () {
diff --git a/scripts/tests/packaged-document.test.ts b/scripts/tests/packaged-document.test.ts
index 6fa5ea0e5..f891d3fa1 100644
--- a/scripts/tests/packaged-document.test.ts
+++ b/scripts/tests/packaged-document.test.ts
@@ -22,10 +22,9 @@
*
* The third kind still decides the other two. An embedded package carries
* everything inside it, so a `src/documents/` or a `components.md` beneath one
- * is already in the binary and must *not* be named again: the code-review
- * package ships both, and naming either individually would compile the same
- * bytes twice while reading as the coverage the whole-package entry already
- * provides. Each discovered asset is therefore required in exactly one place —
+ * is already in the binary and must *not* be named again: naming either
+ * individually would compile the same bytes twice while reading as the
+ * coverage the whole-package entry already provides. Each discovered asset is therefore required in exactly one place —
* the individual list when nothing covers it, the whole-package entry when
* something does.
*/
@@ -44,7 +43,6 @@ import {
EMBEDDED_PACKAGES,
PACKAGED_DOCUMENTATION,
PACKAGED_DOCUMENTS,
- UNEMBEDDED_PACKAGES,
} from "../lib/compile.ts";
const ROOT = fileURLToPath(new URL("../../", import.meta.url));
@@ -58,12 +56,7 @@ interface PackagedDocuments {
}
function* members(): Operation {
- const all = (yield* until(readdir(path.join(ROOT, "packages")))).sort();
- // A package the binary does not contain ships its assets for whoever
- // installed *it*, and embedding them would put bytes in the binary no code
- // in it can reach. The claim that it is outside the binary is checked below
- // rather than taken on trust.
- return all.filter((member) => !UNEMBEDDED_PACKAGES.includes(`packages/${member}`));
+ return (yield* until(readdir(path.join(ROOT, "packages")))).sort();
}
/** Repository-relative paths beneath `directory`, or none when it does not exist. */
@@ -161,26 +154,6 @@ describe("the canonical compile inputs", () => {
coverage(documentation, PACKAGED_DOCUMENTATION);
});
- it("leave a package out of the binary only when nothing in the binary imports it", function* () {
- // The filter above hides a package from the sweep, so it is exactly the
- // kind of claim that rots. Every entry has to still be true: a CLI
- // production module importing one would put it in the binary's module
- // graph, and its assets would then be missing rather than deliberately
- // absent.
- expect(UNEMBEDDED_PACKAGES.length).toBeGreaterThan(0);
- const sources = (yield* beneath("packages/cli/src")).filter((entry) => entry.endsWith(".ts"));
- expect(sources.length).toBeGreaterThan(0);
- const manifest = JSON.parse(yield* readTextFile(path.join(ROOT, "packages/cli/package.json")));
- for (const excluded of UNEMBEDDED_PACKAGES) {
- const name = JSON.parse(yield* readTextFile(path.join(ROOT, excluded, "package.json"))).name;
- for (const source of sources) {
- const text = yield* readTextFile(path.join(ROOT, source));
- expect([source, text.includes(name)]).toEqual([source, false]);
- }
- expect(Object.keys(manifest.dependencies ?? {})).not.toContain(name);
- }
- });
-
it("embed exactly those, and nothing else", function* () {
const argv = compileArguments({ output: "dist/xmd" });
const included = argv.flatMap((token, index) =>
diff --git a/scripts/tests/plan-component-compiled.test.ts b/scripts/tests/plan-component-compiled.test.ts
index 80221f129..88e7a068e 100644
--- a/scripts/tests/plan-component-compiled.test.ts
+++ b/scripts/tests/plan-component-compiled.test.ts
@@ -347,86 +347,52 @@ describe("compiled xmd", { sanitizeOps: false, sanitizeResources: false }, () =>
* asked for by name.
*/
describe(
- "the trusted review graph, as both installations load it",
+ "a selected Plugin's own names, as both installations load it",
{ sanitizeOps: false, sanitizeResources: false },
() => {
/**
- * The review graph belongs to the Plugin an operator selected, in both
- * installations, and a checkout cannot take a name back from it.
- *
- * A review is the one program that must not be answerable by the thing it is
- * reviewing. While its components lived in `.reviews/` and every entrypoint
- * reached them with `--include`, a pull request could add its own
- * `Finding.md` and the review would run the branch's copy while reporting on
- * it. So the graph moved into the package, and this is the proof that it
- * actually reaches a person's installation when they select it. XMD bundles
- * no Plugin: both installations name the package by path, which is also the
- * route an installation with no checkout has.
+ * The names a selected Plugin declares or reserves belong to it, in both
+ * installations, and a checkout cannot take one back.
*
* Everything here is deliberately hostile to the mechanism. The working
* directory is not the checkout, so a lookup that resolved relative to the
- * process or to a subject tree finds nothing. No review include is passed, so
- * a build that still depended on one renders nothing. And the one include
- * that *is* passed contains a same-named `Finding.md` that accepts exactly the
- * props the real one accepts — it would succeed and quietly emit its own
- * marker if selection preferred it, rather than failing in a way that would
- * pass this case for the wrong reason.
+ * process or to a subject tree finds nothing. The one include that is
+ * passed contains same-named `Shadowed.md` and `Stamp.md` files — one for
+ * the declared Markdown tier, one for the reserved registration — that
+ * would succeed and quietly emit their own markers if selection preferred
+ * them, rather than failing in a way that would pass this case for the
+ * wrong reason.
*
* The unrelated `CustomProbe.md` in the same directory is the other half:
- * reserving forty-one names is not the same as turning discovery off, and a
- * change that secured the review by disabling `--include` would be a
- * regression this would catch.
- *
- * No agent, no network, no credential and no browser: the components chosen
- * here render from their own bytes, which is what makes this cheap enough to
- * be an ordinary case.
+ * reserving names is not the same as turning discovery off, and a change
+ * that secured them by disabling `--include` would be a regression this
+ * would catch.
*/
- it("supplies the trusted review graph to both installations that select it", function* () {
+ it("supplies the selected Plugin's components to both installations", function* () {
if (!(yield* exists(BINARY))) {
throw new Error(`${BINARY} is missing — run \`deno task build\` before this case`);
}
- const FINDING = path.join(
- ROOT,
- "packages/code-review-agent/src/documents/components/Finding.md",
- );
- const trusted = yield* readTextFile(FINDING);
- const digest = createHash("sha256").update(trusted, "utf8").digest("hex");
+ // The bytes `declaring.mjs` declares for ``.
+ const shadowed = "the selected declaration, not the checkout's\n";
+ const digest = createHash("sha256").update(shadowed, "utf8").digest("hex");
- const elsewhere = yield* until(mkdtemp(path.join(tmpdir(), "xmd-trusted-review-")));
+ const elsewhere = yield* until(mkdtemp(path.join(tmpdir(), "xmd-selected-plugin-")));
yield* ensure(() => rm(elsewhere, { recursive: true, force: true }));
const custom = path.join(elsewhere, "custom");
yield* until(mkdir(custom));
- yield* writeTextFile(
- path.join(custom, "Finding.md"),
- [
- "---",
- "props:",
- " type: object",
- " properties:",
- " when: { type: boolean }",
- " severity: { type: string }",
- " message: { type: string }",
- " required: [when, message]",
- " additionalProperties: false",
- "---",
- "",
- "HOSTILE-FINDING {props.message}",
- ].join("\n"),
- );
+ yield* writeTextFile(path.join(custom, "Shadowed.md"), "HOSTILE-SHADOWED");
+ yield* writeTextFile(path.join(custom, "Stamp.md"), "HOSTILE-STAMP");
yield* writeTextFile(path.join(custom, "CustomProbe.md"), "the caller's own component ran");
yield* writeTextFile(
path.join(elsewhere, "doc.md"),
- ['', "", ""].join(
- "\n",
- ),
+ ["", "", "", "", ""].join("\n"),
);
- // The graph is a Plugin an operator selects, so both installations name
- // it the same way: an explicit path to the package that ships it. XMD
- // bundles none, and neither of these carries it until it is named.
- const review = `--plugin=${path.join(ROOT, "packages/code-review-agent/mod.ts")}`;
+ // Both installations name the Plugin the same way: an explicit path, which
+ // is also the route an installation with no checkout has.
+ const plugin = `--plugin=${path.join(ROOT, "packages/cli/tests/fixtures/plugins/declaring.mjs")}`;
const installations = [
["compiled", BINARY, [] as string[]],
["source", Deno.execPath(), ["run", "--allow-all", path.join(ROOT, SOURCE_ENTRY)]],
@@ -438,28 +404,27 @@ describe(
for (const [label, command, args] of installations) {
const attempt = yield* timebox(TIMEOUT, function* () {
return yield* exec(command, {
- // One include, and it is the caller's. The Plugin is named, and
- // nothing names the review components — which is the entire claim:
- // a selected declaration beats a same-named file in the include.
- arguments: [...args, "run", review, "doc.md", "--include", "custom"],
+ // One include, and it is the caller's: a selected declaration or
+ // reservation beats a same-named file in it.
+ arguments: [...args, "run", plugin, "doc.md", "--include", "custom"],
cwd: elsewhere,
}).join();
});
if (attempt.timeout) {
- throw new Error(`the ${label} installation timed out running the review components`);
+ throw new Error(`the ${label} installation timed out running the Plugin's components`);
}
const run = attempt.value;
expect(`${label}: ${run.code}\n${run.stderr}`).toBe(`${label}: 0\n`);
rendered[label] = run.stdout;
- // The declared component rendered: its own `ts eval` chose the icon from
- // the severity it was given, which the caller's plain-prose copy could
- // not have produced.
- expect(`${label}: ${run.stdout.includes("🔴 probe")}`).toBe(`${label}: true`);
- // And the checkout's same-named component never ran.
- expect(`${label}: ${run.stdout.includes("HOSTILE-FINDING")}`).toBe(`${label}: false`);
+ expect(`${label}: ${run.stdout.includes(shadowed.trim())}`).toBe(`${label}: true`);
+ expect(`${label}: ${run.stdout.includes("stamped by the selected Plugin")}`).toBe(
+ `${label}: true`,
+ );
+ // And the checkout's same-named components never ran.
+ expect(`${label}: ${run.stdout.includes("HOSTILE")}`).toBe(`${label}: false`);
// While the caller's unrelated component resolved exactly as it always
- // did. A change that secured the review by disabling inclusion fails here.
+ // did. A change that secured the names by disabling inclusion fails here.
expect(`${label}: ${run.stdout.includes("the caller's own component ran")}`).toBe(
`${label}: true`,
);
@@ -467,9 +432,8 @@ describe(
const described = yield* timebox(TIMEOUT, function* () {
return yield* exec(command, {
// The same selection: what an installation describes is what it
- // would run, and describing it without the Plugin would describe a
- // vocabulary the run above did not have.
- arguments: [...args, "syntax", review, "--json", "--include", "custom"],
+ // would run.
+ arguments: [...args, "syntax", plugin, "--json", "--include", "custom"],
cwd: elsewhere,
}).join();
});
@@ -482,48 +446,40 @@ describe(
(category: { entries: unknown[] }) => category.entries,
);
- const finding = entries.filter((entry: { name?: string }) => entry?.name === "Finding");
+ const declared = entries.filter((entry: { name?: string }) => entry?.name === "Shadowed");
// Once. A build listing it twice would be describing the caller's copy
// beside the declared one, which is a vocabulary no run has.
- expect(`${label}: ${finding.length}`).toBe(`${label}: 1`);
- // The identity, whole: the package and the asset rather than a path, and
- // the digest of the bytes this build actually ships. A build carrying
- // different bytes under this name answers differently here.
- expect(finding[0].origin).toEqual({
+ expect(`${label}: ${declared.length}`).toBe(`${label}: 1`);
+ // The identity, whole: the declared origin rather than a path, and the
+ // digest of the bytes the Plugin states.
+ expect(declared[0].origin).toEqual({
kind: "declared-markdown",
- origin: "@executablemd/code-review-agent/components/Finding.md",
+ origin: "fixture/declaring/Shadowed.md",
digest,
});
- expect(finding[0].sourceKind).toBe("declared-markdown");
- reported[label] = finding[0];
-
- // The TypeScript half of the same graph is equally claimed. Moving only
- // the Markdown would have left shadowable exactly the six components that
- // run processes, read credentials and reach the network.
- const context = entries.filter(
- (entry: { name?: string }) => entry?.name === "ReviewContext",
- );
- expect(`${label}: ${context.length}`).toBe(`${label}: 1`);
- expect(context[0].origin).toEqual({
+ expect(declared[0].sourceKind).toBe("declared-markdown");
+ reported[label] = declared[0];
+
+ // The registered tier is reserved as well.
+ const stamp = entries.filter((entry: { name?: string }) => entry?.name === "Stamp");
+ expect(`${label}: ${stamp.length}`).toBe(`${label}: 1`);
+ expect(stamp[0].origin).toEqual({
kind: "registered",
- origin: "@executablemd/code-review-agent",
+ origin: "fixture/declaring",
reserved: true,
});
- // And the whole graph is present, not just the two probed above. A build
- // that shipped a partial `src/documents/` would resolve some names and
- // fail on the rest at a reviewer's first run.
- const declared = entries.filter(
+ // And every declaration is present, not just the one probed above.
+ const all = entries.filter(
(entry: { sourceKind?: string; origin?: { origin?: string } }) =>
entry?.sourceKind === "declared-markdown" &&
- entry.origin?.origin?.startsWith("@executablemd/code-review-agent/"),
+ entry.origin?.origin?.startsWith("fixture/declaring/"),
);
- expect(`${label}: ${declared.length}`).toBe(`${label}: 35`);
+ expect(`${label}: ${all.length}`).toBe(`${label}: 2`);
}
- // The two installations are one product. A compiled binary has no checkout
- // to read from and a source run has nothing else, so an asset that reached
- // one and not the other would show up here rather than at a reviewer's.
+ // The two installations are one product, so a declaration that reached
+ // one and not the other shows up here.
expect(rendered.compiled).toBe(rendered.source);
expect(reported.compiled).toEqual(reported.source);
});
diff --git a/scripts/tests/plugin-compiled.test.ts b/scripts/tests/plugin-compiled.test.ts
index 58e418b6d..217baae83 100644
--- a/scripts/tests/plugin-compiled.test.ts
+++ b/scripts/tests/plugin-compiled.test.ts
@@ -18,7 +18,7 @@
import { describe, it } from "@executablemd/test-support/bdd";
import { expect } from "@executablemd/test-support/expect";
import { ensure, scoped, until } from "effection";
-import { ensureDir, exists, readTextFile, rm, writeTextFile } from "@effectionx/fs";
+import { ensureDir, exists, rm, writeTextFile } from "@effectionx/fs";
import { exec } from "@effectionx/process";
import { timebox } from "@effectionx/timebox";
import type { ProcessResult } from "@effectionx/process";
@@ -127,130 +127,32 @@ describe("compiled xmd", { sanitizeOps: false, sanitizeResources: false }, () =>
expect(dirEntry?.origin?.origin).toBe("@executablemd/core");
expect(dirEntry?.description ?? "").toContain("directory");
// And nothing from a Plugin nobody named.
- expect(names).not.toContain("Finding");
- expect(names).not.toContain("ReviewContext");
+ expect(names).not.toContain("Greeting");
+ expect(names).not.toContain("Stamp");
});
});
- it("gains the review graph from a Plugin the operator named by path", function* () {
+ it("gains a Plugin's vocabulary from a module the operator named by path", function* () {
if (!(yield* exists(BINARY))) {
throw new Error(`${BINARY} is missing — run \`deno task build\` before this case`);
}
- // No `node_modules` here and nothing embedded, so the package is reached
+ // No `node_modules` here and nothing embedded, so the module is reached
// the way the accepted contract says it is: an explicit filesystem path.
- // The binary carries its own copy of core and the package resolves the
- // checkout's, which is the loaded-copy case under real conditions.
+ // The binary carries its own copy of core and the fixture resolves the
+ // checkout's, which is the loaded-copy case under real conditions — for a
+ // declared Markdown component and a reserved registration alike.
yield* useElsewhere(function* (dir) {
- const review = path.join(ROOT, "packages/code-review-agent/mod.ts");
+ const declaring = path.join(ROOT, "packages/cli/tests/fixtures/plugins/declaring.mjs");
const run = yield* runBinary(
- ["syntax", "--json", `--plugin=${review}`, "--include", dir],
+ ["syntax", "--json", `--plugin=${declaring}`, "--include", dir],
dir,
);
if (run.code !== 0) {
throw new Error(`the compiled binary exited ${run.code}\n${run.stderr}`);
}
const names = describedNames(run.stdout);
- expect(names).toContain("Finding");
- expect(names).toContain("ReviewContext");
- });
- });
-
- it("groups diagnostics through the checked-in review roots in the compiled host", function* () {
- const rawDiagnostics = [
- {
- ruleId: "no-console",
- message: "console",
- severity: "warning",
- file: "a.ts",
- line: 2,
- column: 3,
- },
- {
- ruleId: "no-console",
- message: "console",
- severity: "warning",
- file: "b.ts",
- line: 4,
- column: 1,
- },
- {
- ruleId: "import/no-unresolved",
- message: "Cannot find module",
- severity: "warning",
- file: "a.ts",
- line: 1,
- column: 1,
- },
- ];
- const pr = { stats: { additions: 100 } };
- const doctor = {
- oxlintInstalled: true,
- oxlintVersion: "1.74.0",
- tsgolintInstalled: true,
- tsgolintVersion: "0.25.0",
- tsconfigExists: true,
- nodeModulesExists: true,
- typeAwareAvailable: true,
- filesAnalyzed: 2,
- filesSkipped: 0,
- importErrors: 1,
- availableRuleIds: [],
- bloatRulesAvailable: [],
- bloatRulesMissing: [],
- recommendation: "type-aware-filtered",
- nativeSpecifiers: { count: 0, files: [], jsr: 0, npm: 0 },
- };
- const header = `---
-props:
- type: object
- properties:
- rawDiagnostics: { type: array }
- pr: { type: object }
- doctor: { type: object }
- required: [rawDiagnostics, pr, doctor]
- additionalProperties: false
----
-`;
- const review = path.join(ROOT, "packages/code-review-agent/mod.ts");
- yield* useElsewhere(function* (dir) {
- for (const root of ["ReviewPR.md", "ReviewPR.local.md"]) {
- const source = yield* readTextFile(path.join(ROOT, ".reviews", root));
- const sensorEnd = source.indexOf("/>", source.indexOf('as="rawDiagnostics"')) + 2;
- const stageEnd = source.indexOf("<", sensorEnd + 1);
- // The stage is taken from the actual root, so restoring its old import
- // fails this compiled path even if the adapter itself still works.
- const remainder = source.slice(sensorEnd);
- const stage = remainder.startsWith("\n\n```ts eval")
- ? remainder.slice(0, remainder.indexOf("```", remainder.indexOf("```") + 3) + 3)
- : source.slice(sensorEnd, source.indexOf("/>", stageEnd) + 2);
- const document = path.join(dir, "diagnostics.md");
- yield* writeTextFile(
- document,
- `${header}\n\n\n${stage}\n\n`,
- );
- const run = yield* runBinary(
- [
- "run",
- `--plugin=${review}`,
- document,
- "--props",
- JSON.stringify({ rawDiagnostics, pr, doctor }),
- ],
- dir,
- );
- if (run.code !== 0) {
- throw new Error(`${root}: compiled diagnostics exited ${run.code}\n${run.stderr}`);
- }
- const grouped = JSON.parse(run.stdout);
- expect(grouped.total).toBe(2);
- expect(grouped.fileCount).toBe(2);
- expect(grouped.ruleCount).toBe(1);
- expect(grouped.density).toBe(0.02);
- expect(grouped.groups[0].files).toEqual(["a.ts", "b.ts"]);
- expect(grouped.groups[0].instances).toEqual(rawDiagnostics.slice(0, 2));
- expect(grouped.byCategory.verbosity[0].ruleId).toBe("no-console");
- expect(grouped.summary).toContain("2 diagnostics across 2 files (1 rule)");
- }
+ expect(names).toContain("Greeting");
+ expect(names).toContain("Stamp");
});
});
diff --git a/scripts/tests/review-infrastructure.test.ts b/scripts/tests/review-infrastructure.test.ts
deleted file mode 100644
index 5d39194ae..000000000
--- a/scripts/tests/review-infrastructure.test.ts
+++ /dev/null
@@ -1,1261 +0,0 @@
-import { useComponentModuleLoader } from "@executablemd/test-support/host";
-import { describe, it } from "@executablemd/test-support/bdd";
-import { expect } from "@executablemd/test-support/expect";
-import { FetchApi, type FetchResponse } from "@effectionx/fetch";
-import { expandGlob, readTextFile } from "@effectionx/fs";
-import { InMemoryStream } from "@executablemd/durable-streams";
-import { API } from "@executablemd/runtime";
-import { useStubFs } from "@executablemd/runtime/test";
-import { execute, executeInstalled } from "../../packages/core/src/execute.ts";
-import {
- REVIEW_DOCUMENTS,
- REVIEW_REGISTRATIONS,
- reviewComponentDeclarations,
-} from "../../packages/code-review-agent/src/review-components.ts";
-import type { ReviewDocument } from "../../packages/code-review-agent/src/review-components.ts";
-import { createHash } from "node:crypto";
-import { Sample } from "../../packages/core/src/sample-api.ts";
-import { useTempFileCompiler } from "../../packages/core/src/temp-file-compiler.ts";
-import { forEach } from "@effectionx/stream-helpers";
-import { each, scoped, until } from "effection";
-import type { Operation } from "effection";
-
-interface RequestRecord {
- input: string;
- init?: RequestInit;
- shouldExpect: boolean;
-}
-
-interface ProcessResult {
- exitCode: number;
- stdout: string;
- stderr: string;
-}
-
-interface DocumentRunOptions {
- includes?: string[];
- env?: Record;
- glob?: Array<{ path: string; isFile: boolean; isDirectory: boolean }>;
- stat?: (path: string) => { exists: boolean; isFile: boolean; isDirectory: boolean } | undefined;
- process?: ProcessResult | ((command: readonly string[]) => ProcessResult);
-}
-
-function response(body: string): FetchResponse {
- const raw = new Response(body, { status: 200 });
- return {
- raw,
- get bodyUsed() {
- return raw.bodyUsed;
- },
- get ok() {
- return raw.ok;
- },
- get status() {
- return raw.status;
- },
- get statusText() {
- return raw.statusText;
- },
- get headers() {
- return raw.headers;
- },
- get url() {
- return raw.url;
- },
- get redirected() {
- return raw.redirected;
- },
- get type() {
- return raw.type;
- },
- *json(parse?: (value: unknown) => T): Operation {
- const value: unknown = JSON.parse(body);
- return parse ? parse(value) : (value as T);
- },
- *text(): Operation {
- return body;
- },
- *arrayBuffer(): Operation {
- return yield* until(raw.arrayBuffer());
- },
- *blob(): Operation {
- return yield* until(raw.blob());
- },
- *formData(): Operation {
- return yield* until(raw.formData());
- },
- body() {
- throw new Error("body streaming is not used by this test");
- },
- *expect(): Operation {
- return this;
- },
- };
-}
-
-function* run(
- provider: string,
- token?: string,
-): Operation<{ requests: RequestRecord[]; journal: string }> {
- const requests: RequestRecord[] = [];
- const files: Record = {
- "doc.md": [
- "",
- "",
- '',
- '',
- '',
- "",
- "",
- '',
- ].join("\n"),
- "components/GitHubAuth.md": provider,
- "components/Probe.md": [
- "---",
- "props:",
- " type: object",
- " properties:",
- " label: { type: string }",
- " url: { type: string }",
- " required: [label, url]",
- " additionalProperties: false",
- "---",
- "",
- "```ts eval",
- "const value = yield* fetch(props.url, { method: 'POST', headers: { 'X-Caller': 'kept', 'Content-Type': 'application/custom' }, body: 'request-body' }).expect().json();",
- "```",
- "",
- "{props.label}={value.message}",
- ].join("\n"),
- "components/Wrapper.md": '',
- "components/NoExpect.md": [
- "```ts eval",
- 'yield* fetch("https://api.github.com/repos/no-expect");',
- "```",
- ].join("\n"),
- };
-
- yield* useStubFs(files);
- yield* API.Env.around({
- *env([name], next) {
- if (name === "GITHUB_TOKEN") {
- return token;
- }
- return yield* next(name);
- },
- });
- yield* FetchApi.around(
- {
- *fetch([input, init, shouldExpect]) {
- const request =
- input instanceof Request ? input.url : input instanceof URL ? input.href : input;
- requests.push({ input: request, init, shouldExpect });
- return response(JSON.stringify({ message: "ok" }));
- },
- },
- { at: "min" },
- );
-
- const stream = new InMemoryStream();
- const execution = yield* execute({ path: "doc.md", stream, includes: ["components"] });
- yield* forEach(function* () {}, execution.output);
- const result = yield* execution;
- expect(result.ok).toBe(true);
- return { requests, journal: JSON.stringify(stream.snapshot()) };
-}
-
-function* runDocumentResult(
- document: string,
- components: Record,
- options: DocumentRunOptions = {},
-): Operation<{ ok: boolean; error?: Error; journal: string; text: string }> {
- return yield* scoped(function* () {
- const environment = options.env;
- if (environment) {
- yield* API.Env.around({
- *env([name], next) {
- return name in environment ? environment[name] : yield* next(name);
- },
- });
- }
- const stat = options.stat;
- const entries = options.glob;
- if (stat || entries) {
- yield* API.Fs.around({
- *stat([path], next) {
- const value = stat?.(path);
- return value ?? (yield* next(path));
- },
- *glob([parameters], next) {
- return entries ?? (yield* next(parameters));
- },
- });
- }
- yield* useComponentModuleLoader();
- yield* useStubFs({ "doc.md": document, ...components });
- const process = options.process;
- if (process) {
- yield* API.Process.around({
- *exec([parameters]) {
- return typeof process === "function" ? process(parameters.command) : process;
- },
- });
- }
- const stream = new InMemoryStream();
- const execution = yield* execute({
- path: "doc.md",
- stream,
- includes: options.includes ?? ["components"],
- });
- yield* forEach(function* () {}, execution.output);
- const result = yield* execution;
- return {
- ok: result.ok,
- error: result.ok ? undefined : result.error,
- journal: JSON.stringify(stream.snapshot()),
- text: result.ok && typeof result.value === "string" ? result.value.trim() : "",
- };
- });
-}
-
-/**
- * Where the review components now live, as explicit fixture includes.
- *
- * The package's own directories rather than a checkout path: these are the
- * shipped definitions, and reading them here is what makes the behavioral cases
- * below about the real components rather than about copies of them.
- *
- * This is *not* how a run finds them. Production selection is the host's
- * declaration, asserted in "the trusted review graph" below and end to end in
- * `scripts/tests/plan-component-compiled.test.ts`. These cases supply the files
- * explicitly because their subject is what one component does with its inputs,
- * which needs no profile at all.
- */
-const OPERATIONAL_DIRS = [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/documents/policies",
- "packages/code-review-agent/src/components",
-];
-
-/**
- * Read operational components from the repository before `useStubFs` replaces
- * the filesystem, so a run expands the shipped review documents rather than a
- * copy. TypeScript selections retain these exact bytes while their dependencies
- * resolve from their physical origin.
- */
-function* operationalComponents(paths: string[]): Operation> {
- const components: Record = {};
- for (const path of paths) {
- components[path] = yield* readTextFile(path);
- }
- return components;
-}
-
-function props(values: Record): string[] {
- return [
- "```js eval",
- ...Object.entries(values).map(([name, value]) => `const ${name} = ${JSON.stringify(value)};`),
- "```",
- "",
- ];
-}
-
-function* runDocument(
- document: string,
- components: Record,
- options: DocumentRunOptions = {},
-): Operation {
- const result = yield* runDocumentResult(document, components, options);
- return result.ok;
-}
-
-describe("review infrastructure", () => {
- it("uses the real GitHubAuth component for exact-host scoped middleware", function* () {
- yield* useTempFileCompiler();
- const provider = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/GitHubAuth.md",
- );
- const result = yield* run(provider, "secret-token");
-
- expect(result.requests).toHaveLength(6);
- expect(result.requests[0].input).toBe("https://api.github.com/repos/nested");
- expect(result.requests[0].init?.headers).toBeInstanceOf(Headers);
- const nestedHeaders = new Headers(result.requests[0].init?.headers);
- expect(nestedHeaders.get("Authorization")).toBe("Bearer secret-token");
- expect(nestedHeaders.get("Accept")).toBe("application/vnd.github+json");
- expect(nestedHeaders.get("X-Caller")).toBe("kept");
- expect(result.requests[0].init?.body).toBe("request-body");
- expect(result.requests[0].shouldExpect).toBe(true);
- expect(new Headers(result.requests[1].init?.headers).get("Authorization")).toBe(null);
- expect(new Headers(result.requests[2].init?.headers).get("Authorization")).toBe(null);
- expect(new Headers(result.requests[3].init?.headers).get("Authorization")).toBe(null);
- expect(new Headers(result.requests[4].init?.headers).get("Authorization")).toBe(
- "Bearer secret-token",
- );
- expect(result.requests[4].shouldExpect).toBe(false);
- expect(new Headers(result.requests[5].init?.headers).get("Authorization")).toBe(null);
- expect(result.journal).not.toContain("secret-token");
- });
-
- it("delegates unchanged when the token is unavailable and restores scope", function* () {
- yield* useTempFileCompiler();
- const provider = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/GitHubAuth.md",
- );
- const result = yield* run(provider);
-
- for (const request of result.requests) {
- expect(new Headers(request.init?.headers).get("Authorization")).toBe(null);
- if (request.init?.body !== undefined) {
- expect(request.init.body).toBe("request-body");
- }
- }
- expect(result.journal).not.toContain("secret-token");
- });
-
- it("accepts exit 1 Oxlint diagnostics and journals only bounded fields", function* () {
- yield* useTempFileCompiler();
-
- const valid = yield* runDocumentResult(
- '',
- yield* operationalComponents([
- "packages/code-review-agent/src/components/OxlintDiagnostics.ts",
- ]),
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- process: {
- exitCode: 1,
- stdout: JSON.stringify([
- {
- message: "unused",
- code: "eslint(no-unused-vars)",
- severity: "warning",
- filename: "a.ts",
- labels: [{ span: { line: 2, column: 3 } }],
- source: "raw source must not cross the boundary",
- cause: { secret: "unbounded payload" },
- },
- ]),
- stderr: "",
- },
- },
- );
- expect(valid.ok).toBe(true);
- expect(valid.journal).toContain("1:no-unused-vars");
- expect(valid.journal).not.toContain("raw source must not cross the boundary");
- expect(valid.journal).not.toContain("unbounded payload");
- });
-
- for (const scenario of [
- {
- name: "malformed JSON",
- process: { exitCode: 0, stdout: "not json", stderr: "" },
- diagnostic: "Oxlint returned malformed JSON",
- },
- {
- name: "invocation failure",
- process: { exitCode: 2, stdout: "[]", stderr: "invocation failed" },
- diagnostic: "invocation failed",
- },
- ]) {
- it(`rejects Oxlint ${scenario.name} with its diagnostic`, function* () {
- yield* useTempFileCompiler();
- const result = yield* runDocumentResult(
- '',
- yield* operationalComponents([
- "packages/code-review-agent/src/components/OxlintDiagnostics.ts",
- ]),
- {
- includes: OPERATIONAL_DIRS,
- process: scenario.process,
- },
- );
- expect(result.ok).toBe(false);
- expect(result.error?.message).toContain(scenario.diagnostic);
- });
- }
-
- it("skips Oxlint without starting a process for empty input", function* () {
- yield* useTempFileCompiler();
- let calls = 0;
- yield* API.Process.around({
- *exec() {
- calls++;
- return { exitCode: 2, stdout: "", stderr: "must not run" };
- },
- });
- expect(
- yield* runDocument(
- '',
- yield* operationalComponents([
- "packages/code-review-agent/src/components/OxlintDiagnostics.ts",
- ]),
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- },
- ),
- ).toBe(true);
- expect(calls).toBe(0);
- });
-
- it("extracts human comment replies and rejects malformed GitHub comments", function* () {
- yield* useTempFileCompiler();
- const comments = [
- {
- id: 100,
- user: { login: "github-actions[bot]", type: "Bot" },
- body: "Redundant comment: this repeats the code",
- path: "src/example.ts",
- original_line: 4,
- diff_hunk: "@@\n+const value = 1;",
- },
- {
- id: 200,
- in_reply_to_id: 100,
- user: { login: "human", type: "User" },
- body: "Please keep this comment",
- },
- {
- id: 201,
- in_reply_to_id: 100,
- user: { login: "acknowledged", type: "User" },
- body: "Remove it",
- },
- {
- id: 202,
- in_reply_to_id: 100,
- user: { login: "automation", type: "Bot" },
- body: "Bot reply must not be classified",
- },
- ];
- let malformed = false;
- yield* FetchApi.around({
- *fetch([input]) {
- const url =
- input instanceof Request ? input.url : input instanceof URL ? input.href : input;
- if (url.includes("/pulls/1/comments?")) {
- return response(malformed ? JSON.stringify({ comments }) : JSON.stringify(comments));
- }
- if (url.includes("/comments/201/reactions")) {
- return response(
- JSON.stringify([
- { user: { login: "github-actions[bot]", type: "Bot" }, content: "+1" },
- ]),
- );
- }
- return response("[]");
- },
- });
-
- const document = [
- "",
- ].join("\n");
- const options = {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- env: {
- GITHUB_TOKEN: "test-token",
- GITHUB_REPOSITORY: "taras/executable.md",
- PR_NUMBER: "1",
- },
- };
- const result = yield* runDocumentResult(
- document,
- yield* operationalComponents([
- "packages/code-review-agent/src/components/CommentReviewData.ts",
- ]),
- options,
- );
- expect(result.ok).toBe(true);
- expect(result.journal).toContain("1:1:1");
- expect(result.journal).toContain("Please keep this comment");
- expect(result.journal).not.toContain("Bot reply must not be classified");
-
- malformed = true;
- const malformedResult = yield* runDocumentResult(
- document,
- yield* operationalComponents([
- "packages/code-review-agent/src/components/CommentReviewData.ts",
- ]),
- options,
- );
- expect(malformedResult.ok).toBe(false);
- });
-
- it("parses CommentReviewState classifications and durable checklist state", function* () {
- yield* useTempFileCompiler();
- const document = [
- "",
- ].join("\n");
- const result = yield* runDocumentResult(
- document,
- yield* operationalComponents([
- "packages/code-review-agent/src/components/CommentReviewState.ts",
- ]),
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- },
- );
- expect(result.ok).toBe(true);
- expect(result.journal).toContain("true:true:1");
- expect(result.journal).toContain("old.ts:1");
- expect(result.journal).toContain("keep it");
- });
-
- it("covers Doctor availability, probes, crashes, and failures", function* () {
- yield* useTempFileCompiler();
- const document = [
- "",
- ].join("\n");
- const component = yield* operationalComponents([
- "packages/code-review-agent/src/components/Doctor.ts",
- ]);
- const stats = (available: boolean) => (path: string) => {
- if (path === ".reviews/.oxlint/oxlint" || path === ".reviews/.oxlint/tsgolint") {
- return { exists: available, isFile: available, isDirectory: false };
- }
- if (path === ".reviews/tsconfig.oxlint.json") {
- return { exists: available, isFile: available, isDirectory: false };
- }
- if (path === "node_modules") {
- return { exists: available, isFile: false, isDirectory: available };
- }
- return undefined;
- };
- const baseOptions = {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- glob: [],
- };
-
- const unavailable = yield* runDocumentResult(document, component, {
- ...baseOptions,
- stat: stats(false),
- });
- expect(unavailable.ok).toBe(true);
- expect(unavailable.journal).toContain("false:syntax-only");
-
- const successful = yield* runDocumentResult(document, component, {
- ...baseOptions,
- stat: stats(true),
- process: (command) => {
- if (command.at(-1) === "--version") {
- return { exitCode: 0, stdout: `${command[0]} 1.0\n`, stderr: "" };
- }
- return {
- exitCode: 1,
- stdout: JSON.stringify([
- {
- message: "unused",
- code: "no-unused-vars",
- severity: "warning",
- filename: "a.ts",
- labels: [{ span: { line: 1, column: 1 } }],
- },
- ]),
- stderr: "",
- };
- },
- });
- expect(successful.ok).toBe(true);
- expect(successful.journal).toContain("true:type-aware");
-
- const crash = yield* runDocumentResult(document, component, {
- ...baseOptions,
- stat: stats(true),
- process: (command) =>
- command.at(-1) === "--version"
- ? { exitCode: 0, stdout: "tool 1.0\n", stderr: "" }
- : { exitCode: 1, stdout: "", stderr: "tsgolint panic: OOM" },
- });
- expect(crash.ok).toBe(true);
- expect(crash.journal).toContain("false:syntax-only");
-
- const malformed = yield* runDocumentResult(document, component, {
- ...baseOptions,
- stat: stats(true),
- process: (command) =>
- command.at(-1) === "--version"
- ? { exitCode: 0, stdout: "tool 1.0\n", stderr: "" }
- : { exitCode: 0, stdout: "not json", stderr: "" },
- });
- expect(malformed.ok).toBe(false);
- expect(malformed.error?.message).toContain("Oxlint returned malformed JSON");
-
- const failed = yield* runDocumentResult(document, component, {
- ...baseOptions,
- stat: stats(true),
- process: { exitCode: 2, stdout: "[]", stderr: "invocation failed" },
- });
- expect(failed.ok).toBe(false);
- expect(failed.error?.message).toContain("invocation failed");
- });
-
- it("constructs ReviewContext from git and fails on git errors", function* () {
- yield* useTempFileCompiler();
- const document = [
- "",
- ].join("\n");
- const diff = [
- "diff --git a/src/example.ts b/src/example.ts",
- "new file mode 100644",
- "--- /dev/null",
- "+++ b/src/example.ts",
- "@@ -0,0 +1,1 @@",
- "+const value = 1;",
- ].join("\n");
- const result = yield* runDocumentResult(
- document,
- yield* operationalComponents(["packages/code-review-agent/src/components/ReviewContext.ts"]),
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- env: { BASE_SHA: "base", HEAD_SHA: "head", PR_BODY: "local body" },
- process: (command) =>
- command.includes("--name-status")
- ? { exitCode: 0, stdout: "A\tsrc/example.ts\n", stderr: "" }
- : { exitCode: 0, stdout: diff, stderr: "" },
- },
- );
- expect(result.ok).toBe(true);
- expect(result.journal).toContain("1:src/example.ts");
-
- const failed = yield* runDocumentResult(
- document,
- yield* operationalComponents(["packages/code-review-agent/src/components/ReviewContext.ts"]),
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- process: { exitCode: 1, stdout: "", stderr: "git diff failed" },
- },
- );
- expect(failed.ok).toBe(false);
- });
-
- it("fails when a provider returns 2xx without model content", function* () {
- yield* useTempFileCompiler();
- const deepInfra = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/DeepInfraProvider.md",
- );
- const ollama = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/OllamaProvider.md",
- );
- const sample = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/Sample.md",
- );
- const requests: string[] = [];
- yield* FetchApi.around({
- *fetch([input]) {
- requests.push(
- input instanceof Request ? input.url : input instanceof URL ? input.href : input,
- );
- return response(JSON.stringify({ choices: [] }));
- },
- });
-
- expect(
- yield* runDocument(
- '',
- {
- "components/DeepInfraProvider.md": deepInfra,
- "components/Sample.md": sample,
- },
- { env: { DEEPINFRA_TOKEN: "test-token" } },
- ),
- ).toBe(false);
- expect(requests).toHaveLength(1);
- expect(requests[0]).toBe("https://api.deepinfra.com/v1/openai/chat/completions");
- expect(
- yield* runDocument(
- '',
- {
- "components/OllamaProvider.md": ollama,
- "components/Sample.md": sample,
- },
- ),
- ).toBe(false);
- expect(requests).toHaveLength(2);
- expect(requests[1]).toBe("http://localhost:11434/v1/chat/completions");
- });
-
- it("requires GitHubComment metadata", function* () {
- yield* useTempFileCompiler();
- const comment = yield* readTextFile(
- "packages/code-review-agent/src/documents/components/GitHubComment.md",
- );
- expect(
- yield* runDocument("", {
- "components/GitHubComment.md": comment,
- }),
- ).toBe(false);
- });
-
- it("executes the real RepositoryInventory function component", function* () {
- yield* useTempFileCompiler();
- const inventoryResult = yield* runDocumentResult(
- '\n{inventory.fileCount}:{inventory.lineCount}',
- {
- ...(yield* operationalComponents([
- "packages/code-review-agent/src/components/RepositoryInventory.ts",
- ])),
- "packages/example.ts": "first\nsecond\n",
- },
- {
- includes: [
- "packages/code-review-agent/src/documents/components",
- "packages/code-review-agent/src/components",
- ],
- glob: [{ path: "packages/example.ts", isFile: true, isDirectory: false }],
- },
- );
- expect(inventoryResult.ok).toBe(true);
- });
-
- it("retires the Show component from the review library", function* () {
- const tag = /<\/?Show[\s/>]/;
- const documents: string[] = [];
- for (const entry of yield* each(
- expandGlob("packages/code-review-agent/src/documents/**/*.md"),
- )) {
- documents.push(entry.path);
- const source = yield* readTextFile(entry.path);
- expect([entry.path, tag.test(source)]).toEqual([entry.path, false]);
- yield* each.next();
- }
- // The walk found the review library, so the scan above is not vacuous.
- expect(
- documents.some((path) =>
- path.endsWith("packages/code-review-agent/src/documents/components/Finding.md"),
- ),
- ).toBe(true);
- expect(documents.some((path) => path.endsWith("Show.md"))).toBe(false);
-
- const focused = yield* readTextFile("packages/core/tests/unused-in-diff.test.ts");
- expect(focused).toContain("components/UnusedInDiff.md");
- expect(focused).not.toContain("Show.md");
-
- const spec = yield* readTextFile("specs/code-review-agent-spec.md");
- expect(spec).toContain("### 5.10 `CommentReview.md`");
- expect(tag.test(spec)).toBe(false);
- expect(spec).not.toContain("Show.md");
- expect(spec).not.toContain("`Show`");
- });
-
- it("renders Finding's selected icon and message and suppresses its false case", function* () {
- yield* useTempFileCompiler();
- const components = yield* operationalComponents([
- "packages/code-review-agent/src/documents/components/Finding.md",
- ]);
-
- const selected = yield* runDocumentResult(
- '',
- components,
- { includes: OPERATIONAL_DIRS },
- );
- expect(selected.ok).toBe(true);
- expect(selected.text).toBe("🔴 Broken contract.");
-
- const suppressed = yield* runDocumentResult(
- '',
- components,
- { includes: OPERATIONAL_DIRS },
- );
- expect(suppressed.ok).toBe(true);
- expect(suppressed.text).toBe("");
- });
-
- it("renders OxlintSummary's clean section and its unavailable warning", function* () {
- yield* useTempFileCompiler();
- const components = yield* operationalComponents([
- "packages/code-review-agent/src/documents/components/OxlintSummary.md",
- "packages/code-review-agent/src/documents/components/ReviewSection.md",
- ]);
- const summary = (oxlintInstalled: boolean) =>
- [
- ...props({
- diagnostics: { total: 0, summary: "1 warning" },
- doctor: { oxlintInstalled, bloatRulesMissing: [] },
- }),
- "",
- ].join("\n");
-
- const clean = yield* runDocumentResult(summary(true), components, {
- includes: OPERATIONAL_DIRS,
- });
- expect(clean.ok).toBe(true);
- expect(clean.text).toBe("### Static Analysis\n\n✅ Oxlint found no issues.");
-
- const unavailable = yield* runDocumentResult(summary(false), components, {
- includes: OPERATIONAL_DIRS,
- });
- expect(unavailable.ok).toBe(true);
- // The selected branch keeps the blank lines the two suppressed blocks left
- // behind, so this is exact rather than a containment check.
- expect(unavailable.text).toBe(
- [
- "### Static Analysis",
- "",
- "",
- "",
- "",
- "",
- "🟡 Oxlint not installed. Static analysis skipped.",
- ].join("\n"),
- );
- });
-
- it("suppresses ReleaseSpecWarning for ordinary files and warns on release changes", function* () {
- yield* useTempFileCompiler();
- const components = yield* operationalComponents([
- "packages/code-review-agent/src/documents/components/ReleaseSpecWarning.md",
- ]);
- const warning = (files: string[]) =>
- [...props({ files }), ""].join("\n");
-
- const ordinary = yield* runDocumentResult(
- warning(["packages/core/src/expand.ts"]),
- components,
- {
- includes: OPERATIONAL_DIRS,
- },
- );
- expect(ordinary.ok).toBe(true);
- expect(ordinary.text).toBe("");
-
- const changed = yield* runDocumentResult(
- warning([".github/workflows/release.yml"]),
- components,
- { includes: OPERATIONAL_DIRS },
- );
- expect(changed.ok).toBe(true);
- expect(changed.text).toContain("> [!WARNING]");
- expect(changed.text).toContain(
- "> This PR changes release configuration (.github/workflows/release.yml) without touching",
- );
- expect(changed.text).not.toContain("ERROR");
- });
-
- it("expands UnusedInDiff and CommentReview to nothing without an If component", function* () {
- yield* useTempFileCompiler();
- const unusedComponents = yield* operationalComponents([
- "packages/code-review-agent/src/documents/components/UnusedInDiff.md",
- ]);
- const unused = yield* runDocumentResult(
- [
- ...props({ pr: { added: [{ file: "a.ts", lineNumber: 1, content: "const plain = 1;" }] } }),
- '',
- ].join("\n"),
- unusedComponents,
- { includes: OPERATIONAL_DIRS },
- );
- expect(unused.ok).toBe(true);
- expect(unused.text).toBe("");
-
- const reviewComponents = yield* operationalComponents([
- "packages/code-review-agent/src/documents/components/CommentReview.md",
- "packages/code-review-agent/src/components/CommentReviewData.ts",
- "packages/code-review-agent/src/components/CommentReviewState.ts",
- "packages/code-review-agent/src/documents/components/SuggestRemoval.md",
- "packages/code-review-agent/src/documents/components/Sample.md",
- ]);
- const requests: string[] = [];
- yield* FetchApi.around({
- *fetch([input]) {
- requests.push(
- input instanceof Request ? input.url : input instanceof URL ? input.href : input,
- );
- return response("[]");
- },
- });
- const sampled: string[] = [];
- yield* Sample.around({
- // deno-lint-ignore require-yield
- *sample([context]) {
- sampled.push(context.content);
- return "[sampled]";
- },
- });
-
- const review = yield* runDocumentResult(
- [...props({ pr: { added: [] } }), ""].join("\n"),
- reviewComponents,
- {
- includes: OPERATIONAL_DIRS,
- env: {
- GITHUB_TOKEN: "test-token",
- GITHUB_REPOSITORY: "taras/executable.md",
- PR_NUMBER: "1",
- },
- },
- );
- expect(review.ok).toBe(true);
- // The typed data component ran — it fetched the pull request's comments —
- // while both captures and both trailing branches stayed empty.
- expect(requests.some((url) => url.includes("/pulls/1/comments"))).toBe(true);
- expect(review.text).toBe("");
- expect(sampled).toEqual([]);
- });
-
- it("skips the ExtraneousCodePolicy sample below the review threshold", function* () {
- yield* useTempFileCompiler();
- const components = yield* operationalComponents([
- "packages/code-review-agent/src/documents/policies/ExtraneousCodePolicy.md",
- "packages/code-review-agent/src/documents/components/ReviewSection.md",
- "packages/code-review-agent/src/documents/components/Sample.md",
- ]);
- function runPolicy(totalChanges: number): Operation<{ calls: string[]; text: string }> {
- return scoped(function* () {
- const calls: string[] = [];
- yield* Sample.around({
- // deno-lint-ignore require-yield
- *sample([context]) {
- calls.push(context.content);
- return "[sampled]";
- },
- });
- const result = yield* runDocumentResult(
- [
- ...props({
- pr: {
- stats: { totalChanges },
- meta: { title: "Title", body: "Body" },
- diffPreview: "+const value = 1;",
- },
- diagnostics: { summary: "no diagnostics", density: 0 },
- doctor: { oxlintInstalled: true },
- }),
- "",
- ].join("\n"),
- components,
- { includes: OPERATIONAL_DIRS },
- );
- expect(result.ok).toBe(true);
- return { calls, text: result.text };
- });
- }
-
- const small = yield* runPolicy(20);
- expect(small.calls).toEqual([]);
- expect(small.text).toBe("### Correctness\n\n✅ Small PR — correctness review skipped.");
-
- // The same probe records a call when the branch is selected, so the empty
- // result above is non-execution rather than a probe that never wired up.
- const large = yield* runPolicy(21);
- expect(large.calls).toHaveLength(1);
- expect(large.calls[0]).toContain("You are reviewing a TypeScript PR for EXTRANEOUS code only.");
- expect(large.text).toContain("[sampled]");
- expect(large.text).not.toContain("ERROR");
- });
-
- it("renders RepoCleanupPolicy's clean section without running either branch", function* () {
- yield* useTempFileCompiler();
- const components = yield* operationalComponents([
- "packages/code-review-agent/src/documents/policies/RepoCleanupPolicy.md",
- "packages/code-review-agent/src/documents/components/ReviewSection.md",
- "packages/code-review-agent/src/documents/components/Sample.md",
- ]);
- const calls: string[] = [];
- yield* Sample.around({
- // deno-lint-ignore require-yield
- *sample([context]) {
- calls.push(context.content);
- return "[sampled]";
- },
- });
-
- const clean = yield* runDocumentResult(
- [
- ...props({
- diagnostics: { total: 0, summary: "MUST NOT RENDER" },
- doctor: { oxlintInstalled: true },
- fileList: "packages/core/src/expand.ts",
- }),
- "",
- ].join("\n"),
- components,
- { includes: OPERATIONAL_DIRS },
- );
- expect(clean.ok).toBe(true);
- expect(clean.text).toBe("### Cleanup Policy\n\n✅ No code health issues detected.");
- expect(calls).toEqual([]);
- });
-});
-
-/**
- * The review graph as the installed `run` profile supplies it.
- *
- * The cases above supply the package's component files as explicit fixtures,
- * because what they are measuring is what one component does with its inputs.
- * These ask a different question: not whether a component works, but whether the
- * *host* is the one supplying it — so they pass no include at all and let the
- * declaration answer.
- *
- * A review is the one program that must not be answerable by the thing it is
- * reviewing. While the graph lived in `.reviews/`, a pull request could add its
- * own `Finding.md` and the review would run the branch's copy — so what is
- * asserted here is not that the components work, but that the *host* is the one
- * supplying them and a checkout cannot take a name back.
- */
-/** The packaged assets, read the way anything but the package would read them. */
-function* packagedSource(document: ReviewDocument): Operation {
- return yield* readTextFile(
- `packages/code-review-agent/src/documents/${document.group}/${document.name}.md`,
- );
-}
-
-describe("the trusted review graph", () => {
- it("declares exactly the packaged Markdown, each identified by origin and digest", function* () {
- const declared = yield* reviewComponentDeclarations();
-
- // The count is stated rather than derived from the manifest the
- // implementation also derives from: a manifest that lost an entry would
- // otherwise agree with itself.
- expect(declared).toHaveLength(35);
- expect(new Set(declared.map((one) => one.name)).size).toBe(35);
-
- for (const document of REVIEW_DOCUMENTS) {
- const one = declared.find((candidate) => candidate.name === document.name);
- expect(one).toBeDefined();
- const source = yield* packagedSource(document);
- // The bytes are the packaged asset's, and the digest is of those bytes.
- // A declaration stating a digest of something else is refused at
- // admission, so this is the half admission cannot check for itself:
- // whether what was read is what the package ships.
- expect(one?.source).toBe(source);
- expect(one?.digest).toBe(createHash("sha256").update(source, "utf8").digest("hex"));
- // The package and the asset, never a filesystem path — the same component
- // sits at three different absolute paths across a checkout, a
- // `node_modules` tree and a binary, and all three are one component.
- expect(one?.origin).toBe(
- `@executablemd/code-review-agent/${document.group}/${document.name}.md`,
- );
- // Unstated, so both spellings keep working. A declaration that narrowed
- // `forms` would change what the existing review roots may write.
- expect(one?.forms).toBeUndefined();
- }
- });
-
- it("reserves the seven TypeScript components rather than offering them as defaults", function* () {
- expect(REVIEW_REGISTRATIONS.map((one) => one.name)).toEqual([
- "CommentReviewData",
- "CommentReviewState",
- "Doctor",
- "OxlintDiagnostics",
- "ReviewDiagnostics",
- "RepositoryInventory",
- "ReviewContext",
- ]);
- for (const registration of REVIEW_REGISTRATIONS) {
- // Reserved is the registration tier's way of saying what a declaration
- // says. Ordinary would have moved thirty-five components out of a
- // subject's reach and left shadowable the implementations that run processes,
- // read credentials and reach the network.
- expect(registration.reserved).toBe(true);
- expect(registration.origin).toBe("@executablemd/code-review-agent");
- expect(registration.props).toBeDefined();
- expect(registration.returns).toBeDefined();
- // Each is documented by this package, which the documentation index
- // checks against this same list.
- expect(typeof registration.description).toBe("string");
- }
- });
-
- it("claims each name once across both tiers", function* () {
- const declared = (yield* reviewComponentDeclarations()).map((one) => one.name);
- const reserved = REVIEW_REGISTRATIONS.map((one) => one.name);
- const all = [...declared, ...reserved];
- // A declaration colliding with a reserved registration is refused before the
- // root document is imported, so an overlap here is a build that cannot run
- // a review at all rather than a precedence question.
- expect(new Set(all).size).toBe(all.length);
- expect(all).toHaveLength(42);
- });
-
- /**
- * Every supported entrypoint takes the graph from the installation.
- *
- * The declaration tier winning is only half the outcome. While a command still
- * passed `--include .reviews/components`, the retired directory was a live
- * dependency of that command: deleting it broke the entrypoint, and restoring
- * a file under it put a checkout-supplied component back in front of a review
- * — which is the thing this issue exists to stop.
- *
- * So this reads the commands themselves rather than trusting that the
- * migration reached all seven. Import-graph selection cannot see a task string
- * or a workflow step, so nothing else in the suite would notice one being put
- * back.
- *
- * Scoped to these seven by name. `ci.yml` passes `--include
- * packages/core/components` for unrelated smoke fixtures and is none of this
- * assertion's business — a sweep over every workflow would fail on it and
- * teach the next person to weaken the check.
- */
- it("runs every supported review entrypoint with no component include", function* () {
- const RETIRED = [".reviews/components", ".reviews/policies", "packages/core/components"];
-
- const tasks: Record = JSON.parse(yield* readTextFile("deno.json")).tasks;
- const ROOTS: Record = {
- review: ".reviews/ReviewPR.md",
- "review:local": ".reviews/ReviewPR.local.md",
- analyze: ".reviews/AnalyzeRepo.md",
- "analyze:ci": ".reviews/AnalyzeRepoCI.md",
- "analyze:dispatch": ".reviews/DispatchRepoAnalysis.md",
- };
-
- for (const [name, root] of Object.entries(ROOTS)) {
- const command = tasks[name];
- // The task still exists and still runs its own root document. A
- // migration that removed an entrypoint would otherwise satisfy every
- // negative assertion below.
- expect(`${name}: ${typeof command}`).toBe(`${name}: string`);
- expect(`${name}: ${command.includes(root)}`).toBe(`${name}: true`);
- // No component search path at all, rather than no retired path: an
- // include pointing somewhere new would be a second way to answer for a
- // review's components.
- expect(`${name}: ${command.includes("--include")}`).toBe(`${name}: false`);
- for (const retired of RETIRED) {
- expect(`${name}: ${retired}: ${command.includes(retired)}`).toBe(
- `${name}: ${retired}: false`,
- );
- }
- }
-
- const WORKFLOWS: Record = {
- ".github/workflows/review.yml": ".reviews/ReviewPR.md",
- ".github/workflows/repo-analysis.yml": ".reviews/AnalyzeRepoCI.md",
- };
-
- for (const [file, root] of Object.entries(WORKFLOWS)) {
- const source = yield* readTextFile(file);
- expect(`${file}: ${source.includes(`run ${root}`)}`).toBe(`${file}: true`);
- for (const retired of RETIRED) {
- expect(`${file}: ${retired}: ${source.includes(retired)}`).toBe(
- `${file}: ${retired}: false`,
- );
- }
- }
- });
-
- it("ships review copies of Sample and Instruction byte-identical to core's", function* () {
- // Deliberate duplication: the review graph owns its own copies so that the
- // set a review runs is complete in one package. This asserts the copies have
- // not drifted, which makes a future divergence an explicit decision in this
- // package rather than something that happens to one of them.
- for (const name of ["Sample", "Instruction"]) {
- const review = yield* readTextFile(
- `packages/code-review-agent/src/documents/components/${name}.md`,
- );
- expect(review).toBe(yield* readTextFile(`packages/core/components/${name}.md`));
- }
- });
-
- it("runs the declared Finding even when the checkout supplies its own", function* () {
- yield* useTempFileCompiler();
- const declarations = yield* reviewComponentDeclarations();
- expect(declarations.some((one) => one.name === "Finding")).toBe(true);
-
- // A checkout that supplies both a same-named component and an unrelated one.
- // The first must lose; the second must still resolve, because claiming names
- // is not the same as turning discovery off.
- yield* useStubFs({
- "doc.md": '\n\n',
- // Not a broken file: it accepts exactly the props the real one accepts,
- // so a run that selected it would *succeed* and quietly emit this marker
- // instead of the finding. A hostile component that merely failed would
- // let this case pass for the wrong reason.
- "components/Finding.md": [
- "---",
- "props:",
- " type: object",
- " properties:",
- " when: { type: boolean }",
- " severity: { type: string }",
- " message: { type: string }",
- " required: [when, message]",
- " additionalProperties: false",
- "---",
- "",
- "HOSTILE-FINDING {props.message}",
- ].join("\n"),
- "components/CustomProbe.md": "custom probe ran",
- });
-
- const stream = new InMemoryStream();
- const execution = yield* executeInstalled(
- { path: "doc.md", stream, includes: ["components"] },
- [{ declarations }],
- );
- yield* forEach(function* () {}, execution.output);
- const result = yield* execution;
-
- expect(result.ok).toBe(true);
- const text = result.ok && typeof result.value === "string" ? result.value : "";
- // The hostile marker never reaches the output, and the trusted component's
- // own rendering does.
- expect(text).not.toContain("HOSTILE-FINDING");
- // The declared component's own rendering: its `ts eval` chose the icon from
- // the severity it was given, which a body of plain prose could not produce.
- expect(text).toContain("🔴 probe");
- // Ordinary inclusion still works: a caller's own component, of a name the
- // host does not claim, resolves exactly as it always did.
- expect(text).toContain("custom probe ran");
- });
-});
diff --git a/scripts/tests/scope-bound-event-registration.test.ts b/scripts/tests/scope-bound-event-registration.test.ts
index 309082e03..81e05886f 100644
--- a/scripts/tests/scope-bound-event-registration.test.ts
+++ b/scripts/tests/scope-bound-event-registration.test.ts
@@ -442,7 +442,7 @@ describe("local/require-scope-bound-event-registration", () => {
});
it("finds no configuration entry turning the rule off for a path", function* () {
- for (const config of [".oxlintrc.json", "oxlint.shared.json", ".reviews/.oxlintrc.json"]) {
+ for (const config of [".oxlintrc.json", "oxlint.shared.json"]) {
const source = yield* readTextFile(path.join(ROOT, config));
const off = new RegExp(`"local/${RULE}":\\s*(?:"off"|\\["off")`, "u").test(source);
expect([config, off]).toEqual([config, false]);
diff --git a/scripts/validate-documentation.ts b/scripts/validate-documentation.ts
index bf2e32c8f..65f237ace 100644
--- a/scripts/validate-documentation.ts
+++ b/scripts/validate-documentation.ts
@@ -14,11 +14,8 @@
* exactly the reason they would fail a run.
*
* XMD bundles one Plugin — `@executablemd/git` — so this assembles the run
- * profile: the bundled value, then the review graph named explicitly. A
- * distribution carries the first, which makes its documentation this gate's
- * business; the second is an operator's selection, and this script is a build
- * tool rather than the product, which is why it may name the package that
- * ships it. Nothing in the CLI does.
+ * profile a command gets with no `--plugin`: the bundled value. A distribution
+ * carries it, which makes its documentation this gate's business.
*/
import { main, scoped } from "effection";
@@ -28,7 +25,6 @@ import type { ComponentOrigin, DocumentationIndex } from "@executablemd/core";
import { useCommandComponents } from "../packages/cli/src/syntax.ts";
import { BUNDLED_PLUGIN } from "../packages/cli/src/run-profile.ts";
import { installPlugins } from "../packages/cli/src/plugin-host.ts";
-import reviewPlugin from "../packages/code-review-agent/mod.ts";
/** Assemble the complete index, throwing whatever it refuses with. */
export function* validateDocumentation(): Operation {
@@ -36,13 +32,11 @@ export function* validateDocumentation(): Operation {
// that installed it: collecting outside would find core's terminal alone and
// pass every rule vacuously.
const index = yield* scoped(function* () {
- // The run profile as a command assembles it: the bundled Git Plugin first,
- // then the first-party review Plugin exactly as `--plugin` installs one. A
- // Plugin's registrations and the documentation describing them arrive
- // together, so a build that shipped one without the other fails here — and
- // the bundled one is in this list because a distribution carries it, which
- // makes its documentation this gate's business too.
- yield* installPlugins([BUNDLED_PLUGIN, reviewPlugin], { command: "run", args: ["run"] });
+ // The run profile as a command assembles it, with the bundled Git Plugin
+ // installed exactly as a Plugin is. Its registrations and the
+ // documentation describing them arrive together, so a build that shipped
+ // one without the other fails here.
+ yield* installPlugins([BUNDLED_PLUGIN], { command: "run", args: ["run"] });
yield* useCommandComponents();
return documentationIndexFor(yield* capturedDocumentation());
});
@@ -57,12 +51,12 @@ export function* validateDocumentation(): Operation {
origin: "@executablemd/web",
reserved: false,
});
- // And one from the selected Plugin, so an index assembled without it cannot
+ // And one from the bundled Plugin, so an index assembled without it cannot
// pass this check either.
- read(index, "ReviewContext", {
+ read(index, "Repository", {
kind: "registered",
- origin: "@executablemd/code-review-agent",
- reserved: true,
+ origin: "@executablemd/workflow/composition",
+ reserved: false,
});
return 1;
}
diff --git a/specs/code-review-agent-spec.md b/specs/code-review-agent-spec.md
deleted file mode 100644
index 4b5f33347..000000000
--- a/specs/code-review-agent-spec.md
+++ /dev/null
@@ -1,1362 +0,0 @@
-# Specification: `@executablemd/code-review-agent`
-
-**Status:** Draft
-**Scope:** An Executable Markdown Agent that reviews pull requests for extraneous code, posts findings as GitHub comments, and runs locally via Ollama or in CI via DeepInfra.
-
----
-
-## 1. Architecture
-
-A PR review is an executable Markdown document. Typed function components
-gather the diff, environment observations, and bounded diagnostics. Markdown
-then composes the policy components, model provider, and optional GitHub
-delivery.
-
-```
-ReviewPR.md
- ├─