SLOPSHOPPER

context-report

Prints which instruction files the engine loaded (tier, path, @ imports, size) once per context load, and hints when a project CLAUDE.md keeps its AGENTS.md…

newprompt
A shopper browsing a rack in a slop shop
README

cc-settings

cc-settings gives Claude Code and Codex the Darkroom engineering team's standards, task workflows, safety checks, and proof gates. One installer makes a new machine behave like the rest of the team without replacing personal configuration that cc-settings does not own.

The practical effect is simple: "fix this bug" gets a cause-first debugging workflow, "review my changes" stays read-only, and "ship it" must prove the real build and tests before anything is published.

New here? Install it (5 minutes), run one read-only task, then learn the daily loop. That is enough to get most of the value. Everything after that section is reference you can come back to.

Five-minute first success

1. Install the product you plan to use

Install and authenticate Claude Code, Codex CLI, or both. cc-settings configures those products; it does not install a subscription or account.

2. Install cc-settings

Any platform (Node 18+):

npx darkroom-settings

Every installer flag works: npx darkroom-settings --light --auto-update=on. bunx darkroom-settings is equivalent. The npm package is only a downloader; the configuration always installs from this repository's pinned GitHub origin.

macOS or Linux, without Node:

curl -fsSL https://raw.githubusercontent.com/darkroomengineering/cc-settings/main/setup.sh | bash

Flags go after -s --. Every setup.sh flag works remotely, with no clone or download needed:

curl -fsSL https://raw.githubusercontent.com/darkroomengineering/cc-settings/main/setup.sh | bash -s -- --light --auto-update=on

Windows PowerShell:

powershell -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/darkroomengineering/cc-settings/main/setup.ps1 | iex"

To pass flags remotely on Windows, invoke the downloaded script as a script block:

powershell -ExecutionPolicy Bypass -c "& ([scriptblock]::Create((irm https://raw.githubusercontent.com/darkroomengineering/cc-settings/main/setup.ps1))) --light"

The default target installs both products when codex is on PATH, and Claude Code only otherwise. --light installs a minimal beginner tier; re-run without it to get the full setup. Clone the repository only when you want a source checkout you own:

git clone https://github.com/darkroomengineering/cc-settings.git
cd cc-settings
bash setup.sh --target=both --dry-run
bash setup.sh --target=both

Review all requirements, tiers, system changes, prompts, managed paths, and undo behavior in the installation reference.

3. Restart and inspect

Restart every selected product. In Codex full installs, open /hooks and review the installed hooks once. Claude users can inspect the installed user-scope configuration from any directory:

bun ~/.claude/src/scripts/whats-on.ts

That report shows what is installed and shaping Claude user scope. It does not identify which skill handled a previous prompt or fully resolve project overrides.

4. Run one harmless task

Open a repository and say:

Explain where this project's configuration is loaded. Read only. Cite the files and lines.

The result should name its read-only scope, cite evidence, and leave the working tree unchanged. Your first session shows the expected output, background behavior, follow-up, and recovery.

The daily loop

You do not need to memorize commands. Describe the outcome in plain language and cc-settings picks the matching workflow (a "skill"). Type the skill name only when you want to force a specific one: /name in Claude Code, $name in Codex.

Most days are some path through these steps:

StepSay something likeOr pinWhat you get back
Understand"how does checkout work here?"/exploreA read-only map with file and line citations
Fix"the login redirect loops on Safari"/fixThe named cause, a reproduction, the smallest fix, and the tests that prove it
Build"add a stats dashboard to the admin page"/buildA GO/NO-GO check, a plan, the implementation, tests, and a review
Check"review my changes"/reviewFindings on the current diff by severity, with no edits
Prove"is this review-ready?"/proof-of-workThe project's real typecheck, tests, lint, and a screenshot for UI work
Ship"ship it"/shipA pushed branch, a PR in the house format, and CI watched until it settles
Pause"done for today"/handoffSaved state; "continue where we left off" resumes it in a new session

Claude Code also has a built-in /review. When you want the cc-settings one, say "run the cc-settings local pre-commit review" or pick it from the skill picker.

Big tasks split themselves. Work that spans several files, a long chain of tool calls, or security-sensitive code gets handed to focused agents (explore, implement, test, review, security) that run in the background and report back. You can keep talking in the main conversation while they work.

Every workflow for every situation, including audits, triage of a client repo, and adversarial verification, is in the manual. The skill guide says what each one can change and when it stops to ask.

Set up the projects you work in

cc-settings brings the team standards to every repository. Each project can add its own instructions on top.

  • Starting a new Darkroom project: say "new darkroom project" or run /dr-init. It creates the project from the satus or novus starter. The native /init is a different command that only writes a CLAUDE.md.
  • Giving a project its own instructions: put them in AGENTS.md at the project root. Claude Code and Codex both read it. Good content is what an agent cannot learn from the code: the commands to run, the environments, the traps, and the decisions that look wrong but are deliberate.
  • Existing project with a CLAUDE.md: say "migrate to agents.md" or run /cc migrate. Claude Code ignores AGENTS.md while a CLAUDE.md exists, so the two drift apart. The session banner tells you when a project needs this.
  • Planned work on GitHub: link the issue and /handoff posts progress to it, so agents read the issue and update it as they go.

Habits that change results

Small habits make the biggest difference in how well sessions go:

  1. Say the outcome, not the steps. "Fix the flaky checkout test" works better than a list of commands. Add constraints you care about ("read only", "don't touch the API").
  2. One task per session. Run /clear between unrelated tasks. Long, mixed sessions get slower, cost more, and lose track of details.
  3. Save before you stop or risk something. /handoff at the end of a day or a long session; /checkpoint before a risky refactor or migration, so you can roll back.
  4. Raise effort only for hard turns. The default is tuned for everyday work. Use /effort high or /effort xhigh for hard debugging, audits, or migrations, or add ultrathink to a single message.
  5. Ask for a second opinion when it matters. "Poke holes in this" (/poke-holes) sends independent agents to find and disprove problems. Asking "what could go wrong?" before you commit to a plan gets a risk review.
  6. Read the statusline. It shows context size and usage limits. When context passes about 150K tokens, hand off or compact instead of pushing on.
  7. When something feels off, look before you guess. whats-on.ts shows what is installed and active; troubleshooting covers hook warnings and install health.

Make it better for everyone

cc-settings improves when people feed back what they learn. There are three levels, from quickest to most involved:

  1. Share a lesson. When you hit a gotcha, a convention, or a decision the team should know, say "share this" or run /share-learning. It lands in the shared team-knowledge repository, and every machine sees it right before the command or file edit it applies to.
  2. Keep a workflow that worked. When a session found a good way to do something repeatable, say "turn this session into a skill" or run /harvest. It proposes a skill, rule, or team note built from what actually happened.
  3. Look back weekly. /retro reports what you shipped, how sessions went, and quality trends, and shows which guardrails fired and whether they helped.

To change cc-settings itself, clone it and read the maintainer docs. The short version:

  • Settings live in config/ as fragments that the installer merges into ~/.claude/settings.json. Edit the fragments, never the installed file.
  • A new or reworded skill ships with an eval case in evals/; see skill authoring.
  • Run bun test, bun run typecheck, and bun run lint before pushing.
  • If you only want to suggest something, open an issue with the behavior you saw and what you expected.

Keep it current

  • Update: say "update cc-settings" or run /cc update in a session, or re-run the install command. Restart the product afterwards.
  • Auto-update (macOS): add --auto-update=on to the install command for a daily check at 10:00 local time. --auto-update=off removes it.
  • Check health: npx darkroom-settings --status reports installed versus packaged state.
  • Undo: bun src/setup.ts --rollback from a checkout restores the newest backup. The installation reference covers uninstall.

What cc-settings adds on top of a vanilla install

A fresh Claude Code or Codex install is a capable general assistant with no memory of how this team works. cc-settings installs that memory, plus the checks that make "done" mean the same thing on every machine. The same request behaves differently once it is installed:

You sayVanilla Claude Code or CodexWith cc-settings
"Fix this bug"Edits the first plausible cause and reports doneReproduces first, names the cause before the fix, keeps the change inside the bug's scope, runs the real tests afterwards
"Review my changes"May start editing while it reviewsStays read-only, checks the diff against the team checklist, reports by severity
"Ship it"Pushes whatever is in the treeRuns the repository's own type check, build, tests, and lint, opens the PR in the house style, watches CI
"Build a header component"Writes a component its wayUses the Darkroom starter conventions: CSS modules, no manual memoization, the accessibility and performance rules
git push --force origin mainRuns itA permission rule denies it; a hook blocks rm -rf and other destructive commands before they execute
bun drizzle-kit push on a project with dataRuns itA hook surfaces the team note that this command can truncate production tables
Working in a large repoReads files one at a timeDelegates to focused agents for exploration, implementation, testing, review, and security, on cheaper models
Something worth remembering for the teamLost when the session ends/share-learning posts it to the shared knowledge repo, and later sessions on every machine are reminded of it when it applies

The pieces

  • Standards. AGENTS.md holds the coding standards and guardrails every tool reads; Claude Code gets its copy as CLAUDE-FULL.md, installed as ~/.claude/CLAUDE.md. Twelve topic rules (TypeScript, React, performance, accessibility, security, git, motion, style) load only for the files they cover, and six stack profiles (Next.js, React Router, React Native, Tauri, WebGL, orchestration) add the specifics of each starter.
  • 26 skills. Named workflows selected from ordinary language or pinned with /name in Claude and $name in Codex: fix, build, review, ship, audit, poke-holes, handoff, and the rest. The skill guide lists what each one changes and when it asks.
  • 10 role agents. Planner, explorer, implementer, tester, reviewer, security reviewer, scaffolder, deslopper, orchestrator, and a cross-model verifier. Big work is divided instead of held in one conversation, and each role runs on the model tier its job needs.
  • 36 hooks on 18 lifecycle events. Small programs that run around tool calls, commits, pushes, compaction, and session start or end. They block destructive commands, require proof before a PR, remind about docs before an install, nudge when unreviewed agent output piles up, and inject context the model would otherwise never see. Claude gets the full set; Codex gets the compatible plugin subset and asks you to review it once through /hooks.
  • Model routing. Effort pinned to medium, subagents on Sonnet, planning and decisions on the session model, bulk or mechanical work and one cross-model review per PR or direct push routed to Codex when the bridge is installed. The statusline shows the usage limits that drive that routing.
  • Connected tools. In Claude, four MCP servers: Context7 for current library docs, a TypeScript code map for call graphs and blast radius, Figma, and Chrome DevTools for screenshots and Lighthouse. Codex gets the Figma server only and reports a missing capability instead of faking the rest.
  • Verbatim compaction. With a TypeSafe key, long sessions compact near the 200K working ceiling by removing stale tool output that Jev scores as no longer needed, while every user and assistant message stays word for word. Without a key, native summary compaction applies. See verbatim compaction with Jev.
  • Team knowledge. A shared repository of decisions, conventions, and gotchas that every machine reads. Notes are posted with /share-learning and surface automatically before the command or file edit they apply to.
  • Ownership and rollback. The installer records what it owns, keeps a backup per product, fingerprints its hooks, and can preview, roll back, or uninstall without touching your own configuration. Read SECURITY.md if a session ever warns about hook trust.

What it leaves alone

Your login and subscription, your permission mode, your personal memory, and any setting the installer does not own. Put your own global instructions in ~/.claude/personal.md: the installed CLAUDE.md imports it, and setup never replaces it (Claude Code only). It does not grant GitHub, Figma, or browser access, and it does not make the two products identical: see Claude Code and Codex for what each host can and cannot do.

Choose where to read next

GoalStart here
Find the workflow for a specific outcomeManual
Choose a skill and understand what it can changeSkill guide
Prove the setup with a harmless first taskYour first session
Install safely and understand every side effectInstallation
Compare Claude Code and Codex behaviorHost parity
Diagnose an installed setupTroubleshooting
Understand the whole systemSystem overview
Understand why advice becomes an enforced gateThe flow
Browse every user, concept, maintainer, and history documentDocumentation index

Why the team maintains it

Written standards, workflows, and proof gates reduce per-machine drift. They also make the codebase more legible to humans: the conventions an agent needs are the same debt the team owes its engineers.

darkroom.engineering | MIT

Source 1 files
hooks/register.ts 154 lines
1// context-report — a function-hook plugin that says, once per context load,
2// exactly which instruction files the engine put behind the `claudeMd` block:
3// tier, path, whether AGENTS.md arrived through an `@` import or the native
4// read (Claude Code 2.1.277+), and what that costs. The classic SessionStart
5// banner inferred this from the filesystem; `prompt.context` hands it over as
6// fact, `@` imports included. Two hints ride on the same facts: a project
7// whose CLAUDE.md keeps its AGENTS.md from loading, and a user CLAUDE.md that
8// lost its @AGENTS.md import. See docs/hooks-reference.md "Function hooks
9// (early access)".
10//
11// Deliberately has NO runtime imports (`import type` erases), so the pure
12// functions here run under `bun test` without resolving 'claude-code'.
13import type { InstructionFile, On, PluginOptions, PromptContextInput, Register, SessionStartInput } from "claude-code";
14
15export type ReportInput = {
16  files: readonly InstructionFile[];
17  /** The session's working directory, absolute. */
18  cwd: string;
19  /** The home directory, for `~/` spellings; undefined shortens nothing. */
20  home: string | undefined;
21  /** Whether `<cwd>/AGENTS.md` or `<cwd>/.claude/AGENTS.md` exists on disk,
22   *  for the migration hint's wording; undefined when unknown. */
23  projectAgentsMdExists: boolean | undefined;
24};
25
26const PROJECT_CLAUDE_NAMES = new Set(["CLAUDE.md", "CLAUDE.local.md"]);
27
28function basename(path: string): string {
29  const i = path.lastIndexOf("/");
30  return i === -1 ? path : path.slice(i + 1);
31}
32
33function isUnder(path: string, dir: string): boolean {
34  return path === dir || path.startsWith(dir.endsWith("/") ? dir : `${dir}/`);
35}
36
37/** `~/x` under home, `./x` under cwd, else the absolute path. */
38export function shortPath(path: string, cwd: string, home: string | undefined): string {
39  if (isUnder(path, cwd)) return `./${path.slice(cwd.length).replace(/^\/+/, "")}`;
40  if (home && isUnder(path, home)) return `~/${path.slice(home.length).replace(/^\/+/, "")}`;
41  return path;
42}
43
44function kib(bytes: number): string {
45  return `${(bytes / 1024).toFixed(1)} KB`;
46}
47
48/**
49 * Pure: the transcript lines for one context load. The first line always
50 * lists what loaded; the hints follow only when the facts call for them.
51 */
52export function report(input: ReportInput): string[] {
53  const { files, cwd, home } = input;
54  if (files.length === 0) return ["Instructions loaded: none."];
55
56  const bytes = files.reduce((n, f) => n + f.content.length, 0);
57  const byKind = new Map<string, string[]>();
58  let memoryCount = 0;
59  for (const f of files) {
60    if (f.kind === "memory") {
61      memoryCount += 1;
62      continue;
63    }
64    // An imported file shows as `+@name` after its importer, so a CLAUDE.md
65    // that pulls in AGENTS.md reads `~/.claude/CLAUDE.md +@AGENTS.md`.
66    const label = f.parent ? `+@${basename(f.path)}` : shortPath(f.path, cwd, home);
67    const list = byKind.get(f.kind) ?? [];
68    list.push(label);
69    byKind.set(f.kind, list);
70  }
71  const parts: string[] = [];
72  for (const kind of ["managed", "user", "project", "local"]) {
73    const list = byKind.get(kind);
74    if (list && list.length > 0) parts.push(`${kind} ${list.join(" ")}`);
75  }
76  if (memoryCount > 0) parts.push(`memory ${memoryCount}`);
77  const lines = [`Instructions loaded (${files.length} files, ${kib(bytes)}): ${parts.join(" · ")}`];
78
79  // A project-tier CLAUDE.md or CLAUDE.local.md is what the engine read
80  // instead of the project's AGENTS.md (2.1.277 default mode).
81  const projectClaude = files.filter(
82    (f) => (f.kind === "project" || f.kind === "local") && !f.parent && PROJECT_CLAUDE_NAMES.has(basename(f.path)),
83  );
84  if (projectClaude.length > 0) {
85    const names = projectClaude.map((f) => shortPath(f.path, cwd, home)).join(", ");
86    const verb = input.projectAgentsMdExists === false ? "rename it to" : "merge it into";
87    lines.push(
88      `${names} is the project instructions here, so its AGENTS.md is not read (Claude Code 2.1.277+ reads AGENTS.md only where no CLAUDE.md exists). Run /cc migrate to ${verb} AGENTS.md, which Codex and Cursor read too.`,
89    );
90  }
91
92  // The user CLAUDE.md cc-settings installs must import AGENTS.md; Claude Code
93  // never reads ~/.claude/AGENTS.md on its own.
94  const userClaude = files.find((f) => f.kind === "user" && !f.parent && basename(f.path) === "CLAUDE.md");
95  if (userClaude) {
96    const importsAgents = files.some((f) => f.parent === userClaude.path && basename(f.path) === "AGENTS.md");
97    if (!importsAgents) {
98      lines.push(
99        `${shortPath(userClaude.path, cwd, home)} has no @AGENTS.md import, so the standards file is not loaded; rerun bash setup.sh (cc-settings 15.24.0+).`,
100      );
101    }
102  }
103  return lines;
104}
105
106// $ is never bound to a name: `claude plugin validate` requires every call on
107// it to read as `$.noun.event(...)` at the call site.
108export const register: Register = (on: On, _options: PluginOptions) => {
109  let isInteractive = true;
110  let lastReport = "";
111
112  on("session.start", ($, event: SessionStartInput, next) => {
113    isInteractive = event.isInteractive;
114    return next(event);
115  });
116
117  on("prompt.context", async ($, event: PromptContextInput, next) => {
118    // Report on what leaves the chain, not what enters it: the built-in
119    // agents-md mod sits beneath this plugin and adds a project's AGENTS.md
120    // files inside next(), so `event.instructionFiles` would miss them.
121    const result = await next(event);
122    const files = result.instructionFiles;
123    // Undefined means a hook rewrote the claudeMd text; the files behind it
124    // are unknown and there is nothing honest to report.
125    if (files) {
126      try {
127        const cwd = await $.session.cwd();
128        const home = await $.env.get("HOME");
129        let projectAgentsMdExists: boolean | undefined;
130        try {
131          const [root, nested] = await Promise.all([
132            $.fs.stat(`${cwd}/AGENTS.md`),
133            $.fs.stat(`${cwd}/.claude/AGENTS.md`),
134          ]);
135          projectAgentsMdExists = root.kind === "file" || nested.kind === "file";
136        } catch {
137          projectAgentsMdExists = undefined;
138        }
139        const lines = report({ files, cwd, home, projectAgentsMdExists });
140        const text = lines.join("\n");
141        if (text !== lastReport) {
142          lastReport = text;
143          for (const line of lines) $.ui.log(line, { to: isInteractive ? "transcript" : "debug" });
144        }
145      } catch (error) {
146        $.ui.log(`context-report: skipped (${error instanceof Error ? error.message : String(error)})`, {
147          to: "debug",
148        });
149      }
150    }
151    return result;
152  });
153};
154