Cursor CLI / cookbook

Recipes for the agent binary: a permissions model with real glob patterns, a print mode that infers itself when you pipe, and shell mode for when you just want to run the command yourself. Hand-written, unlike the cheat sheet — the commands are real, the combinations are editorial.

Cheat sheet Cookbook Changes

Permissions

Make the agent structurally unable to read your secrets starter

You want .env, keys, and credentials off the table entirely — not "the model was told not to".

on the sheet: permissionsconfig

  1. Deny by pattern. The rules take globs, so you can name whole shapes of file rather than enumerating them.

    .cursor/cli.json
    {
      "permissions": {
        "allow": [
          "Shell(git)",
          "Shell(npm run test)",
          "Read(src/**/*.ts)"
        ],
        "deny": [
          "Read(.env*)",
          "Write(**/*.key)",
          "Shell(rm)"
        ]
      }
    }
    

A deny on Read(.env*) beats any instruction in a prompt, because it holds even when the agent has a good reason to look — a failing test that needs the database URL is exactly the moment a polite "don't read secrets" gets rationalized away.

Give a CI agent exactly one job intermediate

An agent runs on every PR and should comment — but never push, never branch, never merge.

on the sheet: permissionsheadless

  1. State the boundary in the prompt and enforce it in the config. The prompt tells it what you want; the deny list is what holds when the prompt doesn't.

    agent -p "Review the diff and post findings as a PR comment.
    IMPORTANT: Do NOT create branches, commit, push, or merge." \
      --model gpt-5
    
  2. Back it with a config the prompt can't talk its way past.

    .cursor/cli.json
    {
      "permissions": {
        "allow": ["Shell(gh pr comment)", "Shell(git diff)", "Shell(git log)"],
        "deny": ["Shell(git push)", "Shell(git commit)", "Shell(gh pr merge)"]
      }
    }
    

Two layers, because they fail differently. The prompt shapes intent and produces better work; the deny list is what's left standing when the model decides pushing a fix would be helpful.

Headless & CI

Pipe the agent without remembering a flag starter

You want the agent's answer in a shell variable or another process.

on the sheet: headlessflags

  1. Print mode is inferred when stdout isn't a TTY — piping is enough, -p is belt and braces.

    agent -p "List every TODO in src/ with its file and line." > todos.txt
    
  2. For machine-readable output, ask for JSON explicitly. --output-format is only valid in print mode.

    agent -p "Is the build config valid? Answer yes or no." \
      --output-format json | jq -r '.result'
    
  3. stream-json emits NDJSON — one line per assistant message — so you can act on progress instead of waiting.

    agent -p "Refactor the auth module." --output-format stream-json \
      | while read -r line; do echo "$line" | jq -r '.type'; done
    

The inference is a nice touch but it cuts both ways: a script that works interactively can silently change behavior when its stdout gets redirected. If you depend on the format, name it.

In the session

Run the command yourself, mid-conversation starter

You need to check something — the test output, the branch, the file listing — without dropping the thread you're in.

on the sheet: shellkeys

  1. Shell mode runs in your login shell ($SHELL) with the CLI's working directory. It's a real shell, so chain when you need to move.

    cd subdir && npm test
    
  2. Keep the commands non-interactive. A prompt-for-input command will just hang — cancel with Ctrl+C and add the non-interactive flag.

    npm install --yes        # not: npm install (which may prompt)
    git rebase --continue    # not: git rebase -i
    

The output lands in the conversation, so the agent sees what you saw. That's the point — it beats copy-pasting a stack trace back in, and it keeps the agent's picture of the repo honest.

Continue where you stopped starter

The task outlived the session and you don't want to re-explain it.

on the sheet: launchflags

  1. Resume the previous conversation rather than starting cold.

    agent --continue
    

Re-explaining context isn't just slow, it's lossy — you'll summarize away the detail that turns out to matter. Resuming keeps the agent's working memory of the codebase intact.