@olegkoval/agent-skills 1.75.0 → 1.75.1

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.
@@ -49,6 +49,9 @@ export function caseKey(e) {
49
49
  }
50
50
 
51
51
  const SHA = /^[0-9a-f]{7,40}$/i;
52
+ const FULL_SHA = /^[0-9a-f]{40}$/i;
53
+ const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
54
+ const SEVERITIES = new Set(['critical', 'important', 'observation', 'idiomatic']);
52
55
 
53
56
  const isMultiRun = (v) => Array.isArray(v) && v.length > 0 && v.every(Array.isArray);
54
57
 
@@ -71,6 +74,37 @@ export function validateManifest(manifest) {
71
74
  return prs;
72
75
  }
73
76
 
77
+ export function normalizeArchivedEvidence(entry, run, worktree, document) {
78
+ const key = caseKey(entry);
79
+ const unscored = (reason) => ({ key, reason });
80
+ if (typeof entry.runId !== 'string' || !RUN_ID.test(entry.runId)) return unscored('unsafe or missing runId');
81
+ if (typeof entry.sha !== 'string' || !FULL_SHA.test(entry.sha)) return unscored('manifest needs a full 40-character sha');
82
+ if (!run) return unscored('run.json missing or malformed');
83
+ if (run.runId !== entry.runId) return unscored('runId mismatch');
84
+ if (run.status !== 'ok') return unscored('run status is not ok');
85
+ if (!['lgtm', 'lgtm-with-changes', 'needs-work'].includes(run.verdict)) return unscored('run verdict is missing or invalid');
86
+ if (run.review?.name !== 'lekker-review-core' || run.review.status !== 'completed') return unscored('completed review record unavailable');
87
+ if (run.repo !== entry.repo || run.target !== `pr-${entry.pr}`) return unscored('run repo or target mismatch');
88
+ if (!worktree) return unscored('worktree.json missing or malformed');
89
+ if (typeof worktree.headSha !== 'string' || !FULL_SHA.test(worktree.headSha)) return unscored('worktree headSha is not a full SHA');
90
+ if (worktree.headSha.toLowerCase() !== entry.sha.toLowerCase()) return unscored('worktree headSha mismatch');
91
+ const findings = Array.isArray(document) ? document : document?.findings;
92
+ if (!Array.isArray(findings)) return unscored('findings.json missing or malformed');
93
+ if (findings.some((f) => !f || typeof f.file !== 'string' || !f.file.trim() || !SEVERITIES.has(f.severity)
94
+ || !(f.line === null || (Number.isInteger(f.line) && f.line > 0)))) {
95
+ return unscored('findings.json contains a malformed finding');
96
+ }
97
+ return { key, findings, reason: null };
98
+ }
99
+
100
+ // Validate archive references before reading any run directories.
101
+ export function validateArchiveRunIds(entries) {
102
+ const duplicate = entries.find((entry, index) => entry.runId
103
+ && entries.slice(0, index).some(previous => previous.runId === entry.runId));
104
+ if (duplicate) throw new Error(`archive manifest reuses runId ${duplicate.runId}`);
105
+ return entries;
106
+ }
107
+
74
108
  export function score(manifest, results) {
75
109
  const rows = validateManifest(manifest).map((e) => {
76
110
  const key = caseKey(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olegkoval/agent-skills",
3
- "version": "1.75.0",
3
+ "version": "1.75.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-apple-kit",
3
3
  "description": "Build and ship Apple platform apps: macOS menubar apps, App Store submissions.",
4
- "version": "1.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-creative",
3
3
  "description": "Creative and personal projects: photo galleries, music players, listings, wiki editing.",
4
- "version": "1.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-garmin-kit",
3
3
  "description": "Build, test and publish Garmin Connect IQ watch faces.",
4
- "version": "1.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-git-tools",
3
3
  "description": "Everyday git and GitHub CLI operations: conventional commits, branch hygiene.",
4
- "version": "1.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-github-pr",
3
3
  "description": "Drive GitHub pull requests to merge-ready: review-bot loops, CI fixes, descriptions, dependency triage.",
4
- "version": "1.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -49,6 +49,9 @@ export function caseKey(e) {
49
49
  }
50
50
 
51
51
  const SHA = /^[0-9a-f]{7,40}$/i;
52
+ const FULL_SHA = /^[0-9a-f]{40}$/i;
53
+ const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
54
+ const SEVERITIES = new Set(['critical', 'important', 'observation', 'idiomatic']);
52
55
 
53
56
  const isMultiRun = (v) => Array.isArray(v) && v.length > 0 && v.every(Array.isArray);
54
57
 
@@ -71,6 +74,37 @@ export function validateManifest(manifest) {
71
74
  return prs;
72
75
  }
73
76
 
77
+ export function normalizeArchivedEvidence(entry, run, worktree, document) {
78
+ const key = caseKey(entry);
79
+ const unscored = (reason) => ({ key, reason });
80
+ if (typeof entry.runId !== 'string' || !RUN_ID.test(entry.runId)) return unscored('unsafe or missing runId');
81
+ if (typeof entry.sha !== 'string' || !FULL_SHA.test(entry.sha)) return unscored('manifest needs a full 40-character sha');
82
+ if (!run) return unscored('run.json missing or malformed');
83
+ if (run.runId !== entry.runId) return unscored('runId mismatch');
84
+ if (run.status !== 'ok') return unscored('run status is not ok');
85
+ if (!['lgtm', 'lgtm-with-changes', 'needs-work'].includes(run.verdict)) return unscored('run verdict is missing or invalid');
86
+ if (run.review?.name !== 'lekker-review-core' || run.review.status !== 'completed') return unscored('completed review record unavailable');
87
+ if (run.repo !== entry.repo || run.target !== `pr-${entry.pr}`) return unscored('run repo or target mismatch');
88
+ if (!worktree) return unscored('worktree.json missing or malformed');
89
+ if (typeof worktree.headSha !== 'string' || !FULL_SHA.test(worktree.headSha)) return unscored('worktree headSha is not a full SHA');
90
+ if (worktree.headSha.toLowerCase() !== entry.sha.toLowerCase()) return unscored('worktree headSha mismatch');
91
+ const findings = Array.isArray(document) ? document : document?.findings;
92
+ if (!Array.isArray(findings)) return unscored('findings.json missing or malformed');
93
+ if (findings.some((f) => !f || typeof f.file !== 'string' || !f.file.trim() || !SEVERITIES.has(f.severity)
94
+ || !(f.line === null || (Number.isInteger(f.line) && f.line > 0)))) {
95
+ return unscored('findings.json contains a malformed finding');
96
+ }
97
+ return { key, findings, reason: null };
98
+ }
99
+
100
+ // Validate archive references before reading any run directories.
101
+ export function validateArchiveRunIds(entries) {
102
+ const duplicate = entries.find((entry, index) => entry.runId
103
+ && entries.slice(0, index).some(previous => previous.runId === entry.runId));
104
+ if (duplicate) throw new Error(`archive manifest reuses runId ${duplicate.runId}`);
105
+ return entries;
106
+ }
107
+
74
108
  export function score(manifest, results) {
75
109
  const rows = validateManifest(manifest).map((e) => {
76
110
  const key = caseKey(e);
@@ -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.75.0",
4
+ "version": "1.75.1",
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.75.0",
4
+ "version": "1.75.1",
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.75.0",
4
+ "version": "1.75.1",
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.75.0",
4
+ "version": "1.75.1",
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.75.0",
4
+ "version": "1.75.1",
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.75.0",
4
+ "version": "1.75.1",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },