gen-agents-digest.ts
95 lines · scripts/gen-agents-digest.ts at 3f79d81
1#!/usr/bin/env -S bun --no-env-file --config=/dev/null
2/**
3 * Generate agents-digest/gstack-AGENTS.md — the instruction-only tier.
4 *
5 * A ~2KB behavioral digest any rules-reading agent host can load (Zed, Amp,
6 * Jules, Cursor side-projects, OpenClaw/Hermes methodology mode): the ETHOS
7 * principles as one-liners, the reuse ladder, the voice rules, and a pointer
8 * to the full install. It is a COMMITTED generated artifact (like llms.txt)
9 * so the setup explainer arms can point at it before any toolchain exists.
10 *
11 * Delivery is print-path + user-performed copy ONLY — never write or
12 * overwrite a user's own AGENTS.md (a gstack-AGENTS.md is auto-read by no
13 * host; clobbering AGENTS.md destroys user content on every re-run).
14 *
15 * One host-neutral file, deliberately: the digest contains no host-specific
16 * paths, so per-host copies would be byte-identical duplication (the exact
17 * per-host rule-text sprawl gstack's generator model exists to avoid). If a
18 * host ever needs rewrites, this generator gains a per-host emit then.
19 *
20 * Size budget: <= 2,048 bytes, enforced by test/agents-digest.test.ts. Every
21 * host that loads this pays for it in every session — trim, don't grow.
22 *
23 * Refresh: invoked from scripts/gen-skill-docs.ts after SKILL.md generation.
24 */
26import * as fs from 'fs';
27import * as path from 'path';
29const ROOT = path.resolve(import.meta.dir, '..');
30// Repo-relative with FORWARD slashes on every platform: this constant is
31// compared against literals in shell scripts, host configs, and docs (the
32// wiring test), where a Windows path.join backslash form would never match.
33// path.join(root, DIGEST_RELPATH) at the write site normalizes it fine.
34export const DIGEST_RELPATH = 'agents-digest/gstack-AGENTS.md';
35export const DIGEST_BYTE_BUDGET = 2048;
37export function generateAgentsDigest(opts?: { root?: string }): { content: string; bytes: number } {
38 const root = opts?.root ?? ROOT;
39 const version = fs.readFileSync(path.join(root, 'VERSION'), 'utf-8').trim();
41 const content = `# gstack digest v${version} — regenerate/re-copy after upgrading gstack
43Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
44for agent hosts without a full skill install. The full skills add workflows,
45reviews, and evals on top of these rules.
47## Ethos
49- **Boil the Ocean** — complete coverage, minimal code: cover the whole requirement (tests, edge cases, error paths) and write no code beyond it. Shortcuts need an explicit, recorded decision.
50- **Search Before Building** — know what exists before deciding what to build. Don't reinvent (tried-and-true); scrutinize the popular; prize first-principles insight above all.
51- **User Sovereignty** — models recommend, the user decides. Cross-model agreement is signal, never permission. Ask before changing the user's stated direction.
52- **Build for Yourself** — the specificity of a real problem beats the generality of a hypothetical one.
54## The reuse ladder
56Before writing new code, stop at the first rung that holds:
571. A helper, util, or pattern already in this repo.
582. The standard library.
593. A native platform feature (CSS over JS, DB constraint over app code).
604. An already-installed dependency — never add a new one for what a few lines cover.
62Then build the complete version of what remains. Bug fixes hit root cause,
63not symptom: one guard in the shared function beats a guard in every caller.
65## Voice
67Direct, concrete, builder-to-builder. Name the file, function, command, and
68user-visible impact. Short paragraphs; end with what to do. No filler, no
69corporate tone, no AI vocabulary.
71## Full gstack
73Clone https://github.com/garrytan/gstack and run \`./setup\` for the full
74skill suite (reviews, ship, QA, evals). This digest is generated — edit
75scripts/gen-agents-digest.ts, not this file.
76`;
78 return { content, bytes: Buffer.byteLength(content, 'utf-8') };
79}
81export function writeAgentsDigest(opts?: { root?: string; outRoot?: string }): { outPath: string; bytes: number } {
82 const root = opts?.root ?? ROOT;
83 const { content, bytes } = generateAgentsDigest({ root });
84 // outRoot: outputs-only rule for --out-dir renders — inputs (VERSION) read
85 // from root, the artifact lands wherever the caller isolates outputs.
86 const outPath = path.join(opts?.outRoot ?? root, DIGEST_RELPATH);
87 fs.mkdirSync(path.dirname(outPath), { recursive: true });
88 fs.writeFileSync(outPath, content);
89 return { outPath, bytes };
90}
92if (import.meta.main) {
93 const { outPath, bytes } = writeAgentsDigest();
94 console.log(`[gen-agents-digest] ${path.relative(ROOT, outPath)}: ${bytes} bytes (budget ${DIGEST_BYTE_BUDGET})`);
95}