57f54e37c5
* add bundled git-archaeology skill, auto-installed for opencode and claude ships a SKILL.md teaching agents the underused git history primitives (pickaxe -S/-G, -L for function/line ranges, --reverse blame, deleted-file recovery) so they stop scrolling git log -p when blame comes up empty. introduces a lightweight bundled-skill path alongside the existing addSkill (npx skills add) flow used for external skills like agent-browser. SKILL.md is inlined into dist/cli.mjs via esbuild's text loader and written to <home>/.agents/skills/<name>/SKILL.md at runtime — no network, no version drift, no per-run install cost. * fix: register vitest plugin to load .md as text for bundled-skill tests * fix: drop vite type import from vitest plugin (vite isn't a direct dep) * fix: load bundled skills via readFileSync so source mode works esbuild's text loader only applies to the npm-bundled dist/cli.mjs path. the preview / oss path runs cli.ts directly with node (PULLFROG_FORCE_LOCAL_CLI=1 in runCli.ts#runLocalCli), where node has no idea how to import .md files — ERR_UNKNOWN_FILE_EXTENSION crashes the action before any agent starts. switch to runtime readFileSync that checks both candidate locations: - source mode: <actionRoot>/skills/<name>/SKILL.md (relative to utils/skills.ts) - bundled mode: <distDir>/skills/<name>/SKILL.md (esbuild copies the tree) drops the no-longer-needed esbuild text loader, vitest .md plugin, and ambient *.md type declaration. wiki/skills.md updated with the why. * fix: write bundled skills to per-agent dirs so claude actually registers them
105 lines
3.2 KiB
JavaScript
105 lines
3.2 KiB
JavaScript
// @ts-check
|
|
|
|
import { build } from "esbuild";
|
|
import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
|
|
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
|
|
rmSync("./dist", { recursive: true, force: true });
|
|
mkdirSync("./dist", { recursive: true });
|
|
|
|
// Plugin to strip shebangs from output files
|
|
/**
|
|
* @type {import("esbuild").Plugin}
|
|
*/
|
|
const stripShebangPlugin = {
|
|
name: "strip-shebang",
|
|
setup(build) {
|
|
build.onEnd((result) => {
|
|
if (result.errors.length > 0) return;
|
|
|
|
// Strip shebang from the output file
|
|
const outputFile = build.initialOptions.outfile;
|
|
if (outputFile) {
|
|
try {
|
|
const content = readFileSync(outputFile, "utf8");
|
|
// Remove shebang line from the beginning if present
|
|
const withoutShebang = content.startsWith("#!")
|
|
? content.slice(content.indexOf("\n") + 1)
|
|
: content;
|
|
writeFileSync(outputFile, withoutShebang);
|
|
} catch (error) {
|
|
// File might not exist, ignore
|
|
}
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
/**
|
|
* @type {import("esbuild").BuildOptions}
|
|
*/
|
|
const sharedConfig = {
|
|
bundle: true,
|
|
format: "esm",
|
|
platform: "node",
|
|
target: "node24",
|
|
minify: false,
|
|
sourcemap: false,
|
|
// Bundle all dependencies - GitHub Actions doesn't have node_modules
|
|
// Only mark optional peer dependencies as external
|
|
external: [
|
|
"@valibot/to-json-schema",
|
|
"effect",
|
|
"sury",
|
|
],
|
|
// Provide a proper require shim for CommonJS modules bundled into ESM
|
|
// We use a unique variable name to avoid conflicts with bundled imports
|
|
banner: {
|
|
js: `import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __dirnameFn } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __dirnameFn(__filename);`,
|
|
},
|
|
// Enable tree-shaking to remove unused code
|
|
treeShaking: true,
|
|
// Drop console statements in production (but keep for debugging)
|
|
drop: [],
|
|
};
|
|
|
|
// Build the CLI bundle (published to npm, used by npx)
|
|
await build({
|
|
...sharedConfig,
|
|
entryPoints: ["./cli.ts"],
|
|
outfile: "./dist/cli.mjs",
|
|
target: "node20",
|
|
plugins: [stripShebangPlugin],
|
|
define: {
|
|
"process.env.CLI_VERSION": JSON.stringify(pkg.version),
|
|
},
|
|
});
|
|
|
|
// Build ESM library entrypoints for programmatic imports
|
|
await build({
|
|
...sharedConfig,
|
|
entryPoints: ["./index.ts"],
|
|
outfile: "./dist/index.js",
|
|
target: "node20",
|
|
});
|
|
|
|
await build({
|
|
...sharedConfig,
|
|
entryPoints: ["./internal/index.ts"],
|
|
outfile: "./dist/internal.js",
|
|
target: "node20",
|
|
});
|
|
|
|
// prepend shebang after strip (esbuild banner can't guarantee line 1 placement)
|
|
const cliPath = "./dist/cli.mjs";
|
|
const cliContent = readFileSync(cliPath, "utf8");
|
|
writeFileSync(cliPath, `#!/usr/bin/env node\n${cliContent}`);
|
|
|
|
// copy bundled SKILL.md files into dist/ so the npm-published runtime can read
|
|
// them via readFileSync. source-mode runs (PULLFROG_FORCE_LOCAL_CLI=1) read
|
|
// directly from action/skills/ instead. see utils/skills.ts.
|
|
cpSync("./skills", "./dist/skills", { recursive: true });
|
|
|
|
console.log("» build completed successfully");
|