@fro.bot/systematic 2.24.0 → 2.26.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.
Files changed (26) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/lib/frontmatter.d.ts +12 -0
  4. package/package.json +5 -5
  5. package/skills/ce-brainstorm/SKILL.md +18 -1
  6. package/skills/ce-brainstorm/references/brainstorm-sections.md +50 -0
  7. package/skills/ce-brainstorm/references/markdown-rendering.md +202 -0
  8. package/skills/ce-brainstorm/references/requirements-capture.md +20 -0
  9. package/skills/ce-brainstorm/references/synthesis-summary.md +273 -0
  10. package/skills/ce-brainstorm/references/universal-brainstorming.md +1 -1
  11. package/skills/ce-brainstorm/references/visual-communication.md +29 -0
  12. package/skills/ce-compound/references/schema.yaml +27 -2
  13. package/skills/ce-plan/SKILL.md +120 -0
  14. package/skills/ce-plan/references/markdown-rendering.md +203 -0
  15. package/skills/ce-plan/references/plan-handoff.md +5 -5
  16. package/skills/ce-plan/references/plan-sections.md +117 -0
  17. package/skills/ce-plan/references/synthesis-summary.md +395 -0
  18. package/skills/ce-plan/references/universal-planning.md +3 -3
  19. package/skills/ce-review/SKILL.md +33 -7
  20. package/skills/ce-review/references/review-output-template.md +8 -0
  21. package/skills/ce-review/references/validator-template.md +96 -0
  22. package/skills/claude-permissions-optimizer/scripts/extract-commands.mjs +2 -2
  23. package/skills/claude-permissions-optimizer/scripts/normalize.mjs +8 -8
  24. package/skills/onboarding/scripts/inventory.mjs +2 -2
  25. package/skills/writing-skills/scripts/render-graphs.js +4 -4
  26. /package/dist/{index-175fc4yn.js → index-3q3ns1xh.js} +0 -0
@@ -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 = '^' + escaped + '($| .*)'
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 = 'Bash(' + normalize(command) + ')'
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 'pnpm --filter * ' + pnpmFilter[1] + ' *'
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 ? 'sed ' + sedFlag[1] + ' *' : 'sed *'
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 + ' --rewrite *' : 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 ? 'find ' + findFlag[1] + ' *' : 'find *'
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('git ' + gitC[1])
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 + ' ' + parts[1]
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 ? ' ' + preservedFlags.join(' ') : ''
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) => ' ' + line)
64
+ .map((line) => ` ${line}`)
65
65
  .join('\n')}
66
66
  }`
67
67
  })
File without changes