@adia-ai/adia-ui-forge 0.1.3 → 0.8.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/.claude-plugin/plugin.json +3 -6
- package/CHANGELOG.md +27 -34
- package/README.md +35 -40
- package/agents/a2ui-engineer.md +22 -53
- package/agents/component-author.md +29 -0
- package/agents/framework-verifier.md +26 -0
- package/agents/release-engineer.md +21 -81
- package/agents/routing-corpus.json +293 -64
- package/bin/demo-postwrite-pattern-gate +94 -0
- package/bin/forge-lint +17 -2
- package/bin/sidecar-prewrite-guard +104 -0
- package/commands/deploy.md +9 -0
- package/commands/dogfood.md +10 -0
- package/commands/gen-review.md +9 -0
- package/commands/release.md +10 -0
- package/hooks/hooks.json +15 -0
- package/package.json +3 -6
- package/references/contracts/a2ui-mcp-surface.md +35 -0
- package/references/contracts/migration-guide-format.md +34 -0
- package/references/shared/content-trust.md +19 -74
- package/skills/adia-a2ui/SKILL.md +99 -0
- package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
- package/skills/adia-a2ui/references/anti-patterns.md +27 -0
- package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
- package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
- package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
- package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
- package/skills/adia-a2ui/references/leverage-rules.md +52 -0
- package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
- package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
- package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
- package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
- package/skills/adia-a2ui/references/strategy-engines.md +109 -0
- package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
- package/skills/adia-author/SKILL.md +112 -0
- package/skills/adia-author/evals/routing-corpus.json +222 -0
- package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
- package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
- package/skills/adia-author/references/canonical-pattern-index.md +243 -0
- package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
- package/skills/adia-author/references/common-gotchas.md +129 -0
- package/skills/adia-author/references/composite-demo-protocol.md +271 -0
- package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
- package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
- package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
- package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +20 -38
- package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
- package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
- package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
- package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
- package/skills/adia-deploy/SKILL.md +128 -0
- package/skills/{adia-ui-ops → adia-deploy}/references/deploy-playbooks.md +47 -30
- package/skills/adia-dogfood/SKILL.md +124 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
- package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
- package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
- package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
- package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
- package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
- package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
- package/skills/adia-gen-review/SKILL.md +127 -0
- package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
- package/skills/adia-gen-review/references/loop-protocol.md +216 -0
- package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
- package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
- package/skills/adia-gen-review/references/rubric-score.md +214 -0
- package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
- package/skills/adia-llm-internals/SKILL.md +77 -0
- package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
- package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
- package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
- package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
- package/skills/adia-release/SKILL.md +72 -0
- package/skills/adia-release/references/changelog-discipline.md +119 -0
- package/skills/adia-release/references/cut-procedure.md +247 -0
- package/skills/adia-release/references/gates-catalog.md +222 -0
- package/skills/adia-release/references/independent-package-release.md +52 -0
- package/skills/adia-release/references/migration-guide-authoring.md +86 -0
- package/skills/adia-release/references/notes-authoring.md +90 -0
- package/skills/adia-release/references/recovery-paths.md +106 -0
- package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +27 -2
- package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +4 -1
- package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/package-paths.mjs +6 -0
- package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +13 -21
- package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +5 -1
- package/skills/adia-site-docs/SKILL.md +68 -0
- package/skills/adia-site-docs/evals/audit-report.md +30 -0
- package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
- package/skills/adia-site-docs/intent.md +72 -0
- package/agents/README.md +0 -209
- package/agents/author.md +0 -56
- package/agents/repo-steward.md +0 -56
- package/agents/spec-architect.md +0 -53
- package/agents/tech-lead.md +0 -57
- package/agents/verifier.md +0 -55
- package/bin/lib/audit-axes.mjs +0 -555
- package/bin/lib/dry-run-irreversible.mjs +0 -236
- package/bin/lib/run-skill-evals.mjs +0 -487
- package/bin/lib/teach-router.mjs +0 -250
- package/commands/adia-forge-a2ui.md +0 -10
- package/commands/adia-forge-author.md +0 -10
- package/commands/adia-forge-dogfood.md +0 -8
- package/commands/adia-forge-llm.md +0 -8
- package/commands/adia-forge-orient.md +0 -10
- package/commands/adia-forge-release.md +0 -8
- package/commands/adia-forge-review.md +0 -10
- package/references/shared/pev-rationale.md +0 -64
- package/references/shared/skill-conventions.md +0 -133
- package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
- package/skills/adia-ui-a2ui/SKILL.md +0 -243
- package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
- package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
- package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
- package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
- package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
- package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
- package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
- package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
- package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
- package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
- package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
- package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
- package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
- package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
- package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
- package/skills/adia-ui-a2ui/skill.json +0 -38
- package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
- package/skills/adia-ui-authoring/SKILL.md +0 -256
- package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
- package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
- package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
- package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
- package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
- package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
- package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
- package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
- package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
- package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
- package/skills/adia-ui-authoring/skill.json +0 -45
- package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
- package/skills/adia-ui-dogfood/README.md +0 -62
- package/skills/adia-ui-dogfood/SKILL.md +0 -866
- package/skills/adia-ui-dogfood/skill.json +0 -40
- package/skills/adia-ui-forge/SKILL.md +0 -88
- package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
- package/skills/adia-ui-gen-review/SKILL.md +0 -266
- package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
- package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
- package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
- package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
- package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
- package/skills/adia-ui-gen-review/skill.json +0 -23
- package/skills/adia-ui-llm/CHANGELOG.md +0 -25
- package/skills/adia-ui-llm/SKILL.md +0 -165
- package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
- package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
- package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
- package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
- package/skills/adia-ui-llm/skill.json +0 -33
- package/skills/adia-ui-ops/CHANGELOG.md +0 -291
- package/skills/adia-ui-ops/SKILL.md +0 -401
- package/skills/adia-ui-ops/references/INDEX.md +0 -158
- package/skills/adia-ui-ops/references/audit-cadence.md +0 -263
- package/skills/adia-ui-ops/references/audit-patterns/archive-link-sweep.md +0 -96
- package/skills/adia-ui-ops/references/audit-patterns/audit-history-ledger.md +0 -162
- package/skills/adia-ui-ops/references/audit-patterns/browser-bundle-node-imports.md +0 -154
- package/skills/adia-ui-ops/references/audit-patterns/changelog-unreleased-bloat.md +0 -75
- package/skills/adia-ui-ops/references/audit-patterns/coverage-gaps.md +0 -187
- package/skills/adia-ui-ops/references/audit-patterns/entry-file-coverage.md +0 -122
- package/skills/adia-ui-ops/references/audit-patterns/format-hygiene.md +0 -217
- package/skills/adia-ui-ops/references/audit-patterns/lockstep-versioning.md +0 -113
- package/skills/adia-ui-ops/references/audit-patterns/memory-fragmentation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/orphan-detection.md +0 -202
- package/skills/adia-ui-ops/references/audit-patterns/pointer-validation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/redundancy-detection.md +0 -210
- package/skills/adia-ui-ops/references/audit-patterns/spec-dating-sweep.md +0 -91
- package/skills/adia-ui-ops/references/audit-patterns/stale-content.md +0 -182
- package/skills/adia-ui-ops/references/audit-patterns/staleness-tooling.md +0 -156
- package/skills/adia-ui-ops/references/audit-patterns/token-waste-detection.md +0 -196
- package/skills/adia-ui-ops/references/doc-types/adr-pattern.md +0 -210
- package/skills/adia-ui-ops/references/doc-types/architecture-md.md +0 -190
- package/skills/adia-ui-ops/references/doc-types/changelog.md +0 -183
- package/skills/adia-ui-ops/references/doc-types/decisions-log.md +0 -146
- package/skills/adia-ui-ops/references/doc-types/plan-roadmap.md +0 -182
- package/skills/adia-ui-ops/references/doc-types/postmortem-pattern.md +0 -206
- package/skills/adia-ui-ops/references/genres/prose-and-writing.md +0 -149
- package/skills/adia-ui-ops/references/guidance/context-budget.md +0 -137
- package/skills/adia-ui-ops/references/guidance/llm-doc-writing.md +0 -116
- package/skills/adia-ui-ops/references/guidance/reliability-dial.md +0 -186
- package/skills/adia-ui-ops/references/recipes/adr-introduction.md +0 -211
- package/skills/adia-ui-ops/references/recipes/audit-existing-repo.md +0 -234
- package/skills/adia-ui-ops/references/recipes/cold-start-harvest.md +0 -263
- package/skills/adia-ui-ops/references/recipes/concurrent-learnings-merge.md +0 -158
- package/skills/adia-ui-ops/references/recipes/continuous-learning-loop.md +0 -169
- package/skills/adia-ui-ops/references/recipes/external-reference-verification.md +0 -158
- package/skills/adia-ui-ops/references/recipes/findings-index-readout.md +0 -126
- package/skills/adia-ui-ops/references/recipes/greenfield-setup.md +0 -252
- package/skills/adia-ui-ops/references/recipes/harvest-repo-brain.md +0 -169
- package/skills/adia-ui-ops/references/recipes/import-repo-brain-harvest.md +0 -153
- package/skills/adia-ui-ops/references/recipes/memory-organization.md +0 -182
- package/skills/adia-ui-ops/references/recipes/recommend-then-validate.md +0 -199
- package/skills/adia-ui-ops/references/recipes/self-healing-hooks.md +0 -366
- package/skills/adia-ui-ops/references/recipes/skill-stewardship-loop.md +0 -113
- package/skills/adia-ui-ops/references/standards/agents-md-spec.md +0 -138
- package/skills/adia-ui-ops/references/standards/claude-md-convention.md +0 -123
- package/skills/adia-ui-ops/references/standards/cross-tool-matrix.md +0 -85
- package/skills/adia-ui-ops/references/standards/readme-conventions.md +0 -232
- package/skills/adia-ui-ops/references/teach-protocol.md +0 -215
- package/skills/adia-ui-ops/scripts/audit-ops-roster.mjs +0 -104
- package/skills/adia-ui-ops/skill.json +0 -65
- package/skills/adia-ui-release/CHANGELOG.md +0 -66
- package/skills/adia-ui-release/SKILL.md +0 -323
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
- package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
- package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
- package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
- package/skills/adia-ui-release/evals/evals.json +0 -164
- package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
- package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
- package/skills/adia-ui-release/references/exe-deploy.md +0 -149
- package/skills/adia-ui-release/references/gates-catalog.md +0 -778
- package/skills/adia-ui-release/references/independent-package-release.md +0 -129
- package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
- package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
- package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
- package/skills/adia-ui-release/references/notes-authoring.md +0 -212
- package/skills/adia-ui-release/references/recovery-paths.md +0 -262
- package/skills/adia-ui-release/references/rollup-notes.md +0 -208
- package/skills/adia-ui-release/references/teach-protocol.md +0 -468
- package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
- package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -200
- package/skills/adia-ui-release/skill.json +0 -77
- package/skills/dogfood-sweep/CHANGELOG.md +0 -37
- package/skills/dogfood-sweep/README.md +0 -105
- package/skills/dogfood-sweep/SKILL.md +0 -1000
- package/skills/dogfood-sweep/analyze.mjs +0 -600
- package/skills/dogfood-sweep/skill.json +0 -31
- /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
- /package/skills/{adia-ui-release → adia-release}/scripts/assert-monorepo-root.mjs +0 -0
|
@@ -1,487 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* run-skill-evals.mjs — Portable eval runner for rollup-family skills.
|
|
4
|
-
*
|
|
5
|
-
* Walks a skill's evals/ directory and scores each corpus type:
|
|
6
|
-
* routing-corpus.json — heuristic routing accuracy (F1 score)
|
|
7
|
-
* adversarial-*.json — structural validation + case count
|
|
8
|
-
* teach-routing-cases.json — branch routing eval (live or structural)
|
|
9
|
-
* evals.json — structural validation + category counts
|
|
10
|
-
*
|
|
11
|
-
* Usage (from repo root):
|
|
12
|
-
* node ${CLAUDE_PLUGIN_ROOT}/bin/lib/run-skill-evals.mjs --skill=adia-ui-kit
|
|
13
|
-
* node ${CLAUDE_PLUGIN_ROOT}/bin/lib/run-skill-evals.mjs --skill=all
|
|
14
|
-
* node ${CLAUDE_PLUGIN_ROOT}/bin/lib/run-skill-evals.mjs --skill=adia-ui-kit --corpus=routing
|
|
15
|
-
* node ${CLAUDE_PLUGIN_ROOT}/bin/lib/run-skill-evals.mjs --skill=adia-ui-kit --json
|
|
16
|
-
* node ${CLAUDE_PLUGIN_ROOT}/bin/lib/run-skill-evals.mjs --skill=adia-ui-kit --strict
|
|
17
|
-
*
|
|
18
|
-
* Exit code: 0 = all pass (or warnings only), 1 = failures (in --strict mode
|
|
19
|
-
* or when hard-failure thresholds are missed).
|
|
20
|
-
*
|
|
21
|
-
* Scoring note: routing scores are heuristic (keyword overlap, not real LLM
|
|
22
|
-
* routing). Treat as a structural signal and vocabulary-drift detector — not
|
|
23
|
-
* ground truth for harness routing accuracy.
|
|
24
|
-
*
|
|
25
|
-
* @module run-skill-evals
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import fs from 'node:fs';
|
|
29
|
-
import path from 'node:path';
|
|
30
|
-
import { spawnSync } from 'node:child_process';
|
|
31
|
-
import process from 'node:process';
|
|
32
|
-
|
|
33
|
-
// ─── constants ──────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
const REPO = process.cwd();
|
|
36
|
-
const SKILLS_DIR = path.join(REPO, 'skills');
|
|
37
|
-
|
|
38
|
-
// Routing heuristic thresholds
|
|
39
|
-
const ROUTING_F1_PASS = 0.70;
|
|
40
|
-
const ROUTING_F1_WARN = 0.85;
|
|
41
|
-
|
|
42
|
-
// Stopwords excluded from token overlap
|
|
43
|
-
const STOPWORDS = new Set([
|
|
44
|
-
'a', 'an', 'the', 'and', 'or', 'of', 'in', 'to', 'for', 'on', 'at',
|
|
45
|
-
'is', 'are', 'was', 'with', 'this', 'that', 'it', 'its', 'be', 'as',
|
|
46
|
-
'by', 'not', 'do', 'use', 'how', 'what', 'when', 'which', 'from',
|
|
47
|
-
'into', 'about', 'after', 'over', 'can', 'has', 'have', 'all', 'any',
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
function readJson(p) {
|
|
53
|
-
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function tokenize(text) {
|
|
57
|
-
return text.toLowerCase().split(/\W+/).filter(w => w.length > 2 && !STOPWORDS.has(w));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Extract quoted trigger phrases from a skill description. */
|
|
61
|
-
function extractTriggerPhrases(description) {
|
|
62
|
-
const triggers = [];
|
|
63
|
-
// "Triggers on ..." section — extract quoted phrases
|
|
64
|
-
const triggerBlock = description.match(/Triggers on\s+([\s\S]*?)(?:\.|Does NOT trigger|$)/i);
|
|
65
|
-
if (triggerBlock) {
|
|
66
|
-
const quotes = triggerBlock[1].matchAll(/"([^"]+)"/g);
|
|
67
|
-
for (const m of quotes) triggers.push(m[1]);
|
|
68
|
-
}
|
|
69
|
-
return triggers;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Compute heuristic routing score for a phrase against a skill. */
|
|
73
|
-
function scorePhrase(phrase, descTokenSet, triggerPhrases) {
|
|
74
|
-
// Explicit trigger phrase match → high-confidence positive
|
|
75
|
-
for (const t of triggerPhrases) {
|
|
76
|
-
if (phrase.toLowerCase().includes(t.toLowerCase())) return 1.0;
|
|
77
|
-
}
|
|
78
|
-
// Word overlap ratio: phrase tokens that appear in description vocabulary
|
|
79
|
-
const phraseTokens = tokenize(phrase);
|
|
80
|
-
if (phraseTokens.length === 0) return 0;
|
|
81
|
-
const overlap = phraseTokens.filter(t => descTokenSet.has(t)).length;
|
|
82
|
-
return overlap / phraseTokens.length;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Find calibrated threshold maximizing F1 on labeled data. */
|
|
86
|
-
function calibrateThreshold(scoredPhrases) {
|
|
87
|
-
// Extract unique score values as candidate thresholds
|
|
88
|
-
const candidates = [...new Set(scoredPhrases.map(p => p.score))].sort((a, b) => a - b);
|
|
89
|
-
let bestF1 = -1;
|
|
90
|
-
let bestT = 0.15;
|
|
91
|
-
|
|
92
|
-
for (const t of candidates) {
|
|
93
|
-
const { f1 } = computeF1(scoredPhrases, t);
|
|
94
|
-
if (f1 > bestF1) { bestF1 = f1; bestT = t; }
|
|
95
|
-
}
|
|
96
|
-
return { threshold: bestT, calibratedF1: bestF1 };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function computeF1(scoredPhrases, threshold) {
|
|
100
|
-
let tp = 0, fp = 0, fn = 0, tn = 0;
|
|
101
|
-
for (const { score, expected } of scoredPhrases) {
|
|
102
|
-
const predicted = score >= threshold;
|
|
103
|
-
if (predicted && expected) tp++;
|
|
104
|
-
else if (predicted && !expected) fp++;
|
|
105
|
-
else if (!predicted && expected) fn++;
|
|
106
|
-
else tn++;
|
|
107
|
-
}
|
|
108
|
-
const precision = tp + fp > 0 ? tp / (tp + fp) : 0;
|
|
109
|
-
const recall = tp + fn > 0 ? tp / (tp + fn) : 0;
|
|
110
|
-
const f1 = precision + recall > 0 ? 2 * precision * recall / (precision + recall) : 0;
|
|
111
|
-
return { tp, fp, fn, tn, precision, recall, f1 };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ─── corpus evaluators ──────────────────────────────────────────────────────
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Evaluate routing-corpus.json: heuristic phrase classification + F1.
|
|
118
|
-
*/
|
|
119
|
-
function evalRoutingCorpus(corpusPath, skillDir) {
|
|
120
|
-
const corpus = readJson(corpusPath);
|
|
121
|
-
if (!corpus) return { type: 'routing', status: 'error', message: 'Failed to parse corpus' };
|
|
122
|
-
|
|
123
|
-
const phrases = corpus.phrases ?? [];
|
|
124
|
-
if (phrases.length === 0) {
|
|
125
|
-
return { type: 'routing', status: 'fail', message: 'No phrases found' };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Detect label field (e.g. should_route_to_kit, should_route_to_ops)
|
|
129
|
-
const firstPhrase = phrases[0];
|
|
130
|
-
const labelField = Object.keys(firstPhrase).find(k => k.startsWith('should_route_to_'));
|
|
131
|
-
if (!labelField) {
|
|
132
|
-
return { type: 'routing', status: 'fail', message: 'No should_route_to_* field found in phrases' };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Load skill description for heuristic scoring
|
|
136
|
-
const skillJson = readJson(path.join(skillDir, 'skill.json')) ?? {};
|
|
137
|
-
const description = skillJson.description ?? '';
|
|
138
|
-
const descTokenSet = new Set(tokenize(description));
|
|
139
|
-
const triggerPhrases = extractTriggerPhrases(description);
|
|
140
|
-
|
|
141
|
-
// Score each phrase
|
|
142
|
-
const scoredPhrases = phrases.map(p => ({
|
|
143
|
-
id: p.id,
|
|
144
|
-
phrase: p.phrase,
|
|
145
|
-
score: scorePhrase(p.phrase, descTokenSet, triggerPhrases),
|
|
146
|
-
expected: Boolean(p[labelField]),
|
|
147
|
-
expected_alternative: p.expected_alternative ?? null,
|
|
148
|
-
}));
|
|
149
|
-
|
|
150
|
-
const positives = scoredPhrases.filter(p => p.expected);
|
|
151
|
-
const adversarials = scoredPhrases.filter(p => !p.expected);
|
|
152
|
-
|
|
153
|
-
// Calibrate threshold on labeled data
|
|
154
|
-
const { threshold, calibratedF1 } = calibrateThreshold(scoredPhrases);
|
|
155
|
-
const { tp, fp, fn, tn, precision, recall, f1 } = computeF1(scoredPhrases, threshold);
|
|
156
|
-
|
|
157
|
-
// Check minimums
|
|
158
|
-
const mins = corpus.minimums_per_spec ?? {};
|
|
159
|
-
const minTrigger = mins.trigger_phrases ?? 10;
|
|
160
|
-
const minAdversarial = mins.adversarial_phrases ?? 5;
|
|
161
|
-
const minimumsMet = positives.length >= minTrigger && adversarials.length >= minAdversarial;
|
|
162
|
-
|
|
163
|
-
const status = !minimumsMet ? 'fail'
|
|
164
|
-
: f1 < ROUTING_F1_PASS ? 'fail'
|
|
165
|
-
: f1 < ROUTING_F1_WARN ? 'warn'
|
|
166
|
-
: 'pass';
|
|
167
|
-
|
|
168
|
-
// Per-case results (only report misclassifications)
|
|
169
|
-
const misclassified = scoredPhrases.filter(p => (p.score >= threshold) !== p.expected);
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
type: 'routing',
|
|
173
|
-
file: path.relative(skillDir, corpusPath),
|
|
174
|
-
status,
|
|
175
|
-
label_field: labelField,
|
|
176
|
-
counts: { total: phrases.length, positives: positives.length, adversarials: adversarials.length },
|
|
177
|
-
minimums: { trigger_phrases: { required: minTrigger, found: positives.length, ok: positives.length >= minTrigger },
|
|
178
|
-
adversarial_phrases: { required: minAdversarial, found: adversarials.length, ok: adversarials.length >= minAdversarial } },
|
|
179
|
-
heuristic: { threshold: +threshold.toFixed(3), trigger_phrases_extracted: triggerPhrases.length },
|
|
180
|
-
scores: { tp, fp, fn, tn, precision: +precision.toFixed(3), recall: +recall.toFixed(3), f1: +f1.toFixed(3) },
|
|
181
|
-
misclassified: misclassified.map(p => ({
|
|
182
|
-
id: p.id, phrase: p.phrase, expected: p.expected, score: +p.score.toFixed(3),
|
|
183
|
-
})),
|
|
184
|
-
summary: `F1=${f1.toFixed(2)} (${tp} TP, ${fp} FP, ${fn} FN, ${tn} TN), ${positives.length}/${minTrigger} trigger, ${adversarials.length}/${minAdversarial} adversarial`,
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Evaluate adversarial-*.json: structural validation + severity breakdown.
|
|
190
|
-
*/
|
|
191
|
-
function evalAdversarialCorpus(corpusPath, skillDir) {
|
|
192
|
-
const corpus = readJson(corpusPath);
|
|
193
|
-
if (!corpus) return { type: 'adversarial', status: 'error', message: 'Failed to parse corpus' };
|
|
194
|
-
|
|
195
|
-
const cases = corpus.cases ?? corpus.phrases ?? [];
|
|
196
|
-
const required = ['id', 'expected_behavior'];
|
|
197
|
-
const invalid = cases.filter(c => required.some(f => !c[f]));
|
|
198
|
-
|
|
199
|
-
const severityCounts = {};
|
|
200
|
-
for (const c of cases) {
|
|
201
|
-
const sev = c.severity ?? 'unknown';
|
|
202
|
-
severityCounts[sev] = (severityCounts[sev] ?? 0) + 1;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const status = cases.length < 3 ? 'warn'
|
|
206
|
-
: invalid.length > 0 ? 'warn'
|
|
207
|
-
: 'pass';
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
type: 'adversarial',
|
|
211
|
-
file: path.relative(skillDir, corpusPath),
|
|
212
|
-
status,
|
|
213
|
-
counts: { total: cases.length, invalid: invalid.length },
|
|
214
|
-
severity_breakdown: severityCounts,
|
|
215
|
-
invalid_ids: invalid.map(c => c.id ?? '(no id)'),
|
|
216
|
-
summary: `${cases.length} cases, severity: ${Object.entries(severityCounts).map(([k, v]) => `${v} ${k}`).join(', ')}`,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Evaluate teach-routing-cases.json: branch coverage + live routing if
|
|
222
|
-
* scripts/teach-route.mjs exists in the skill directory.
|
|
223
|
-
*/
|
|
224
|
-
async function evalTeachRoutingCorpus(corpusPath, skillDir) {
|
|
225
|
-
const corpus = readJson(corpusPath);
|
|
226
|
-
if (!corpus) return { type: 'teach-routing', status: 'error', message: 'Failed to parse corpus' };
|
|
227
|
-
|
|
228
|
-
const cases = corpus.cases ?? [];
|
|
229
|
-
const mins = corpus.minimums_per_spec ?? {};
|
|
230
|
-
const minTotal = mins.total_cases ?? 7;
|
|
231
|
-
|
|
232
|
-
// Check branch coverage
|
|
233
|
-
const branchCoverage = {};
|
|
234
|
-
for (const c of cases) {
|
|
235
|
-
const b = c.expected_branch ?? '?';
|
|
236
|
-
branchCoverage[b] = (branchCoverage[b] ?? 0) + 1;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const teachScript = path.join(skillDir, 'scripts', 'teach-route.mjs');
|
|
240
|
-
const scriptExists = fs.existsSync(teachScript);
|
|
241
|
-
|
|
242
|
-
if (!scriptExists) {
|
|
243
|
-
// Structural-only evaluation
|
|
244
|
-
const status = cases.length < minTotal ? 'warn' : 'pass';
|
|
245
|
-
return {
|
|
246
|
-
type: 'teach-routing',
|
|
247
|
-
file: path.relative(skillDir, corpusPath),
|
|
248
|
-
status,
|
|
249
|
-
live_run: false,
|
|
250
|
-
counts: { total: cases.length, required: minTotal },
|
|
251
|
-
branch_coverage: branchCoverage,
|
|
252
|
-
summary: `${cases.length} cases, branches: ${Object.keys(branchCoverage).sort().join(',')} — teach-route.mjs not yet authored (pending Round 3)`,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Live routing evaluation: run teach-route.mjs --eval=<payload> for each case
|
|
257
|
-
let passed = 0;
|
|
258
|
-
let failed = 0;
|
|
259
|
-
const results = [];
|
|
260
|
-
|
|
261
|
-
for (const c of cases) {
|
|
262
|
-
// Pass --json so teach-route.mjs emits structured output regardless of format
|
|
263
|
-
const proc = spawnSync(process.execPath, [teachScript, `--payload=${c.payload}`, '--json'], {
|
|
264
|
-
cwd: REPO, encoding: 'utf8', timeout: 5000,
|
|
265
|
-
});
|
|
266
|
-
const output = (proc.stdout ?? '').trim();
|
|
267
|
-
let gotBranch = null;
|
|
268
|
-
try {
|
|
269
|
-
const parsed = JSON.parse(output);
|
|
270
|
-
gotBranch = parsed.branch ?? parsed.id ?? null;
|
|
271
|
-
} catch {
|
|
272
|
-
// Fall back to parsing text output: " branch: A (name)" or leading capital
|
|
273
|
-
const textMatch = output.match(/branch:\s*([A-Z])(?:\s|$)/m);
|
|
274
|
-
gotBranch = textMatch?.[1] ?? output.match(/^([A-Z])\b/)?.[1] ?? null;
|
|
275
|
-
}
|
|
276
|
-
const ok = gotBranch === c.expected_branch;
|
|
277
|
-
if (ok) passed++; else failed++;
|
|
278
|
-
results.push({
|
|
279
|
-
id: c.id, expected: c.expected_branch, got: gotBranch,
|
|
280
|
-
status: ok ? 'pass' : 'fail',
|
|
281
|
-
...(!ok ? { failure_reason: `expected '${c.expected_branch}', got '${gotBranch}'` } : {}),
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const f1 = cases.length > 0 ? passed / cases.length : 0;
|
|
286
|
-
const status = failed > 0 ? 'fail' : cases.length < minTotal ? 'warn' : 'pass';
|
|
287
|
-
|
|
288
|
-
return {
|
|
289
|
-
type: 'teach-routing',
|
|
290
|
-
file: path.relative(skillDir, corpusPath),
|
|
291
|
-
status,
|
|
292
|
-
live_run: true,
|
|
293
|
-
counts: { total: cases.length, passed, failed, required: minTotal },
|
|
294
|
-
branch_coverage: branchCoverage,
|
|
295
|
-
results,
|
|
296
|
-
summary: `${passed}/${cases.length} passed (live routing via teach-route.mjs)`,
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Evaluate evals.json: structural validation + category counts.
|
|
302
|
-
* (Full behavioral execution requires an LLM — not run here.)
|
|
303
|
-
*/
|
|
304
|
-
function evalEvalsJson(corpusPath, skillDir) {
|
|
305
|
-
const corpus = readJson(corpusPath);
|
|
306
|
-
if (!corpus) return { type: 'evals', status: 'error', message: 'Failed to parse corpus' };
|
|
307
|
-
|
|
308
|
-
const evals = corpus.evals ?? [];
|
|
309
|
-
const invariants = corpus.non_eval_invariants ?? corpus.invariants ?? [];
|
|
310
|
-
|
|
311
|
-
// Count by category
|
|
312
|
-
const categoryCounts = {};
|
|
313
|
-
for (const e of evals) {
|
|
314
|
-
const cat = e.category ?? 'uncategorized';
|
|
315
|
-
categoryCounts[cat] = (categoryCounts[cat] ?? 0) + 1;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const status = evals.length === 0 ? 'warn' : 'pass';
|
|
319
|
-
|
|
320
|
-
return {
|
|
321
|
-
type: 'evals',
|
|
322
|
-
file: path.relative(skillDir, corpusPath),
|
|
323
|
-
status,
|
|
324
|
-
counts: { evals: evals.length, invariants: invariants.length },
|
|
325
|
-
categories: categoryCounts,
|
|
326
|
-
note: 'Structural check only — behavioral execution requires LLM',
|
|
327
|
-
summary: `${evals.length} evals (${Object.entries(categoryCounts).map(([k, v]) => `${v} ${k}`).join(', ')}), ${invariants.length} invariants`,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ─── skill runner ────────────────────────────────────────────────────────────
|
|
332
|
-
|
|
333
|
-
async function runSkillEvals(skillName, corpusFilter) {
|
|
334
|
-
const skillDir = path.join(SKILLS_DIR, skillName);
|
|
335
|
-
const evalsDir = path.join(skillDir, 'evals');
|
|
336
|
-
|
|
337
|
-
if (!fs.existsSync(skillDir)) {
|
|
338
|
-
return { skill: skillName, status: 'error', message: `Skill directory not found: ${skillDir}` };
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (!fs.existsSync(evalsDir)) {
|
|
342
|
-
return { skill: skillName, status: 'warn', message: 'No evals/ directory', corpora: [] };
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const files = fs.readdirSync(evalsDir).filter(f => f.endsWith('.json'));
|
|
346
|
-
if (files.length === 0) {
|
|
347
|
-
return { skill: skillName, status: 'warn', message: 'evals/ directory is empty', corpora: [] };
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const corpora = [];
|
|
351
|
-
|
|
352
|
-
for (const file of files.sort()) {
|
|
353
|
-
const fullPath = path.join(evalsDir, file);
|
|
354
|
-
|
|
355
|
-
// Detect corpus type and apply filter
|
|
356
|
-
let type;
|
|
357
|
-
if (file === 'routing-corpus.json') type = 'routing';
|
|
358
|
-
else if (file === 'teach-routing-cases.json') type = 'teach';
|
|
359
|
-
else if (file.startsWith('adversarial-')) type = 'adversarial';
|
|
360
|
-
else if (file === 'evals.json') type = 'evals';
|
|
361
|
-
else type = 'unknown';
|
|
362
|
-
|
|
363
|
-
if (corpusFilter && corpusFilter !== 'all' && type !== corpusFilter) continue;
|
|
364
|
-
if (type === 'unknown') {
|
|
365
|
-
corpora.push({ type: 'unknown', file, status: 'skip', summary: 'Unrecognized corpus type' });
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
let result;
|
|
370
|
-
if (type === 'routing') result = evalRoutingCorpus(fullPath, skillDir);
|
|
371
|
-
else if (type === 'adversarial') result = evalAdversarialCorpus(fullPath, skillDir);
|
|
372
|
-
else if (type === 'teach') result = await evalTeachRoutingCorpus(fullPath, skillDir);
|
|
373
|
-
else if (type === 'evals') result = evalEvalsJson(fullPath, skillDir);
|
|
374
|
-
|
|
375
|
-
corpora.push(result);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const statuses = corpora.map(c => c.status);
|
|
379
|
-
const overallStatus = statuses.includes('error') ? 'error'
|
|
380
|
-
: statuses.includes('fail') ? 'fail'
|
|
381
|
-
: statuses.includes('warn') ? 'warn'
|
|
382
|
-
: 'pass';
|
|
383
|
-
|
|
384
|
-
return { skill: skillName, status: overallStatus, corpora };
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// ─── formatting ──────────────────────────────────────────────────────────────
|
|
388
|
-
|
|
389
|
-
function icon(status) {
|
|
390
|
-
return { pass: '✓', warn: '⚠', fail: '✗', error: '!', skip: '·' }[status] ?? '?';
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function formatSkillResult(result) {
|
|
394
|
-
const lines = [`[${result.skill}] ${icon(result.status)} ${result.status.toUpperCase()}`];
|
|
395
|
-
|
|
396
|
-
if (result.message) {
|
|
397
|
-
lines.push(` ${result.message}`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
for (const corpus of result.corpora ?? []) {
|
|
401
|
-
const ind = icon(corpus.status);
|
|
402
|
-
lines.push(` ${ind} ${corpus.file ?? corpus.type}: ${corpus.summary ?? corpus.message ?? ''}`);
|
|
403
|
-
|
|
404
|
-
if (corpus.status === 'fail' || corpus.status === 'warn') {
|
|
405
|
-
if (corpus.misclassified?.length) {
|
|
406
|
-
lines.push(` Misclassified (${corpus.misclassified.length}):`);
|
|
407
|
-
for (const m of corpus.misclassified.slice(0, 5)) {
|
|
408
|
-
lines.push(` [${m.id}] score=${m.score} expected=${m.expected} — "${m.phrase}"`);
|
|
409
|
-
}
|
|
410
|
-
if (corpus.misclassified.length > 5) {
|
|
411
|
-
lines.push(` ... ${corpus.misclassified.length - 5} more`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
if (corpus.invalid_ids?.length) {
|
|
415
|
-
lines.push(` Invalid cases: ${corpus.invalid_ids.join(', ')}`);
|
|
416
|
-
}
|
|
417
|
-
if (corpus.results?.filter(r => r.status === 'fail').length) {
|
|
418
|
-
for (const r of corpus.results.filter(r => r.status === 'fail')) {
|
|
419
|
-
lines.push(` [${r.id}] ${r.failure_reason}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (corpus.minimums) {
|
|
423
|
-
for (const [k, v] of Object.entries(corpus.minimums)) {
|
|
424
|
-
if (!v.ok) lines.push(` Minimum not met: ${k} requires ${v.required}, found ${v.found}`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return lines.join('\n');
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// ─── main ────────────────────────────────────────────────────────────────────
|
|
434
|
-
|
|
435
|
-
async function main() {
|
|
436
|
-
const rawArgs = process.argv.slice(2);
|
|
437
|
-
const args = Object.fromEntries(
|
|
438
|
-
rawArgs.filter(a => a.startsWith('--')).map(a => {
|
|
439
|
-
const [k, ...v] = a.slice(2).split('=');
|
|
440
|
-
return [k, v.length ? v.join('=') : true];
|
|
441
|
-
})
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
const { skill: skillArg = 'all', corpus: corpusFilter, json: jsonMode, strict } = args;
|
|
445
|
-
|
|
446
|
-
// Discover skills to run
|
|
447
|
-
let skillNames;
|
|
448
|
-
if (skillArg === 'all') {
|
|
449
|
-
skillNames = fs.readdirSync(SKILLS_DIR).filter(name => {
|
|
450
|
-
const d = path.join(SKILLS_DIR, name);
|
|
451
|
-
return fs.statSync(d).isDirectory() && fs.existsSync(path.join(d, 'SKILL.md'));
|
|
452
|
-
}).sort();
|
|
453
|
-
} else {
|
|
454
|
-
skillNames = [skillArg];
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const results = [];
|
|
458
|
-
for (const name of skillNames) {
|
|
459
|
-
results.push(await runSkillEvals(name, corpusFilter ?? 'all'));
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (jsonMode) {
|
|
463
|
-
const driftCount = results.filter(r => r.status === 'fail' || r.status === 'error').length;
|
|
464
|
-
console.log(JSON.stringify({ results, driftCount }, null, 2));
|
|
465
|
-
} else {
|
|
466
|
-
const hasEvals = results.filter(r => r.corpora?.length > 0 || r.status === 'warn');
|
|
467
|
-
const noEvals = results.filter(r => !r.corpora?.length && r.status !== 'warn' && r.message?.includes('No evals'));
|
|
468
|
-
|
|
469
|
-
for (const r of hasEvals) console.log(formatSkillResult(r));
|
|
470
|
-
|
|
471
|
-
if (noEvals.length && !corpusFilter) {
|
|
472
|
-
console.log(`\n (${noEvals.length} skill(s) have no evals/ yet: ${noEvals.map(r => r.skill).join(', ')})`);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const passed = results.filter(r => r.status === 'pass').length;
|
|
476
|
-
const warned = results.filter(r => r.status === 'warn').length;
|
|
477
|
-
const failed = results.filter(r => r.status === 'fail' || r.status === 'error').length;
|
|
478
|
-
const withEvals = results.filter(r => r.corpora?.length > 0).length;
|
|
479
|
-
console.log(`\n${withEvals} skill(s) with evals: ${passed} pass, ${warned} warn, ${failed} fail`);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const anyFail = results.some(r => r.status === 'fail' || r.status === 'error');
|
|
483
|
-
const anyWarn = results.some(r => r.status === 'warn');
|
|
484
|
-
if (anyFail || (strict && anyWarn)) process.exit(1);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
main().catch(err => { console.error(err); process.exit(2); });
|