Knowledge base · Guide

Automate editing your content with Claude Code

90Ø120 pitchØ60 pitchAB
Fig. 01 Spur wheel and pinionThirty teeth to fifteen: the wheel turns once for every two turns of its pinion.
Kind
Guide
Published
20 Oct 2026
Reading
22 min

Applies to: every Coho project, with the coho CLI and Claude Code, in a terminal or in the Code tab of the Claude desktop app

You will learn: how to give Claude Code a safe, limited way into one Coho project, how to tell it about your content and your house style, and a working routine where Claude drafts changes on a branch and you review and release them


The short version

Claude Code can run commands in a terminal, and Coho's CLI can do everything the portal does. Put the two together and you can ask for copy changes in plain language ("tighten the pricing intro", "fix every SEO description over 160 characters") and have Claude read the real entries, edit them, and show you exactly what it changed.

The setup keeps Claude in its lane:

  • It gets a project token with the author role. It can read and write content and create branches in one project. It cannot merge, tag, promote, or touch any other project.
  • It works on a branch of its own. Nothing it writes reaches your trunk, let alone your site, until you merge it.
  • You review the diff and release it, the same way you would any change.

Three things do the explaining, so you do not have to:

  • The Claude Code plugin teaches Claude how the CLI works, and pre-approves the commands that only read.
  • coho init --agents writes COHO.md from your live project: every type and field, the locales you use, your refs, and the rules for writing safely.
  • coho status tells Claude what its token may do here, before it tries.

It takes about ten minutes to set up. It works the same in a terminal and in the Claude desktop app: both read the same CLAUDE.md and settings files, and this guide points out the few places where the steps differ.


Step 1: install the CLI and the plugin

uv tool install coho-cli
# or
pipx install coho-cli

The package is coho-cli; coho on PyPI is an unrelated project. Check it with coho --version.

Then add the Coho plugin, in Claude Code:

/plugin marketplace add coho-cms/coho-cli
/plugin install coho@coho-cms

The plugin carries a skill that teaches Claude the CLI: start from coho status, read COHO.md, keep to what the role allows, leave signing in and releasing to a person, and ask before anything destructive. It loads only when a session touches Coho, so it costs nothing the rest of the time. Without it Claude can still run coho; it will just spend its first few commands working out how.


Step 2: make a token for Claude

A project token is a credential for automation: one role, in one project, with an expiry, revocable at any time. A project owner makes it:

coho use acme "Marketing site"
coho token create --role author --label "Claude Code (Ana)" --expires 30

The token, coho_pt_…, is printed once. Keep it for the next step.

Why author:

RoleCanCannot
viewerRead contentWrite anything
authorRead and write content, create branchesMerge, tag, or promote
maintainerAll of that, plus merge, tag, and promote to dev and qaPromote to stage or prod

author is enough to draft, and stops short of anything that changes what is released. A token belongs to the project, not to you, and every change it makes is recorded as made by that token, so its label is how you will recognize Claude's edits in an entry's history. One token per person using Claude makes that history easy to read.

A token works only for its own project's content. It cannot list your projects, read your account, or run coho whoami, so the CLI needs the account and project by id. Find them with coho status while you are signed in as yourself. coho role ladder prints every role with the permissions it holds, if you want to see the whole ladder before you choose one.


Step 3: the folder Claude works in

Claude Code works in a folder, and there are two kinds. Pick the one that matches your job: everything after this step is the same either way.

A folder of its own, for content work. If you write and edit content and somebody else ships the code, make an empty folder:

mkdir acme-content && cd acme-content
mkdir .claude

Claude's work happens in Coho rather than in local files, so this folder holds nothing but the two files the next steps put there. There is no build to break and no repository to protect.

Your existing repository, for code and content together. If you build the site as well as write it, so that a type gains a field, the component that renders it changes, and the copy lands in one release, work in the repository itself. There is no folder to make: coho init --agents writes COHO.md beside your code, and .claude/ is probably already there. Two things to know:

  • A worktree earns its keep now. Claude is editing local files as well as content, so an isolated checkout is worth having. Commit COHO.md and CLAUDE.md, or list them in .worktreeinclude, or the session starts without them.
  • Three refs do three different jobs. Mixing them up is the usual confusion:
Set byDecides
The CLI's refcoho ref switch, coho branch createwhat Claude reads and writes
ref in coho.config.jsonyou, by handwhich ref the generated types come from
COHO_REF where the site buildsyour .envwhich content the site renders

A content change on a branch is invisible to the build until you merge and release it. That is the point: the site goes on rendering the release it was given while Claude works.

Give Claude the token

Claude needs three environment variables: COHO_TOKEN, COHO_ACCOUNT, and COHO_PROJECT. A project token knows which project it belongs to, but the CLI still needs the ids to address it. There are two ways to set them.

In the folder's settings (terminal and desktop app). Create .claude/settings.local.json:

{
  "env": {
    "COHO_TOKEN": "coho_pt_…",
    "COHO_ACCOUNT": "0192b4…",
    "COHO_PROJECT": "0192c5…"
  }
}

settings.local.json is your personal settings file, never meant to be shared. If the folder is a git repository, make sure .claude/settings.local.json is in .gitignore so the token is never committed. This is the simplest choice, and the permissions in step 6 go in the same file.

Outside the folder, if you would rather not keep the token in a file:

  • Terminal: export the three variables in your shell before you start claude.
  • Desktop app: set them in a saved local environment. In the Code tab, open the environment selector, edit a local environment, choose Environment variables, and enter one KEY=value per line. The app stores them encrypted on your machine, and only sessions started from that environment get them. On Windows, the desktop app does not read PowerShell profiles, so use this rather than a profile script.

If a variable is set in both places, the one Claude Code starts with wins over the settings file. When an old token seems to linger, check your shell profile and the app's environments first.

Do not set COHO_REF. A fresh folder has no ref in context, so point it at your trunk once, with your own login or the token:

coho ref switch v0.0.x

The ref is then remembered, and coho branch create moves it to each new branch. Set COHO_REF in the environment instead and it overrides that for every command, so Claude's writes would land on whatever it names rather than on the branch it just made.

Open the folder in Claude Code

  • Terminal: cd into the folder, then run claude.
  • Desktop app: in the Code tab, choose the folder with the project folder control in the row under the prompt box, before you send your first message. Worktree follows the choice above: leave it off for a folder of its own, where there is nothing local to isolate, and turn it on in a repository if that is how you already work.

Check that it works

Send Claude this:

Run coho status and tell me what I can do in this project.

Claude runs one command and tells you the project, the role the token holds, and the permissions that come with it. If it reports exit code 3 (UNAUTHENTICATED), the token has expired or was revoked; if permissions comes back empty, the token belongs to a different project. If it reports coho: command not found, the folder uv or pipx installed it into is not on the PATH Claude Code sees: add it to your shell profile, or, on Windows, to your user environment variables, and restart the app.


Step 4: describe the project to Claude

What a new session lacks is your project: its types, its fields, which text is localized, what your refs are called. Generate that:

coho init --agents

It reads the live project and writes COHO.md: the account and project ids, every content and component type with every field (its kind, whether it is required or localized, its choices, the types it references, the help text its editor shows), the locales your entries actually use, your branches and environments and what each points at, your latest tags, and the rules for changing content safely. AGENTS.md and CLAUDE.md get a short block pointing at it, between <!-- BEGIN:coho-agent-rules --> and <!-- END:coho-agent-rules -->; nothing outside the markers is touched, so a file you already have keeps everything in it.

Commit all three. Run it again whenever the content model changes: the file is generated from the project, so it never drifts the way a hand-written description does, and a difference in the diff is the model having changed. coho init --agents --check writes nothing and exits 1 when the files are out of date, which is what you want in CI, with a read-only token:

COHO_TOKEN=$COHO_READ_TOKEN coho init --agents --check

Do not edit COHO.md by hand. What you want to say in your own words goes in the next step.


Step 5: tell Claude how you write

COHO.md describes your content. It cannot know your voice. Claude Code reads a file called CLAUDE.md in the folder at the start of every session, and that is where you put what you would tell a new copywriter on their first day. In a repository you will already have one, about the code: add this as a section of it rather than replacing it.

# Marketing site copy

The content of our marketing site, in Coho. `COHO.md` describes the project:
read it first.

## Our voice
- Plain, specific, and short. Say what the product does, not how it feels.
- Sentence case for headings. No exclamation marks.
- "Coho", never "COHO" or "Coho's platform".
- SEO titles under 50 characters (the site adds " · Coho CMS"); descriptions 140–160.

## How we work here
- One branch per job: `coho branch create copy/<short-topic>`.
- When the job is done, run `coho diff` against the trunk and summarize every change.
- When asked for copy ideas, offer two or three options before writing any.
- Never merge, tag, or promote. A person does that after review.

That is all it needs. The content model, the locale on every text field, the ETag on every write, exit code 4 meaning read it again: that is in COHO.md already, and the plugin's skill covers how the CLI behaves. Keep this file for the things only you know, and it stays short enough to keep current.


Step 6: decide what Claude may run without asking

The plugin already pre-approves the commands that only read, status, type get, entry get, entry history, diff and the rest, so you are not answering a prompt for every lookup. What is left to decide is the writes. Add them to .claude/settings.local.json, beside env (or on their own, if you set the token elsewhere):

{
  "env": { "…": "…" },
  "permissions": {
    "ask": [
      "Bash(coho branch create *)",
      "Bash(coho entry put *)",
      "Bash(coho entry create *)"
    ],
    "deny": [
      "Bash(coho entry delete *)",
      "Bash(coho type put *)",
      "Bash(coho type delete *)",
      "Bash(coho merge *)",
      "Bash(coho tag *)",
      "Bash(coho promote *)",
      "Bash(coho rollback *)"
    ]
  }
}

The author token already cannot merge, tag, or promote, and the skill tells Claude not to try; the deny list makes it certain. It also keeps Claude away from two things an author can do, deleting entries and changing content types, which are better left to a person. Once you trust the routine, move entry put from ask to allow and review the diff at the end instead of each write.

If the content model is part of your job, as it is when you are changing the code too, move coho type put from deny to ask and run coho init --agents after each change, so the generated file and your types never disagree.

Bash(coho entry get *) matches coho entry get with any arguments. An allow rule does not match a command that starts by setting a variable (COHO_REF=… coho entry get …), which is why Claude runs commands as they are.

Where you answer the prompts. In the terminal, Claude Code asks in the session, and Shift+Tab switches permission modes. In the desktop app, prompts also appear in the session, and you switch modes with the selector in the row under the prompt box, or Cmd+Shift+M (Ctrl+Shift+M on Windows). For this routine, stay in the default mode, called Manual in the desktop app, so your ask rules are honored. A mode that skips permission prompts would let writes through without asking.


Step 7: a working session

Open the folder in Claude Code, in a terminal or the desktop app, and ask for what you want, the way you would ask a colleague. The examples below are the same in both.

A targeted edit:

The pricing intro is too long. Read it, and give me three shorter versions
that keep the point about never charging for good practice.

Claude reads the module with coho entry get, offers three versions, and waits. You pick one; it creates copy/pricing-intro, writes the field, and shows you the diff.

A sweep:

Check every KB article's SEO description. Fix the ones over 160 characters
or under 120, and list what you changed.

Claude lists the articles, reads each, rewrites the ones out of range on a branch, and reports old and new side by side.

A consistency fix:

We misspelled "inheritance" somewhere. Find every occurrence in any text
field and fix it.

A first draft:

Draft a KB article on rolling back a release, using our existing articles
for tone. Create it as a kbArticle with kind "guide", but don't fill in
publishedAt.

A field, its component and its copy, in one job. In a repository, a change often spans both halves:

Testimonials need an attribution line. Add an optional attribution text field
to the testimonialModule type, regenerate the types from my branch, render it
under the quote, and fill it in for the three testimonials we have.

Claude writes the new definition to a file for you to look at (the ask rule from step 6) and applies it on its branch. Then it pulls the model from that branch rather than from the ref your config names:

npx coho-codegen pull --ref copy/testimonial-attribution
npm run generate

It edits the component against the regenerated types, fills the three entries, and leaves you two diffs: git diff for the code, coho diff for the content. Point coho.config.json back at a tag before you ship, so the site is built from a release and not from a branch that can still move.

Throughout, Claude is working with your real content, under your content model's validation. A field it gets wrong is refused with VALIDATION_FAILED, naming the field, and it corrects course.


Step 8: review and release

When Claude says the job is done, look at the change the way you would look at anyone's:

coho diff v0.0.x copy/pricing-intro

Or open Compare in the portal, pick copy/pricing-intro against v0.0.x, and read the changes field by field. If you want something changed, tell Claude: it is still on the branch.

When you are happy, merge and release with your own login (or have someone with the maintainer role do it):

coho merge copy/pricing-intro --into v0.0.x -m "Shorter pricing intro" --reviewed
coho tag create v1.5.0 --branch v0.0.x -m "Pricing copy" --validate
coho promote prod v1.5.0

--reviewed merges exactly the diff you looked at: if anything changed on either branch since, the merge is refused. The release then goes out like any other, and rolls back like any other.


Tips

  • Keep your voice in CLAUDE.md and your content model in COHO.md. One you write, the other you regenerate.
  • Regenerate after a model change. Run coho init --agents again and commit it. A session working from a stale COHO.md writes fields that no longer exist.
  • Ask for options before edits when the words matter. Choosing between three drafts is faster than correcting one.
  • Give it the numbers. Character limits, reading level, the words you never use: Claude follows rules it is told.
  • One branch per job. Small branches are quick to review, and one you do not like can simply be left unmerged.
  • Let it check its own authority. coho status tells Claude what its role allows, so "can you promote this?" gets an honest answer instead of a failed command.
  • Rotate the token. It expires on its own (30 days, above). coho token list shows when each was last used, and coho token revoke stops one immediately.

Common questions

Can Claude see all my projects? Not with a project token: it works in one project only. You could run Claude Code with your own coho login instead, but then it acts with every role you hold, in every project. A token per project is the safer habit.

Can Claude change the content model? An author token can, which is why the permissions above deny coho type put and coho type delete. If you want help with a type change, ask Claude to write the new definition to a file and apply it yourself after review; changing types is a decision for the people who own the model. Run coho init --agents afterwards, so the next session sees the new shape.

Do I have to keep COHO.md up to date by hand? No. coho init --agents regenerates it from the project, and coho init --agents --check fails a CI job when it has gone stale. Nothing in it is yours to write, so there is never anything to reconcile between your words and the project's.

What if someone edits the same entry while Claude is working? Every write carries the version it read. If the entry changed in between, the write is refused with exit code 4 and Claude reads it again before retrying, as the rules in CLAUDE.md tell it to. Nobody's edit is silently overwritten.

How do I see what Claude changed later? coho entry history @<slug> lists every version of an entry and what made it. Claude's changes carry its token's id, and coho token list matches that id to the label you gave the token.

Is there anything the desktop app does differently? Only how you open the folder, where you can keep the token outside it, and where you switch permission modes, all covered above. Both read the same CLAUDE.md and .claude/settings.local.json, run the same coho commands, and stop at the same prompts. If you turn on Worktree for a session, Claude still reads the folder's settings, but CLAUDE.md comes from the worktree, so commit it or list it in a .worktreeinclude file.

Does this work with other AI tools? Anything that can run a terminal command can use the CLI the same way. The token, the branch, and the review are what keep it safe, whichever tool is typing.