TypeScript ESM CLI for Google Analytics 4 (Data + Admin APIs). Claude operates this repo
mostly autonomously. This file is the contract; deeper facts live in @.serena/memories/.
Keep it lean — if you're tempted to add >5 lines, ask whether it belongs in a memory, a skill,
or a path-scoped .claude/rules/*.md instead.
- Imports end in
.js, never.ts—import { foo } from './bar.service.js'. ESM +moduleResolution: bundlerrequires it. Hard build break if violated. - Every command
.action()starts withresolveGlobalOptions(command)— single place where global flags +~/.gacli/config.json+ env vars are reconciled. - Service results return
ReportData({headers, rows, rowCount}) — even single-value or"Deleted X"responses get coerced into a 1×N row. Formatters depend on it. handleError(err)is terminal (: never) — never wrap, never rethrow, nevertry-around it. It is the leaf of every action.- Pipeline order is inviolable: Commander →
resolveGlobalOptions+validate→ service →ReportData→formatOutput→writeOutput. No layer-skipping. - API clients only via
getClient()/getAdminClient()— directnew BetaAnalyticsDataClient()/new AnalyticsAdminServiceClient()bypasses the auth resolution chain and cache. validate(schema, opts)is terminal —process.exit(1)onZodError. Don't catchZodErroroutsidevalidate. New options need a schema insrc/validation/schemas.ts.- New CLI config key = update BOTH
CLIConfiginterface ANDCONFIG_KEYSmap insrc/types/config.ts, orsetConfigValuerejects them. - stderr = status (
logger/ora). stdout = data (writeOutput). Mixing breaks--format json | jqpiping. - New top-level command =
program.addCommand(createXxxCommand())insrc/index.tsor it's invisible at the CLI surface.
- Feature/refactor →
superpowers:brainstorming→superpowers:writing-plans. - Implementing →
superpowers:test-driven-development(tests first unless rename/docs only). - Before claiming done →
superpowers:verification-before-completion(see Verification gate below). - Anything failing twice →
superpowers:systematic-debugging. No guess-fixing. - Multi-task work →
superpowers:dispatching-parallel-agentsorsuperpowers:subagent-driven-development. - End of branch →
superpowers:finishing-a-development-branch. - Polish →
simplifyskill on any non-trivial diff before declaring done.
src/**/*.tsdiscovery:mcp__serena__get_symbols_overviewthenfind_symbol(include_body=trueonly when needed). Don'tReadwhole TS files unless serena fails or the file is <50 lines.src/**/*.tsedits: preferreplace_symbol_body,insert_after_symbol,insert_before_symbol. UseEditonly for non-symbolic regions (imports, top-level constants).- Renaming or signature changes:
find_referencing_symbolsBEFORE editing. Update all call sites in the same change. - Tests, configs, markdown: regular
Read/Editis fine. - Detail:
@.serena/memories/serena_workflow_tips.md.
PROCEED without asking when ALL true:
- changes are local, reversible (git can undo), in-scope of the task
- no secrets, no network publish, no schema/contract break
pnpm type-check+pnpm teststill green after the change
ASK FIRST when ANY true:
- irreversible op (force-push, history rewrite, file deletion outside the changed feature)
- touches
.git/,package.jsondeps,tsconfig.json,vitest.config.ts,biome.json - introduces a new dependency
- changes the public CLI surface (renamed command, new flag semantics, removed option)
- touches credentials, tokens,
.env*,~/.gacli/
STOP and ask when:
- same error after 3 fix attempts → invoke
superpowers:systematic-debugging, then ask - spec ambiguous between two reasonable interpretations
- a serena memory contradicts the requested change → surface the conflict before proceeding
Before saying "done", "fixed", "works", or opening a PR, you MUST in this order:
- Run
pnpm type-check— paste exit code. - Run
pnpm test— paste pass/fail summary. - For new/changed commands: run
pnpm dev <command> --helpand a happy-path invocation; paste output. - Re-read the original requirement; state point-by-point whether each item is met.
If 1–3 didn't run, the work is implemented, not done — say so explicitly. Never claim success on the basis that "the code looks right".
After each non-trivial task, run a 4-question retrospective:
- New project fact (architecture, gotcha, convention)? →
mcp__serena__write_memory. Update an existing memory when the topic matches; create a new file only for genuinely distinct topics. Names mirror existing convention (snake_case, scope-narrow). - Reusable technique (debug trick, library workaround, pattern usable in OTHER repos)? → invoke
claudeceptionto extract a skill. - Re-derived a rule that should be in CLAUDE.md? → propose an edit. Do NOT silently bloat. If the rule is path-scoped, propose
.claude/rules/<name>.mdwithpaths:frontmatter instead. - Frustration markers ("again", "still", "why", "useless")? →
frustration-detectorshould fire. If not, stop, summarize attempts, ask for direction.
Cross-session recall. Before any non-trivial task, optionally call mcp__mempalace__mempalace_search "<keywords>" to surface what prior sessions learned, decided, or got stuck on. The Stop hook auto-saves every 15 messages; PreCompact emergency-saves before context loss. Treat the palace as the long-term substrate; serena memories as the curated structured layer; CLAUDE.md as the contract.
pnpm dev <args>— run from source via tsx (no build needed).pnpm type-check—tsc --noEmit, the build gate.pnpm test/pnpm test:watch/pnpm test:coverage.pnpm buildthenpnpm start— production-artifact verification.- Full list:
@.serena/memories/suggested_commands.md.
.serena/memories/key_entrypoints.md— fast jump map (any task that needs to find code)..serena/memories/codebase_structure.md— directory tree, layered dependency rule..serena/memories/serena_workflow_tips.md— symbol-tool best practices for THIS repo..serena/memories/task_completion_checklist.md— the full pre-merge gate (covers risk-bearing edits)..serena/memories/testing_conventions.md— Jest+ESM patterns when adding tests..serena/memories/tech_stack.md— exact dependency versions and notable absences..serena/memories/suggested_commands.md— full command list (dev/build/test/git/serena)..serena/memories/project_overview.md— scope, distribution model, repo state.README.md,help.md— human-facing setup and command reference.
Always-loaded (3 imports — the foundation):
@.serena/memories/architecture_patterns.md @.serena/memories/command_pattern.md @.serena/memories/code_style_conventions.md
Not a memory dump (use serena). Not a skill catalogue (skills self-describe). Not a linter
(tsc --strict is). Not commit conventions (global ~/.claude/CLAUDE.md). Not onboarding
(README.md).