@olegkoval/agent-skills 1.70.0 → 1.71.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 (37) hide show
  1. package/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +11 -2
  2. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/conventions.md +3 -0
  3. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/implementation.md +3 -0
  4. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/quality.md +3 -0
  5. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/simplification.md +3 -0
  6. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/test-quality.md +3 -0
  7. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/verifier.md +28 -3
  8. package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/project-rules/CLAUDE.md +4 -0
  9. package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/project-rules/planted.diff +9 -0
  10. package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/project-rule-findings.mjs +122 -0
  11. package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +160 -0
  12. package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/setup-worktree.sh +63 -5
  13. package/package.json +1 -1
  14. package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
  15. package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
  16. package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
  17. package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
  18. package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
  19. package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +11 -2
  20. package/plugins/olko-github-pr/skills/lekker-review/references/agents/conventions.md +3 -0
  21. package/plugins/olko-github-pr/skills/lekker-review/references/agents/implementation.md +3 -0
  22. package/plugins/olko-github-pr/skills/lekker-review/references/agents/quality.md +3 -0
  23. package/plugins/olko-github-pr/skills/lekker-review/references/agents/simplification.md +3 -0
  24. package/plugins/olko-github-pr/skills/lekker-review/references/agents/test-quality.md +3 -0
  25. package/plugins/olko-github-pr/skills/lekker-review/references/agents/verifier.md +28 -3
  26. package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/project-rules/CLAUDE.md +4 -0
  27. package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/project-rules/planted.diff +9 -0
  28. package/plugins/olko-github-pr/skills/lekker-review/scripts/project-rule-findings.mjs +122 -0
  29. package/plugins/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +160 -0
  30. package/plugins/olko-github-pr/skills/lekker-review/scripts/setup-worktree.sh +63 -5
  31. package/plugins/olko-github-pr/skills/lekker-review/workflow.js +62 -2
  32. package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
  33. package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
  34. package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
  35. package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
  36. package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
  37. package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
@@ -33,6 +33,7 @@ const FINDINGS_ARRAY_SCHEMA = {
33
33
  anyOf: [
34
34
  { properties: { severity: { enum: ['observation', 'idiomatic'] } } },
35
35
  { required: ['boundary'] },
36
+ { required: ['rule'], properties: { rule: { pattern: '^(PROJECT:[a-z0-9]+(-[a-z0-9]+)*|weakens-project-rule)$' } } },
36
37
  ],
37
38
  properties: {
38
39
  file: { type: 'string' },
@@ -44,6 +45,8 @@ const FINDINGS_ARRAY_SCHEMA = {
44
45
  fix: { type: 'string' },
45
46
  precedent: { type: 'string' },
46
47
  rule: { type: 'string', minLength: 1, pattern: '\\S' },
48
+ // Verbatim project rule a PROJECT:<slug> finding enforces.
49
+ ruleQuote: { type: 'string' },
47
50
  boundary: BOUNDARY_SCHEMA,
48
51
  // Short kebab-case slug naming the class of bug ("missing-null-check").
49
52
  bugClass: { type: 'string', minLength: 1, pattern: '^[a-z0-9]+(-[a-z0-9]+)*$' },
@@ -107,6 +110,8 @@ const VERDICT_SCHEMA = {
107
110
  verdict: { enum: ['confirmed', 'downgraded', 'dropped'] },
108
111
  newSeverity: { enum: ['important', 'observation'] },
109
112
  reasoning: { type: 'string' },
113
+ // True when the PROJECT-rule validator could not run (exit other than 0 or 1).
114
+ validatorUnavailable: { type: 'boolean' },
110
115
  },
111
116
  }
112
117
 
@@ -162,6 +167,23 @@ function titleTokens(title) {
162
167
  }
163
168
 
164
169
  function sameIssue(a, b) {
170
+ // Lens-generated slugs can differ even when they identify the same project rule.
171
+ const sameProjectQuote = typeof a.rule === 'string' && a.rule.startsWith('PROJECT:')
172
+ && typeof b.rule === 'string' && b.rule.startsWith('PROJECT:')
173
+ && typeof a.ruleQuote === 'string' && a.ruleQuote.trim() !== ''
174
+ && a.ruleQuote === b.ruleQuote
175
+ if (typeof a.rule === 'string' && typeof b.rule === 'string' && a.rule !== b.rule
176
+ && (a.rule.startsWith('PROJECT:') || b.rule.startsWith('PROJECT:'))
177
+ && !sameProjectQuote) {
178
+ return false
179
+ }
180
+ // Two quotes of one PROJECT rule that differ are two different rule passages, not one issue.
181
+ if (typeof a.rule === 'string' && a.rule === b.rule && a.rule.startsWith('PROJECT:')
182
+ && typeof a.ruleQuote === 'string' && a.ruleQuote.trim() !== ''
183
+ && typeof b.ruleQuote === 'string' && b.ruleQuote.trim() !== ''
184
+ && a.ruleQuote !== b.ruleQuote) {
185
+ return false
186
+ }
165
187
  // Merging two classes would send one class's instances to the other's fix.
166
188
  if (a.bugClass && b.bugClass && a.bugClass !== b.bugClass) {
167
189
  return false
@@ -182,6 +204,22 @@ const BUG_CLASS_INSTRUCTION = `BUG CLASS: give every finding a "bugClass" (short
182
204
  + ` hit, search the whole diff for the same pattern and list every other place as`
183
205
  + ` "file:line" (empty array when there is none). Report the class, not just the first hit.`
184
206
 
207
+ const PROJECT_RULE_DIMENSIONS = ['quality', 'implementation', 'simplification', 'conventions', 'test-quality',
208
+ 'triage-quality', 'triage-logic']
209
+
210
+ const PROJECT_RULE_INSTRUCTION = `PROJECT RULES AS HARD RULES: read "projectRules" from CONTEXT_FILE.`
211
+ + ` When a changed line violates a project rule written as a prohibition or an obligation`
212
+ + ` (NEVER, MUST, MUST NOT, ALWAYS, DO NOT, or a plain-English equivalent), raise a Critical`
213
+ + ` finding with "rule" set to "PROJECT:<short-kebab-slug>", "ruleQuote" set to the exact`
214
+ + ` sentence from the rules (use "baseProjectRules" when it is a string: it is the base`
215
+ + ` branch's copy, which the PR author cannot edit; otherwise "projectRules"), and file:line`
216
+ + ` on the violating line. When the diff deletes or weakens a rule line that is in`
217
+ + ` baseProjectRules, raise an Important finding with rule "weakens-project-rule" quoting`
218
+ + ` the removed line. One finding per rule,`
219
+ + ` with every other violating site in "otherInstances". Never quote a rule that is not in`
220
+ + ` the authoritative source ("baseProjectRules" when it is a string, otherwise "projectRules"). Preferences and suggestions are not hard rules: report them as ordinary`
221
+ + ` findings. A REVIEW.md ignore entry narrows where you look, it never disables a rule.`
222
+
185
223
  const BUG_CLASS_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/
186
224
  const INSTANCE_PATTERN = new RegExp(INSTANCE_PATTERN_SOURCE)
187
225
 
@@ -234,6 +272,14 @@ function mergeFindings(a, b) {
234
272
 
235
273
  if (!merged.rule && b.rule) {
236
274
  merged.rule = b.rule
275
+ if (Object.prototype.hasOwnProperty.call(b, 'ruleQuote')) {
276
+ merged.ruleQuote = b.ruleQuote
277
+ } else {
278
+ delete merged.ruleQuote
279
+ }
280
+ } else if (merged.rule && merged.rule === b.rule && !merged.ruleQuote
281
+ && typeof b.ruleQuote === 'string' && b.ruleQuote.trim()) {
282
+ merged.ruleQuote = b.ruleQuote
237
283
  }
238
284
 
239
285
  const agreedBy = []
@@ -547,7 +593,7 @@ if (Number(cycle) >= RETHINK_CYCLE) {
547
593
  recurringClasses: Array.isArray(recurringClasses) ? recurringClasses : [],
548
594
  findings: [],
549
595
  preExisting: [],
550
- droppedCount: 0, downgradedCount: 0, hardRuleCount: 0, agentCount: 0,
596
+ droppedCount: 0, downgradedCount: 0, hardRuleCount: 0, validatorUnavailableCount: 0, agentCount: 0,
551
597
  proveAttemptCount: 0, provenCount: 0, suppressedCount: 0,
552
598
  outputTokens: 0, turnTokensTotal: budget.spent(),
553
599
  engine: ENGINE,
@@ -562,6 +608,7 @@ const budgetAtStart = budget.spent()
562
608
  let droppedCount = 0
563
609
  let downgradedCount = 0
564
610
  let hardRuleCount = 0
611
+ let validatorUnavailableCount = 0
565
612
  let provenCount = 0
566
613
  let suppressedCount = 0
567
614
  let proveAttemptCount = 0
@@ -613,6 +660,10 @@ const budgetAtStart = budget.spent()
613
660
 
614
661
  prompt += ' ' + BUG_CLASS_INSTRUCTION
615
662
 
663
+ if (PROJECT_RULE_DIMENSIONS.includes(dim.key)) {
664
+ prompt += ' ' + PROJECT_RULE_INSTRUCTION
665
+ }
666
+
616
667
  // The deterministic lint pre-flight has already collected every rule the
617
668
  // repo's own eslint config fires on this diff, and those are exactly the
618
669
  // comments the bot reviewers bill for. An agent re-reporting them costs a
@@ -650,7 +701,7 @@ const budgetAtStart = budget.spent()
650
701
  `FINDING (JSON): ${JSON.stringify(finding)}.`,
651
702
  `DIFF_FILE=${diffFile}, CONTEXT_FILE=${contextFile}, WORKTREE_PATH=${wtDisplay}.`,
652
703
  `HOUSE_RULES_FILE: read key "houseRulesFile" from CONTEXT_FILE, then read that exact path before validating a hard rule.`,
653
- `Default to dropping when uncertain.`,
704
+ `Default to dropping when uncertain, except when the PROJECT-rule validator was unavailable: then check by hand that the exact quote appears in baseProjectRules when it is a string, otherwise projectRules (drop if it does not), keep the finding only if it does, set validatorUnavailable true, and judge applicability.`,
654
705
  ].join(' ')
655
706
  }
656
707
 
@@ -708,6 +759,9 @@ const budgetAtStart = budget.spent()
708
759
  // A model verdict cannot replace the required production boundary map.
709
760
  function enforceBoundary(finding) {
710
761
  if (!shouldVerify(finding, depth)) return finding
762
+ // A project rule or a weakened project rule is violated by the line itself; it needs no production entry point.
763
+ if (typeof finding.rule === 'string'
764
+ && (finding.rule.startsWith('PROJECT:') || finding.rule === 'weakens-project-rule')) return finding
711
765
  const boundary = finding.boundary
712
766
  const hasText = function(value) {
713
767
  return typeof value === 'string' && value.trim().length > 0
@@ -764,6 +818,11 @@ const budgetAtStart = budget.spent()
764
818
  return kept
765
819
  }
766
820
 
821
+ if (verdict.validatorUnavailable === true) {
822
+ validatorUnavailableCount++
823
+ log(`PROJECT-rule validator unavailable for ${finding.file}:${finding.line}; ${verdict.verdict === 'dropped' ? 'finding dropped' : 'finding kept'}`)
824
+ }
825
+
767
826
  if (verdict.verdict === 'dropped') {
768
827
  droppedCount++
769
828
  return null
@@ -1087,6 +1146,7 @@ const budgetAtStart = budget.spent()
1087
1146
  droppedCount,
1088
1147
  downgradedCount,
1089
1148
  hardRuleCount,
1149
+ validatorUnavailableCount,
1090
1150
  agentCount,
1091
1151
  proveAttemptCount,
1092
1152
  provenCount,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-obsidian",
3
3
  "description": "Keep an Obsidian vault in sync with work: PR sync, task rollover, morning routine.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-product",
3
3
  "description": "Take a product idea to a shippable build: MVP passes, full-stack scaffolds, UX/UI quality loops, launch plans.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-reflection",
3
3
  "description": "Look back and improve: self-critique, retrospectives, performance review, rapid learning.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-release",
3
3
  "description": "Ship a release: semantic-release setup, changelogs, store listing copy, release-day routine.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-skill-meta",
3
3
  "description": "Author and maintain agent skills and the AI toolchain itself.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-web-ops",
3
3
  "description": "Operate a website: WAF rules, search console audits, analytics bootstrap, docs indexes.",
4
- "version": "1.70.0",
4
+ "version": "1.71.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },