From 1ef94b9f55041ec449249849077de882b3f06821 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 4 Sep 2026 11:17:06 +0900 Subject: [PATCH 1/2] feat(wbfy): add generate-user-agent-instructions command Overwrites ~/.codex/AGENTS.md, ~/.claude/CLAUDE.md and ~/.gemini/GEMINI.md with the organization's fixed user-level agent instructions. Co-authored-by: WillBooster (Claude Code) --- packages/wbfy/README.md | 1 + .../src/generators/userAgentInstructions.ts | 23 +++++++++++++++++++ packages/wbfy/src/index.ts | 11 +++++++++ 3 files changed, 35 insertions(+) create mode 100644 packages/wbfy/src/generators/userAgentInstructions.ts diff --git a/packages/wbfy/README.md b/packages/wbfy/README.md index a3f793c28..a3016e234 100644 --- a/packages/wbfy/README.md +++ b/packages/wbfy/README.md @@ -17,3 +17,4 @@ This tool must keep idempotency, i.e., it always yields the same result when a u ## How to Use 1. `bunx @willbooster/wbfy ` +2. `bunx @willbooster/wbfy generate-user-agent-instructions` to overwrite your user-level agent instruction files (`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, `~/.gemini/GEMINI.md`) with the organization's fixed content diff --git a/packages/wbfy/src/generators/userAgentInstructions.ts b/packages/wbfy/src/generators/userAgentInstructions.ts new file mode 100644 index 000000000..25b357df1 --- /dev/null +++ b/packages/wbfy/src/generators/userAgentInstructions.ts @@ -0,0 +1,23 @@ +import os from 'node:os'; +import path from 'node:path'; + +import { fsUtil } from '../utils/fsUtil.js'; + +/** + * The user-level instruction file of each supported agent, relative to the home directory. These + * apply to every repository the agent works in, so they hold only the conventions that are true on + * every WillBooster developer machine rather than in a particular repository. + */ +const userAgentInstructionFilePaths = ['.codex/AGENTS.md', '.claude/CLAUDE.md', '.gemini/GEMINI.md'] as const; + +const userAgentInstructionContent = `- Repos live under \`~/ghq/github.com\`; always clone with \`ghq get -p\`, never \`git clone\`. +- LLM/AI agent responses may take 1–2 hours; wait patiently, don't assume failure. +- No AI attribution (e.g., \`Co-Authored-By\` trailers, "Generated with ..." footers) in commits, issues, or PRs unless explicitly requested. This hides nothing: if asked which AI agent did the work, answer truthfully. +`; + +/** Overwrites the user-level instruction files of every supported agent with the fixed content. */ +export async function generateUserAgentInstructions(): Promise { + for (const relativePath of userAgentInstructionFilePaths) { + await fsUtil.generateFile(path.join(os.homedir(), relativePath), userAgentInstructionContent); + } +} diff --git a/packages/wbfy/src/index.ts b/packages/wbfy/src/index.ts index 5cdf28443..80ac66e35 100644 --- a/packages/wbfy/src/index.ts +++ b/packages/wbfy/src/index.ts @@ -30,6 +30,7 @@ import { generateReadme, readAppliedWbfyVersionLabel } from './generators/readme import { generateReleaserc } from './generators/releaserc.js'; import { generateRenovateJsonc } from './generators/renovateJsonc.js'; import { generateTsconfig } from './generators/tsconfig.js'; +import { generateUserAgentInstructions } from './generators/userAgentInstructions.js'; import { generateVscodeSettings } from './generators/vscodeSettings.js'; import { ensureWbEnvDefinitions } from './generators/wbEnv.js'; import { generateSelfContainedWorkflows } from './generators/selfContainedWorkflow.js'; @@ -58,6 +59,8 @@ import { getWorkspaceSubDirPaths } from './utils/workspaceUtil.js'; * name would swallow a directory that happens to share it. */ const applyReleaseAgeGateCommand = 'apply-release-age-gate'; +/** Same naming rationale as applyReleaseAgeGateCommand. */ +const generateUserAgentInstructionsCommand = 'generate-user-agent-instructions'; async function main(): Promise { const argv = await yargs(process.argv.slice(2)) @@ -65,6 +68,10 @@ async function main(): Promise { applyReleaseAgeGateCommand, "Apply only the organization's minimum-release-age policy to this machine's global package-manager configs" ) + .command( + generateUserAgentInstructionsCommand, + "Overwrite this user's agent instruction files (~/.codex/AGENTS.md, ~/.claude/CLAUDE.md, ~/.gemini/GEMINI.md) with the organization's fixed content" + ) .command('$0 [paths..]', 'Make a given project follow the WillBooster standard', (yargs) => { yargs.positional('paths', { describe: 'project paths to be wbfied', @@ -104,6 +111,10 @@ async function main(): Promise { if (!ensureGlobalReleaseAgeGates()) process.exitCode = 1; return; } + if (argv._[0] === generateUserAgentInstructionsCommand) { + await generateUserAgentInstructions(); + return; + } let hasInvalidPackageConfig = false; try { From f596904ec6415506dd35123d0a635b25b2536eed Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 4 Sep 2026 11:41:05 +0900 Subject: [PATCH 2/2] fix(wbfy): exit non-zero when a user agent instruction file is skipped Co-authored-by: WillBooster (Claude Code) --- .../wbfy/src/generators/userAgentInstructions.ts | 12 +++++++++--- packages/wbfy/src/index.ts | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/wbfy/src/generators/userAgentInstructions.ts b/packages/wbfy/src/generators/userAgentInstructions.ts index 25b357df1..d27124319 100644 --- a/packages/wbfy/src/generators/userAgentInstructions.ts +++ b/packages/wbfy/src/generators/userAgentInstructions.ts @@ -15,9 +15,15 @@ const userAgentInstructionContent = `- Repos live under \`~/ghq/github.com\`; al - No AI attribution (e.g., \`Co-Authored-By\` trailers, "Generated with ..." footers) in commits, issues, or PRs unless explicitly requested. This hides nothing: if asked which AI agent did the work, answer truthfully. `; -/** Overwrites the user-level instruction files of every supported agent with the fixed content. */ -export async function generateUserAgentInstructions(): Promise { +/** + * Overwrites the user-level instruction files of every supported agent with the fixed content. + * Returns false when any file was skipped (e.g. a symlinked file), so the command can fail loudly. + */ +export async function generateUserAgentInstructions(): Promise { + let hasWrittenAll = true; for (const relativePath of userAgentInstructionFilePaths) { - await fsUtil.generateFile(path.join(os.homedir(), relativePath), userAgentInstructionContent); + const hasWritten = await fsUtil.generateFile(path.join(os.homedir(), relativePath), userAgentInstructionContent); + hasWrittenAll &&= hasWritten; } + return hasWrittenAll; } diff --git a/packages/wbfy/src/index.ts b/packages/wbfy/src/index.ts index 80ac66e35..ff660281f 100644 --- a/packages/wbfy/src/index.ts +++ b/packages/wbfy/src/index.ts @@ -112,7 +112,7 @@ async function main(): Promise { return; } if (argv._[0] === generateUserAgentInstructionsCommand) { - await generateUserAgentInstructions(); + if (!(await generateUserAgentInstructions())) process.exitCode = 1; return; }