From c994ad643bc59e8348c6059b7a56379b8a1f6afc Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 26 Jun 2026 18:42:07 -0500 Subject: [PATCH 1/2] Add standalone Bun release archive packaging --- .gitignore | 1 + AGENTS.md | 1 + CHANGELOG.md | 9 +++ README.md | 28 ++++++- docs/configuration.md | 6 +- docs/usage.md | 15 +++- package.json | 3 +- scripts/build-bun.mjs | 11 +++ scripts/package-bun.mjs | 109 +++++++++++++++++++++++++++ skills/agent-pack/SKILL.md | 81 ++++++++++++++++++++ skills/agent-pack/agents/openai.yaml | 7 ++ src/cli/agent-pack.ts | 2 +- test/smoke/cli-git-smoke.test.ts | 36 ++++++++- 13 files changed, 300 insertions(+), 9 deletions(-) create mode 100644 scripts/package-bun.mjs create mode 100644 skills/agent-pack/SKILL.md create mode 100644 skills/agent-pack/agents/openai.yaml diff --git a/.gitignore b/.gitignore index 76eef74..6dadd4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ dist/ dist-bin/ +dist-release/ coverage/ .agent-pack/ *.log diff --git a/AGENTS.md b/AGENTS.md index bf0f3b4..04a55bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,3 +46,4 @@ This file is a lightweight internal onboarding note for agents working in this r - Run `npm run check`. - Run `node scripts/release.mjs patch`, `minor`, or `major` from `main`. - The release script bumps versions, promotes changelog entries, tags, pushes, creates a standard GitHub release, and opens a fresh `## [Unreleased]` section. +- Standalone Bun archives are not built by `scripts/release.mjs`; build and upload them after the GitHub release exists with `npm run package:bun -- --all` and `gh release upload "v${VERSION}" dist-release/agent-pack-"${VERSION}"-*.tar.gz`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c2f91..e9f3293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,17 @@ ### Added +- Added `npm run package:bun` for building standalone Bun release archives that + include the executable, README, docs, and examples. +- Added a packaged `agent-pack` skill that teaches agents how to run catalog + packs, follow briefs, and update task state. + ### Changed +- Changed standalone Bun help to print adjacent packaged resource paths when + README, docs, and examples are distributed beside the executable. +- Changed npm and standalone release packaging to include top-level `skills/`. + ### Fixed ### Removed diff --git a/README.md b/README.md index 291c56d..0d096c6 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,28 @@ npm run build:bun ./dist-bin/agent-pack --help ``` -The Bun build is not part of the npm release flow. It writes a local executable to `dist-bin/agent-pack` for manual copying. Because the executable is a single file, top-level help omits npm package resource paths such as README, usage docs, and examples. +The Bun build is not part of the npm release flow. It writes a local executable +to `dist-bin/agent-pack` for manual copying. To create a standalone release +archive that includes the executable, README, usage docs, and examples: + +```bash +npm run package:bun -- --platform linux-x64 +``` + +The archive is written to `dist-release/agent-pack-VERSION-PLATFORM.tar.gz`. +Use `--platform macos-arm64`, `--platform macos-x64`, `--platform linux-arm64`, +or `--all` to build additional assets. Packaged standalone help prints resource +paths when `README.md`, `docs/usage.md`, and `examples/` are beside the +executable. Release archives also include `skills/`. + +After `node scripts/release.mjs patch` creates the GitHub release, upload +standalone archives with: + +```bash +VERSION="$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")" +npm run package:bun -- --all +gh release upload "v${VERSION}" dist-release/agent-pack-"${VERSION}"-*.tar.gz +``` `agent-pack` works with any agent CLI or editor agent that can read a text prompt and run shell commands in your workspace. The examples use POSIX shell syntax; on Windows PowerShell, use backticks for line continuations or write commands on one line, and prefer double-quoted globs such as `"./docs/**/*.md"`. @@ -56,7 +77,9 @@ agent-pack init \ "Run the demo task and record evidence." ``` -> On a standalone Bun executable, top-level help omits resource paths, so `EXAMPLES_DIR` resolves empty. Use a real checkout instead and pass `--manifest ./examples/manifests/demo.yaml`. +> On a standalone Bun executable copied by itself, top-level help omits resource +> paths, so `EXAMPLES_DIR` resolves empty. Use a packaged standalone archive or +> a real checkout and pass `--manifest ./examples/manifests/demo.yaml`. Expected output: @@ -176,5 +199,6 @@ Each concept is explained in full in [docs/concepts.md](docs/concepts.md). | [docs/brief-format.md](docs/brief-format.md) | The exact brief, summary, report, and `task show` output — the agent contract | Agents and authors who need the rendered output spec | | [docs/usage.md](docs/usage.md) | Compact installed cheat sheet linking into the canonical docs | Installed users who want a quick reference | | [examples/](examples/) | Ready-made catalog: 12 manifests, 4 agent files, and 2 task files | Anyone trying packaged workflows by bare catalog name | +| [skills/](skills/) | Packaged skills, including an explicit-invocation `agent-pack` skill | Agent environments that can install bundled skills | The [examples/](examples/) directory is laid out as a catalog root. Point `AGENT_PACK_CONFIG_DIR` at it (or at a real checkout) to use the packaged manifests, agents, and tasks by bare name; see [docs/configuration.md](docs/configuration.md). diff --git a/docs/configuration.md b/docs/configuration.md index 5801cf8..fabdc5f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -133,7 +133,11 @@ EXAMPLES_DIR="$(agent-pack --help | sed -n 's/^[[:space:]]*Examples[[:space:]][[ AGENT_PACK_CONFIG_DIR="$EXAMPLES_DIR" agent-pack init --manifest code-review "Review scope: unstaged changes." ``` -Standalone Bun executables do not include these package resource paths, so the `agent-pack --help` examples path is empty there. To use the examples with a copied executable, point `AGENT_PACK_CONFIG_DIR` at a real `examples/` checkout or another catalog directory. +Standalone Bun executables only print package resource paths when the resources +are present beside the executable. The release archive includes them; a copied +single executable does not. To use the examples by bare catalog name, point +`AGENT_PACK_CONFIG_DIR` at the printed `examples/` path, a real `examples/` +checkout, or another catalog directory. ## State & Portability diff --git a/docs/usage.md b/docs/usage.md index ac6f0ac..73ffd3e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -8,7 +8,8 @@ Run commands from the repository or workspace that contains the files you want t - Node.js 20 or newer (a packaging requirement; the CLI does not verify the Node version) - Git and `tar` on `PATH` for git-backed inputs -- Bun when manually building a standalone executable with `npm run build:bun` +- Bun when manually building or packaging a standalone executable with + `npm run build:bun` or `npm run package:bun` ## Quick start @@ -22,7 +23,10 @@ agent-pack init \ "Run the demo task and record the result." ``` -On a standalone executable the `EXAMPLES_DIR` trick yields an empty value, because compiled-Bun help omits resource paths. Use a real checkout instead, for example `./examples/manifests/demo.yaml`. +On a standalone executable copied by itself, the `EXAMPLES_DIR` trick yields an +empty value because compiled-Bun help only prints adjacent packaged resources. +Use a packaged standalone archive or a real checkout, for example +`./examples/manifests/demo.yaml`. Set the generated pack id, then read the brief: @@ -102,6 +106,8 @@ The npm package includes an `examples/` directory with reusable files for common - 12 manifests under `examples/manifests/`, including `demo`, `code-review`, `docs-review`, `design-review`, `feature-design-summary`, `architecture-review`, `bug-investigation`, `codebase-onboarding`, `dependency-audit`, `refactor-execution`, `security-review`, and `testing-audit`. - 4 agent files under `examples/agents/`: `claude.yaml`, `claude-exec.yaml`, `codex.yaml`, `codex-exec.yaml`. - 2 task files under `examples/tasks/`: `findings-synthesis.yaml`, `review-gate.yaml`. +- 1 skill under `skills/agent-pack/`: an explicit-invocation skill for agents + that need to run catalog packs, follow briefs, and work through tasks. Create a code-review pack: @@ -152,7 +158,10 @@ EXAMPLES_DIR="$(agent-pack --help | sed -n 's/^[[:space:]]*Examples[[:space:]][[ AGENT_PACK_CONFIG_DIR="$EXAMPLES_DIR" agent-pack init --manifest code-review "Review scope: unstaged changes." ``` -On a standalone Bun executable the help examples path is empty, so point `AGENT_PACK_CONFIG_DIR` at a real `examples/` checkout or another catalog directory instead. See [configuration.md](configuration.md) for the catalog layout. +On a standalone Bun executable copied by itself the help examples path is empty. +Use a packaged standalone archive, point `AGENT_PACK_CONFIG_DIR` at a real +`examples/` checkout, or use another catalog directory instead. See +[configuration.md](configuration.md) for the catalog layout. Example agent model names (such as `claude-opus-4-7` or `gpt-5.5`) and backend flags (such as `--effort`) shown in the shipped agent files are illustrative only; the parser does not validate them. Backend-specific flags (model, effort, and so on) go in an agent's `args`. See [authoring.md](authoring.md) for the agent file schema. diff --git a/package.json b/package.json index 2e717d4..e532e2f 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "bin": { "agent-pack": "dist/cli/main.js" }, - "files": ["dist", "README.md", "docs", "examples"], + "files": ["dist", "README.md", "docs", "examples", "skills"], "scripts": { "build": "tsc -p tsconfig.json && node scripts/chmod-bin.mjs", "build:bun": "node scripts/build-bun.mjs", + "package:bun": "node scripts/package-bun.mjs", "typecheck": "tsc -p tsconfig.test.json --noEmit", "test": "vitest run test/unit test/integration", "test:smoke": "npm run build && vitest run test/smoke", diff --git a/scripts/build-bun.mjs b/scripts/build-bun.mjs index 10b15d4..ad18a82 100644 --- a/scripts/build-bun.mjs +++ b/scripts/build-bun.mjs @@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url"; const args = process.argv.slice(2); let outfile = path.join("dist-bin", "agent-pack"); +let target; for (let index = 0; index < args.length; index += 1) { const arg = args[index]; @@ -18,6 +19,15 @@ for (let index = 0; index < args.length; index += 1) { index += 1; continue; } + if (arg === "--target") { + const value = args[index + 1]; + if (!value) { + throw new Error("--target requires a Bun compile target"); + } + target = value; + index += 1; + continue; + } throw new Error(`unsupported argument: ${arg}`); } @@ -43,6 +53,7 @@ execFileSync( "--compile", "--define", `__AGENT_PACK_VERSION__=${JSON.stringify(pkg.version)}`, + ...(target ? [`--target=${target}`] : []), "--outfile", absoluteOutfile, "src/cli/main.ts", diff --git a/scripts/package-bun.mjs b/scripts/package-bun.mjs new file mode 100644 index 0000000..364fb46 --- /dev/null +++ b/scripts/package-bun.mjs @@ -0,0 +1,109 @@ +#!/usr/bin/env node +import { execFileSync } from "node:child_process"; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const platformTargets = new Map([ + ["linux-x64", "bun-linux-x64"], + ["linux-arm64", "bun-linux-arm64"], + ["macos-arm64", "bun-darwin-arm64"], + ["macos-x64", "bun-darwin-x64"], +]); + +const args = process.argv.slice(2); +let outdir = "dist-release"; +const platforms = []; + +for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + if (arg === "--outdir") { + const value = args[index + 1]; + if (!value) { + throw new Error("--outdir requires a path"); + } + outdir = value; + index += 1; + continue; + } + if (arg === "--platform") { + const value = args[index + 1]; + if (!value) { + throw new Error("--platform requires a platform"); + } + platforms.push(value); + index += 1; + continue; + } + if (arg === "--all") { + platforms.push(...platformTargets.keys()); + continue; + } + throw new Error(`unsupported argument: ${arg}`); +} + +if (platforms.length === 0) { + platforms.push(currentPlatform()); +} + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const absoluteOutdir = path.isAbsolute(outdir) ? outdir : path.resolve(process.cwd(), outdir); +const stageParent = path.join(repoRoot, ".agent-pack", "release-stage"); +const pkg = JSON.parse(readFileSync(path.join(repoRoot, "package.json"), "utf8")); +if (pkg.name !== "@kcosr/agent-pack") { + throw new Error(`unexpected package name: ${pkg.name}`); +} +if (typeof pkg.version !== "string" || !pkg.version) { + throw new Error("package.json version must be a non-empty string"); +} + +mkdirSync(absoluteOutdir, { recursive: true }); +mkdirSync(stageParent, { recursive: true }); + +const uniquePlatforms = [...new Set(platforms)]; +for (const platform of uniquePlatforms) { + const target = platformTargets.get(platform); + if (!target) { + throw new Error(`unsupported platform: ${platform}`); + } + + const archiveRoot = `agent-pack-${pkg.version}-${platform}`; + const archivePath = path.join(absoluteOutdir, `${archiveRoot}.tar.gz`); + const stage = mkdtempSync(path.join(stageParent, "package-bun-")); + + try { + const root = path.join(stage, archiveRoot); + mkdirSync(root, { recursive: true }); + + execFileSync( + process.execPath, + ["scripts/build-bun.mjs", "--outfile", path.join(root, "agent-pack"), "--target", target], + { cwd: repoRoot, stdio: "inherit" }, + ); + + for (const entry of ["README.md", "LICENSE", "CHANGELOG.md", "docs", "examples", "skills"]) { + cpSync(path.join(repoRoot, entry), path.join(root, entry), { recursive: true }); + } + + execFileSync("tar", ["-C", stage, "-czf", archivePath, archiveRoot], { stdio: "inherit" }); + console.log(archivePath); + } finally { + rmSync(stage, { recursive: true, force: true }); + } +} + +function currentPlatform() { + if (process.platform === "linux" && process.arch === "x64") { + return "linux-x64"; + } + if (process.platform === "linux" && process.arch === "arm64") { + return "linux-arm64"; + } + if (process.platform === "darwin" && process.arch === "arm64") { + return "macos-arm64"; + } + if (process.platform === "darwin" && process.arch === "x64") { + return "macos-x64"; + } + throw new Error(`unsupported current platform: ${process.platform}-${process.arch}`); +} diff --git a/skills/agent-pack/SKILL.md b/skills/agent-pack/SKILL.md new file mode 100644 index 0000000..5ac7ed5 --- /dev/null +++ b/skills/agent-pack/SKILL.md @@ -0,0 +1,81 @@ +--- +name: agent-pack +description: Use only when explicitly requested to run or follow an agent-pack workflow, pack, catalog manifest, brief, or task list. Helps an agent execute an existing agent-pack catalog workflow, read the generated brief, and update task status while working through the pack. +--- + +# Agent Pack + +Use `agent-pack` to run an existing workflow from a catalog manifest and work through the generated brief. Do not author new manifests unless the user explicitly asks for that; normally the user provides a command like `agent-pack init --manifest ...` or names the pack to run. + +## Basic Flow + +If the user provides an exact command, run it. Otherwise use the named catalog manifest: + +```bash +agent-pack init --manifest "User's requested work scope." +``` + +After `init`, run the brief command printed by the CLI, usually: + +```bash +agent-pack brief --id +``` + +Follow the brief. Keep task state current as work proceeds: + +```bash +agent-pack task list --id +agent-pack task show --id +agent-pack task start --id --note "Started." +agent-pack task note --id "Evidence or decision." +agent-pack task done --id --note "Completion evidence." +agent-pack task block --id --note "Specific blocker." +``` + +Use `task show` before working a task when the brief is compact or the task details matter. Use `task note` for evidence that should survive context loss. Mark a task `blocked` only when progress really requires user input or external change. + +## Useful Discovery + +To see where `agent-pack` is reading config and state: + +```bash +agent-pack status +agent-pack status --json +``` + +To get the current config/catalog directory: + +```bash +CONFIG_DIR="$(agent-pack status --json | node -e 'console.log(JSON.parse(require("fs").readFileSync(0, "utf8")).configDir)')" +``` + +To inspect available workflows: + +```bash +agent-pack catalog list --type manifest +agent-pack catalog show manifest +``` + +If packaged examples are configured as the catalog, these manifests may be available: + +- `architecture-review` +- `bug-investigation` +- `code-review` +- `codebase-onboarding` +- `demo` +- `dependency-audit` +- `design-review` +- `docs-review` +- `feature-design-summary` +- `refactor-execution` +- `security-review` +- `testing-audit` + +Treat that list as a convenience hint, not a source of truth. Always prefer `agent-pack catalog list --type manifest` for the actual configured environment. + +If packaged examples are present beside the installed package or standalone archive, `agent-pack --help` prints their path. To use those examples by bare catalog name for one command: + +```bash +EXAMPLES_DIR="$(agent-pack --help | sed -n 's/^[[:space:]]*Examples[[:space:]][[:space:]]*//p')" +AGENT_PACK_CONFIG_DIR="$EXAMPLES_DIR" agent-pack catalog list --type manifest +``` diff --git a/skills/agent-pack/agents/openai.yaml b/skills/agent-pack/agents/openai.yaml new file mode 100644 index 0000000..57f27da --- /dev/null +++ b/skills/agent-pack/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Agent Pack" + short_description: "Run agent-pack catalog workflows" + default_prompt: "Use $agent-pack to run the specified catalog pack and follow its brief." + +policy: + allow_implicit_invocation: false diff --git a/src/cli/agent-pack.ts b/src/cli/agent-pack.ts index afc73ba..187cedd 100644 --- a/src/cli/agent-pack.ts +++ b/src/cli/agent-pack.ts @@ -939,7 +939,7 @@ function packageHelpText(): string { function packageResourceRoot(): string | undefined { if (isCompiledBun()) { - return undefined; + return path.dirname(process.execPath); } return packageRoot(); } diff --git a/test/smoke/cli-git-smoke.test.ts b/test/smoke/cli-git-smoke.test.ts index 955e6fa..a186235 100644 --- a/test/smoke/cli-git-smoke.test.ts +++ b/test/smoke/cli-git-smoke.test.ts @@ -1,6 +1,6 @@ import { execFileSync } from "node:child_process"; import { existsSync } from "node:fs"; -import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises"; +import { cp, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; @@ -118,6 +118,40 @@ describe("agent-pack CLI git smoke", () => { expect(report).toContain("- t001 [pending] Inspect Bun binary"); }); + bunIt("prints adjacent package resources from a standalone Bun executable", async () => { + const scratchRoot = path.resolve(".agent-pack", "smoke"); + await mkdir(scratchRoot, { recursive: true }); + const workspace = await mkdtemp(path.join(scratchRoot, "bun-bin-resources-")); + const binPath = path.join(workspace, "agent-pack"); + + execFileSync(process.execPath, ["scripts/build-bun.mjs", "--outfile", binPath], { + cwd: path.resolve("."), + stdio: "pipe", + }); + await cp(path.resolve("README.md"), path.join(workspace, "README.md")); + await mkdir(path.join(workspace, "docs"), { recursive: true }); + await cp(path.resolve("docs/usage.md"), path.join(workspace, "docs/usage.md")); + await cp(path.resolve("examples"), path.join(workspace, "examples"), { recursive: true }); + + const help = execFileSync(binPath, ["--help"], { + cwd: workspace, + encoding: "utf8", + env: { + ...process.env, + AGENT_PACK_CACHE_DIR: path.join(workspace, ".agent-pack/cache"), + AGENT_PACK_CONFIG_DIR: undefined, + AGENT_PACK_GIT_REFRESH: undefined, + AGENT_PACK_ID: undefined, + AGENT_PACK_STATE_DIR: undefined, + }, + }); + + expect(help).toContain("Resources:"); + expect(help).toContain(path.join(workspace, "README.md")); + expect(help).toContain(path.join(workspace, "docs/usage.md")); + expect(help).toContain(path.join(workspace, "examples")); + }); + it("uses packaged examples as a catalog root", async () => { const workspace = await mkdtemp(path.join(os.tmpdir(), "agent-pack-examples-catalog-smoke-")); const env = { AGENT_PACK_CONFIG_DIR: path.resolve("examples") }; From 77c3816981bf8b6d31f453e94fe2215f49800b29 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 26 Jun 2026 19:03:10 -0500 Subject: [PATCH 2/2] remove demo from skill --- skills/agent-pack/SKILL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/skills/agent-pack/SKILL.md b/skills/agent-pack/SKILL.md index 5ac7ed5..36cc713 100644 --- a/skills/agent-pack/SKILL.md +++ b/skills/agent-pack/SKILL.md @@ -62,7 +62,6 @@ If packaged examples are configured as the catalog, these manifests may be avail - `bug-investigation` - `code-review` - `codebase-onboarding` -- `demo` - `dependency-audit` - `design-review` - `docs-review`