chore: standardized code quality and formatting for tooling scripts

- Added `scripts/**/*.ts` to the linter and formatter inclusion list in `biome.json`.
- Replaced manual array index searches with standard `indexOf` operations.
- Cleaned up unused variables, obsolete helpers, and command-line flags.
- Applied consistent formatting and code style across tooling scripts.
This commit is contained in:
can1357
2026-06-30 23:51:16 +02:00
parent 13b1b5b134
commit cbb471b666
28 changed files with 294 additions and 281 deletions
+1
View File
@@ -56,6 +56,7 @@
"packages/*/examples/**/*.ts",
"packages/*/scripts/**/*.ts",
"packages/*/*.ts",
"scripts/**/*.ts",
"!packages/natives/native/index.d.ts",
"!**/vendor/**/*",
"!**/node_modules/**/*",
+6 -6
View File
@@ -15,10 +15,7 @@
import * as path from "node:path";
const PROVIDER_FILE = path.join(
import.meta.dir,
"../packages/catalog/src/wire/gemini-headers.ts",
);
const PROVIDER_FILE = path.join(import.meta.dir, "../packages/catalog/src/wire/gemini-headers.ts");
interface VersionCheck {
/** Human label for the report. */
@@ -32,7 +29,10 @@ interface VersionCheck {
}
/** Fetch latest non-prerelease tag from a GitHub repo. */
async function fetchLatestGitHubRelease(repo: string, parseTag: (tag: string) => string | null): Promise<string | null> {
async function fetchLatestGitHubRelease(
repo: string,
parseTag: (tag: string) => string | null,
): Promise<string | null> {
try {
// /releases/latest only returns non-prerelease, non-draft releases
const res = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {
@@ -53,7 +53,7 @@ const checks: VersionCheck[] = [
name: "Gemini CLI",
sourcePattern: /PI_AI_GEMINI_CLI_VERSION\s*\|\|\s*"(\d+\.\d+\.\d+)"/,
repo: "google-gemini/gemini-cli",
parseTag: (tag) => SEMVER_RE.exec(tag)?.[1] ?? null,
parseTag: tag => SEMVER_RE.exec(tag)?.[1] ?? null,
},
];
+1 -1
View File
@@ -25,7 +25,7 @@ function parseTargetVariants(): NativeBuildVariant[] {
const rawVariants = (Bun.env.TARGET_VARIANTS ?? "").trim();
if (!rawVariants) return [];
return rawVariants.split(/\s+/).map((rawVariant) => {
return rawVariants.split(/\s+/).map(rawVariant => {
const variant = variantConfigs[rawVariant as keyof typeof variantConfigs];
if (!variant) {
throw new Error(`Unsupported TARGET_VARIANTS entry: ${rawVariant}. Expected baseline or modern.`);
+2 -6
View File
@@ -237,12 +237,8 @@ const workflowYaml = await Bun.file(WORKFLOW_PATH).text();
const concurrencySection = workflowYaml.slice(workflowYaml.indexOf("\nconcurrency:") + 1);
const groupRaw = /^\s*group:\s*(\S.*?)\s*$/m.exec(concurrencySection)?.[1];
const cancelRaw = /^\s*cancel-in-progress:\s*(\S.*?)\s*$/m.exec(concurrencySection)?.[1];
const groupTemplate =
groupRaw && groupRaw.startsWith('"') && groupRaw.endsWith('"') ? groupRaw.slice(1, -1) : groupRaw;
const cancelTemplate =
cancelRaw && cancelRaw.startsWith('"') && cancelRaw.endsWith('"')
? cancelRaw.slice(1, -1)
: cancelRaw;
const groupTemplate = groupRaw?.startsWith('"') && groupRaw.endsWith('"') ? groupRaw.slice(1, -1) : groupRaw;
const cancelTemplate = cancelRaw?.startsWith('"') && cancelRaw.endsWith('"') ? cancelRaw.slice(1, -1) : cancelRaw;
if (!groupTemplate || !cancelTemplate) {
throw new Error("could not locate concurrency.group / cancel-in-progress in ci.yml");
}
+5 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bun
import { createRequire } from "node:module";
import * as fs from "node:fs/promises";
import { createRequire } from "node:module";
import * as path from "node:path";
interface BinaryTarget {
@@ -66,11 +66,11 @@ const targets: BinaryTarget[] = [
];
function parseRequestedTargets(): Set<string> | null {
const flagIndex = process.argv.findIndex(arg => arg === "--targets");
const flagIndex = process.argv.indexOf("--targets");
const flagValue =
flagIndex >= 0
? process.argv[flagIndex + 1]
: process.argv.find(arg => arg.startsWith("--targets="))?.split("=", 2)[1] ?? Bun.env.RELEASE_TARGETS;
: (process.argv.find(arg => arg.startsWith("--targets="))?.split("=", 2)[1] ?? Bun.env.RELEASE_TARGETS);
if (!flagValue) {
return null;
@@ -147,9 +147,7 @@ async function buildBinary(target: BinaryTarget): Promise<void> {
return;
}
const buildEnv = shouldAdhocSignDarwinBinary(target)
? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" }
: Bun.env;
const buildEnv = shouldAdhocSignDarwinBinary(target) ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
await runCommand(buildCompileCommand(target), repoRoot, buildEnv);
// Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
@@ -186,9 +184,7 @@ async function resetArtifacts(): Promise<void> {
async function main(): Promise<void> {
const requestedTargets = parseRequestedTargets();
const selectedTargets = requestedTargets
? targets.filter(target => requestedTargets.has(target.id))
: targets;
const selectedTargets = requestedTargets ? targets.filter(target => requestedTargets.has(target.id)) : targets;
if (requestedTargets) {
const unknownTargets = [...requestedTargets].filter(
+3 -10
View File
@@ -1,9 +1,5 @@
import { describe, expect, it } from "bun:test";
import {
compareVersions,
enumerateChangelogVersions,
mergePackageSection,
} from "./ci-release-notes";
import { compareVersions, enumerateChangelogVersions, mergePackageSection } from "./ci-release-notes";
const FIXTURE = [
"# Changelog",
@@ -86,9 +82,7 @@ describe("mergePackageSection", () => {
it("includes every silent-tag section above floor up to target inclusive", () => {
const merged = mergePackageSection(FIXTURE, "15.12.4", "15.13.0");
// 15.12.6 and 15.12.5 unique fingerprints must land.
expect(merged).toContain(
"Removed `writeLine`/`writeLineSync` from the public SessionStorageWriter contract.",
);
expect(merged).toContain("Removed `writeLine`/`writeLineSync` from the public SessionStorageWriter contract.");
expect(merged).toContain("Added package-level exports for session context.");
expect(merged).toContain("Changed terminal resize handling to paint only the visible viewport.");
// 15.12.4 entry stays excluded — it is the floor.
@@ -120,8 +114,7 @@ describe("mergePackageSection", () => {
});
it("returns empty string when no version in the requested range carries body content", () => {
const empty = ["# Changelog", "", "## [15.13.0] - 2026-06-14", "", "## [15.12.6] - 2026-06-14"]
.join("\n");
const empty = ["# Changelog", "", "## [15.13.0] - 2026-06-14", "", "## [15.12.6] - 2026-06-14"].join("\n");
expect(mergePackageSection(empty, "15.12.5", "15.13.0")).toBe("");
});
+11 -13
View File
@@ -39,8 +39,14 @@ const CATEGORY_ORDER = ["Breaking Changes", "Added", "Changed", "Fixed", "Remove
/** Compare two `X.Y.Z` (or `vX.Y.Z`) version strings; non-semver returns 0. */
export function compareVersions(a: string, b: string): number {
const am = a.replace(/^v/, "").trim().match(/^(\d+)\.(\d+)\.(\d+)$/);
const bm = b.replace(/^v/, "").trim().match(/^(\d+)\.(\d+)\.(\d+)$/);
const am = a
.replace(/^v/, "")
.trim()
.match(/^(\d+)\.(\d+)\.(\d+)$/);
const bm = b
.replace(/^v/, "")
.trim()
.match(/^(\d+)\.(\d+)\.(\d+)$/);
if (!am || !bm) return 0;
if (am[1] !== bm[1]) return Number(am[1]) - Number(bm[1]);
if (am[2] !== bm[2]) return Number(am[2]) - Number(bm[2]);
@@ -88,11 +94,7 @@ export function enumerateChangelogVersions(content: string): ChangelogVersionSpa
* `floorExclusive === null` → take only the target version (legacy behavior).
* Returns "" when no in-range version contributes any bullet.
*/
export function mergePackageSection(
content: string,
floorExclusive: string | null,
targetInclusive: string,
): string {
export function mergePackageSection(content: string, floorExclusive: string | null, targetInclusive: string): string {
const spans = enumerateChangelogVersions(content)
.filter(v => {
if (compareVersions(v.version, targetInclusive) > 0) return false;
@@ -240,9 +242,7 @@ async function resolvePublishedFloorTag(targetVersion: string): Promise<string |
async function main(): Promise<void> {
const tagInput = process.argv[2] ?? process.env.GITHUB_REF_NAME ?? "";
if (!tagInput) {
console.error(
"Error: version not provided. Pass as argv (e.g. `v15.4.3`) or set GITHUB_REF_NAME.",
);
console.error("Error: version not provided. Pass as argv (e.g. `v15.4.3`) or set GITHUB_REF_NAME.");
process.exit(1);
}
const version = tagInput.replace(/^v/, "").trim();
@@ -267,9 +267,7 @@ async function main(): Promise<void> {
}
if (sections.length === 0) {
console.warn(
`No CHANGELOG entries found for version ${version}; writing empty release notes to ${outputPath}.`,
);
console.warn(`No CHANGELOG entries found for version ${version}; writing empty release notes to ${outputPath}.`);
await Bun.write(outputPath, "");
process.exit(0);
}
+10 -3
View File
@@ -31,9 +31,9 @@ import * as os from "node:os";
import * as path from "node:path";
import { $ } from "bun";
import {
type GeneratedLeafPackage,
generateNpmPackages,
LEAF_TARGETS,
type GeneratedLeafPackage,
} from "../packages/natives/scripts/gen-npm-packages.ts";
export interface PublishPackage {
@@ -271,7 +271,12 @@ async function publishNativeLeafPackage(tag: string): Promise<void> {
const pkgDir = path.join(repoRoot, pkg.dir);
const coreManifest = (await Bun.file(path.join(pkgDir, "package.json")).json()) as PackageManifest;
if (typeof coreManifest.version !== "string") throw new Error(`Missing version in ${pkg.dir}/package.json`);
const leaves = await generateNpmPackages({ packageDir: pkgDir, dryRun: isDryRun, version: coreManifest.version, tags: [tag] });
const leaves = await generateNpmPackages({
packageDir: pkgDir,
dryRun: isDryRun,
version: coreManifest.version,
tags: [tag],
});
const leaf = leaves[0];
if (!leaf) throw new Error(`No native leaf generated for ${tag}`);
await publishGeneratedLeafPackage(leaf);
@@ -283,7 +288,9 @@ async function publishNativePackage(pkg: PublishPackage): Promise<void> {
const name = manifest.name ?? path.basename(pkg.dir);
if (isDryRun) {
console.log(`DRY RUN native core manifest rewrite (${pkg.dir})`);
console.log(JSON.stringify({ optionalDependencies: manifest.optionalDependencies, files: manifest.files }, null, "\t"));
console.log(
JSON.stringify({ optionalDependencies: manifest.optionalDependencies, files: manifest.files }, null, "\t"),
);
}
await packAndPublish(pkgDir, name);
}
+18 -30
View File
@@ -59,7 +59,7 @@ const validModes: Record<Mode, true> = {
};
// `chunkSize` splits a bucket's file list into that-many-file groups, each run as a
// separate `bun --smol test` child process. A fresh process per chunk resets Bun's
// separate `bun test` child process. A fresh process per chunk resets Bun's
// heap and reaps any dangling spawned children between groups, keeping peak RSS
// under the CI runner's OOM ceiling (a single 170–370-file invocation gets
// SIGKILLed at 137). The singleton/global-state bucket is left whole: its suites
@@ -100,8 +100,7 @@ const nativeAndIntegrationPackages = [
// Packages the CI buckets deliberately skip but a local full run should still
// cover. mnemopi's embedding suites need a ~270MB fastembed model absent from CI
// runners (so it flakes/times out there); robomp-web lives under python/robomp
// and is outside every CI TS bucket. Both run with `--smol` to bound RSS when
// fanned out alongside everything else.
// and is outside every CI TS bucket.
const localOnlyWorkspacePackages = ["packages/mnemopi", "python/robomp/web"];
// Repo-level script tests. CI's `workspace` bucket only runs the concurrency
@@ -194,13 +193,7 @@ const codingAgentUiContentMarkers = [
"renderToString",
];
const codingAgentRuntimeContentMarkers = [
"AgentSession",
"SessionManager",
"AuthStorage",
"Bun.sleep",
"setTimeout(",
];
const codingAgentRuntimeContentMarkers = ["AgentSession", "SessionManager", "AuthStorage", "Bun.sleep", "setTimeout("];
let codingAgentTestPartitionPromise: Promise<CodingAgentTestPartition> | null = null;
@@ -211,16 +204,12 @@ function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`;
}
function workspaceTestCommand(
pkg: string,
parallel: number,
options: { smol?: boolean; extraArgs?: string[] } = {},
): TestCommand {
const { smol = false, extraArgs = [] } = options;
function workspaceTestCommand(pkg: string, parallel: number, options: { extraArgs?: string[] } = {}): TestCommand {
const { extraArgs = [] } = options;
return {
label: pkg,
cwd: pkg,
command: ["bun", ...(smol ? ["--smol"] : []), "test", `--parallel=${parallel}`, ...extraArgs],
command: ["bun", "test", `--parallel=${parallel}`, ...extraArgs],
};
}
@@ -273,10 +262,7 @@ function classifyCodingAgentTest(testFile: string, content: string): CodingAgent
) {
return "native";
}
if (
matchesAnyPath(testFile, codingAgentUiPathPatterns) ||
hasAnyMarker(content, codingAgentUiContentMarkers)
) {
if (matchesAnyPath(testFile, codingAgentUiPathPatterns) || hasAnyMarker(content, codingAgentUiContentMarkers)) {
return "ui";
}
if (
@@ -335,7 +321,7 @@ async function codingAgentTestCommands(bucket: CodingAgentBucket): Promise<TestC
commands.push({
label: `packages/coding-agent (${plan.label}; ${testFiles.length} files; parallel=${plan.parallel}${chunkLabel}; ${chunk.length} files)`,
cwd: "packages/coding-agent",
command: ["bun", "--smol", "test", `--parallel=${plan.parallel}`, ...onlyFailuresArgs, ...chunk],
command: ["bun", "test", `--parallel=${plan.parallel}`, ...onlyFailuresArgs, ...chunk],
});
}
return commands;
@@ -383,12 +369,8 @@ async function commandsForMode(mode: Mode): Promise<TestCommand[]> {
case "local-ts":
return [
...fastWorkspacePackages.map(pkg => workspaceTestCommand(pkg, 8, { extraArgs: onlyFailuresArgs })),
...nativeAndIntegrationPackages.map(pkg =>
workspaceTestCommand(pkg, 4, { smol: true, extraArgs: onlyFailuresArgs }),
),
...localOnlyWorkspacePackages.map(pkg =>
workspaceTestCommand(pkg, 4, { smol: true, extraArgs: onlyFailuresArgs }),
),
...nativeAndIntegrationPackages.map(pkg => workspaceTestCommand(pkg, 4, { extraArgs: onlyFailuresArgs })),
...localOnlyWorkspacePackages.map(pkg => workspaceTestCommand(pkg, 4, { extraArgs: onlyFailuresArgs })),
...(await commandsForMode("coding-agent-heavy")),
{
label: "scripts",
@@ -616,7 +598,11 @@ export function formatFailureReport(failures: ChunkOutcome[], total: number, rep
const header = `${failures.length} of ${total} test chunk(s) FAILED`;
const lines: string[] = ["", style.bold(style.red(`━━━ ${header} ━━━`))];
for (const failure of failures) {
lines.push("", style.bold(style.red(`✗ ${failure.label} (exit ${failure.exitCode})`)), style.dim(`$ ${failure.command}`));
lines.push(
"",
style.bold(style.red(`✗ ${failure.label} (exit ${failure.exitCode})`)),
style.dim(`$ ${failure.command}`),
);
const failing = extractFailingTests(failure.output);
// Fully attributed only when every failure carries its own bun block;
// otherwise (no markers, or a marker with no preceding frame — timeouts,
@@ -703,7 +689,9 @@ async function runTestCommandsInParallel(commands: TestCommand[], concurrency: n
}
if (quiet) {
const totalSeconds = (performance.now() - runStartedAt) / 1000;
process.stdout.write(`${formatSummaryFooter(commands.length - failures.length, failures.length, totalSeconds)}\n`);
process.stdout.write(
`${formatSummaryFooter(commands.length - failures.length, failures.length, totalSeconds)}\n`,
);
}
if (failures.length > 0) {
process.exitCode = 1;
+1 -1
View File
@@ -23,7 +23,7 @@ interface ReleaseAsset {
function parseArgs(argv: readonly string[]): { tag: string; out: string | null } {
const rest = [...argv];
let out: string | null = null;
const outIdx = rest.findIndex(a => a === "--out");
const outIdx = rest.indexOf("--out");
if (outIdx >= 0) {
out = rest[outIdx + 1] ?? null;
if (!out) throw new Error("--out requires a path");
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bun
import { runClaudeTraceCommand, type ClaudeTraceCommandArgs } from "../packages/coding-agent/src/cli/claude-trace-cli";
import { type ClaudeTraceCommandArgs, runClaudeTraceCommand } from "../packages/coding-agent/src/cli/claude-trace-cli";
const HELP = `Usage: bun scripts/claude-trace.ts [options]
+19 -15
View File
@@ -90,7 +90,10 @@ function parseNumber(text: string): number {
}
function getCell(text: string, label: string): string | null {
const re = new RegExp(`^\\|\\s*\\*?\\*?${escapeRegex(label)}\\*?\\*?\\s*\\|\\s*\\*?\\*?(.+?)\\*?\\*?\\s*\\|\\s*$`, "m");
const re = new RegExp(
`^\\|\\s*\\*?\\*?${escapeRegex(label)}\\*?\\*?\\s*\\|\\s*\\*?\\*?(.+?)\\*?\\*?\\s*\\|\\s*$`,
"m",
);
const m = text.match(re);
return m ? m[1].trim() : null;
}
@@ -125,12 +128,6 @@ function parseRatePair(value: string | null): { numerator: number; denominator:
};
}
function parseFraction(value: string | null): { num: number; denom: number } {
if (!value) return { num: 0, denom: 0 };
const m = value.match(/([0-9,]+)\s*\/\s*([0-9,]+)/);
return m ? { num: parseNumber(m[1]), denom: parseNumber(m[2]) } : { num: 0, denom: 0 };
}
async function parseReport(file: string): Promise<ReportRow> {
const text = await Bun.file(file).text();
@@ -221,11 +218,9 @@ function sortRows(rows: ReportRow[], by: typeof sortBy): ReportRow[] {
};
const cmp: Record<typeof sortBy, (a: ReportRow, b: ReportRow) => number> = {
sep: (a, b) =>
sepOrder.indexOf(a.sepSlug) - sepOrder.indexOf(b.sepSlug) ||
modelOrder(a.model) - modelOrder(b.model),
sepOrder.indexOf(a.sepSlug) - sepOrder.indexOf(b.sepSlug) || modelOrder(a.model) - modelOrder(b.model),
model: (a, b) =>
modelOrder(a.model) - modelOrder(b.model) ||
sepOrder.indexOf(a.sepSlug) - sepOrder.indexOf(b.sepSlug),
modelOrder(a.model) - modelOrder(b.model) || sepOrder.indexOf(a.sepSlug) - sepOrder.indexOf(b.sepSlug),
task: (a, b) => b.taskSuccessPct - a.taskSuccessPct,
edit: (a, b) => b.editSuccessPct - a.editSuccessPct,
tokens: (a, b) => a.totalTokensAvg - b.totalTokensAvg,
@@ -236,7 +231,9 @@ function sortRows(rows: ReportRow[], by: typeof sortBy): ReportRow[] {
const entries = (
await Promise.all(
resolvedDirs.map(async d =>
(await fs.readdir(d, { withFileTypes: true }))
(
await fs.readdir(d, { withFileTypes: true })
)
.filter(e => e.isFile() && e.name.endsWith(".md"))
.map(e => path.join(d, e.name)),
),
@@ -283,13 +280,20 @@ function mergeRows(input: ReportRow[]): ReportRow[] {
successfulRuns,
taskSuccessPct: ratio(successfulRuns, totalRuns),
verifiedPct: ratio(successfulRuns, totalRuns),
editToolUsagePct: ratio(sumField(list, r => Math.round((r.editToolUsagePct / 100) * r.totalRuns)), totalRuns),
editToolUsagePct: ratio(
sumField(list, r => Math.round((r.editToolUsagePct / 100) * r.totalRuns)),
totalRuns,
),
editSuccessPct: ratio(patchAttempts - patchFailures, patchAttempts),
patchFailurePct: ratio(patchFailures, patchAttempts),
patchFailures,
patchAttempts,
mutationIntentPct: list.reduce((a, r) => a + (Number.isFinite(r.mutationIntentPct) ? r.mutationIntentPct : 0), 0) / list.length,
autocorrectFreePct: list.reduce((a, r) => a + (Number.isFinite(r.autocorrectFreePct) ? r.autocorrectFreePct : 0), 0) / list.length,
mutationIntentPct:
list.reduce((a, r) => a + (Number.isFinite(r.mutationIntentPct) ? r.mutationIntentPct : 0), 0) /
list.length,
autocorrectFreePct:
list.reduce((a, r) => a + (Number.isFinite(r.autocorrectFreePct) ? r.autocorrectFreePct : 0), 0) /
list.length,
tasksAllPassing: sumField(list, r => r.tasksAllPassing),
tasksFlakyFailing: sumField(list, r => r.tasksFlakyFailing),
timeoutRuns: sumField(list, r => r.timeoutRuns),
+29 -27
View File
@@ -1,8 +1,8 @@
import { describe, expect, it } from "bun:test";
import { $ } from "bun";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { $ } from "bun";
import { collectPromotableAddedItemLines, fixChangelogContent, runChangelogFixer } from "./fix-changelogs";
describe("collectPromotableAddedItemLines", () => {
@@ -82,32 +82,34 @@ describe("fixChangelogContent", () => {
expect(result.promotedItems).toBe(2);
expect(result.mergedDuplicateHeadings).toBe(1);
expect(result.content).toBe([
"# Changelog",
"",
"## [Unreleased]",
"",
"### Added",
"",
"- New addition in released section.",
"",
"### Fixed",
"",
"- Existing fix.",
"- Second fix.",
"- New fix in released section.",
"",
"## [1.0.0] - 2026-01-01",
"",
"### Added",
"",
"- Historical addition.",
"",
"### Fixed",
"",
"- Historical fix.",
"",
].join("\n"));
expect(result.content).toBe(
[
"# Changelog",
"",
"## [Unreleased]",
"",
"### Added",
"",
"- New addition in released section.",
"",
"### Fixed",
"",
"- Existing fix.",
"- Second fix.",
"- New fix in released section.",
"",
"## [1.0.0] - 2026-01-01",
"",
"### Added",
"",
"- Historical addition.",
"",
"### Fixed",
"",
"- Historical fix.",
"",
].join("\n"),
);
});
it("drops Unreleased items that already appear verbatim in a released section", () => {
+2 -14
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bun
import { $, Glob } from "bun";
import * as path from "node:path";
import { $, Glob } from "bun";
const CHANGELOG_GLOB = "packages/*/CHANGELOG.md";
const ORDERED_SECTION_TITLES = ["Breaking Changes", "Added", "Changed", "Fixed", "Removed"] as const;
@@ -97,7 +97,6 @@ interface HistoricalReleaseRecovery {
sectionsByTitle: Map<string, ReleaseSection>;
}
function isReleaseHeading(line: string): boolean {
return /^## \[[^\]]+\]/.test(line);
}
@@ -377,7 +376,6 @@ function compactAdjacentListSpacing(lines: readonly string[]): string[] {
return flattenedItems;
}
function normalizeSection(section: ReleaseSection): FixCounters {
const counters: FixCounters = {
promotedItems: 0,
@@ -456,7 +454,6 @@ function sortReleaseSections(document: ChangelogDocument): void {
document.sections = [...unreleasedSections, ...releasedSections];
}
function rebuildReleasedSectionsFromHistory(
content: string,
historicalSectionsByTitle: ReadonlyMap<string, ReleaseSection>,
@@ -502,7 +499,6 @@ function rebuildReleasedSectionsFromHistory(
return renderChangelog(document);
}
export function renderChangelog(document: ChangelogDocument): string {
const output: string[] = [];
const prefix = trimBlankLines(numberedText(document.prefixLines));
@@ -590,7 +586,6 @@ function isAddedReleaseHeadingLine(line: string): boolean {
return line.startsWith("+## [");
}
function itemKey(pathName: string, text: string): string {
return `${pathName}\0${normalizeItemText(text)}`;
}
@@ -600,7 +595,6 @@ export function collectPromotableAddedItemLines(diffText: string): Map<string, S
const removals: RemovedItemOccurrence[] = [];
const addedReleaseHeadingHunks = new Set<string>();
let currentPath = "";
let oldLine = 0;
let newLine = 0;
let hunkIndex = -1;
for (const rawLine of diffText.replace(/\r\n/g, "\n").split("\n")) {
@@ -617,7 +611,6 @@ export function collectPromotableAddedItemLines(diffText: string): Map<string, S
const hunkMatch = rawLine.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (hunkMatch) {
oldLine = Number(hunkMatch[1]);
newLine = Number(hunkMatch[2]);
hunkIndex++;
continue;
@@ -655,12 +648,10 @@ export function collectPromotableAddedItemLines(diffText: string): Map<string, S
pairedWithAddition: false,
});
}
oldLine++;
continue;
}
if (marker === " ") {
oldLine++;
newLine++;
}
}
@@ -765,9 +756,7 @@ async function resolveSince(repoRoot: string, since: string | undefined): Promis
*/
async function recoveryTags(repoRoot: string): Promise<string[]> {
const baseline = await changelogBaselineCommit(repoRoot);
const listArgs = baseline
? ["tag", "--contains", baseline, "--sort=v:refname"]
: ["tag", "--sort=v:refname"];
const listArgs = baseline ? ["tag", "--contains", baseline, "--sort=v:refname"] : ["tag", "--sort=v:refname"];
return (await git(listArgs, repoRoot))
.split("\n")
.map(tag => tag.trim())
@@ -825,7 +814,6 @@ async function collectHistoricalReleaseRecovery(
return recoveryByPath;
}
export async function changelogPaths(repoRoot: string): Promise<string[]> {
const glob = new Glob(CHANGELOG_GLOB);
+5 -8
View File
@@ -1,4 +1,6 @@
#!/usr/bin/env bun
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname, extname, join, relative, resolve, sep } from "node:path";
/**
* Codemod: rewrite relative test imports that reach into a package's `src/`
* into the package's public subpath import.
@@ -17,16 +19,13 @@
* bun scripts/fix-test-imports.ts --write # apply the changes
*/
import { Glob } from "bun";
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname, extname, join, relative, resolve, sep } from "node:path";
const ROOT = resolve(import.meta.dir, "..");
const WRITE = process.argv.includes("--write");
// Matches the module specifier of `from "x"`, `import "x"`, `import("x")`,
// `require("x")` / `export ... from "x"` — but only when it starts with `./`/`../`.
const SPEC_RE =
/(\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|\bimport\s+)(["'])((?:\.\.?\/)[^"']*)\2/g;
const SPEC_RE = /(\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|\bimport\s+)(["'])((?:\.\.?\/)[^"']*)\2/g;
// Source-module extensions. A specifier resolving to one of these has a public
// `./*` -> `./src/*.ts` subpath; anything else (.json/.md/...) is an asset.
@@ -148,7 +147,7 @@ const skippedAssets: string[] = [];
for (const file of [...files].sort()) {
const { content, changes, skipped } = rewriteFile(file);
if (skipped.length) skippedAssets.push(...skipped.map((s) => `${relative(ROOT, file)}: ${s}`));
if (skipped.length) skippedAssets.push(...skipped.map(s => `${relative(ROOT, file)}: ${s}`));
if (!changes.length) continue;
changedFiles++;
totalChanges += changes.length;
@@ -158,9 +157,7 @@ for (const file of [...files].sort()) {
if (WRITE) writeFileSync(file, content);
}
console.log(
`\n${WRITE ? "Applied" : "Would apply"} ${totalChanges} rewrite(s) across ${changedFiles} file(s).`,
);
console.log(`\n${WRITE ? "Applied" : "Would apply"} ${totalChanges} rewrite(s) across ${changedFiles} file(s).`);
if (skippedAssets.length) {
console.log(`\nSkipped ${skippedAssets.length} asset import(s) into src (no public subpath):`);
for (const s of skippedAssets) console.log(` ${s}`);
+1 -1
View File
@@ -414,7 +414,7 @@ describe("inline-functions: type soundness", () => {
describe("inline-functions: formatting safety", () => {
test("wraps the tail exactly one indent level deeper than the inverted guard", () => {
const { text } = run(
'function dispatch(r: Runtime): void {\n\thandle(r.item);\n}\n' +
"function dispatch(r: Runtime): void {\n\thandle(r.item);\n}\n" +
'function handle(item: Item | null): void {\n\tif (item?.type !== "x") return;\n\ttouch(item);\n}\n',
);
// `if` sits at the function-body indent (one tab); its body is one deeper.
+21 -20
View File
@@ -71,7 +71,6 @@ import * as os from "node:os";
import * as path from "node:path";
import { parseArgs } from "node:util";
import { $ } from "bun";
import { IndentationText, Node, Project, SyntaxKind, VariableDeclarationKind } from "ts-morph";
import type {
CallExpression,
Expression,
@@ -81,6 +80,7 @@ import type {
SourceFile,
Statement,
} from "ts-morph";
import { IndentationText, Node, Project, SyntaxKind, VariableDeclarationKind } from "ts-morph";
// ---------------------------------------------------------------------------
// Types
@@ -241,7 +241,8 @@ function isPureExpr(node: Node, strict: boolean): boolean {
if (ASSIGNMENT_OPS.has(node.getOperatorToken().getText())) return false;
return isPureExpr(node.getLeft(), strict) && isPureExpr(node.getRight(), strict);
}
if (Node.isTemplateExpression(node)) return node.getTemplateSpans().every((s) => isPureExpr(s.getExpression(), strict));
if (Node.isTemplateExpression(node))
return node.getTemplateSpans().every(s => isPureExpr(s.getExpression(), strict));
// Defining a closure is itself side-effect-free; we substitute the value, never invoke it.
if (Node.isArrowFunction(node) || Node.isFunctionExpression(node)) return true;
return false;
@@ -293,7 +294,7 @@ function argNeedsParens(node: Node): boolean {
function subText(node: Node, edits: readonly Edit[]): string {
const start = node.getStart();
const end = node.getEnd();
const local = edits.filter((e) => e.start >= start && e.end <= end).sort((a, b) => b.start - a.start);
const local = edits.filter(e => e.start >= start && e.end <= end).sort((a, b) => b.start - a.start);
let text = node.getText();
for (const e of local) text = text.slice(0, e.start - start) + e.text + text.slice(e.end - start);
return text;
@@ -349,9 +350,9 @@ function wrap(part: NegatedExpr, minPrec: number): string {
/** Combine leading guard conditions into the single positive run-condition. */
function combineGuards(guards: readonly Expression[], edits: readonly Edit[]): string {
const parts = guards.map((g) => negate(g, edits));
const parts = guards.map(g => negate(g, edits));
if (parts.length === 1) return parts[0].text;
return parts.map((p) => wrap(p, PREC_AND)).join(" && ");
return parts.map(p => wrap(p, PREC_AND)).join(" && ");
}
// ---------------------------------------------------------------------------
@@ -410,15 +411,16 @@ function isNameOnly(id: Identifier): boolean {
* exception — it names a real value binding even though it lives in a type node.
*/
function isTypePositioned(id: Identifier): boolean {
if (!id.getFirstAncestor((a) => Node.isTypeNode(a))) return false;
return !id.getFirstAncestor((a) => Node.isTypeQuery(a));
if (!id.getFirstAncestor(a => Node.isTypeNode(a))) return false;
return !id.getFirstAncestor(a => Node.isTypeQuery(a));
}
/** Free identifiers in the body that resolve outside it (module / global) — shadow-sensitive. */
function computeFreeNames(fn: FunctionDeclaration, paramNames: readonly string[]): Set<string> {
const body = fn.getBodyOrThrow();
const local = new Set<string>(paramNames);
for (const v of body.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) collectBindingNames(v.getNameNode(), local);
for (const v of body.getDescendantsOfKind(SyntaxKind.VariableDeclaration))
collectBindingNames(v.getNameNode(), local);
for (const f of body.getDescendantsOfKind(SyntaxKind.FunctionDeclaration)) {
const n = f.getName();
if (n) local.add(n);
@@ -536,7 +538,7 @@ function asGuard(stmt: Statement): Expression | null {
}
function nearestFunction(node: Node): Node | undefined {
return node.getFirstAncestor((a) => isFunctionLike(a));
return node.getFirstAncestor(a => isFunctionLike(a));
}
/** Does an unlabeled/labeled `break`/`continue` target something outside `root`? */
@@ -716,13 +718,16 @@ function analyze(fn: FunctionDeclaration, opts: Options): Candidate | null {
const stmt = parent.getParent();
if (!stmt || !Node.isExpressionStatement(stmt)) return skip(opts, name, "call result is used");
const args = parent.getArguments();
if (args.some((a) => Node.isSpreadElement(a))) return skip(opts, name, "call uses spread args");
if (args.some(a => Node.isSpreadElement(a))) return skip(opts, name, "call uses spread args");
if (args.length > params.length) return skip(opts, name, "call passes more args than params");
callSites.push({ stmt, call: parent });
}
if (callSites.length === 0) return skip(opts, name, "no call sites");
const freeNames = computeFreeNames(fn, params.map((p) => p.name));
const freeNames = computeFreeNames(
fn,
params.map(p => p.name),
);
for (const cs of callSites) {
if (callSiteShadows(cs.call, freeNames)) return skip(opts, name, "would shadow a free identifier at a call site");
}
@@ -785,11 +790,7 @@ function reservedFor(call: CallExpression, reserved: Map<Node, Set<string>>): Se
* land in the caller block — declarations nested in loops, blocks, or functions keep
* their own scope and never collide, so they are left untouched.
*/
function renameCollidingLocals(
candidate: Candidate,
call: CallExpression,
reserved: Map<Node, Set<string>>,
): Edit[] {
function renameCollidingLocals(candidate: Candidate, call: CallExpression, reserved: Map<Node, Set<string>>): Edit[] {
const edits: Edit[] = [];
const taken = reservedFor(call, reserved);
const lo = candidate.tail[0].getStart();
@@ -890,18 +891,18 @@ function buildReplacement(
return {
prefix,
condition: combineGuards(candidate.guards, paramEdits),
tail: candidate.tail.map((s) => subText(s, paramEdits)),
tail: candidate.tail.map(s => subText(s, paramEdits)),
};
}
const allEdits = paramEdits.concat(renameCollidingLocals(candidate, call, reserved));
return { prefix, condition: null, tail: candidate.tail.map((s) => subText(s, allEdits)) };
return { prefix, condition: null, tail: candidate.tail.map(s => subText(s, allEdits)) };
}
function applyReplacement(stmt: Statement, repl: Replacement): void {
const parent = stmt.getParent();
if (parent && Node.isStatemented(parent)) {
const idx = parent.getStatements().findIndex((s) => s === stmt);
const idx = parent.getStatements().indexOf(stmt);
if (repl.condition === null) {
const inserted = parent.insertStatements(idx, [...repl.prefix, ...repl.tail]);
stmt.remove();
@@ -934,7 +935,7 @@ function applyReplacement(stmt: Statement, repl: Replacement): void {
function inlineCandidate(candidate: Candidate, strict: boolean): void {
const reserved = new Map<Node, Set<string>>();
// Build every replacement before mutating, so positions stay valid.
const plans = candidate.callSites.map((cs) => ({
const plans = candidate.callSites.map(cs => ({
stmt: cs.stmt,
repl: buildReplacement(candidate, cs.call, reserved, strict),
}));
+7 -17
View File
@@ -40,11 +40,10 @@ async function watchCI(): Promise<boolean> {
// Check job-level status for in-progress runs (fail fast on first job failure)
const failedJobs: Array<{ workflow: string; job: string; jobId: number; conclusion: string }> = [];
const inProgressRuns = runs.filter((r) => r.status === "in_progress" || r.status === "queued");
const inProgressRuns = runs.filter(r => r.status === "in_progress" || r.status === "queued");
for (const run of inProgressRuns) {
const jobsOutput =
await $`gh run view ${run.databaseId} --json jobs`.quiet().nothrow().text();
const jobsOutput = await $`gh run view ${run.databaseId} --json jobs`.quiet().nothrow().text();
try {
const { jobs } = JSON.parse(jobsOutput) as {
jobs: Array<{ name: string; databaseId: number; status: string; conclusion: string | null }>;
@@ -80,9 +79,9 @@ async function watchCI(): Promise<boolean> {
}
// Check workflow-level status
const pending = runs.filter((r) => r.status !== "completed");
const failed = runs.filter((r) => r.status === "completed" && r.conclusion !== "success");
const passed = runs.filter((r) => r.status === "completed" && r.conclusion === "success");
const pending = runs.filter(r => r.status !== "completed");
const failed = runs.filter(r => r.status === "completed" && r.conclusion !== "success");
const passed = runs.filter(r => r.status === "completed" && r.conclusion === "success");
console.log(` ${passed.length} passed, ${pending.length} pending, ${failed.length} failed`);
@@ -257,10 +256,7 @@ async function cmdRelease(versionOrBump: string): Promise<void> {
// Update @oh-my-pi/* catalog entries in root package.json
console.log("Updating root catalog versions...");
let rootPkgRaw = await Bun.file("package.json").text();
rootPkgRaw = rootPkgRaw.replace(
/("@oh-my-pi\/[^"]+":\s*)"[^"]+"/g,
`$1"${version}"`,
);
rootPkgRaw = rootPkgRaw.replace(/("@oh-my-pi\/[^"]+":\s*)"[^"]+"/g, `$1"${version}"`);
await Bun.write("package.json", rootPkgRaw);
console.log(" Updated root catalog @oh-my-pi/* entries");
@@ -372,13 +368,7 @@ async function cmdRelease(versionOrBump: string): Promise<void> {
const tagRef = `v${version}`;
const sha = (await git(["rev-parse", "HEAD"]).text()).trim();
await git(["tag", "-f", tagRef]);
await git([
"push",
"--atomic",
"origin",
"refs/heads/main:refs/heads/main",
`${sha}:refs/tags/${tagRef}`,
]);
await git(["push", "--atomic", "origin", "refs/heads/main:refs/heads/main", `${sha}:refs/tags/${tagRef}`]);
console.log();
// 9. Watch CI
+63 -48
View File
@@ -31,13 +31,22 @@
import * as path from "node:path";
import { parseArgs } from "node:util";
import { type Api, AuthStorage, completeSimple, Effort, type Model, SqliteAuthCredentialStore, type Tool, type ToolCall } from "@oh-my-pi/pi-ai";
import {
type Api,
AuthStorage,
completeSimple,
Effort,
type Model,
SqliteAuthCredentialStore,
type Tool,
type ToolCall,
} from "@oh-my-pi/pi-ai";
import { type GeneratedProvider, getBundledModel } from "@oh-my-pi/pi-catalog/models";
import { getAgentDbPath } from "@oh-my-pi/pi-utils";
import { z } from "zod/v4";
import {
changelogPaths,
type ChangelogDocument,
changelogPaths,
type NumberedLine,
parseChangelog,
parseItems,
@@ -143,12 +152,14 @@ interface RewrittenSection {
}
const REWRITE_RESPONSE = z.object({
sections: z.array(
z.object({
category: z.enum(["Breaking Changes", "Added", "Changed", "Fixed", "Removed"]),
items: z.array(z.string()),
})
).default([]),
sections: z
.array(
z.object({
category: z.enum(["Breaking Changes", "Added", "Changed", "Fixed", "Removed"]),
items: z.array(z.string()),
}),
)
.default([]),
});
const REWRITE_PARAMETERS = {
@@ -200,7 +211,10 @@ function validateRewrite(args: Record<string, unknown>): RewrittenSection[] {
}
function normalizeRewriteItem(text: string): string[] {
const lines = text.trim().split("\n").map(l => l.trimEnd());
const lines = text
.trim()
.split("\n")
.map(l => l.trimEnd());
if (lines.length === 0) return [];
const first = lines[0] ?? "";
const content = first.startsWith("- ") ? first.slice(2) : first.startsWith("* ") ? first.slice(2) : first;
@@ -212,7 +226,11 @@ function normalizeRewriteItem(text: string): string[] {
return out;
}
async function requestRewrite(model: RewriteModel, packageName: string, unreleasedBody: string): Promise<RewrittenSection[]> {
async function requestRewrite(
model: RewriteModel,
packageName: string,
unreleasedBody: string,
): Promise<RewrittenSection[]> {
const userText = `Package: \`${packageName}\`
Original \`[Unreleased]\` section body:
@@ -239,7 +257,9 @@ Consolidate and rewrite this content into user-visible release notes. Keep all p
continue;
}
const call = response.content.find((content): content is ToolCall => content.type === "toolCall" && content.name === "rewrite");
const call = response.content.find(
(content): content is ToolCall => content.type === "toolCall" && content.name === "rewrite",
);
if (!call) {
lastError = "model returned no structured tool call";
continue;
@@ -248,7 +268,6 @@ Consolidate and rewrite this content into user-visible release notes. Keep all p
return validateRewrite(call.arguments);
} catch (error) {
lastError = error instanceof Error ? error.message : String(error);
continue;
}
}
throw new Error(`rewrite call failed for ${packageName}: ${lastError}`);
@@ -278,11 +297,13 @@ interface RunResult {
}
function applyRewrite(section: ReleaseSection, sections: RewrittenSection[]): void {
section.subsections = sections.map(sec => {
const rawLines = sec.items.flatMap(normalizeRewriteItem);
const lines: NumberedLine[] = rawLines.map(text => ({ text, lineNumber: 0 }));
return { title: sec.category, lines };
}).filter(sub => sub.lines.length > 0);
section.subsections = sections
.map(sec => {
const rawLines = sec.items.flatMap(normalizeRewriteItem);
const lines: NumberedLine[] = rawLines.map(text => ({ text, lineNumber: 0 }));
return { title: sec.category, lines };
})
.filter(sub => sub.lines.length > 0);
}
async function run(options: RunOptions): Promise<RunResult> {
@@ -300,41 +321,35 @@ async function run(options: RunOptions): Promise<RunResult> {
const i = pathIndex++;
const changelogPath = paths[i];
if (!changelogPath) continue;
const absolutePath = path.join(repoRoot, changelogPath);
const content = await Bun.file(absolutePath).text();
const document = parseChangelog(content);
const section = unreleasedSection(document);
if (!section) continue;
try {
const absolutePath = path.join(repoRoot, changelogPath);
const content = await Bun.file(absolutePath).text();
const document = parseChangelog(content);
const section = unreleasedSection(document);
if (!section) continue;
const originalCount = section.subsections.reduce((sum, sub) => sum + parseItems(sub.lines).length, 0);
if (originalCount === 0) continue;
const originalCount = section.subsections.reduce((sum, sub) => sum + parseItems(sub.lines).length, 0);
if (originalCount === 0) continue;
const unreleasedBody = renderChangelog({ prefixLines: [], sections: [section] })
.replace(/^## \[Unreleased\]\n?/, "")
.trim();
const unreleasedBody = renderChangelog({ prefixLines: [], sections: [section] })
.replace(/^## \[Unreleased\]\n?/, "")
.trim();
const rewritten = await requestRewrite(model, changelogPath, unreleasedBody);
applyRewrite(section, rewritten);
const next = renderChangelog(document);
if (next === content) continue;
const rewritten = await requestRewrite(model, changelogPath, unreleasedBody);
applyRewrite(section, rewritten);
const next = renderChangelog(document);
if (next === content) continue;
const rewrittenCount = rewritten.reduce((sum, sec) => sum + sec.items.length, 0);
if (options.write) {
await Bun.write(absolutePath, next);
}
results[i] = {
path: changelogPath,
originalCount,
rewrittenCount,
sections: rewritten,
};
} catch (error) {
// Bubble errors from workers
throw error;
const rewrittenCount = rewritten.reduce((sum, sec) => sum + sec.items.length, 0);
if (options.write) {
await Bun.write(absolutePath, next);
}
results[i] = {
path: changelogPath,
originalCount,
rewrittenCount,
sections: rewritten,
};
}
}
@@ -447,4 +462,4 @@ if (import.meta.main) {
await main();
}
export { applyRewrite, collectEntries, run, type RunResult, unreleasedSection, validateRewrite };
export { applyRewrite, collectEntries, type RunResult, run, unreleasedSection, validateRewrite };
+9 -8
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env bun
/**
* Rewrite the natural-language prose of a prompt file into the terse
* implementation-scratchpad voice (see `rewrite-system-prompt.style.md`),
@@ -37,8 +38,8 @@
* --dry-run classify + chunk, print a plan, make no network calls
*/
import { parseArgs } from "node:util";
import * as path from "node:path";
import { parseArgs } from "node:util";
import STYLE_GUIDE from "./rewrite-system-prompt.style.md" with { type: "text" };
const DEFAULT_INPUT = "packages/coding-agent/src/prompts/system/system-prompt.md";
@@ -217,7 +218,7 @@ export function planRewrite(content: string): RewritePlan {
if (isVerbatimLine(line)) continue;
const { prefix, core, suffix } = peel(line);
if (core.trim() === "") continue; // nothing rewritable after peeling
const tokens = Array.from(core.matchAll(FRAGILE_RE), (m) => m[0]);
const tokens = Array.from(core.matchAll(FRAGILE_RE), m => m[0]);
prose.push({ lineIndex: i, prefix, suffix, core, tokens });
}
return { lines, prose };
@@ -284,8 +285,8 @@ export async function rewriteAll(
const groups = chunk(toRewrite, opts.chunkSize);
let done = 0;
const resolved = await mapPool(groups, opts.concurrency, async (group) => {
const items: RewriteItem[] = group.map((e) => ({
const resolved = await mapPool(groups, opts.concurrency, async group => {
const items: RewriteItem[] = group.map(e => ({
id: e.lineIndex,
text: e.core,
tokens: e.tokens,
@@ -296,7 +297,7 @@ export async function rewriteAll(
} catch {
map = new Map();
}
const out = group.map((entry) => {
const out = group.map(entry => {
const candidate = map.get(entry.lineIndex);
return { entry, text: candidate ?? entry.core, ok: candidate != null };
});
@@ -392,7 +393,7 @@ const REWRITE_RESPONSE_FORMAT = {
/** Build a {@link RewriteChunk} backed by an OpenRouter chat-completions endpoint. */
export function makeOpenRouterRewriter(opts: OpenRouterOptions): RewriteChunk {
return async (items) => {
return async items => {
const result = new Map<number, string>();
let pending = items;
let lastErr: unknown;
@@ -406,7 +407,7 @@ export function makeOpenRouterRewriter(opts: OpenRouterOptions): RewriteChunk {
{ role: "system", content: opts.system },
{
role: "user",
content: JSON.stringify({ items: pending.map((p) => ({ id: p.id, text: p.text })) }),
content: JSON.stringify({ items: pending.map(p => ({ id: p.id, text: p.text })) }),
},
],
});
@@ -445,7 +446,7 @@ export function makeOpenRouterRewriter(opts: OpenRouterOptions): RewriteChunk {
}
if (pending.length > 0) {
console.error(
` ${pending.length} line(s) [${pending.map((p) => p.id).join(",")}] kept original: ${String(lastErr ?? "rewrite dropped a token")}`,
` ${pending.length} line(s) [${pending.map(p => p.id).join(",")}] kept original: ${String(lastErr ?? "rewrite dropped a token")}`,
);
}
return result;
+3 -14
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bun
import { $ } from "bun";
import * as path from "node:path";
import { $ } from "bun";
const RUST_AFFECTING_FILE_NAMES = [
"Cargo.toml",
@@ -21,16 +21,7 @@ const TASK_COMMANDS = {
],
"fix:rs": [
["cargo", "fmt", "--all"],
[
"cargo",
"clippy",
"--workspace",
"--fix",
"--allow-dirty",
"--no-deps",
"--allow-staged",
"--allow-no-vcs",
],
["cargo", "clippy", "--workspace", "--fix", "--allow-dirty", "--no-deps", "--allow-staged", "--allow-no-vcs"],
],
"fmt:rs": [["cargo", "fmt", "--all"]],
"lint:rs": [["cargo", "clippy", "--workspace", "--", "-D", "warnings"]],
@@ -112,9 +103,7 @@ function isRustAffectingPath(changedPath: string): boolean {
const normalized = changedPath.replace(/\\/g, "/");
const fileName = normalized.slice(normalized.lastIndexOf("/") + 1);
return (
normalized.endsWith(".rs") ||
normalized.startsWith(".cargo/") ||
isOneOf(fileName, RUST_AFFECTING_FILE_NAMES)
normalized.endsWith(".rs") || normalized.startsWith(".cargo/") || isOneOf(fileName, RUST_AFFECTING_FILE_NAMES)
);
}
+4 -1
View File
@@ -120,7 +120,10 @@ it("scanFile recovers usage, turns, spawns, residency, and pruned result sizes",
},
}),
// second user turn + final answer with no tools
JSON.stringify({ type: "message", message: { role: "user", content: "thanks, also re-read it", timestamp: 4000 } }),
JSON.stringify({
type: "message",
message: { role: "user", content: "thanks, also re-read it", timestamp: 4000 },
}),
asst({
ts: 5000,
usage: { input: 400, output: 40, cacheRead: 600, cost: 2.0 },
+35 -10
View File
@@ -472,7 +472,9 @@ export async function scanFile(filePath: string): Promise<FileScan | undefined>
const name = call?.name ?? (msg.toolName as string) ?? "?";
const textBlob = contentText(msg.content);
const truncated = TRUNCATED_RESULT_RE.exec(textBlob);
const toks = truncated ? Math.max(estTokens(textBlob), Number.parseInt(truncated[1], 10)) : estTokens(textBlob);
const toks = truncated
? Math.max(estTokens(textBlob), Number.parseInt(truncated[1], 10))
: estTokens(textBlob);
const agg = tool(name);
agg.resultToks += toks;
if (msg.isError === true) {
@@ -571,7 +573,7 @@ async function discoverGroups(opts: CliOptions): Promise<DiscoveredGroup[]> {
}
}
for (const [id, main] of mains) {
let childPaths: string[] = [];
const childPaths: string[] = [];
let mtime = main.mtime;
if (subdirs.has(id)) {
const dirPath = path.join(folderPath, id);
@@ -616,7 +618,11 @@ async function scanGroup(d: DiscoveredGroup): Promise<SessionGroup | undefined>
}
/** Run `fn` over `items` with bounded concurrency, preserving order. */
async function mapPool<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]> {
async function mapPool<T, R>(
items: readonly T[],
limit: number,
fn: (item: T, index: number) => Promise<R>,
): Promise<R[]> {
const out = new Array<R>(items.length);
let next = 0;
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
@@ -756,7 +762,8 @@ function buildDigest(g: SessionGroup): string {
.slice(0, 8);
if (repeats.length) {
lines.push(`\n## Repeated reads of the same file (waste signal)`);
for (const [p, r] of repeats) lines.push(`${p} ×${r.count} (~${fmtTok(r.toks)}tok total, ~${fmtTok(r.residency)} residency)`);
for (const [p, r] of repeats)
lines.push(`${p} ×${r.count} (~${fmtTok(r.toks)}tok total, ~${fmtTok(r.residency)} residency)`);
}
if (m.topResults.length) {
@@ -894,7 +901,16 @@ const SESSION_SCHEMA = {
},
headline: { type: "string", description: "one-sentence takeaway for this session" },
},
required: ["score", "multiTopic", "topics", "shouldHaveSplit", "handoffOpportunities", "spawnVerdicts", "waste", "headline"],
required: [
"score",
"multiTopic",
"topics",
"shouldHaveSplit",
"handoffOpportunities",
"spawnVerdicts",
"waste",
"headline",
],
} as const;
interface AggregateFindings {
@@ -1215,9 +1231,12 @@ function printVerdicts(res: AuditResult): void {
for (const g of ordered) {
const v = res.verdicts.get(g.id);
if (!v) continue;
console.log(`\n[${fmtMoney(g.usage.cost)}] "${clip(g.main.title ?? g.id, 60)}" (${g.folder}) — score ${v.score}/10`);
console.log(
`\n[${fmtMoney(g.usage.cost)}] "${clip(g.main.title ?? g.id, 60)}" (${g.folder}) — score ${v.score}/10`,
);
console.log(` ${v.headline}`);
if (v.multiTopic) console.log(` topics: ${v.topics.join(" | ")}${v.shouldHaveSplit ? " → should have split" : ""}`);
if (v.multiTopic)
console.log(` topics: ${v.topics.join(" | ")}${v.shouldHaveSplit ? " → should have split" : ""}`);
for (const h of v.handoffOpportunities) console.log(` handoff: ${h}`);
for (const s of v.spawnVerdicts) {
if (s.verdict === "good") continue;
@@ -1319,7 +1338,9 @@ async function main(): Promise<void> {
const sessionFilter = opts.session?.toLowerCase();
const matched = sessionFilter
? groups.filter(
g => g.id.toLowerCase().includes(sessionFilter) || (g.main.title ?? "").toLowerCase().includes(sessionFilter),
g =>
g.id.toLowerCase().includes(sessionFilter) ||
(g.main.title ?? "").toLowerCase().includes(sessionFilter),
)
: groups.filter(g => g.usage.cost >= opts.minCost);
const candidates = matched.sort((a, b) => b.usage.cost - a.usage.cost).slice(0, opts.maxLlm);
@@ -1365,7 +1386,9 @@ async function main(): Promise<void> {
addUsage(res.classifierUsage, usage);
process.stderr.write(` ✓ ${clip(g.main.title ?? g.id, 50)} (score ${verdict.score})\n`);
} catch (err) {
process.stderr.write(` ✗ ${clip(g.main.title ?? g.id, 50)}: ${err instanceof Error ? err.message : err}\n`);
process.stderr.write(
` ✗ ${clip(g.main.title ?? g.id, 50)}: ${err instanceof Error ? err.message : err}\n`,
);
}
});
await saveVerdictCache(cache);
@@ -1402,7 +1425,9 @@ async function main(): Promise<void> {
headline: v.headline,
topics: v.topics,
shouldHaveSplit: v.shouldHaveSplit,
spawnIssues: v.spawnVerdicts.filter(s => s.verdict !== "good").map(s => ({ label: s.label, verdict: s.verdict })),
spawnIssues: v.spawnVerdicts
.filter(s => s.verdict !== "good")
.map(s => ({ label: s.label, verdict: s.verdict })),
waste: v.waste.map(w => ({ source: w.source, estTokens: w.estTokens, estUsd: round2(w.estUsd) })),
}),
);
+16 -4
View File
@@ -105,7 +105,12 @@ function parseArgs(argv: readonly string[]): Options {
opts.workflow = argv[++i];
break;
case "--only":
opts.only = new Set((argv[++i] ?? "").split(",").map(s => s.trim()).filter(Boolean));
opts.only = new Set(
(argv[++i] ?? "")
.split(",")
.map(s => s.trim())
.filter(Boolean),
);
break;
default:
console.error(`Unknown argument: ${arg}`);
@@ -291,7 +296,10 @@ function placeholderReadme(name: string, target: NativeLeafTarget): string {
async function publishNativeLeafPlaceholder(name: string, target: NativeLeafTarget, repo: string): Promise<boolean> {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-native-placeholder-"));
try {
await Bun.write(path.join(tmpDir, "package.json"), `${JSON.stringify(placeholderManifest(name, target, repo), null, "\t")}\n`);
await Bun.write(
path.join(tmpDir, "package.json"),
`${JSON.stringify(placeholderManifest(name, target, repo), null, "\t")}\n`,
);
await Bun.write(path.join(tmpDir, "README.md"), placeholderReadme(name, target));
return (await npmInteractive(["publish", tmpDir, "--access", "public"])) === 0;
} finally {
@@ -336,7 +344,9 @@ async function main(): Promise<void> {
const workflow = opts.workflow;
if (!(await Bun.file(path.join(repoRoot, ".github", "workflows", workflow)).exists())) {
console.warn(`Warning: .github/workflows/${workflow} not found; npm will still accept it, but OIDC won't match a non-existent workflow.`);
console.warn(
`Warning: .github/workflows/${workflow} not found; npm will still accept it, but OIDC won't match a non-existent workflow.`,
);
}
if (opts.dryRun) {
@@ -369,7 +379,9 @@ async function main(): Promise<void> {
}
console.log("The first mutating npm operation triggers 2FA. When prompted, complete it and choose");
console.log("'skip 2FA for the next 5 minutes' on the npm site so placeholder publishes and trust setup run unattended.\n");
console.log(
"'skip 2FA for the next 5 minutes' on the npm site so placeholder publishes and trust setup run unattended.\n",
);
const outcomes = new Map<string, Outcome>();
let bootstrapped = 0;
+1 -1
View File
@@ -12,7 +12,7 @@ const INDEX_FILE = join(THEMES_DIR, "index.ts");
async function main() {
const files = await readdir(THEMES_DIR);
const jsonFiles = files.filter((f) => f.endsWith(".json")).sort();
const jsonFiles = files.filter(f => f.endsWith(".json")).sort();
const imports: string[] = [];
const exportEntries: string[] = [];
+5 -4
View File
@@ -22,8 +22,8 @@ interface PackageInfo {
const packagesDir = join(process.cwd(), "packages");
const packageDirs = readdirSync(packagesDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
// Read all package.json files and build version map
const packages: Record<string, PackageInfo> = {};
@@ -61,7 +61,8 @@ console.log("\n✅ All packages at same version (lockstep)");
// Update all inter-package dependencies
let totalUpdates = 0;
for (const [dir, pkg] of Object.entries(packages)) {
for (const dir in packages) {
const pkg = packages[dir];
let updated = false;
// Check dependencies
@@ -98,7 +99,7 @@ for (const [dir, pkg] of Object.entries(packages)) {
// Write if updated
if (updated) {
await Bun.write(pkg.path, JSON.stringify(pkg.data, null, "\t") + "\n");
await Bun.write(pkg.path, `${JSON.stringify(pkg.data, null, "\t")}\n`);
}
}
+12 -6
View File
@@ -86,7 +86,7 @@ function parseEncoding(value: string | undefined): Encoding {
const normalized = value.toLowerCase().replace(/-/g, "_");
if (normalized === "o200k" || normalized === "o200k_base") return Encoding.O200kBase;
if (normalized === "cl100k" || normalized === "cl100k_base") return Encoding.Cl100kBase;
throw new Error(`Unknown encoding \"${value}\". Expected o200k_base or cl100k_base.`);
throw new Error(`Unknown encoding "${value}". Expected o200k_base or cl100k_base.`);
}
function parseCli(): CliOptions | null {
@@ -116,7 +116,9 @@ function relativePath(filePath: string): string {
async function collectPromptPaths(positionals: readonly string[]): Promise<string[]> {
if (positionals.length === 0) {
const files = await Array.fromAsync(new Bun.Glob("*.md").scan({ cwd: TOOL_PROMPT_DIR, absolute: true, onlyFiles: true }));
const files = await Array.fromAsync(
new Bun.Glob("*.md").scan({ cwd: TOOL_PROMPT_DIR, absolute: true, onlyFiles: true }),
);
return files.sort((a, b) => relativePath(a).localeCompare(relativePath(b)));
}
@@ -202,8 +204,6 @@ function renderContext(): Record<string, unknown> {
};
}
async function estimatePrompt(filePath: string, encoding: Encoding): Promise<PromptEstimate> {
const template = await Bun.file(filePath).text();
const rendered = prompt.render(template, renderContext());
@@ -221,7 +221,11 @@ function printTable(estimates: PromptEstimate[], encoding: Encoding): void {
const totalTokens = rows.reduce((sum, row) => sum + row.tokens, 0);
const totalChars = rows.reduce((sum, row) => sum + row.chars, 0);
const totalLines = rows.reduce((sum, row) => sum + row.lines, 0);
const tokenWidth = Math.max("tokens".length, String(totalTokens).length, ...rows.map(row => String(row.tokens).length));
const tokenWidth = Math.max(
"tokens".length,
String(totalTokens).length,
...rows.map(row => String(row.tokens).length),
);
const charWidth = Math.max("chars".length, String(totalChars).length, ...rows.map(row => String(row.chars).length));
const lineWidth = Math.max("lines".length, String(totalLines).length, ...rows.map(row => String(row.lines).length));
@@ -237,7 +241,9 @@ function printTable(estimates: PromptEstimate[], encoding: Encoding): void {
);
}
console.log(`${"-".repeat(tokenWidth)} ${"-".repeat(charWidth)} ${"-".repeat(lineWidth)} ${"-".repeat(6)}`);
console.log(`${String(totalTokens).padStart(tokenWidth)} ${String(totalChars).padStart(charWidth)} ${String(totalLines).padStart(lineWidth)} TOTAL (${rows.length} prompts)`);
console.log(
`${String(totalTokens).padStart(tokenWidth)} ${String(totalChars).padStart(charWidth)} ${String(totalLines).padStart(lineWidth)} TOTAL (${rows.length} prompts)`,
);
}
async function run(): Promise<void> {
+3 -3
View File
@@ -10,20 +10,20 @@ Bun.plugin({
name: "trace-loader",
setup(build) {
// Trace module resolution (doesn't interfere with loading)
build.onResolve({ filter: /.*/ }, (args) => {
build.onResolve({ filter: /.*/ }, args => {
// Skip if already traced this path
if (resolved.has(args.path)) {
return undefined;
}
resolved.add(args.path);
const elapsed = ((Bun.nanoseconds() - startTime) / 1e6).toFixed(1);
// Only trace local/project files, not node_modules
if (!args.path.includes("node_modules") && !args.path.startsWith("node:")) {
const shortPath = args.path.replace(process.cwd(), ".");
process.stderr.write(`[${elapsed}ms] resolve: ${shortPath}\n`);
}
// Return undefined to let Bun handle resolution normally
return undefined;
});