libretto 0.4.4 → 0.5.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.
- package/README.md +106 -36
- package/dist/cli/cli.js +39 -113
- package/dist/cli/commands/ai.js +1 -1
- package/dist/cli/commands/browser.js +87 -60
- package/dist/cli/commands/execution.js +201 -88
- package/dist/cli/commands/init.js +30 -8
- package/dist/cli/commands/logs.js +5 -6
- package/dist/cli/commands/shared.js +30 -29
- package/dist/cli/commands/snapshot.js +26 -39
- package/dist/cli/core/ai-config.js +9 -2
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +141 -33
- package/dist/cli/core/context.js +7 -18
- package/dist/cli/core/session-telemetry.js +5 -2
- package/dist/cli/core/session.js +23 -10
- package/dist/cli/core/snapshot-analyzer.js +16 -33
- package/dist/cli/core/snapshot-api-config.js +2 -6
- package/dist/cli/core/telemetry.js +10 -2
- package/dist/cli/framework/simple-cli.js +45 -25
- package/dist/cli/router.js +14 -21
- package/dist/cli/workers/run-integration-runtime.js +26 -7
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -10
- package/dist/runtime/download/download.js +5 -1
- package/dist/runtime/extract/extract.js +11 -2
- package/dist/runtime/network/network.js +8 -1
- package/dist/runtime/recovery/agent.js +6 -2
- package/dist/runtime/recovery/errors.js +3 -1
- package/dist/runtime/recovery/recovery.js +3 -1
- package/dist/shared/condense-dom/condense-dom.js +6 -13
- package/dist/shared/config/config.d.ts +1 -9
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.d.ts +2 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/pause.js +9 -3
- package/dist/shared/instrumentation/instrument.js +101 -5
- package/dist/shared/llm/ai-sdk-adapter.js +3 -1
- package/dist/shared/llm/client.js +3 -1
- package/dist/shared/logger/index.js +4 -1
- package/dist/shared/paths/paths.js +2 -1
- package/dist/shared/paths/repo-root.d.ts +3 -0
- package/dist/shared/paths/repo-root.js +24 -0
- package/dist/shared/run/api.js +3 -1
- package/dist/shared/run/browser.js +7 -2
- package/dist/shared/state/session-state.d.ts +2 -1
- package/dist/shared/state/session-state.js +5 -2
- package/dist/shared/visualization/ghost-cursor.js +19 -10
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +4 -5
- package/dist/shared/workflow/workflow.js +3 -5
- package/package.json +11 -8
- package/scripts/check-skills-sync.mjs +25 -0
- package/scripts/compare-eval-summary.mjs +47 -0
- package/scripts/postinstall.mjs +26 -17
- package/scripts/prepare-release.sh +97 -0
- package/scripts/skills-libretto.mjs +103 -0
- package/scripts/summarize-evals.mjs +135 -0
- package/scripts/sync-skills.mjs +12 -0
- package/skills/libretto/SKILL.md +130 -377
- package/skills/libretto/references/auth-profiles.md +30 -0
- package/skills/libretto/{code-generation-rules.md → references/code-generation-rules.md} +27 -42
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- package/skills/libretto/references/pages-and-page-targeting.md +29 -0
- package/skills/libretto/references/site-security-review.md +143 -0
- package/src/cli/cli.ts +86 -0
- package/src/cli/commands/ai.ts +35 -0
- package/src/cli/commands/browser.ts +189 -0
- package/src/cli/commands/execution.ts +822 -0
- package/src/cli/commands/init.ts +350 -0
- package/src/cli/commands/logs.ts +128 -0
- package/src/cli/commands/shared.ts +69 -0
- package/src/cli/commands/snapshot.ts +312 -0
- package/src/cli/core/ai-config.ts +264 -0
- package/src/cli/core/api-snapshot-analyzer.ts +108 -0
- package/src/cli/core/browser.ts +976 -0
- package/src/cli/core/context.ts +127 -0
- package/src/cli/core/pause-signals.ts +35 -0
- package/src/cli/core/session-telemetry.ts +564 -0
- package/src/cli/core/session.ts +223 -0
- package/src/cli/core/snapshot-analyzer.ts +855 -0
- package/src/cli/core/snapshot-api-config.ts +231 -0
- package/src/cli/core/telemetry.ts +459 -0
- package/src/cli/framework/simple-cli.ts +1340 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/router.ts +20 -0
- package/src/cli/workers/run-integration-runtime.ts +338 -0
- package/src/cli/workers/run-integration-worker-protocol.ts +16 -0
- package/src/cli/workers/run-integration-worker.ts +72 -0
- package/src/index.ts +127 -0
- package/src/runtime/download/download.ts +104 -0
- package/src/runtime/download/index.ts +7 -0
- package/src/runtime/extract/extract.ts +102 -0
- package/src/runtime/extract/index.ts +1 -0
- package/src/runtime/network/index.ts +5 -0
- package/src/runtime/network/network.ts +119 -0
- package/{dist/runtime/recovery/agent.cjs → src/runtime/recovery/agent.ts} +114 -76
- package/src/runtime/recovery/errors.ts +155 -0
- package/src/runtime/recovery/index.ts +7 -0
- package/src/runtime/recovery/recovery.ts +53 -0
- package/{dist/shared/condense-dom/condense-dom.cjs → src/shared/condense-dom/condense-dom.ts} +249 -124
- package/src/shared/config/config.ts +3 -0
- package/src/shared/config/index.ts +0 -0
- package/src/shared/debug/index.ts +1 -0
- package/src/shared/debug/pause.ts +91 -0
- package/src/shared/instrumentation/errors.ts +84 -0
- package/src/shared/instrumentation/index.ts +9 -0
- package/src/shared/instrumentation/instrument.ts +406 -0
- package/src/shared/llm/ai-sdk-adapter.ts +81 -0
- package/{dist/shared/llm/client.cjs → src/shared/llm/client.ts} +86 -80
- package/src/shared/llm/index.ts +3 -0
- package/src/shared/llm/types.ts +63 -0
- package/src/shared/logger/index.ts +13 -0
- package/src/shared/logger/logger.ts +358 -0
- package/src/shared/logger/sinks.ts +148 -0
- package/src/shared/paths/paths.ts +110 -0
- package/src/shared/paths/repo-root.ts +27 -0
- package/src/shared/run/api.ts +6 -0
- package/src/shared/run/browser.ts +107 -0
- package/src/shared/state/index.ts +11 -0
- package/src/shared/state/session-state.ts +77 -0
- package/src/shared/visualization/ghost-cursor.ts +213 -0
- package/src/shared/visualization/highlight.ts +149 -0
- package/src/shared/visualization/index.ts +18 -0
- package/src/shared/workflow/workflow.ts +36 -0
- package/dist/index.cjs +0 -144
- package/dist/index.d.cts +0 -21
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/shared/condense-dom/condense-dom.d.cts +0 -34
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/debug/index.cjs +0 -28
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/pause.cjs +0 -86
- package/dist/shared/debug/pause.d.cts +0 -12
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -71
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/client.d.cts +0 -13
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -67
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/logger.cjs +0 -232
- package/dist/shared/logger/logger.d.cts +0 -86
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/session-state.cjs +0 -92
- package/dist/shared/state/session-state.d.cts +0 -40
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/skills/libretto/integration-approach-selection.md +0 -174
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
+
import { SKILL_DIRS, syncSkillDir } from "./skills-libretto.mjs";
|
|
9
|
+
|
|
8
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
11
|
const packageRoot = join(__dirname, "..");
|
|
10
12
|
|
|
@@ -14,28 +16,35 @@ spawnSync("npx", ["playwright", "install", "chromium"], {
|
|
|
14
16
|
shell: true,
|
|
15
17
|
});
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
const installCwd = process.env.INIT_CWD?.trim() || null;
|
|
20
|
+
if (!installCwd) {
|
|
21
|
+
console.warn(
|
|
22
|
+
"libretto: automatic skill install failed because INIT_CWD is not set. Run `npx skills add saffron-health/libretto` to add the skills manually.",
|
|
23
|
+
);
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Resolve the consuming project's repo root from the original install cwd,
|
|
28
|
+
// not pnpm's content-addressable store path.
|
|
18
29
|
const gitResult = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
30
|
+
cwd: installCwd,
|
|
19
31
|
encoding: "utf-8",
|
|
20
32
|
stdio: ["pipe", "pipe", "pipe"],
|
|
21
33
|
});
|
|
22
|
-
const repoRoot =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
const repoRoot =
|
|
35
|
+
gitResult.status === 0 && gitResult.stdout
|
|
36
|
+
? gitResult.stdout.trim()
|
|
37
|
+
: installCwd;
|
|
26
38
|
|
|
27
|
-
// Sync skills to any agent dirs at repo root
|
|
28
39
|
const sourceDir = join(packageRoot, "skills", "libretto");
|
|
29
40
|
if (!existsSync(sourceDir)) process.exit(0);
|
|
30
41
|
|
|
31
|
-
const
|
|
32
|
-
for (const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const count = readdirSync(dest).length;
|
|
40
|
-
console.log(`libretto: synced ${count} skill files to ${dest}`);
|
|
42
|
+
const syncMissingDirs = repoRoot === packageRoot;
|
|
43
|
+
for (const dir of SKILL_DIRS.slice(1)) {
|
|
44
|
+
const rootName = dir.split("/")[0];
|
|
45
|
+
const rootDir = join(repoRoot, rootName);
|
|
46
|
+
if (!syncMissingDirs && !existsSync(rootDir)) continue;
|
|
47
|
+
const dest = join(repoRoot, dir);
|
|
48
|
+
syncSkillDir(sourceDir, dest);
|
|
49
|
+
console.log(`libretto: synced skills/libretto -> ${dest}`);
|
|
41
50
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage: scripts/prepare-release.sh [patch|minor|major]
|
|
7
|
+
|
|
8
|
+
Creates a release PR branch from main, bumps package.json, pushes the branch,
|
|
9
|
+
and opens a pull request targeting main.
|
|
10
|
+
EOF
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
bump="${1:-patch}"
|
|
14
|
+
|
|
15
|
+
case "$bump" in
|
|
16
|
+
patch|minor|major)
|
|
17
|
+
;;
|
|
18
|
+
-h|--help|help)
|
|
19
|
+
usage
|
|
20
|
+
exit 0
|
|
21
|
+
;;
|
|
22
|
+
*)
|
|
23
|
+
echo "Invalid bump type: $bump" >&2
|
|
24
|
+
usage >&2
|
|
25
|
+
exit 1
|
|
26
|
+
;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
30
|
+
echo "gh CLI is required." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
35
|
+
echo "Working tree must be clean before preparing a release." >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
current_branch="$(git branch --show-current)"
|
|
40
|
+
if [ "$current_branch" != "main" ]; then
|
|
41
|
+
echo "Switching from $current_branch to main."
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
git fetch origin
|
|
45
|
+
git checkout main
|
|
46
|
+
git pull --ff-only origin main
|
|
47
|
+
|
|
48
|
+
pnpm install --frozen-lockfile
|
|
49
|
+
pnpm type-check
|
|
50
|
+
pnpm test
|
|
51
|
+
|
|
52
|
+
current_version="$(node -p "require('./package.json').version")"
|
|
53
|
+
next_version="$(node -e '
|
|
54
|
+
const [major, minor, patch] = process.argv[1].split(".").map(Number)
|
|
55
|
+
const bump = process.argv[2]
|
|
56
|
+
|
|
57
|
+
let next
|
|
58
|
+
if (bump === "major") next = [major + 1, 0, 0]
|
|
59
|
+
else if (bump === "minor") next = [major, minor + 1, 0]
|
|
60
|
+
else next = [major, minor, patch + 1]
|
|
61
|
+
|
|
62
|
+
process.stdout.write(next.join("."))
|
|
63
|
+
' "$current_version" "$bump")"
|
|
64
|
+
branch_name="tk-release-v${next_version}"
|
|
65
|
+
|
|
66
|
+
if git show-ref --verify --quiet "refs/heads/${branch_name}"; then
|
|
67
|
+
echo "Local branch ${branch_name} already exists." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if git ls-remote --exit-code --heads origin "${branch_name}" >/dev/null 2>&1; then
|
|
72
|
+
echo "Remote branch ${branch_name} already exists." >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
npm version "$next_version" --no-git-tag-version >/dev/null
|
|
77
|
+
|
|
78
|
+
git checkout -b "$branch_name"
|
|
79
|
+
git add package.json
|
|
80
|
+
git commit -m "release: v${next_version}"
|
|
81
|
+
git push -u origin "$branch_name"
|
|
82
|
+
|
|
83
|
+
gh pr create \
|
|
84
|
+
--base main \
|
|
85
|
+
--head "$branch_name" \
|
|
86
|
+
--title "release: v${next_version}" \
|
|
87
|
+
--body "$(cat <<EOF
|
|
88
|
+
## Summary
|
|
89
|
+
|
|
90
|
+
- release libretto v${next_version}
|
|
91
|
+
|
|
92
|
+
## Verification
|
|
93
|
+
|
|
94
|
+
- pnpm type-check
|
|
95
|
+
- pnpm test
|
|
96
|
+
EOF
|
|
97
|
+
)"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { relative, resolve, join } from "node:path";
|
|
12
|
+
|
|
13
|
+
export const SKILL_DIRS = [
|
|
14
|
+
"skills/libretto",
|
|
15
|
+
".agents/skills/libretto",
|
|
16
|
+
".claude/skills/libretto",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function walkFiles(dir, baseDir = dir) {
|
|
20
|
+
const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) =>
|
|
21
|
+
a.name.localeCompare(b.name),
|
|
22
|
+
);
|
|
23
|
+
const files = [];
|
|
24
|
+
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
const fullPath = join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
files.push(...walkFiles(fullPath, baseDir));
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (entry.isFile()) files.push(relative(baseDir, fullPath));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function syncSkillDir(sourceDir, destDir) {
|
|
38
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
39
|
+
mkdirSync(destDir, { recursive: true });
|
|
40
|
+
cpSync(sourceDir, destDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function syncRepoSkills(repoRoot) {
|
|
44
|
+
const sourceDir = resolve(repoRoot, "skills/libretto");
|
|
45
|
+
for (const dir of SKILL_DIRS.slice(1)) {
|
|
46
|
+
syncSkillDir(sourceDir, resolve(repoRoot, dir));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function compareSkillDirs(repoRoot) {
|
|
51
|
+
const roots = SKILL_DIRS.map((dir) => ({
|
|
52
|
+
label: dir,
|
|
53
|
+
absPath: resolve(repoRoot, dir),
|
|
54
|
+
}));
|
|
55
|
+
const missing = roots.filter(({ absPath }) => !existsSync(absPath));
|
|
56
|
+
const mismatches = [];
|
|
57
|
+
|
|
58
|
+
if (missing.length > 0) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
issues: missing.map(({ label }) => `missing directory: ${label}`),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const expectedFiles = walkFiles(roots[0].absPath);
|
|
66
|
+
const expectedFileSet = new Set(expectedFiles);
|
|
67
|
+
|
|
68
|
+
for (const root of roots.slice(1)) {
|
|
69
|
+
const actualFiles = walkFiles(root.absPath);
|
|
70
|
+
const actualFileSet = new Set(actualFiles);
|
|
71
|
+
|
|
72
|
+
for (const file of expectedFiles) {
|
|
73
|
+
if (!actualFileSet.has(file)) {
|
|
74
|
+
mismatches.push(`${root.label} is missing file: ${file}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const file of actualFiles) {
|
|
79
|
+
if (!expectedFileSet.has(file)) {
|
|
80
|
+
mismatches.push(`${root.label} has unexpected file: ${file}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const file of expectedFiles) {
|
|
86
|
+
const expectedContent = readFileSync(join(roots[0].absPath, file));
|
|
87
|
+
for (const root of roots.slice(1)) {
|
|
88
|
+
const targetPath = join(root.absPath, file);
|
|
89
|
+
if (!existsSync(targetPath)) continue;
|
|
90
|
+
const actualContent = readFileSync(targetPath);
|
|
91
|
+
if (!expectedContent.equals(actualContent)) {
|
|
92
|
+
mismatches.push(
|
|
93
|
+
`${root.label} differs from ${roots[0].label}: ${file}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
ok: mismatches.length === 0,
|
|
101
|
+
issues: mismatches,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error(
|
|
9
|
+
"Usage: node scripts/summarize-evals.mjs <score-dir> <summary-json-path>",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeFailureRecord(failure) {
|
|
14
|
+
return {
|
|
15
|
+
criterion: String(failure?.criterion ?? "").trim(),
|
|
16
|
+
reason: String(failure?.reason ?? "").trim(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeRecord(record) {
|
|
21
|
+
const failures = Array.isArray(record?.failures)
|
|
22
|
+
? record.failures
|
|
23
|
+
.map(normalizeFailureRecord)
|
|
24
|
+
.filter(
|
|
25
|
+
(failure) =>
|
|
26
|
+
failure.criterion.length > 0 && failure.reason.length > 0,
|
|
27
|
+
)
|
|
28
|
+
: [];
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
name: String(record?.name ?? "").trim(),
|
|
32
|
+
passed: Number(record?.passed ?? 0),
|
|
33
|
+
total: Number(record?.total ?? 0),
|
|
34
|
+
percent: Number(record?.percent ?? 0),
|
|
35
|
+
failures,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function loadScoreRecords(scoreDirArg) {
|
|
40
|
+
const scoreDir = resolve(scoreDirArg);
|
|
41
|
+
return readdirSync(scoreDir, { withFileTypes: true })
|
|
42
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
43
|
+
.map((entry) =>
|
|
44
|
+
JSON.parse(readFileSync(join(scoreDir, entry.name), "utf8")),
|
|
45
|
+
)
|
|
46
|
+
.map(normalizeRecord)
|
|
47
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildSummary(records) {
|
|
51
|
+
const passed = records.reduce(
|
|
52
|
+
(sum, record) => sum + Number(record.passed || 0),
|
|
53
|
+
0,
|
|
54
|
+
);
|
|
55
|
+
const total = records.reduce(
|
|
56
|
+
(sum, record) => sum + Number(record.total || 0),
|
|
57
|
+
0,
|
|
58
|
+
);
|
|
59
|
+
const percent = total > 0 ? Number(((passed / total) * 100).toFixed(2)) : 0;
|
|
60
|
+
const failingRecords = records.filter((record) => record.failures.length > 0);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
generatedAt: new Date().toISOString(),
|
|
64
|
+
recordCount: records.length,
|
|
65
|
+
passed,
|
|
66
|
+
total,
|
|
67
|
+
percent,
|
|
68
|
+
failingRecordCount: failingRecords.length,
|
|
69
|
+
records,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildMarkdown(summary, summaryPathArg) {
|
|
74
|
+
const lines = [
|
|
75
|
+
"# Eval Summary",
|
|
76
|
+
"",
|
|
77
|
+
`- Overall score: \`${summary.percent}%\``,
|
|
78
|
+
`- Passed criteria: \`${summary.passed}/${summary.total}\``,
|
|
79
|
+
`- Recorded score entries: \`${summary.recordCount}\``,
|
|
80
|
+
`- Failed evals: \`${summary.failingRecordCount}\``,
|
|
81
|
+
`- Summary file: \`${basename(summaryPathArg)}\``,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
if (summary.records.length > 0) {
|
|
85
|
+
lines.push("", "## Breakdown", "");
|
|
86
|
+
for (const record of summary.records) {
|
|
87
|
+
const status = record.failures.length > 0 ? "fail" : "pass";
|
|
88
|
+
lines.push(
|
|
89
|
+
`- ${status} \`${record.name}\`: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (summary.failingRecordCount > 0) {
|
|
95
|
+
lines.push("", "## Failed Evals", "");
|
|
96
|
+
for (const record of summary.records.filter(
|
|
97
|
+
(candidate) => candidate.failures.length > 0,
|
|
98
|
+
)) {
|
|
99
|
+
lines.push(`### \`${record.name}\``);
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push(
|
|
102
|
+
`- Score: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
103
|
+
);
|
|
104
|
+
for (const failure of record.failures) {
|
|
105
|
+
lines.push(`- ${failure.criterion}: ${failure.reason}`);
|
|
106
|
+
}
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main(argv) {
|
|
115
|
+
const [, , scoreDirArg, summaryPathArg] = argv;
|
|
116
|
+
|
|
117
|
+
if (!scoreDirArg || !summaryPathArg) {
|
|
118
|
+
usage();
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const summaryPath = resolve(summaryPathArg);
|
|
123
|
+
const records = loadScoreRecords(scoreDirArg);
|
|
124
|
+
const summary = buildSummary(records);
|
|
125
|
+
|
|
126
|
+
writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
|
|
127
|
+
process.stdout.write(buildMarkdown(summary, summaryPath));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (
|
|
131
|
+
process.argv[1] &&
|
|
132
|
+
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
133
|
+
) {
|
|
134
|
+
main(process.argv);
|
|
135
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { SKILL_DIRS, syncRepoSkills } from "./skills-libretto.mjs";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const repoRoot = join(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
syncRepoSkills(repoRoot);
|
|
12
|
+
console.log(`libretto: synced skill mirrors across ${SKILL_DIRS.join(", ")}`);
|