Skip to content

Refactor safely

kast never writes code behind your back. Every mutation follows plan → hash → apply: you ask for a change, kast returns an edit plan plus content hashes of the files it read, you eyeball the plan, then send it back for application. If any file changed on disk in between, the hashes won't match and the daemon refuses to write. Conflict-aware, fully auditable, no surprises.

The plan → hash → apply flow

The sequence below is the full round-trip for a guarded rename. The same flow applies to any mutation that returns fileHashes.

sequenceDiagram
    title Guarded Mutation Round Trip
    participant U as "You / Agent"
    participant CLI as "kast CLI"
    participant D as "Daemon"
    U ->> CLI: raw/rename (file, offset, newName)
    CLI ->> D: raw/rename
    D -->> CLI: edit plan + fileHashes
    CLI -->> U: Review edits
    Note over U: Verify edits and hashes
    U ->> CLI: raw/apply-edits (plan + hashes)
    CLI ->> D: raw/apply-edits
    D -->> D: Verify hashes match disk
    D -->> CLI: Applied result
    CLI -->> U: Success
    U ->> CLI: raw/resolve (verify new name)
    CLI ->> D: raw/resolve
    D -->> CLI: Updated symbol identity

Rename a symbol

raw/rename computes every text edit needed to rename a symbol across the workspace — without writing anything. The response carries fileHashes you can pipe straight into apply-edits.

Plan a rename
kast rpc '{"jsonrpc":"2.0","id":1,"method":"raw/rename","params":{"position":{"filePath":"/absolute/path/to/src/Sample.kt","offset":20},"newName":"welcome","dryRun":true}}'
rename request
{
  "method": "raw/rename",
  "id": 1,
  "jsonrpc": "2.0",
  "params": {
    "position": {
      "filePath": "/workspace/src/Sample.kt",
      "offset": 20
    },
    "newName": "welcome",
    "dryRun": true
  }
}
Natural-language prompt
Use the Kast skill to rename the function at offset 20 in
/workspace/src/Sample.kt to "welcome". Show me the edit plan
before applying it.

The response contains the full edit plan:

rename response
{
  "edits": [
    {
      "filePath": "/workspace/src/Sample.kt",
      "startOffset": 20,
      "endOffset": 25,
      "newText": "welcome"
    },
    {
      "filePath": "/workspace/src/Sample.kt",
      "startOffset": 48,
      "endOffset": 53,
      "newText": "welcome"
    }
  ],
  "fileHashes": [
    {
      "filePath": "/workspace/src/Sample.kt",
      "hash": "fd31168346a51e49dbb21eca8e5d7cc897afe7116bb3ef21754f782ddb261f72"
    }
  ],
  "affectedFiles": ["/workspace/src/Sample.kt"]
}

fileHashes captures the content hash of every affected file at plan time. Hold onto these — you'll send them back when you apply.

Tip

Rename defaults to dryRun: true. Set dryRun: false in the JSON-RPC request to compute and apply in one call. The CLI always uses dry-run so you can review before writing.

Apply edits

Once you've reviewed the plan, send it back with the same fileHashes. The daemon re-reads each file, recomputes the hash, and rejects the request if anything drifted.

Apply the rename plan
kast rpc --request-file=rename-plan.json

Save a raw/apply-edits request containing the reviewed edits and fileHashes into rename-plan.json.

raw/apply-edits request
{
  "method": "raw/apply-edits",
  "id": 2,
  "jsonrpc": "2.0",
  "params": {
    "edits": [
      {
        "filePath": "/workspace/src/Sample.kt",
        "startOffset": 20,
        "endOffset": 25,
        "newText": "welcome"
      },
      {
        "filePath": "/workspace/src/Sample.kt",
        "startOffset": 48,
        "endOffset": 53,
        "newText": "welcome"
      }
    ],
    "fileHashes": [
      {
        "filePath": "/workspace/src/Sample.kt",
        "hash": "fd31168346a51e49dbb21eca8e5d7cc897afe7116bb3ef21754f782ddb261f72"
      }
    ]
  }
}
Natural-language prompt
Apply the rename plan you just showed me.

When the hashes match, the daemon writes the edits and responds:

raw/apply-edits response
{
  "applied": [
    {
      "filePath": "/workspace/src/Sample.kt",
      "startOffset": 20,
      "endOffset": 25,
      "newText": "welcome"
    },
    {
      "filePath": "/workspace/src/Sample.kt",
      "startOffset": 48,
      "endOffset": 53,
      "newText": "welcome"
    }
  ],
  "affectedFiles": ["/workspace/src/Sample.kt"]
}

If a file changed between plan and apply, the daemon returns an error instead of writing partial edits. Re-run the rename to get a fresh plan with current hashes.

Verify the result

Conflict detection guarantees the edits kast wrote match the edits kast planned. It does not guarantee the result still compiles. After any non-trivial refactor, run diagnostics on the affected files — or re-resolve the renamed symbol to confirm identity survived.

Confirm the workspace still compiles
kast rpc '{"jsonrpc":"2.0","id":1,"method":"raw/diagnostics","params":{"filePaths":["/absolute/path/to/src/Sample.kt"]}}'
Or re-resolve the renamed symbol
kast rpc '{"jsonrpc":"2.0","id":1,"method":"raw/resolve","params":{"position":{"filePath":"/absolute/path/to/src/Sample.kt","offset":20}}}'

If diagnostics surface an unexpected error — or resolve returns a different symbol — read the stale-results troubleshooting entry before trying another rename. The daemon may need a raw/workspace-refresh via kast rpc to pick up edits made outside its observation window.

Optimize imports

raw/optimize-imports removes unused imports and sorts the rest for the files you name. Same plan-and-apply shape as rename: you get an edit plan with fileHashes, you review, you apply.

Optimize imports
kast rpc '{"jsonrpc":"2.0","id":1,"method":"raw/optimize-imports","params":{"filePaths":["/absolute/path/to/src/Sample.kt"]}}'
imports/optimize request
{
  "method": "raw/optimize-imports",
  "id": 3,
  "jsonrpc": "2.0",
  "params": {
    "filePaths": ["/workspace/src/Sample.kt"]
  }
}
Natural-language prompt
Use the Kast skill to optimize imports in
/workspace/src/Sample.kt.

Same shape as rename — edits, fileHashes, affectedFiles. Send to apply-edits when ready.

Next steps

  • Validate code — run diagnostics after your refactor to confirm the workspace compiles cleanly
  • Conflict-rejected apply — what to do when the daemon refuses to write because hashes drifted