@olegkoval/agent-skills 1.60.0 → 1.62.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/.github/copilot-instructions.md +2 -0
- package/.github/prompts/carousel-maker.prompt.md +102 -0
- package/.github/prompts/shorts-animator.prompt.md +103 -0
- package/.kiro/steering/carousel-maker.md +101 -0
- package/.kiro/steering/shorts-animator.md +102 -0
- package/.windsurf/rules/carousel-maker.md +100 -0
- package/.windsurf/rules/shorts-animator.md +101 -0
- package/README.md +4 -2
- package/adapters/claude/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +30 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/context-gathering.md +3 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/finding-state.mjs +207 -0
- package/adapters/codex/olko-creative/README.md +2 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/hermes/olko-creative/README.md +2 -0
- package/catalog/skills.json +46 -0
- package/package.json +1 -1
- package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/skills/carousel-maker/SKILL.md +111 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/plugins/olko-creative/skills/shorts-animator/SKILL.md +112 -0
- package/plugins/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/plugins/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/plugins/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +30 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/context-gathering.md +3 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/finding-state.mjs +207 -0
- package/plugins/olko-github-pr/skills/lekker-review/workflow.js +46 -3
- package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
- package/scripts/lib/catalog.mjs +2 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Per-PR finding state for lekker-review, so re-reviews converge.
|
|
3
|
+
//
|
|
4
|
+
// A re-review used to know only the last head sha and dedupe by file:line, so a
|
|
5
|
+
// finding the author had already rejected came back every round. This keeps one
|
|
6
|
+
// small JSON file per PR next to the run ledger (${LEKKER_STATE_DIR:-
|
|
7
|
+
// ~/.local/state/lekker-review}/findings/) holding each finding's status and the
|
|
8
|
+
// number of review cycles.
|
|
9
|
+
//
|
|
10
|
+
// Statuses: open | fixed | rejected | intentional. A finding is
|
|
11
|
+
// fixed when GitHub marks its thread outdated (the line changed)
|
|
12
|
+
// intentional when the author replied "by design" and the code did not change
|
|
13
|
+
// rejected when the author replied with another dismissal, or the thread
|
|
14
|
+
// was resolved without a code change and the PR author is known
|
|
15
|
+
//
|
|
16
|
+
// Usage:
|
|
17
|
+
// node finding-state.mjs begin --repo <slug> --target <pr-N> [--threads <threads.json>]
|
|
18
|
+
// Folds thread outcomes into the stored statuses, counts the cycle, and
|
|
19
|
+
// prints {cycle, rethink, suppressed, recurringClasses} for workflow.js.
|
|
20
|
+
// node finding-state.mjs record --repo <slug> --target <pr-N> --findings <findings.json>
|
|
21
|
+
// Stores this run's findings as open (an already-known finding keeps its status).
|
|
22
|
+
// node finding-state.mjs show --repo <slug> --target <pr-N>
|
|
23
|
+
//
|
|
24
|
+
// threads.json: { prAuthor, threads: [{ path, line, title?, body, isResolved, isOutdated,
|
|
25
|
+
// replies: [{ author, body }] }] }, where `body` is lekker's comment and only
|
|
26
|
+
// replies by `prAuthor` count as the author's answer. A bare array is accepted
|
|
27
|
+
// when the author is unknown; it can then only yield fixed (outdated) threads.
|
|
28
|
+
// When title is absent, the first nonempty comment line supplies the title.
|
|
29
|
+
|
|
30
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
31
|
+
import { readFileSync, writeFileSync, mkdirSync, renameSync, rmSync } from 'node:fs';
|
|
32
|
+
import { homedir } from 'node:os';
|
|
33
|
+
import { dirname, join } from 'node:path';
|
|
34
|
+
import { fileURLToPath } from 'node:url';
|
|
35
|
+
|
|
36
|
+
export const RETHINK_CYCLE = 4;
|
|
37
|
+
const INTENT = /\b(intentional(ly)?|by design|on purpose|deliberate(ly)?|as designed|expected behaviou?r)\b/i;
|
|
38
|
+
const DISMISS = /\b(won'?t fix|wontfix|not a (bug|problem|concern)|false positive|disagree|n\/a|not applicable|out of scope|ignor(e|ing)|nit)\b/i;
|
|
39
|
+
|
|
40
|
+
export function titleTokens(title) {
|
|
41
|
+
return (String(title || '').toLowerCase().match(/[a-z0-9]+/g) || []).filter((w) => w.length > 2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function sameIssue(a, b) {
|
|
45
|
+
const normalize = (title) => String(title || '').trim().replace(/\s+/g, ' ').toLowerCase();
|
|
46
|
+
const title = normalize(a.title);
|
|
47
|
+
return title !== '' && title === normalize(b.title);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function sameFinding(a, b) {
|
|
51
|
+
// Status reuse requires the same anchor and title; word overlap is only a
|
|
52
|
+
// class signal, never proof that two findings identify the same issue.
|
|
53
|
+
return Boolean(a.file) && a.file === b.file && a.line === b.line && sameIssue(a, b);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Group key for "the same kind of finding came back again".
|
|
57
|
+
export function findingClass(f) {
|
|
58
|
+
return f.bugClass || titleTokens(f.title).slice(0, 4).sort().join('-') || 'unclassified';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const NEGATOR = /\b(not|no|never|nor|isn'?t|aren'?t|wasn'?t|don'?t|doesn'?t|won'?t|cannot)\b/i;
|
|
62
|
+
|
|
63
|
+
// Check only the three words before each keyword in its clause for negation.
|
|
64
|
+
function mentions(pattern, text) {
|
|
65
|
+
return [...text.matchAll(new RegExp(pattern.source, 'gi'))]
|
|
66
|
+
.some((match) => {
|
|
67
|
+
const before = text.slice(0, match.index).split(/[.!?;:,\n-]/).pop();
|
|
68
|
+
return !NEGATOR.test(before.trim().split(/\s+/).slice(-3).join(' '));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function classifyThread(thread, prAuthor) {
|
|
73
|
+
const authorReplies = (thread.replies || []).filter((r) => r.author && r.author === prAuthor);
|
|
74
|
+
const text = authorReplies.map((r) => r.body).join('\n');
|
|
75
|
+
if (thread.isOutdated) return 'fixed';
|
|
76
|
+
if (mentions(INTENT, text)) return 'intentional';
|
|
77
|
+
if (mentions(DISMISS, text)) return 'rejected';
|
|
78
|
+
if (thread.isResolved && prAuthor) return 'rejected';
|
|
79
|
+
return 'open';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function applyThreads(findings, threads, prAuthor) {
|
|
83
|
+
return findings.map((f) => {
|
|
84
|
+
const thread = threads.find((t) => {
|
|
85
|
+
const title = t.title?.trim() || String(t.body || '').split(/\r?\n/).find((line) => line.trim())
|
|
86
|
+
?.trim().replace(/^#{1,6}\s+/, '').replace(/^(\*\*|__)(.*?)\1[.:]?$/, '$2');
|
|
87
|
+
return sameFinding(f, { file: t.path, line: t.line, title });
|
|
88
|
+
});
|
|
89
|
+
if (!thread) return f;
|
|
90
|
+
const status = classifyThread(thread, prAuthor);
|
|
91
|
+
return f.status === status ? f : { ...f, status };
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function mergeFindings(prior, found, cycle) {
|
|
96
|
+
const added = found
|
|
97
|
+
.filter((f) => !prior.some((p) => sameFinding(p, f)))
|
|
98
|
+
.map((f) => ({ file: f.file, line: f.line, title: f.title, class: findingClass(f), status: 'open', cycles: [cycle] }));
|
|
99
|
+
const seen = prior.map((p) => (found.some((f) => sameFinding(p, f)) && !p.cycles.includes(cycle)
|
|
100
|
+
? { ...p, cycles: [...p.cycles, cycle] }
|
|
101
|
+
: p));
|
|
102
|
+
return seen.concat(added);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function recurringClasses(findings) {
|
|
106
|
+
const byClass = findings.reduce((acc, f) => {
|
|
107
|
+
const cycles = new Set([...(acc[f.class] || []), ...f.cycles]);
|
|
108
|
+
return { ...acc, [f.class]: [...cycles] };
|
|
109
|
+
}, {});
|
|
110
|
+
return Object.entries(byClass)
|
|
111
|
+
.filter(([, cycles]) => cycles.length >= 2)
|
|
112
|
+
.map(([cls, cycles]) => ({ class: cls, cycles: cycles.length }))
|
|
113
|
+
.sort((a, b) => b.cycles - a.cycles);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function suppressedFindings(findings) {
|
|
117
|
+
return findings
|
|
118
|
+
.filter((f) => f.status === 'rejected' || f.status === 'intentional')
|
|
119
|
+
.map(({ file, line, title, status }) => ({ file, line, title, status }));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// A run id makes begin idempotent: a retry of the same run reuses its cycle instead of inflating the count.
|
|
123
|
+
export function begin(state, threads, prAuthor, run = null) {
|
|
124
|
+
const findings = applyThreads(state.findings, threads, prAuthor);
|
|
125
|
+
const known = run === null ? undefined : state.runs?.[run];
|
|
126
|
+
const cycle = known ?? state.cycle + 1;
|
|
127
|
+
const runs = run === null || known !== undefined ? state.runs : { ...state.runs, [run]: cycle };
|
|
128
|
+
return {
|
|
129
|
+
state: { ...state, cycle: Math.max(state.cycle, cycle), findings, ...(runs && { runs }) },
|
|
130
|
+
summary: {
|
|
131
|
+
cycle,
|
|
132
|
+
rethink: cycle >= RETHINK_CYCLE,
|
|
133
|
+
suppressed: suppressedFindings(findings),
|
|
134
|
+
recurringClasses: recurringClasses(findings),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const STATE_DIR = process.env.LEKKER_STATE_DIR || join(homedir(), '.local', 'state', 'lekker-review');
|
|
140
|
+
// Hash the tuple so punctuation, case and repo/target boundaries stay distinct.
|
|
141
|
+
const statePath = (repo, target) => join(STATE_DIR, 'findings',
|
|
142
|
+
`${createHash('sha256').update(JSON.stringify([repo, target])).digest('hex')}.json`);
|
|
143
|
+
|
|
144
|
+
function load(path) {
|
|
145
|
+
let raw;
|
|
146
|
+
try {
|
|
147
|
+
raw = readFileSync(path, 'utf8');
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (error.code === 'ENOENT') return { cycle: 0, findings: [] };
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
const state = JSON.parse(raw);
|
|
153
|
+
if (!state || typeof state !== 'object' || Array.isArray(state)
|
|
154
|
+
|| !Number.isSafeInteger(state.cycle) || state.cycle < 0 || !Array.isArray(state.findings)) {
|
|
155
|
+
throw new Error(`Invalid finding state: ${path}`);
|
|
156
|
+
}
|
|
157
|
+
if (state.runs !== undefined && (!state.runs || typeof state.runs !== 'object' || Array.isArray(state.runs)
|
|
158
|
+
|| !Object.values(state.runs).every((c) => Number.isSafeInteger(c) && c >= 1))) {
|
|
159
|
+
throw new Error(`Invalid finding state runs: ${path}`);
|
|
160
|
+
}
|
|
161
|
+
return state;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function save(path, state) {
|
|
165
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
166
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
167
|
+
try {
|
|
168
|
+
writeFileSync(temporary, JSON.stringify(state, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
169
|
+
renameSync(temporary, path);
|
|
170
|
+
} finally {
|
|
171
|
+
rmSync(temporary, { force: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function main(argv) {
|
|
176
|
+
const flag = (name) => { const i = argv.indexOf('--' + name); return i === -1 ? null : argv[i + 1]; };
|
|
177
|
+
const repo = flag('repo');
|
|
178
|
+
const target = flag('target');
|
|
179
|
+
if (!repo || !target) { console.log(JSON.stringify({ error: '--repo and --target are required' })); process.exit(2); }
|
|
180
|
+
const path = statePath(repo, target);
|
|
181
|
+
const state = load(path);
|
|
182
|
+
const cmd = argv[0];
|
|
183
|
+
if (cmd === 'begin') {
|
|
184
|
+
const raw = flag('threads') ? JSON.parse(readFileSync(flag('threads'), 'utf8')) : [];
|
|
185
|
+
const threads = Array.isArray(raw) ? raw : raw.threads || [];
|
|
186
|
+
const prAuthor = Array.isArray(raw) ? null : raw.prAuthor || null;
|
|
187
|
+
const next = begin(state, threads, prAuthor, flag('run') ?? null);
|
|
188
|
+
save(path, next.state);
|
|
189
|
+
console.log(JSON.stringify(next.summary));
|
|
190
|
+
} else if (cmd === 'record') {
|
|
191
|
+
const raw = JSON.parse(readFileSync(flag('findings'), 'utf8'));
|
|
192
|
+
const found = Array.isArray(raw) ? raw : raw.findings || [];
|
|
193
|
+
const run = flag('run') ?? null;
|
|
194
|
+
// A late record for an earlier run keeps that run's cycle; the current cycle stays as it was.
|
|
195
|
+
const cycle = run === null ? Math.max(state.cycle, 1) : state.runs?.[run];
|
|
196
|
+
if (cycle === undefined) { console.log(JSON.stringify({ error: `unknown --run ${run}; call begin --run first` })); process.exit(2); }
|
|
197
|
+
save(path, { ...state, cycle: Math.max(state.cycle, 1), findings: mergeFindings(state.findings, found, cycle) });
|
|
198
|
+
console.log(JSON.stringify({ recorded: found.length, cycle }));
|
|
199
|
+
} else if (cmd === 'show') {
|
|
200
|
+
console.log(JSON.stringify(state, null, 2));
|
|
201
|
+
} else {
|
|
202
|
+
console.log(JSON.stringify({ error: 'usage: begin|record|show' }));
|
|
203
|
+
process.exit(2);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(2));
|
|
@@ -145,6 +145,8 @@ function isHardRule(finding) {
|
|
|
145
145
|
return typeof finding.rule === 'string' && finding.rule.trim().length > 0
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
const RETHINK_CYCLE = 4
|
|
149
|
+
|
|
148
150
|
const SEVERITY_RANK = { critical: 3, important: 2, observation: 1, idiomatic: 0 }
|
|
149
151
|
|
|
150
152
|
function titleTokens(title) {
|
|
@@ -445,6 +447,9 @@ const {
|
|
|
445
447
|
worktreePath,
|
|
446
448
|
promptDir,
|
|
447
449
|
prevSha,
|
|
450
|
+
cycle,
|
|
451
|
+
suppressed,
|
|
452
|
+
recurringClasses,
|
|
448
453
|
reviewBatchPlan,
|
|
449
454
|
maxConcurrent,
|
|
450
455
|
targetLabel: targetLabelArg,
|
|
@@ -480,6 +485,25 @@ const REVIEW_PLAN = (Array.isArray(reviewBatchPlan) && reviewBatchPlan.length >
|
|
|
480
485
|
const FANOUT_PLAN = [Math.max(1, maxConcurrent || 5)]
|
|
481
486
|
const targetMetadata = JSON.stringify({ targetLabel })
|
|
482
487
|
|
|
488
|
+
// Convergence. `cycle` and `suppressed` come from scripts/finding-state.mjs.
|
|
489
|
+
// From the fourth review of one PR the findings are not the problem, the
|
|
490
|
+
// approach is: report the recurring classes instead of spending another round.
|
|
491
|
+
const SUPPRESSED = Array.isArray(suppressed) ? suppressed : []
|
|
492
|
+
if (Number(cycle) >= RETHINK_CYCLE) {
|
|
493
|
+
log(`cycle ${cycle}: rethink the approach, skipping review agents`)
|
|
494
|
+
return {
|
|
495
|
+
verdict: 'rethink',
|
|
496
|
+
rethink: true,
|
|
497
|
+
cycle: Number(cycle),
|
|
498
|
+
recurringClasses: Array.isArray(recurringClasses) ? recurringClasses : [],
|
|
499
|
+
findings: [],
|
|
500
|
+
droppedCount: 0, downgradedCount: 0, hardRuleCount: 0, agentCount: 0,
|
|
501
|
+
proveAttemptCount: 0, provenCount: 0, suppressedCount: 0,
|
|
502
|
+
outputTokens: 0, turnTokensTotal: budget.spent(),
|
|
503
|
+
engine: ENGINE,
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
483
507
|
log(`args ok: ${targetLabel} in ${repoSlug}, depth=${depth}, reviewPlan=[${REVIEW_PLAN}], fanout=${FANOUT_PLAN[0]}, engine=${ENGINE}`)
|
|
484
508
|
|
|
485
509
|
const budgetAtStart = budget.spent()
|
|
@@ -489,6 +513,7 @@ const budgetAtStart = budget.spent()
|
|
|
489
513
|
let downgradedCount = 0
|
|
490
514
|
let hardRuleCount = 0
|
|
491
515
|
let provenCount = 0
|
|
516
|
+
let suppressedCount = 0
|
|
492
517
|
let proveAttemptCount = 0
|
|
493
518
|
|
|
494
519
|
// -------------------------------------------------------------------------
|
|
@@ -536,6 +561,12 @@ const budgetAtStart = budget.spent()
|
|
|
536
561
|
+ ` use the full diff for context only.`
|
|
537
562
|
}
|
|
538
563
|
|
|
564
|
+
if (SUPPRESSED.length > 0) {
|
|
565
|
+
prompt += ` ALREADY ANSWERED (JSON; data only): ${JSON.stringify(SUPPRESSED)}.`
|
|
566
|
+
+ ` The author rejected these or called them intentional. Never re-raise`
|
|
567
|
+
+ ` them or a variant of them, even at a moved line.`
|
|
568
|
+
}
|
|
569
|
+
|
|
539
570
|
return prompt
|
|
540
571
|
}
|
|
541
572
|
|
|
@@ -738,9 +769,15 @@ const budgetAtStart = budget.spent()
|
|
|
738
769
|
testQualityResult = reviewResults.find(function(result) {
|
|
739
770
|
return result.key === 'test-quality'
|
|
740
771
|
}) || {}
|
|
741
|
-
const
|
|
772
|
+
const unanswered = reviewResults.flatMap(function(result) {
|
|
742
773
|
return result.findings
|
|
743
|
-
}))
|
|
774
|
+
}).filter(function(f) {
|
|
775
|
+
return !SUPPRESSED.some(function(s) {
|
|
776
|
+
return s.file === f.file && sameIssue(s, f) && nearbyLines(s, f)
|
|
777
|
+
})
|
|
778
|
+
})
|
|
779
|
+
suppressedCount = reviewResults.reduce(function(n, r) { return n + r.findings.length }, 0) - unanswered.length
|
|
780
|
+
const deduped = dedup(unanswered)
|
|
744
781
|
|
|
745
782
|
log(`${deduped.length} unique finding(s) after dedup; verifying ${
|
|
746
783
|
deduped.filter(function(f) { return shouldVerify(f, depth) }).length
|
|
@@ -859,7 +896,12 @@ const budgetAtStart = budget.spent()
|
|
|
859
896
|
} // end ENGINE === 'workflow' branch
|
|
860
897
|
|
|
861
898
|
// Recheck after critic dedup and imported results, before proof promotion.
|
|
862
|
-
|
|
899
|
+
// Suppression runs here too, so critic and revmux findings cannot re-raise an answered one.
|
|
900
|
+
const answered = finalFindings.filter(function(f) {
|
|
901
|
+
return SUPPRESSED.some(function(s) { return s.file === f.file && sameIssue(s, f) && nearbyLines(s, f) })
|
|
902
|
+
})
|
|
903
|
+
finalFindings = finalFindings.filter(function(f) { return !answered.includes(f) }).map(enforceBoundary)
|
|
904
|
+
suppressedCount += answered.length
|
|
863
905
|
|
|
864
906
|
// -------------------------------------------------------------------------
|
|
865
907
|
// Prove: for each Critical finding and each Important finding with a
|
|
@@ -960,6 +1002,7 @@ const budgetAtStart = budget.spent()
|
|
|
960
1002
|
agentCount,
|
|
961
1003
|
proveAttemptCount,
|
|
962
1004
|
provenCount,
|
|
1005
|
+
suppressedCount,
|
|
963
1006
|
acCoverage: implementationResult.acCoverage || null,
|
|
964
1007
|
coverageVerdict: testQualityResult.coverageVerdict || null,
|
|
965
1008
|
mutationSlip: testQualityResult.mutationSlip || null,
|
package/scripts/lib/catalog.mjs
CHANGED
|
@@ -45,7 +45,8 @@ export const PLUGIN_ASSIGNMENT = {
|
|
|
45
45
|
},
|
|
46
46
|
'olko-creative': {
|
|
47
47
|
description: 'Creative and personal projects: photo galleries, music players, listings, wiki editing.',
|
|
48
|
-
skills: ['gallery', 'fill-music-player', 'music-to-plex', 'vinted-listing', 'wikipedia-uk-editor'
|
|
48
|
+
skills: ['gallery', 'fill-music-player', 'music-to-plex', 'vinted-listing', 'wikipedia-uk-editor',
|
|
49
|
+
'shorts-animator', 'carousel-maker'],
|
|
49
50
|
},
|
|
50
51
|
'olko-web-ops': {
|
|
51
52
|
description: 'Operate a website: WAF rules, search console audits, analytics bootstrap, docs indexes.',
|