mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-30 18:00:39 +02:00
feat(hosts): instruction-only tier — a 2KB committed rules digest any agent host can read
New agents-digest/gstack-AGENTS.md (1,765 bytes, hard 2,048-byte budget): gstack's ethos one-liners, the reuse ladder, and voice rules for hosts with no install arm — Zed, Amp, Jules, or any AGENTS.md-reading agent. Generated by scripts/gen-agents-digest.ts, auto-refreshed by gen:skill-docs, committed like llms.txt so setup's explainer arms can point at it before any toolchain exists. First line carries the gstack version as its own staleness nudge. Delivery is print-path + user-performed copy ONLY: setup never writes or overwrites a user's AGENTS.md (a test pins this — no cp/ln/mv/redirect into AGENTS.md anywhere in setup). openclaw and hermes explainer arms print the path; slate keeps routing to the full Claude install and gbrain ships from its own repo. HostConfig gains the optional install.instructionTier slot, declared by both instruction-tier hosts. README host table now matches what setup actually does. Inspired by dietrichgebert/ponytail's instruction-tier AGENTS.md fallback — one generated source, never per-host hand copies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cf0b39ddeb
commit
3263fffea9
@@ -111,16 +111,22 @@ cd ~/gstack && ./setup
|
|||||||
|
|
||||||
Or target a specific agent with `./setup --host <name>`:
|
Or target a specific agent with `./setup --host <name>`:
|
||||||
|
|
||||||
| Agent | Flag | Skills install to |
|
| Agent | Flag | What you get |
|
||||||
|-------|------|-------------------|
|
|-------|------|--------------|
|
||||||
| OpenAI Codex CLI | `--host codex` | `${CODEX_HOME:-~/.codex}/skills/gstack-*/` |
|
| OpenAI Codex CLI | `--host codex` | Full install → `${CODEX_HOME:-~/.codex}/skills/gstack-*/` |
|
||||||
| OpenCode | `--host opencode` | `~/.config/opencode/skills/gstack-*/` |
|
| OpenCode | `--host opencode` | Full install → `~/.config/opencode/skills/gstack-*/` |
|
||||||
| Cursor | `--host cursor` | `~/.cursor/skills/gstack-*/` |
|
| Cursor | `--host cursor` | Full install → `~/.cursor/skills/gstack-*/` |
|
||||||
| Factory Droid | `--host factory` | `~/.factory/skills/gstack-*/` |
|
| Factory Droid | `--host factory` | Full install → `~/.factory/skills/gstack-*/` |
|
||||||
| Slate | `--host slate` | `~/.slate/skills/gstack-*/` |
|
| Kiro | `--host kiro` | Full install → `~/.kiro/skills/gstack-*/` |
|
||||||
| Kiro | `--host kiro` | `~/.kiro/skills/gstack-*/` |
|
| Slate | `--host slate` | Pointer to the Claude install (Slate reads `.claude/skills` as a fallback) |
|
||||||
| Hermes | `--host hermes` | `~/.hermes/skills/gstack-*/` |
|
| Hermes | `--host hermes` | Methodology artifacts via `gen:skill-docs --host hermes` + the instruction-only digest below |
|
||||||
| GBrain (mod) | `--host gbrain` | `~/.gbrain/skills/gstack-*/` |
|
| GBrain (mod) | `--host gbrain` | Brain-aware skill variants, shipped from the GBrain repo |
|
||||||
|
|
||||||
|
**Instruction-only tier (any rules-reading agent — Zed, Amp, Jules, side projects):**
|
||||||
|
copy the 2KB digest at [`agents-digest/gstack-AGENTS.md`](agents-digest/gstack-AGENTS.md)
|
||||||
|
into a location your agent reads (for example, append it to your project's `AGENTS.md`).
|
||||||
|
It carries gstack's ethos, reuse ladder, and voice rules — no install required. The
|
||||||
|
digest's first line shows its gstack version; re-copy it after upgrading.
|
||||||
|
|
||||||
For Codex, setup reads the top-level `model` from
|
For Codex, setup reads the top-level `model` from
|
||||||
`${CODEX_HOME:-~/.codex}/config.toml` and generates the matching behavioral
|
`${CODEX_HOME:-~/.codex}/config.toml` and generates the matching behavioral
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# gstack digest v1.71.0.0 — regenerate/re-copy after upgrading gstack
|
||||||
|
|
||||||
|
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||||
|
for agent hosts without a full skill install. The full skills add workflows,
|
||||||
|
reviews, and evals on top of these rules.
|
||||||
|
|
||||||
|
## Ethos
|
||||||
|
|
||||||
|
- **Boil the Ocean** — AI makes completeness cheap, so do the complete thing: tests, edge cases, error paths. Shortcuts need an explicit, recorded decision.
|
||||||
|
- **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.
|
||||||
|
- **User Sovereignty** — models recommend, the user decides. Cross-model agreement is signal, never permission. Ask before changing the user's stated direction.
|
||||||
|
- **Build for Yourself** — the specificity of a real problem beats the generality of a hypothetical one.
|
||||||
|
|
||||||
|
## The reuse ladder
|
||||||
|
|
||||||
|
Before writing new code, stop at the first rung that holds:
|
||||||
|
1. A helper, util, or pattern already in this repo.
|
||||||
|
2. The standard library.
|
||||||
|
3. A native platform feature (CSS over JS, DB constraint over app code).
|
||||||
|
4. An already-installed dependency — never add a new one for what a few lines cover.
|
||||||
|
|
||||||
|
Then build the complete version of what remains. Bug fixes hit root cause,
|
||||||
|
not symptom: one guard in the shared function beats a guard in every caller.
|
||||||
|
|
||||||
|
## Voice
|
||||||
|
|
||||||
|
Direct, concrete, builder-to-builder. Name the file, function, command, and
|
||||||
|
user-visible impact. Short paragraphs; end with what to do. No filler, no
|
||||||
|
corporate tone, no AI vocabulary.
|
||||||
|
|
||||||
|
## Full gstack
|
||||||
|
|
||||||
|
Clone https://github.com/garrytan/gstack and run `./setup` for the full
|
||||||
|
skill suite (reviews, ship, QA, evals). This digest is generated — edit
|
||||||
|
scripts/gen-agents-digest.ts, not this file.
|
||||||
@@ -28,6 +28,13 @@ const hermes = defineHost({
|
|||||||
// If Hermes has GBrain as a mod, brain features activate automatically.
|
// If Hermes has GBrain as a mod, brain features activate automatically.
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// No full install arm — users can hand-copy the instruction-only digest
|
||||||
|
// (setup's explainer arm prints this path; never auto-copied).
|
||||||
|
install: {
|
||||||
|
linkingStrategy: 'symlink-generated',
|
||||||
|
instructionTier: { rulesFile: 'agents-digest/gstack-AGENTS.md' },
|
||||||
|
},
|
||||||
|
|
||||||
coAuthorTrailer: 'Co-Authored-By: Hermes Agent <agent@nousresearch.com>',
|
coAuthorTrailer: 'Co-Authored-By: Hermes Agent <agent@nousresearch.com>',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ const openclaw = defineHost({
|
|||||||
// Suppress Claude-specific preamble sections that don't apply to OpenClaw
|
// Suppress Claude-specific preamble sections that don't apply to OpenClaw
|
||||||
suppressedResolvers: [...CROSS_MODEL_RESOLVERS, ...GBRAIN_RESOLVERS],
|
suppressedResolvers: [...CROSS_MODEL_RESOLVERS, ...GBRAIN_RESOLVERS],
|
||||||
|
|
||||||
|
// No full install arm — users can hand-copy the instruction-only digest
|
||||||
|
// (setup's explainer arm prints this path; never auto-copied).
|
||||||
|
install: {
|
||||||
|
linkingStrategy: 'symlink-generated',
|
||||||
|
instructionTier: { rulesFile: 'agents-digest/gstack-AGENTS.md' },
|
||||||
|
},
|
||||||
|
|
||||||
coAuthorTrailer: 'Co-Authored-By: OpenClaw Agent <agent@openclaw.ai>',
|
coAuthorTrailer: 'Co-Authored-By: OpenClaw Agent <agent@openclaw.ai>',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
/**
|
||||||
|
* Generate agents-digest/gstack-AGENTS.md — the instruction-only tier.
|
||||||
|
*
|
||||||
|
* A ~2KB behavioral digest any rules-reading agent host can load (Zed, Amp,
|
||||||
|
* Jules, Cursor side-projects, OpenClaw/Hermes methodology mode): the ETHOS
|
||||||
|
* principles as one-liners, the reuse ladder, the voice rules, and a pointer
|
||||||
|
* to the full install. It is a COMMITTED generated artifact (like llms.txt)
|
||||||
|
* so the setup explainer arms can point at it before any toolchain exists.
|
||||||
|
*
|
||||||
|
* Delivery is print-path + user-performed copy ONLY — never write or
|
||||||
|
* overwrite a user's own AGENTS.md (a gstack-AGENTS.md is auto-read by no
|
||||||
|
* host; clobbering AGENTS.md destroys user content on every re-run).
|
||||||
|
*
|
||||||
|
* One host-neutral file, deliberately: the digest contains no host-specific
|
||||||
|
* paths, so per-host copies would be byte-identical duplication (the exact
|
||||||
|
* per-host rule-text sprawl gstack's generator model exists to avoid). If a
|
||||||
|
* host ever needs rewrites, this generator gains a per-host emit then.
|
||||||
|
*
|
||||||
|
* Size budget: <= 2,048 bytes, enforced by test/agents-digest.test.ts. Every
|
||||||
|
* host that loads this pays for it in every session — trim, don't grow.
|
||||||
|
*
|
||||||
|
* Refresh: invoked from scripts/gen-skill-docs.ts after SKILL.md generation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dir, '..');
|
||||||
|
export const DIGEST_RELPATH = path.join('agents-digest', 'gstack-AGENTS.md');
|
||||||
|
export const DIGEST_BYTE_BUDGET = 2048;
|
||||||
|
|
||||||
|
export function generateAgentsDigest(opts?: { root?: string }): { content: string; bytes: number } {
|
||||||
|
const root = opts?.root ?? ROOT;
|
||||||
|
const version = fs.readFileSync(path.join(root, 'VERSION'), 'utf-8').trim();
|
||||||
|
|
||||||
|
const content = `# gstack digest v${version} — regenerate/re-copy after upgrading gstack
|
||||||
|
|
||||||
|
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||||
|
for agent hosts without a full skill install. The full skills add workflows,
|
||||||
|
reviews, and evals on top of these rules.
|
||||||
|
|
||||||
|
## Ethos
|
||||||
|
|
||||||
|
- **Boil the Ocean** — AI makes completeness cheap, so do the complete thing: tests, edge cases, error paths. Shortcuts need an explicit, recorded decision.
|
||||||
|
- **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.
|
||||||
|
- **User Sovereignty** — models recommend, the user decides. Cross-model agreement is signal, never permission. Ask before changing the user's stated direction.
|
||||||
|
- **Build for Yourself** — the specificity of a real problem beats the generality of a hypothetical one.
|
||||||
|
|
||||||
|
## The reuse ladder
|
||||||
|
|
||||||
|
Before writing new code, stop at the first rung that holds:
|
||||||
|
1. A helper, util, or pattern already in this repo.
|
||||||
|
2. The standard library.
|
||||||
|
3. A native platform feature (CSS over JS, DB constraint over app code).
|
||||||
|
4. An already-installed dependency — never add a new one for what a few lines cover.
|
||||||
|
|
||||||
|
Then build the complete version of what remains. Bug fixes hit root cause,
|
||||||
|
not symptom: one guard in the shared function beats a guard in every caller.
|
||||||
|
|
||||||
|
## Voice
|
||||||
|
|
||||||
|
Direct, concrete, builder-to-builder. Name the file, function, command, and
|
||||||
|
user-visible impact. Short paragraphs; end with what to do. No filler, no
|
||||||
|
corporate tone, no AI vocabulary.
|
||||||
|
|
||||||
|
## Full gstack
|
||||||
|
|
||||||
|
Clone https://github.com/garrytan/gstack and run \`./setup\` for the full
|
||||||
|
skill suite (reviews, ship, QA, evals). This digest is generated — edit
|
||||||
|
scripts/gen-agents-digest.ts, not this file.
|
||||||
|
`;
|
||||||
|
|
||||||
|
return { content, bytes: Buffer.byteLength(content, 'utf-8') };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeAgentsDigest(opts?: { root?: string }): { outPath: string; bytes: number } {
|
||||||
|
const root = opts?.root ?? ROOT;
|
||||||
|
const { content, bytes } = generateAgentsDigest({ root });
|
||||||
|
const outPath = path.join(root, DIGEST_RELPATH);
|
||||||
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||||
|
fs.writeFileSync(outPath, content);
|
||||||
|
return { outPath, bytes };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
const { outPath, bytes } = writeAgentsDigest();
|
||||||
|
console.log(`[gen-agents-digest] ${path.relative(ROOT, outPath)}: ${bytes} bytes (budget ${DIGEST_BYTE_BUDGET})`);
|
||||||
|
}
|
||||||
@@ -1148,5 +1148,16 @@ if (!DRY_RUN) {
|
|||||||
const msg = err instanceof Error ? err.message : String(err);
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
console.error(`[gen-llms-txt] FAILED: ${msg}`);
|
console.error(`[gen-llms-txt] FAILED: ${msg}`);
|
||||||
}
|
}
|
||||||
|
// Regenerate agents-digest/gstack-AGENTS.md — the instruction-only tier
|
||||||
|
// for rules-reading hosts with no skill install. Committed artifact;
|
||||||
|
// freshness + byte budget asserted in test/agents-digest.test.ts.
|
||||||
|
try {
|
||||||
|
const { writeAgentsDigest, DIGEST_BYTE_BUDGET } = await import('./gen-agents-digest');
|
||||||
|
const digest = writeAgentsDigest();
|
||||||
|
console.log(`[gen-agents-digest] agents-digest/gstack-AGENTS.md: ${digest.bytes} bytes (budget ${DIGEST_BYTE_BUDGET})`);
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
|
console.error(`[gen-agents-digest] FAILED: ${msg}`);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ export interface HostConfig {
|
|||||||
install: {
|
install: {
|
||||||
/** How skills are linked into the host dir. */
|
/** How skills are linked into the host dir. */
|
||||||
linkingStrategy: 'real-dir-symlink' | 'symlink-generated';
|
linkingStrategy: 'real-dir-symlink' | 'symlink-generated';
|
||||||
|
/**
|
||||||
|
* Instruction-only fallback tier: a committed rules digest this host's
|
||||||
|
* users can hand-copy when there is no full install arm. Delivery is
|
||||||
|
* print-path + user-performed copy ONLY — setup must never write or
|
||||||
|
* overwrite a user's own AGENTS.md. See scripts/gen-agents-digest.ts.
|
||||||
|
*/
|
||||||
|
instructionTier?: { rulesFile: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Host-Specific Behavioral Config ---
|
// --- Host-Specific Behavioral Config ---
|
||||||
|
|||||||
@@ -227,6 +227,14 @@ case "$HOST" in
|
|||||||
echo " 2. Or generate artifacts: bun run gen:skill-docs --host openclaw"
|
echo " 2. Or generate artifacts: bun run gen:skill-docs --host openclaw"
|
||||||
echo " 3. See docs/OPENCLAW.md for the full architecture"
|
echo " 3. See docs/OPENCLAW.md for the full architecture"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "Instruction-only tier (no install): copy the 2KB rules digest into a"
|
||||||
|
echo "location your agent reads (e.g. append to your project's AGENTS.md)."
|
||||||
|
echo "It carries gstack's ethos, reuse ladder, and voice rules:"
|
||||||
|
echo ""
|
||||||
|
echo " $(pwd)/agents-digest/gstack-AGENTS.md"
|
||||||
|
echo ""
|
||||||
|
echo "Re-copy it after upgrading gstack — the digest's first line shows its version."
|
||||||
|
echo ""
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
hermes)
|
hermes)
|
||||||
echo ""
|
echo ""
|
||||||
@@ -237,6 +245,14 @@ case "$HOST" in
|
|||||||
echo " 1. Tell your Hermes agent: 'install gstack for hermes'"
|
echo " 1. Tell your Hermes agent: 'install gstack for hermes'"
|
||||||
echo " 2. Or generate artifacts: bun run gen:skill-docs --host hermes"
|
echo " 2. Or generate artifacts: bun run gen:skill-docs --host hermes"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "Instruction-only tier (no install): copy the 2KB rules digest into a"
|
||||||
|
echo "location your agent reads (e.g. append to your project's AGENTS.md)."
|
||||||
|
echo "It carries gstack's ethos, reuse ladder, and voice rules:"
|
||||||
|
echo ""
|
||||||
|
echo " $(pwd)/agents-digest/gstack-AGENTS.md"
|
||||||
|
echo ""
|
||||||
|
echo "Re-copy it after upgrading gstack — the digest's first line shows its version."
|
||||||
|
echo ""
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
gbrain)
|
gbrain)
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* agents-digest/gstack-AGENTS.md — the instruction-only tier artifact.
|
||||||
|
*
|
||||||
|
* Freshness (committed file matches the generator, same pattern as
|
||||||
|
* llms-txt-shape.test.ts), a HARD byte budget (every rules-reading host
|
||||||
|
* loads the whole file every session), and the delivery-safety invariant
|
||||||
|
* (setup prints the path; it must never copy onto a user's AGENTS.md).
|
||||||
|
*/
|
||||||
|
import { describe, test, expect } from 'bun:test';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {
|
||||||
|
generateAgentsDigest,
|
||||||
|
DIGEST_RELPATH,
|
||||||
|
DIGEST_BYTE_BUDGET,
|
||||||
|
} from '../scripts/gen-agents-digest';
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dir, '..');
|
||||||
|
const digestPath = path.join(ROOT, DIGEST_RELPATH);
|
||||||
|
|
||||||
|
describe('agents-digest', () => {
|
||||||
|
test('committed digest is fresh (matches generator output)', () => {
|
||||||
|
const { content } = generateAgentsDigest({ root: ROOT });
|
||||||
|
const committed = fs.readFileSync(digestPath, 'utf-8');
|
||||||
|
expect(committed).toBe(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(`digest stays within its ${DIGEST_BYTE_BUDGET}-byte budget`, () => {
|
||||||
|
const { bytes } = generateAgentsDigest({ root: ROOT });
|
||||||
|
if (bytes > DIGEST_BYTE_BUDGET) {
|
||||||
|
throw new Error(
|
||||||
|
`agents-digest is ${bytes} bytes, over the ${DIGEST_BYTE_BUDGET}-byte budget ` +
|
||||||
|
`(over by ${bytes - DIGEST_BYTE_BUDGET}).\n` +
|
||||||
|
`This file is always-on context for every instruction-tier host — trim, don't grow.\n` +
|
||||||
|
`Trim protocol: cut prose from scripts/gen-agents-digest.ts (shorten explanations, ` +
|
||||||
|
`never drop a section outright), rerun 'bun run gen:skill-docs', and re-check. ` +
|
||||||
|
`Raising the budget requires the same conscious-decision treatment as the ` +
|
||||||
|
`context-budget ratchet: justify it in the PR that does it.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(bytes).toBeLessThanOrEqual(DIGEST_BYTE_BUDGET);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('digest carries its version header and the load-bearing sections', () => {
|
||||||
|
const version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||||
|
const content = fs.readFileSync(digestPath, 'utf-8');
|
||||||
|
expect(content.startsWith(`# gstack digest v${version}`)).toBe(true);
|
||||||
|
expect(content).toContain('re-copy after upgrading');
|
||||||
|
expect(content).toContain('## Ethos');
|
||||||
|
expect(content).toContain('## The reuse ladder');
|
||||||
|
expect(content).toContain('## Voice');
|
||||||
|
expect(content).toContain('## Full gstack');
|
||||||
|
// The ladder must keep the completeness reconciliation clause.
|
||||||
|
expect(content).toContain('build the complete version of what remains');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setup never copies the digest onto a user AGENTS.md (print-path only)', () => {
|
||||||
|
const setup = fs.readFileSync(path.join(ROOT, 'setup'), 'utf-8');
|
||||||
|
// The explainer arms print the digest path…
|
||||||
|
expect(setup).toContain('agents-digest/gstack-AGENTS.md');
|
||||||
|
// …and no line may write to an AGENTS.md destination. A cp/ln/mv or a
|
||||||
|
// shell redirect into AGENTS.md would clobber user content on re-run.
|
||||||
|
const writers = setup
|
||||||
|
.split('\n')
|
||||||
|
.filter((l) => /(^|\s)(cp|ln|mv)\s[^#]*AGENTS\.md|>\s*"?[^"\s]*AGENTS\.md/.test(l));
|
||||||
|
expect(writers).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('instruction-tier hosts declare the digest in host config', () => {
|
||||||
|
for (const host of ['openclaw', 'hermes']) {
|
||||||
|
const src = fs.readFileSync(path.join(ROOT, 'hosts', `${host}.ts`), 'utf-8');
|
||||||
|
expect(src).toContain("instructionTier: { rulesFile: 'agents-digest/gstack-AGENTS.md' }");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user