Skip to content

One command installs the agent rules where agent tools read them #157

Description

@V3RON

Request: #154

Problem

Simlock is advisory. It works only when every agent knows the rules: lease first, never call simctl or adb directly. #147 gives those rules one printable text, simlock instructions. Putting that text where an agent tool reads it is still manual. The operator pastes it into each tool, on each machine or in each project, and again after every Simlock upgrade. A missing or stale copy is invisible until an agent breaks another agent's device.

Who it is for

An operator setting up Simlock for agents that run under Claude Code or Codex. They set up one machine for themselves, or one project so the whole team gets the rules from the repository.

Outcome

  • One command puts the agent rules where the agent tool reads them. From its next session on, the agent has the rules with nothing pasted by hand.
  • Claude Code and Codex are supported. Each gets the rules as a skill, so the tool loads them at the start of every session without the agent asking.
  • By default the command installs for the current user. A flag installs into the current project instead, so the files can be committed and shared.
  • The operator can name one tool. With none named, the command installs for every supported tool already set up on the machine or in the project, and says which it skipped.
  • Running the command again replaces what an earlier run wrote. An upgrade is a re-run.
  • The command writes only inside a directory of its own, named after Simlock, under each tool's skills directory. Everything in that directory is its own to replace. It never edits any other file.
  • It says what it wrote, what it replaced, and what it skipped.
  • The rules text it installs is the text simlock instructions prints. There is no second copy to keep in sync. That text comes from Add simlock instructions: the rules an agent must follow, printable and served over MCP #147, so this feature waits for Add simlock instructions: the rules an agent must follow, printable and served over MCP #147 to close.
  • The user manual names both tools and where each install lands.

Non-goals

  • Editing an existing file the operator owns, such as a project's AGENTS.md or CLAUDE.md, or a tool's settings file.
  • A dry run or an uninstall. The report after the run is the preview. Deleting the Simlock directory by hand is the uninstall.
  • Keeping the installed rules current on its own. Re-running after an upgrade is the operator's job.
  • Connecting the tool to Simlock's MCP server, or any other tool setup beyond the rules.
  • Changing the rules text. That is Add simlock instructions: the rules an agent must follow, printable and served over MCP #147.
  • Other agent tools. Tools with no skills directory keep using simlock instructions and paste.

Completion conditions

  • On a machine with no Simlock files, running the command for Claude Code, then for Codex, then starting each tool, gives the agent the Simlock rules as a skill without a paste. Checked by hand per tool, and by an e2e test that finds the skill at the documented location.
  • The default run writes only under the current user's home. With the project flag it writes only under the current directory.
  • With a tool named, only that tool's Simlock directory is written, and it is created if missing. With none named, only tools whose skills directory already exists are written, and the others are listed as skipped. Naming an unsupported tool is a usage error, exit 2, and writes nothing.
  • The rules text inside each installed skill is byte for byte what simlock instructions prints. Anything around it is the skill's own header, and nothing more.
  • Running the command twice leaves the same files. Running it after the rules text changed leaves only the new text.
  • Nothing outside the Simlock directories changes. A file inside one from an earlier run is replaced.
  • The output is a human view by default and lists every path written, replaced, or skipped. --json prints the same report as one JSON object.
  • The command never connects to or starts the daemon.
  • docs/CLI.md documents the command, both tools, and where each install lands, and adds it to the --json exception list in the intro. pnpm check is green.

Open questions

Decisions

  • None. Simlock's footprint in a tool is one directory named simlock, owned whole; that rule lives in this spec, the tests, and docs/CLI.md, not an ADR.

Technical spec

Modules touched

  • src/instructions/index.ts: add renderSkill(): string. Returns the SKILL.md body: YAML front matter with name: simlock and a one-sentence description (when to use Simlock before touching a simulator or emulator), a blank line, then AGENT_INSTRUCTIONS unchanged. The file ends with AGENT_INSTRUCTIONS byte for byte.

  • src/instructions/setup.ts (new): the one place that knows the tools. A table with two entries, claude-code and codex, each giving for the user scope (base: home directory) and the project scope (base: working directory) a presence directory and a skills directory:

    • claude-code: presence <base>/.claude, skills <base>/.claude/skills
    • codex: presence <base>/.codex, skills <base>/.codex/skills

    The Simlock directory is <skills>/simlock, its only file SKILL.md. Exports setupAgentTools({ filesystem, homeDirectory, workingDirectory, scope, tool? }) returning a SetupReport: { scope: "user" | "project", tools: [{ tool, status: "wrote" | "replaced" | "skipped", path }] }. Two phases, so every exit leaves one state (architecture rule 12):

    1. Inspect. For each selected tool: with tool given, it is selected; otherwise a tool is selected only when its presence directory exists, else skipped. If the Simlock directory path exists and lstat says it is not a directory (file, symlink, other), the whole run is refused before anything is written.
    2. Write. mkdirp the Simlock directory, writeFileAtomic its SKILL.md, then rm every other entry in the Simlock directory. Status is replaced when the directory existed before, else wrote. Nothing outside the Simlock directories is touched.

    Filesystem access goes through the Filesystem port only (architecture rule 9).

  • src/cli/index.ts: new setup command, simlock setup [--project] [--tool <claude-code|codex>] [--json]. --project selects the project scope; default is user. --tool takes exactly one of the two ids; any other value, a second --tool, or a positional is USAGE (exit 2). Never connects to or starts the daemon. Human view by default: one line per tool, <tool> <status> <path>, and for a skipped tool a hint to pass --tool; --json prints the SetupReport as one JSON object. A refused run prints nothing on stdout and fails with the CLI-level code SETUP_REFUSED (exit 2, beside USAGE in the code map), whose message names the path. CliEnvironmentPorts gains homeDirectory and workingDirectory; defaultCliEnvironment fills them from homedir() and process.cwd(), tests from strings. buildCliEnvironment exposes setupAgentTools on CliEnvironment the way it exposes writeConfigFile. Add setup to the USAGE banner.

  • e2e/helpers/cli.ts: CliOptions gains cwd, passed to spawn.

  • docs/CLI.md: ## simlock setup section: both tools, both scopes, the four paths, the report, SETUP_REFUSED; add setup to the --json exception list in the intro and SETUP_REFUSED to the exit-code table. README.md: one sentence under "Getting started" after the simlock instructions one.

Contract and event changes

None. No daemon operation, no event, no config key. SETUP_REFUSED is a CLI-level code like USAGE, not a contract error.

Rules in play

  • docs/internal/agent-rules/documentation.md rule 3: the skill's front matter and the command's output name no path in this repository. Paths on the operator's machine are fine.
  • docs/internal/agent-rules/architecture.md rules 8, 9, 12: frontend-owned, no daemon; Filesystem port only, home and cwd read at the composition root; inspect then write, so a refusal writes nothing.
  • docs/internal/agent-rules/safety.md rule 9, by analogy: a symlink or file where the Simlock directory should be fails closed.
  • docs/internal/agent-rules/testing.md: every title below is a claim; break the code and watch each fail.

Tests

Unit, src/instructions/setup.test.ts on MemoryFilesystem:

  • A named tool is installed even when its presence directory is missing, and the skills directory is created.
  • With no tool named, only tools whose presence directory exists are installed, and the others are reported as skipped.
  • The project scope writes under the working directory and nothing under the home directory, and the user scope the reverse.
  • A second run reports replaced and leaves the same files.
  • A stale file inside the Simlock directory from an earlier run is removed.
  • A file or symlink at the Simlock directory path refuses the run, and nothing is written for any tool.

Unit, src/instructions/index.test.ts:

  • The rendered skill ends with the exact agent instructions, and its front matter names the skill simlock.
  • The rendered skill names no path inside this repository (extend the existing test to cover renderSkill()).

Unit, src/cli/index.test.ts:

  • setup --help prints the usage line and exits 0.
  • setup --tool bogus, two --tool flags, and a positional each fail with USAGE, exit 2, and write nothing.
  • setup --json prints one JSON object with scope and one entry per tool.
  • A refused run fails with SETUP_REFUSED, exit 2, and prints nothing on stdout.

E2e, fast lane, e2e/setup.test.ts, with HOME and cwd pointed at temp directories:

  • simlock setup --tool claude-code writes .claude/skills/simlock/SKILL.md under HOME, exits 0, and starts no daemon.
  • simlock setup --project --tool codex writes .codex/skills/simlock/SKILL.md under the working directory and nothing under HOME.
  • The installed SKILL.md ends with exactly the text simlock instructions prints.

Written by an agent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

feature:readyNo sub-issues; one PR delivers the whole feature.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions