@fro.bot/systematic 2.24.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/frontmatter.d.ts +12 -0
- package/package.json +5 -5
- package/skills/ce-compound/references/schema.yaml +27 -2
- package/skills/ce-review/SKILL.md +33 -7
- package/skills/ce-review/references/review-output-template.md +8 -0
- package/skills/ce-review/references/validator-template.md +96 -0
- package/skills/claude-permissions-optimizer/scripts/extract-commands.mjs +2 -2
- package/skills/claude-permissions-optimizer/scripts/normalize.mjs +8 -8
- package/skills/onboarding/scripts/inventory.mjs +2 -2
- package/skills/writing-skills/scripts/render-graphs.js +4 -4
- /package/dist/{index-175fc4yn.js → index-3q3ns1xh.js} +0 -0
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,6 +4,18 @@ export interface FrontmatterResult<T = Record<string, unknown>> {
|
|
|
4
4
|
hadFrontmatter: boolean;
|
|
5
5
|
parseError: boolean;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns the raw YAML text between the opening and closing `---` delimiters,
|
|
9
|
+
* or `null` when the content has no frontmatter block.
|
|
10
|
+
*
|
|
11
|
+
* Scope: flat top-level frontmatter only. Nested/indented mapping values are
|
|
12
|
+
* returned verbatim as part of the block — callers that scan individual lines
|
|
13
|
+
* (e.g. parse-safety checks) must skip indented lines themselves.
|
|
14
|
+
*
|
|
15
|
+
* Handles LF and CRLF line endings. The closing `---` does not need a trailing
|
|
16
|
+
* newline (handles files that end immediately after the delimiter).
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractFrontmatterBlock(content: string): string | null;
|
|
7
19
|
/**
|
|
8
20
|
* Parses YAML frontmatter from Markdown content.
|
|
9
21
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.25.0",
|
|
4
4
|
"description": "Structured engineering workflows for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"docs:build": "bun run docs:generate && bun run --cwd docs build",
|
|
38
38
|
"docs:preview": "bun run --cwd docs preview",
|
|
39
39
|
"docs:verify": "bun install --frozen-lockfile && bun run --cwd docs playwright install --with-deps chromium && bun run docs:build",
|
|
40
|
-
"docs:generate": "bun docs/scripts/transform-content.ts && bun scripts/generate-config-schema.ts && bun docs/scripts/generate-config-reference.ts",
|
|
40
|
+
"docs:generate": "bun docs/scripts/generate-stats.ts && bun docs/scripts/transform-content.ts && bun scripts/generate-config-schema.ts && bun docs/scripts/generate-config-reference.ts",
|
|
41
41
|
"schema:generate": "bun scripts/generate-config-schema.ts",
|
|
42
42
|
"schema:drift": "bun scripts/generate-config-schema.ts --check",
|
|
43
43
|
"registry:build": "bun scripts/build-registry.ts",
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"@opencode-ai/plugin": "^1.1.30"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@biomejs/biome": "2.4.
|
|
69
|
-
"@opencode-ai/plugin": "1.15.
|
|
70
|
-
"@opencode-ai/sdk": "1.15.
|
|
68
|
+
"@biomejs/biome": "2.4.16",
|
|
69
|
+
"@opencode-ai/plugin": "1.15.13",
|
|
70
|
+
"@opencode-ai/sdk": "1.15.13",
|
|
71
71
|
"@semantic-release/exec": "7.1.0",
|
|
72
72
|
"@types/bun": "latest",
|
|
73
73
|
"@types/js-yaml": "4.0.9",
|
|
@@ -23,12 +23,16 @@ tracks:
|
|
|
23
23
|
- integration_issue
|
|
24
24
|
- logic_error
|
|
25
25
|
knowledge:
|
|
26
|
-
description: "Best practices, workflow improvements, patterns, and documentation"
|
|
26
|
+
description: "Best practices, workflow improvements, patterns, conventions, and documentation"
|
|
27
27
|
problem_types:
|
|
28
28
|
- best_practice
|
|
29
29
|
- documentation_gap
|
|
30
30
|
- workflow_issue
|
|
31
31
|
- developer_experience
|
|
32
|
+
- architecture_pattern
|
|
33
|
+
- design_pattern
|
|
34
|
+
- tooling_decision
|
|
35
|
+
- convention
|
|
32
36
|
|
|
33
37
|
# --- Fields required by BOTH tracks -----------------------------------------
|
|
34
38
|
required_fields:
|
|
@@ -57,7 +61,18 @@ required_fields:
|
|
|
57
61
|
- workflow_issue
|
|
58
62
|
- best_practice
|
|
59
63
|
- documentation_gap
|
|
60
|
-
|
|
64
|
+
- architecture_pattern
|
|
65
|
+
- design_pattern
|
|
66
|
+
- tooling_decision
|
|
67
|
+
- convention
|
|
68
|
+
description: >-
|
|
69
|
+
Primary category — determines track (bug vs knowledge).
|
|
70
|
+
Prefer the narrowest applicable value; best_practice is the fallback
|
|
71
|
+
when no narrower knowledge-track value fits.
|
|
72
|
+
architecture_pattern: structural decisions about how components relate.
|
|
73
|
+
design_pattern: reusable solutions to recurring design problems.
|
|
74
|
+
tooling_decision: choices about tools, libraries, or build infrastructure.
|
|
75
|
+
convention: agreed-upon naming, formatting, or style rules.
|
|
61
76
|
|
|
62
77
|
component:
|
|
63
78
|
type: enum
|
|
@@ -220,3 +235,13 @@ validation_rules:
|
|
|
220
235
|
- "date must match YYYY-MM-DD format"
|
|
221
236
|
- "rails_version, if provided, must match X.Y.Z format and only applies to bug-track docs"
|
|
222
237
|
- "tags should be lowercase and hyphen-separated"
|
|
238
|
+
- |-
|
|
239
|
+
YAML quoting: scalar values that contain ' #' (space-hash) must be
|
|
240
|
+
double-quoted to prevent silent YAML comment truncation (the YAML parser
|
|
241
|
+
treats ' #' as an inline comment, silently dropping the rest of the value
|
|
242
|
+
with parseError: false). Example: problem: "cache miss # under load".
|
|
243
|
+
- |-
|
|
244
|
+
YAML quoting: array-of-strings frontmatter items must be double-quoted when
|
|
245
|
+
the value starts with a YAML reserved indicator (e.g., '{', '[', '|', '>',
|
|
246
|
+
'!', '&', '*', '?', ':', '-') or contains ': ' (colon-space). Example:
|
|
247
|
+
symptoms: - "[mdx-js/rollup] Could not parse expression"
|
|
@@ -477,12 +477,31 @@ Convert multiple reviewer compact JSON returns into one deduplicated, confidence
|
|
|
477
477
|
10. **Collect coverage data.** Union residual_risks and testing_gaps across reviewers.
|
|
478
478
|
11. **Preserve CE agent artifacts.** Keep the learnings, agent-native, schema-drift, and deployment-verification outputs alongside the merged finding set. Do not drop unstructured agent output just because it does not match the persona JSON schema.
|
|
479
479
|
|
|
480
|
+
### Stage 5b: Validation pass
|
|
481
|
+
|
|
482
|
+
Run an independent validation pass over the merged finding set before synthesis. This pass annotates each gated finding `validated: true` or `validated: false` with a one-sentence reason. It never deletes a finding — findings with `validated: false` are surfaced in the "Filtered (not validated)" group in Stage 6, not removed from the report.
|
|
483
|
+
|
|
484
|
+
**Gating band (default):** Validate only findings that are **P0 or P1 severity**, or that have `requires_verification: true`. Findings outside this band pass through to Stage 6 unvalidated and unfiltered — no validator is dispatched for them. This bounds cost: one validator subagent per gated finding.
|
|
485
|
+
|
|
486
|
+
**Dispatch:**
|
|
487
|
+
|
|
488
|
+
1. Identify all gated findings from the Stage 5 merged set.
|
|
489
|
+
2. For each gated finding, spawn one validator subagent in parallel using the validator template at `references/validator-template.md`. Pass the finding fields, the intent summary, the file list, and the full diff.
|
|
490
|
+
3. Collect `{validated, reason}` from each validator. Attach both fields to the finding.
|
|
491
|
+
4. **Never drop a finding.** A finding with `validated: false` is routed to the "Filtered (not validated)" group for Stage 6 presentation. It is not removed from the report, not suppressed, and not excluded from the Coverage count.
|
|
492
|
+
5. Findings with `validated: true` flow through to Stage 6 unchanged — they appear in the normal severity tables.
|
|
493
|
+
6. Findings outside the gating band carry no `validated` annotation and appear in Stage 6 severity tables unchanged.
|
|
494
|
+
|
|
495
|
+
**Failure handling:** If a validator subagent fails or times out, treat the finding as `validated: true` (conservative fallback — keep it in the actioned set) and note the validator failure in the Coverage section.
|
|
496
|
+
|
|
497
|
+
**Output-contract compatibility:** This pass is additive. Existing severity tables in Stage 6 keep their structure, headings, and order. The "Filtered (not validated)" group is appended after the existing severity tables. Consumers relying on existing section order are unaffected — new content only ever appears after existing sections.
|
|
498
|
+
|
|
480
499
|
### Stage 6: Synthesize and present
|
|
481
500
|
|
|
482
501
|
Assemble the final report using **pipe-delimited markdown tables for findings** from the review output template included below. The table format is mandatory for finding rows in interactive mode — do not render findings as freeform text blocks or horizontal-rule-separated prose. Other report sections (Applied Fixes, Learnings, Coverage, etc.) use bullet lists and the `---` separator before the verdict, as shown in the template.
|
|
483
502
|
|
|
484
503
|
1. **Header.** Scope, intent, mode, reviewer team with per-conditional justifications.
|
|
485
|
-
2. **Findings.** Rendered as pipe-delimited tables grouped by severity (`### P0 -- Critical`, `### P1 -- High`, `### P2 -- Moderate`, `### P3 -- Low`). Each finding row shows `#`, file, issue, reviewer(s), confidence, and synthesized route. Omit empty severity levels. Never render findings as freeform text blocks or numbered lists.
|
|
504
|
+
2. **Findings.** Rendered as pipe-delimited tables grouped by severity (`### P0 -- Critical`, `### P1 -- High`, `### P2 -- Moderate`, `### P3 -- Low`). Each finding row shows `#`, file, issue, reviewer(s), confidence, and synthesized route. Omit empty severity levels. Never render findings as freeform text blocks or numbered lists. Only findings with `validated: true` (or no `validated` annotation) appear in these tables.
|
|
486
505
|
3. **Requirements Completeness.** Include only when a plan was found in Stage 2b. For each requirement (R1, R2, etc.) and implementation unit in the plan, report whether corresponding work appears in the diff. Use a simple checklist: met / not addressed / partially addressed. Routing depends on `plan_source`:
|
|
487
506
|
- **`explicit`** (caller-provided or PR body): Flag unaddressed requirements as P1 findings with `autofix_class: manual`, `owner: downstream-resolver`. These enter the residual actionable queue and can become todos.
|
|
488
507
|
- **`inferred`** (auto-discovered): Flag unaddressed requirements as P3 findings with `autofix_class: advisory`, `owner: human`. These stay in the report only — no todos, no autonomous follow-up. An inferred plan match is a hint, not a contract.
|
|
@@ -490,12 +509,13 @@ Assemble the final report using **pipe-delimited markdown tables for findings**
|
|
|
490
509
|
4. **Applied Fixes.** Include only if a fix phase ran in this invocation.
|
|
491
510
|
5. **Residual Actionable Work.** Include when unresolved actionable findings were handed off or should be handed off.
|
|
492
511
|
6. **Pre-existing.** Separate section, does not count toward verdict.
|
|
493
|
-
7. **
|
|
494
|
-
8. **
|
|
495
|
-
9. **
|
|
496
|
-
10. **
|
|
497
|
-
11. **
|
|
498
|
-
12. **
|
|
512
|
+
7. **Filtered (not validated).** Include when Stage 5b produced any findings with `validated: false`. Render as a pipe-delimited table with columns `#`, `File`, `Issue`, `Reviewer`, `Confidence`, `Validator reason`. These findings are surfaced for human review — they are not removed from the report. The validator found evidence that the issue may not be real in the code as written, was not introduced by this diff, or is already handled elsewhere; the human reviewer makes the final call. Omit this section when no findings were filtered.
|
|
513
|
+
8. **Learnings & Past Solutions.** Surface learnings-researcher results: if past solutions are relevant, flag them as "Known Pattern" with links to docs/solutions/ files.
|
|
514
|
+
9. **Agent-Native Gaps.** Surface agent-native-reviewer results. Omit section if no gaps found.
|
|
515
|
+
10. **Schema Drift Check.** If schema-drift-detector ran, summarize whether drift was found. If drift exists, list the unrelated schema objects and the required cleanup command. If clean, say so briefly.
|
|
516
|
+
11. **Deployment Notes.** If deployment-verification-agent ran, surface the key Go/No-Go items: blocking pre-deploy checks, the most important verification queries, rollback caveats, and monitoring focus areas. Keep the checklist actionable rather than dropping it into Coverage.
|
|
517
|
+
12. **Coverage.** Suppressed count, residual risks, testing gaps, failed/timed-out reviewers, validator failures, and any intent uncertainty carried by non-interactive modes.
|
|
518
|
+
13. **Verdict.** Ready to merge / Ready with fixes / Not ready. Fix order if applicable. When an `explicit` plan has unaddressed requirements, the verdict must reflect it — a PR that's code-clean but missing planned requirements is "Not ready" unless the omission is intentional. When an `inferred` plan has unaddressed requirements, note it in the verdict reasoning but do not block on it alone.
|
|
499
519
|
|
|
500
520
|
Do not include time estimates.
|
|
501
521
|
|
|
@@ -539,6 +559,10 @@ Pre-existing issues:
|
|
|
539
559
|
[P2][gated_auto -> downstream-resolver] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
|
540
560
|
Why: <why_it_matters>
|
|
541
561
|
|
|
562
|
+
Filtered (not validated):
|
|
563
|
+
[P1][gated_auto -> downstream-resolver] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
|
564
|
+
Validator reason: <one-sentence reason from the validator>
|
|
565
|
+
|
|
542
566
|
Residual risks:
|
|
543
567
|
- <risk>
|
|
544
568
|
|
|
@@ -559,6 +583,7 @@ Testing gaps:
|
|
|
559
583
|
|
|
560
584
|
Coverage:
|
|
561
585
|
- Suppressed: <N> findings below 0.60 confidence (P0 at 0.50+ retained)
|
|
586
|
+
- Filtered (not validated): <N> findings surfaced for human review
|
|
562
587
|
- Untracked files excluded: <file1>, <file2>
|
|
563
588
|
- Failed reviewers: <reviewer>
|
|
564
589
|
|
|
@@ -576,6 +601,7 @@ Review complete
|
|
|
576
601
|
- The `Artifact:` line gives callers the path to the full run artifact for machine-readable access to the complete findings schema. The text envelope is the primary handoff; the artifact is for debugging and full-fidelity access.
|
|
577
602
|
- Findings with `owner: release` appear in the Advisory section (they are operational/rollout items, not code fixes).
|
|
578
603
|
- Findings with `pre_existing: true` appear in the Pre-existing section regardless of autofix_class.
|
|
604
|
+
- Findings with `validated: false` from Stage 5b appear in the "Filtered (not validated)" section. They are surfaced for human review — not removed. Include the validator reason on the indented `Validator reason:` line.
|
|
579
605
|
- The Verdict appears in the metadata header (deliberately reordered from the interactive format where it appears at the bottom) so programmatic callers get the verdict first.
|
|
580
606
|
- Omit any section with zero items.
|
|
581
607
|
- If all reviewers fail or time out, emit `Code review degraded (headless mode). Reason: 0 of N reviewers returned results.` followed by "Review complete".
|
|
@@ -59,6 +59,12 @@ Use this **exact format** when presenting synthesized review findings. Findings
|
|
|
59
59
|
|---|------|-------|----------|
|
|
60
60
|
| 1 | `orders_controller.rb:12` | Broad rescue masking failed permission check | correctness |
|
|
61
61
|
|
|
62
|
+
### Filtered (not validated)
|
|
63
|
+
|
|
64
|
+
| # | File | Issue | Reviewer | Confidence | Validator reason |
|
|
65
|
+
|---|------|-------|----------|------------|-----------------|
|
|
66
|
+
| 1 | `orders_controller.rb:55` | Rate limit bypass via header spoofing | security | 0.72 | The `X-Forwarded-For` header is already stripped by the load balancer before reaching the application, so the cited bypass path does not exist in this deployment. |
|
|
67
|
+
|
|
62
68
|
### Learnings & Past Solutions
|
|
63
69
|
|
|
64
70
|
- [Known Pattern] `docs/solutions/export-pagination.md` -- previous export pagination fix applies to this endpoint
|
|
@@ -126,6 +132,7 @@ This fails because: no pipe-delimited tables, no severity-grouped `###` headers,
|
|
|
126
132
|
- **Applied Fixes section** -- include only when a fix phase ran in this review invocation
|
|
127
133
|
- **Residual Actionable Work section** -- include only when unresolved actionable findings were handed off for later work
|
|
128
134
|
- **Pre-existing section** -- separate table, no confidence column (these are informational)
|
|
135
|
+
- **Filtered (not validated) section** -- findings where Stage 5b returned `validated: false`. Rendered as a pipe-delimited table with columns `#`, `File`, `Issue`, `Reviewer`, `Confidence`, `Validator reason`. These findings are surfaced for human review, not removed. Omit this section when Stage 5b produced no filtered findings.
|
|
129
136
|
- **Learnings & Past Solutions section** -- results from learnings-researcher, with links to docs/solutions/ files
|
|
130
137
|
- **Agent-Native Gaps section** -- results from agent-native-reviewer. Omit if no gaps found.
|
|
131
138
|
- **Schema Drift Check section** -- results from schema-drift-detector. Omit if the agent did not run.
|
|
@@ -145,4 +152,5 @@ In `mode:headless`, replace the interactive pipe-delimited table report with a s
|
|
|
145
152
|
- **`Artifact:` line** in metadata header gives callers the path to the full run artifact.
|
|
146
153
|
- **`[needs-verification]` marker** on findings where `requires_verification: true`.
|
|
147
154
|
- **Evidence lines** included per finding.
|
|
155
|
+
- **"Filtered (not validated)" section** included when Stage 5b produced findings with `validated: false`. Uses `[severity][autofix_class -> owner] File: <file:line> -- <title>` format with an indented `Validator reason:` line. These findings are surfaced for human review, not removed.
|
|
148
156
|
- **Completion signal:** "Review complete" as the final line.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Validator Subagent Prompt Template
|
|
2
|
+
|
|
3
|
+
This template is used by the Stage 5b orchestrator to spawn one independent validator per gated finding. Variable substitution slots are filled at dispatch time.
|
|
4
|
+
|
|
5
|
+
A finding is **gated** (eligible for validation) when it is P0 or P1 severity, or when `requires_verification: true`. Findings outside this band pass through to Stage 6 unvalidated and unfiltered — no validator is dispatched for them.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Template
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
You are an independent finding validator for a code review.
|
|
13
|
+
|
|
14
|
+
Your sole job is to answer three questions about one specific finding and return a JSON verdict. You do NOT add new findings, suggest fixes, or produce any output other than the JSON object below.
|
|
15
|
+
|
|
16
|
+
<output-contract>
|
|
17
|
+
Return ONLY valid JSON matching this schema. No prose, no markdown, no explanation outside the JSON object.
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
"validated": <boolean>,
|
|
21
|
+
"reason": "<one sentence explaining why the finding is validated or not>"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Rules:
|
|
25
|
+
- You are a leaf validator inside an already-running review workflow. Do not invoke systematic skills or agents. Perform your analysis directly and return the JSON verdict only.
|
|
26
|
+
- You are operationally read-only. You may use non-mutating inspection tools (file reads, glob, grep, git log, git diff, git show, git blame, gh pr view) to examine the code. Do not edit project files, change branches, commit, push, create PRs, or otherwise mutate the checkout or repository state.
|
|
27
|
+
- Answer the three validation questions below. Set `validated: true` only when all three answers are YES. Set `validated: false` when any answer is NO.
|
|
28
|
+
- The `reason` field must be one sentence. State the specific code evidence that drove your verdict (file name, function name, or line reference when relevant). Do not repeat the finding title verbatim.
|
|
29
|
+
- Be conservative: when evidence is ambiguous, prefer `validated: true` (keep the finding in the actioned set). A false negative that lets a real bug through is worse than a false positive that the human reviewer can dismiss.
|
|
30
|
+
- Do not validate based on general coding principles alone. Ground your verdict in the actual code as written in this diff and the surrounding context.
|
|
31
|
+
</output-contract>
|
|
32
|
+
|
|
33
|
+
<validation-questions>
|
|
34
|
+
Answer each question YES or NO based on the code evidence you find.
|
|
35
|
+
|
|
36
|
+
1. **Is the issue real in the code as written?**
|
|
37
|
+
Read the cited file and line. Does the problem the finding describes actually exist in the current code? A finding is not real if the code already handles the case, the cited line does not contain the described issue, or the issue is in a comment or dead code path.
|
|
38
|
+
|
|
39
|
+
2. **Was this issue introduced by THIS diff?**
|
|
40
|
+
Check whether the cited code is new or changed in this diff, or whether it existed before. A finding is not introduced by this diff if the code is unchanged (pre-existing). Exception: if the diff makes a pre-existing issue newly reachable or newly relevant (e.g., a new call site, a removed guard), the finding is still valid — mark it as introduced by this diff.
|
|
41
|
+
|
|
42
|
+
3. **Is the issue already handled elsewhere?**
|
|
43
|
+
Check whether the problem is already mitigated by a guard, middleware, framework behavior, type constraint, or other mechanism not visible at the cited line. A finding is not valid if the issue is fully handled at a higher or lower layer that the reviewer missed.
|
|
44
|
+
|
|
45
|
+
Set `validated: true` only when: the issue is real (Q1 YES), introduced by this diff or newly relevant (Q2 YES), and not already handled elsewhere (Q3 YES).
|
|
46
|
+
Set `validated: false` when any question is NO, and state which question failed and why in `reason`.
|
|
47
|
+
</validation-questions>
|
|
48
|
+
|
|
49
|
+
<finding>
|
|
50
|
+
Title: {finding_title}
|
|
51
|
+
Severity: {finding_severity}
|
|
52
|
+
File: {finding_file}
|
|
53
|
+
Line: {finding_line}
|
|
54
|
+
Reviewer(s): {finding_reviewers}
|
|
55
|
+
Confidence: {finding_confidence}
|
|
56
|
+
requires_verification: {finding_requires_verification}
|
|
57
|
+
Suggested fix: {finding_suggested_fix}
|
|
58
|
+
</finding>
|
|
59
|
+
|
|
60
|
+
<review-context>
|
|
61
|
+
Intent: {intent_summary}
|
|
62
|
+
|
|
63
|
+
Changed files: {file_list}
|
|
64
|
+
|
|
65
|
+
Diff:
|
|
66
|
+
{diff}
|
|
67
|
+
</review-context>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Variable Reference
|
|
73
|
+
|
|
74
|
+
| Variable | Source | Description |
|
|
75
|
+
|----------|--------|-------------|
|
|
76
|
+
| `{finding_title}` | Stage 5 merged finding | The finding's title field |
|
|
77
|
+
| `{finding_severity}` | Stage 5 merged finding | P0, P1, P2, or P3 |
|
|
78
|
+
| `{finding_file}` | Stage 5 merged finding | File path cited by the finding |
|
|
79
|
+
| `{finding_line}` | Stage 5 merged finding | Line number cited by the finding |
|
|
80
|
+
| `{finding_reviewers}` | Stage 5 merged finding | Reviewer(s) that flagged this finding |
|
|
81
|
+
| `{finding_confidence}` | Stage 5 merged finding | Confidence score (0.0–1.0) |
|
|
82
|
+
| `{finding_requires_verification}` | Stage 5 merged finding | Boolean from the merged finding |
|
|
83
|
+
| `{finding_suggested_fix}` | Stage 5 merged finding | Suggested fix text, or "none" |
|
|
84
|
+
| `{intent_summary}` | Stage 2 output | 2–3 line description of what the change is trying to accomplish |
|
|
85
|
+
| `{file_list}` | Stage 1 output | List of changed files from the scope step |
|
|
86
|
+
| `{diff}` | Stage 1 output | The actual diff content to review |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Dispatch Notes
|
|
91
|
+
|
|
92
|
+
- Dispatch one validator subagent per gated finding **in parallel** to keep latency bounded.
|
|
93
|
+
- Pass the full diff and file list so the validator can inspect surrounding context, not just the cited line.
|
|
94
|
+
- The validator returns `{validated, reason}`. Attach both fields to the finding before Stage 6.
|
|
95
|
+
- **Never drop a finding based on the validator verdict.** A finding with `validated: false` moves to the "Filtered (not validated)" presentation group in Stage 6. It is never removed from the report.
|
|
96
|
+
- If a validator subagent fails or times out, treat the finding as `validated: true` (conservative fallback — keep it in the actioned set) and note the validator failure in the Coverage section.
|
|
@@ -93,7 +93,7 @@ function matchGlob(pattern, command) {
|
|
|
93
93
|
if (normalized.endsWith(' *')) {
|
|
94
94
|
const base = normalized.slice(0, -2)
|
|
95
95
|
const escaped = base.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
96
|
-
regexStr =
|
|
96
|
+
regexStr = `^${escaped}($| .*)`
|
|
97
97
|
} else {
|
|
98
98
|
regexStr =
|
|
99
99
|
'^' +
|
|
@@ -530,7 +530,7 @@ for (const [command, data] of commands) {
|
|
|
530
530
|
continue
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
const pattern =
|
|
533
|
+
const pattern = `Bash(${normalize(command)})`
|
|
534
534
|
const { tier, reason } = classify(command)
|
|
535
535
|
|
|
536
536
|
const existing = patternGroups.get(pattern)
|
|
@@ -47,20 +47,20 @@ export function normalize(command) {
|
|
|
47
47
|
|
|
48
48
|
// Handle pnpm --filter <pkg> <subcommand> specially
|
|
49
49
|
const pnpmFilter = command.match(/^pnpm\s+--filter\s+\S+\s+(\S+)/)
|
|
50
|
-
if (pnpmFilter) return
|
|
50
|
+
if (pnpmFilter) return `pnpm --filter * ${pnpmFilter[1]} *`
|
|
51
51
|
|
|
52
52
|
// Handle sed specially -- preserve the mode flag to keep safe patterns narrow.
|
|
53
53
|
// sed -i (in-place) is destructive; sed -n, sed -e, bare sed are read-only.
|
|
54
54
|
if (/^sed\s/.test(command)) {
|
|
55
55
|
if (/\s-i\b/.test(command)) return 'sed -i *'
|
|
56
56
|
const sedFlag = command.match(/^sed\s+(-[a-zA-Z])\s/)
|
|
57
|
-
return sedFlag ?
|
|
57
|
+
return sedFlag ? `sed ${sedFlag[1]} *` : 'sed *'
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// Handle ast-grep specially -- preserve --rewrite flag.
|
|
61
61
|
if (/^(ast-grep|sg)\s/.test(command)) {
|
|
62
62
|
const base = command.startsWith('sg') ? 'sg' : 'ast-grep'
|
|
63
|
-
return /\s--rewrite\b/.test(command) ? base
|
|
63
|
+
return /\s--rewrite\b/.test(command) ? `${base} --rewrite *` : `${base} *`
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Handle find specially -- preserve key action flags.
|
|
@@ -70,12 +70,12 @@ export function normalize(command) {
|
|
|
70
70
|
if (/\s-exec\s/.test(command)) return 'find -exec *'
|
|
71
71
|
// Extract the first predicate flag for a narrower safe pattern
|
|
72
72
|
const findFlag = command.match(/\s(-(?:name|type|path|iname))\s/)
|
|
73
|
-
return findFlag ?
|
|
73
|
+
return findFlag ? `find ${findFlag[1]} *` : 'find *'
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Handle git -C <dir> <subcommand> -- strip the -C <dir> and normalize the git subcommand
|
|
77
77
|
const gitC = command.match(/^git\s+-C\s+\S+\s+(.+)$/)
|
|
78
|
-
if (gitC) return normalize(
|
|
78
|
+
if (gitC) return normalize(`git ${gitC[1]}`)
|
|
79
79
|
|
|
80
80
|
// Split on compound operators -- normalize the first command only
|
|
81
81
|
const compoundMatch = command.match(/^(.+?)\s*(&&|\|\||;)\s*(.+)$/)
|
|
@@ -123,7 +123,7 @@ export function normalize(command) {
|
|
|
123
123
|
let argStart = 1
|
|
124
124
|
|
|
125
125
|
if (multiWordBases.includes(base) && parts.length > 1) {
|
|
126
|
-
prefix = base
|
|
126
|
+
prefix = `${base} ${parts[1]}`
|
|
127
127
|
argStart = 2
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -141,11 +141,11 @@ export function normalize(command) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const flagStr =
|
|
144
|
-
preservedFlags.length > 0 ?
|
|
144
|
+
preservedFlags.length > 0 ? ` ${preservedFlags.join(' ')}` : ''
|
|
145
145
|
const hasVaryingArgs = parts.length > argStart + preservedFlags.length
|
|
146
146
|
|
|
147
147
|
if (hasVaryingArgs) {
|
|
148
|
-
return prefix + flagStr
|
|
148
|
+
return `${prefix + flagStr} *`
|
|
149
149
|
}
|
|
150
150
|
return prefix + flagStr
|
|
151
151
|
}
|
|
@@ -120,7 +120,7 @@ async function listDirNames(dir) {
|
|
|
120
120
|
const entries = await listDir(dir)
|
|
121
121
|
return entries
|
|
122
122
|
.filter((e) => e.isDirectory() && !EXCLUDED_DIRS.has(e.name))
|
|
123
|
-
.map((e) => e.name
|
|
123
|
+
.map((e) => `${e.name}/`)
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
async function listFileNames(dir, opts) {
|
|
@@ -511,7 +511,7 @@ async function getStructure() {
|
|
|
511
511
|
for (const entry of entries) {
|
|
512
512
|
if (EXCLUDED_DIRS.has(entry.name)) continue
|
|
513
513
|
if (entry.isDirectory()) {
|
|
514
|
-
topLevel.push(entry.name
|
|
514
|
+
topLevel.push(`${entry.name}/`)
|
|
515
515
|
} else {
|
|
516
516
|
topLevel.push(entry.name)
|
|
517
517
|
}
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
* Requires: graphviz (dot) installed on system
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
const fs = require('fs')
|
|
23
|
-
const path = require('path')
|
|
24
|
-
const { execSync } = require('child_process')
|
|
22
|
+
const fs = require('node:fs')
|
|
23
|
+
const path = require('node:path')
|
|
24
|
+
const { execSync } = require('node:child_process')
|
|
25
25
|
|
|
26
26
|
function extractDotBlocks(markdown) {
|
|
27
27
|
const blocks = []
|
|
@@ -61,7 +61,7 @@ function combineGraphs(blocks, skillName) {
|
|
|
61
61
|
label="${block.name}";
|
|
62
62
|
${body
|
|
63
63
|
.split('\n')
|
|
64
|
-
.map((line) =>
|
|
64
|
+
.map((line) => ` ${line}`)
|
|
65
65
|
.join('\n')}
|
|
66
66
|
}`
|
|
67
67
|
})
|
|
File without changes
|