Imported from xfontr/monorepo (
infrastructure/scripts/AGENTS.md). Install upstream withnpx skills add xfontr/monorepo --skill scripts. Copyright stays with the author.
🤖 @monorepo/scripts
See README.md for the layout rule and the per-script docs it links to. Use the
scripts-new-script skill when adding a script.
- This is a service, not a library. Nothing in the workspace imports it — scripts are run as
CLIs, which is also why it's
infrastructure/rather thanpackages/, same reasoning astranslations. private: trueand outsidenx release'spackages/*, so it has noCHANGELOG.mdand no version to bump.- One folder per script under
src/, and every one has the same four things in it: a three-lineindex.tsthat callsrunfromshared/cli.ts, amain.ts(or one file per subcommand —issue/hasaddandpick, andindex.tspasses a record),adapters/anddomain/. Create both directories even when one starts with a single file; the point is that the listing answers "what runs, what talks to the outside, what's pure" before anyone opens a file. Don't add loose files at the top ofsrc/. - entry → command →
adapters/→domain/, one way only, never sideways between script folders. The reasoning is in0004. Two rules get got wrong: an adapter is named after the boundary it wraps, sogit.tsliving in three folders at once is correct rather than duplication; and adomain/file is pure — nonode:fs, no subprocess, no clack, noprocess. There is nohelpers/, notypes/and noconstants.tshere — 0004 says why each was rejected, so don't re-add one. (types/also can't work:**/typesis inbaseIgnores, so ESLint would never see it.) - No side effects at module scope. No
run(),git()orreadFileSyncat the top level of a file, and readprocess.argv/process.envinside a command (or viaflag()), never into a module-level const. Both rules exist because breaking them made two modules unimportable outside a git checkout and forced avi.resetModules()into a spec. - Never call
process.exit. ThrowExpectedErrorfor a failure whose message is the whole answer,CancelledErrorfor a cancelled prompt, and letrundecide the code — it setsprocess.exitCode, because exiting right after a write to a pipe can truncate it. - Entry points run straight with
node, no build step and notsx/ts-node— this repo's Node engines range already supports type-stripping. Internal imports use the explicit.tsextension, same astranslations; Node's stripping does no resolution rewriting. - Keep these small. They're personal tooling for a personal monorepo. Prompts and
ghcalls, not layered abstractions — the previous version ofaddIssuehad three templates, a label mapping and four spec files, and got avoided because of it. testruns with--passWithNoTestsbecause most of this is prompts and subprocess calls. Extract and test any real logic a script grows (a parser, a diff, a mapping) —issue/branch.tsis the one that exists; don't write specs that assert a mock ofghagainst itself.- Tagged
type:tooling, so it may depend only on@monorepo/configs. Nothing else may depend ontype:tooling— it's a leaf nothing imports, enforced the same waytype:infrais. - The TODO/FIXME push gate is not here any more. It's a few lines of
awkin.husky/pre-push, which only points atpnpm issue:addin its error message.