Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Op catalog

Generated from ago help (catalog version v9) at build time. 33 patch ops.

Tools

status

Load or refresh the workspace snapshot. Returns package and file counts and any type errors. No arguments.

help

Return this versioned op catalog: every patch op’s argument schema, one worked example, and its v1 ceilings, plus short descriptions of the six tools. No arguments.

query

Semantic questions against the typechecked snapshot, dispatched by kind: search (case-insensitive name fragment -> exact addresses), inspect (kind, signature, decl position, doc), refs (every reference, tests included, defs marked), callers/callees (static call-graph edges; a call through an interface reports the interface method), implementations (interface -> implementing types, or type -> satisfied interfaces), doc (doc comment text). Args: kind (required), pkg, sym, q (name fragment, for kind=search, falls back to sym), offset (page offset for list results). Lists are position-sorted and paged 50 at a time: count is the total found; a truncated response carries truncated=true and next_offset to pass back as offset.

view

Render a declaration as annotated text. Functions and methods get a per-statement nK: handle prefix plus a generation counter for staleness checks; other declarations (const, var, type) render as plain source. Handles are meaningful only against the generation the same response reports. Args: pkg, sym.

patch

Apply an ordered list of ops as one atomic, generation-checked transaction: every op applies to an in-memory copy, the dirty set re-typechecks once, then everything writes and splices together — or nothing does. Ops compose: an op later in the list can address a handle an earlier op returned, referenced as $1, $2, … by 1-based op index. dry_run runs the identical pipeline and reports accept/reject without writing. Op families (full schemas and examples via help): decl ops (rename, set_body, add_param, upsert_decl, delete_decl, set_doc, add_field, remove_field), statement ops (add_assign, add_call, add_return, add_if, add_for, add_switch, add_case, add_defer, add_go, set_cond, replace_expr, delete_node, wrap_stmts, wrap_error), test ops (add_test, add_test_case, set_test_case, remove_test_case), project ops (delete_file, move_file, add_dependency, remove_dependency, mod_tidy). A decl, test, or project op (rename, set_body, add_param, upsert_decl, delete_decl, set_doc, add_field, remove_field, add_test, add_test_case, set_test_case, remove_test_case) and a statement op cannot edit the same file in one patch; run them as separate patches. An accepted patch that touched exactly one declaration embeds that declaration’s fresh view (same {text, nodes, generation} payload the view tool returns) under “view”, so back-to-back edits need no view call in between; when several declarations were touched the response carries views_omitted instead. Args: pkg/sym (defaults for ops that omit them), generation, dry_run, ops (required, the array of op objects).

test

Run go test, scoped to a package (default the whole workspace) and optionally filtered by name, and return structured per-test results: pass/fail, elapsed time, and captured output for failures. Validation of mutations stays compiler-only; this is how you close the behavior loop after a set of changes. Args: pkg, run (a -run filter).

Patch ops

rename

proves post-splice resolution: every rewritten reference must resolve to the renamed object; reference capture rejects even when the compiler is satisfied

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
tostringyesnew name

Example:

[
  {
    "op": "rename",
    "sym": "Double",
    "to": "Twice"
  }
]

set_body

the coarse escape hatch: replaces the whole block between braces, validated by typecheck like every other op

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
bodystringyesnew body as statements, no surrounding braces

Example:

[
  {
    "op": "set_body",
    "sym": "Double",
    "body": "return v + v"
  }
]

add_param

callers updated with default; a top-level local name := <default> in the body is superseded and deleted (parameters share the body scope), any other same-named body declaration is rejected with its position; references to the function as a value (assigned, passed, satisfying an interface) cannot be repaired and are rejected with their positions

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
namestringyesnew parameter name
typestringyesnew parameter type, e.g. context.Context
defaultstringargument expression for existing call sites; required whenever the function already has callers

Example:

[
  {
    "op": "add_param",
    "pkg": "demo/sig",
    "sym": "Scale",
    "name": "offset",
    "type": "int",
    "default": "0"
  }
]

upsert_decl

add or replace a whole top-level declaration; goimports runs in the loop. Replacement finds declarations in _test.go files too, and replaces single members inside grouped const/var/type blocks in place (send the standalone form; iota groups and members a following bare spec inherits from reject with the blocker named). New declarations append to an existing package file (test funcs to a _test.go file, so they actually run) and may reference symbols other ops in the same patch introduce; a package with no such file gets agent.go / agent_test.go created on demand — including a brand-new package mid-patch, so one atomic patch can create a package and move declarations into it

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
textstringyescomplete declaration source, including doc comment if any; the symbol name is parsed from it
imports[]{path,name}imports the declaration needs that goimports cannot infer: an aliased import or an ambiguous package name; name is the alias, empty for the default

Example:

[
  {
    "op": "upsert_decl",
    "pkg": "demo/lib",
    "text": "// Double doubles v.\nfunc Double(v int) int {\n\treturn v + v\n}"
  }
]

delete_decl

rejected while any non-declaring reference remains outside the declaration itself (a recursive self-call does not count), outside the batch, and outside spans earlier ops in the same patch rewrote; the diagnostics list where. A member of a grouped const/var/type block excises in place (iota and inherited-value members reject, position defines them); a method whose receiver type deletes in the same batch skips the reference guard, the end-of-list typecheck arbitrates

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
syms[]stringbatch form: several symbols delete together, so intra-set references (a helper and the test that used it) do not block

Example:

[
  {
    "op": "delete_decl",
    "sym": "Unused"
  }
]

set_doc

doc comment only; replaces an existing one rather than appending, and does not affect the typecheck surface

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
textstringyesdoc comment body; each line is rendered with a “// “ prefix

Example:

[
  {
    "op": "set_doc",
    "sym": "Double",
    "text": "Double doubles v."
  }
]

add_field

appended to the struct’s field list; rejected if the name already exists

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringthe struct type’s name; defaults to the envelope’s sym
namestringyesnew field name
typestringyesnew field type
tagstringstruct tag, without backticks

Example:

[
  {
    "op": "add_field",
    "sym": "Store",
    "name": "Tag",
    "type": "string"
  }
]

remove_field

rejected while referenced. v1 ceiling: a field sharing a multi-name declaration (“a, b int”) or an embedded field is not supported

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringfield: Type.Field; defaults to the envelope’s sym

Example:

[
  {
    "op": "remove_field",
    "sym": "Config.Legacy"
  }
]

move_decl

relocates the whole declaration (doc comment included) and requalifies every reference, adding imports where needed; a type moves together with its whole method set, and one spec of a grouped const/var/type block extracts standalone. v1 ceilings: the declaration must be self-contained (no uses of its old package’s other top-level symbols) and grouped specs may not lean on iota or an inherited value; each rejects with the blocking names

argtyperequireddescription
pkgstringsource package import path; defaults to the envelope’s pkg
symstringtop-level declaration name; defaults to the envelope’s sym
symsarrayseveral declarations moving as one set (type + constructor + tests); intra-set references are legal; mutually exclusive with sym
to_pkgstringyestarget package import path (must exist by the time this op runs; an earlier upsert_decl in the same patch can create it)
create_pkgbooleancreate a missing module-local target package as part of this patch; without it a missing target rejects (typo safety) and offers this flag as a repair

Example:

[
  {
    "op": "move_decl",
    "pkg": "demo/sig",
    "sym": "Fetch",
    "to_pkg": "demo/lib"
  }
]

set_signature

full parameter/result rewrite: parameters are matched to the old signature by name — carried ones keep each call site’s argument (reordering reorders them), dropped ones drop it, new ones take their default; underscore params pair positionally when their type matches, so widening func(ctx context.Context, _ DecryptFn) keeps the _ argument; a spread call site f(args…) survives insertions before the variadic. Value uses of the function and the body itself are not rewritten: repair them with sibling ops in the same patch or the end-of-list typecheck rejects with the site positions

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
symstringsymbol: Name or Type.Member; defaults to the envelope’s sym
signaturestringyesthe complete new signature as Go text: “(params) results”
defaultsobjectargument expression per NEW parameter name, spliced into every existing call site; required for any new parameter when call sites exist

Example:

[
  {
    "op": "set_signature",
    "pkg": "demo/sig",
    "sym": "Fetch",
    "signature": "(ctx context.Context, a int, b string, rest ...int) int",
    "defaults": {
      "ctx": "context.Background()"
    }
  }
]

delete_file

removes one file; rejected while any package-level symbol it declares is referenced from outside it (the rejection lists the reference positions). Deleting a package’s last file removes the package

argtyperequireddescription
pathstringyesfile path relative to the module root (or absolute)

Example:

[
  {
    "op": "upsert_decl",
    "pkg": "demo/scratch",
    "text": "func Tmp() int {\n\treturn 0\n}"
  },
  {
    "op": "delete_file",
    "path": "scratch/agent.go"
  }
]

move_file

same-directory moves are pure renames; a cross-package move rewrites the package clause to the target package and drops a now-self import, and is rejected while the file declares symbols referenced from outside it (their qualifiers would all be wrong — use move_decl per declaration instead)

argtyperequireddescription
fromstringyescurrent file path relative to the module root (or absolute)
tostringyesnew file path; a different directory must hold an already-loaded package

Example:

[
  {
    "op": "move_file",
    "from": "lib/lib.go",
    "to": "lib/core.go"
  }
]

add_dependency

runs go get module@version against the workspace module; go.mod and go.sum restore byte-for-byte on any later rejection in the same patch. Needs the module in the local cache or network access

argtyperequireddescription
modulestringyesmodule path, e.g. golang.org/x/sync
versionstringmodule version; defaults to latest

Example:

[
  {
    "op": "add_dependency",
    "module": "golang.org/x/sync",
    "version": "v0.10.0"
  }
]

remove_dependency

runs go get module@none; rejected while any workspace file still imports the module (the rejection lists the import positions)

argtyperequireddescription
modulestringyesmodule path to drop

Example:

[
  {
    "op": "remove_dependency",
    "module": "golang.org/x/sync"
  }
]

mod_tidy

runs go mod tidy with the same go.mod/go.sum restore-and-validate wrapper as the other module ops

argtyperequireddescription

Example:

[
  {
    "op": "mod_tidy"
  }
]

add_assign

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last (first/last need a block-owning handle)
lhsstringyesassignment target identifier
rhsstringyesright-hand-side expression, parsed and typechecked in scope
definebooluse := instead of =

Example:

[
  {
    "op": "add_assign",
    "at": "n2",
    "where": "after",
    "lhs": "_",
    "rhs": "h(3)",
    "define": false
  }
]

add_call

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
exprstringyesa call expression or channel receive (<-ch); assignments belong to add_assign

Example:

[
  {
    "op": "add_call",
    "at": "n2",
    "where": "after",
    "expr": "fmt.Println(h(1))"
  }
]

add_return

arity and result types are checked against the enclosing signature at end-of-list typecheck, not here

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
exprs[]stringresult expressions, in order; omit for a bare “return”

Example:

[
  {
    "op": "add_return",
    "at": "n2",
    "where": "after",
    "exprs": [
      "h(2)"
    ]
  }
]

add_defer

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
exprstringyesa call expression

Example:

[
  {
    "op": "add_defer",
    "at": "n1",
    "where": "after",
    "expr": "fmt.Println(\"done\")"
  }
]

add_go

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
exprstringyesa call expression

Example:

[
  {
    "op": "add_go",
    "at": "n2",
    "where": "after",
    "expr": "h(1)"
  }
]

delete_node

a block-owning statement with children, or an if with an else, is rejected rather than silently discarding content — delete children first

argtyperequireddescription
atstringyeshandle of the statement or case clause to remove

Example:

[
  {
    "op": "add_return",
    "at": "n3",
    "where": "after",
    "exprs": [
      "h(9)"
    ]
  },
  {
    "op": "delete_node",
    "at": "n3"
  }
]

add_if

returns the new then-block’s own handle via $N; there is no v1 handle for a requested else block (view again to reach it)

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
condstringyescondition expression
elseboolalso create an empty else block

Example:

[
  {
    "op": "add_if",
    "at": "n2",
    "where": "after",
    "cond": "h != nil",
    "else": false
  }
]

add_for

empty body, returns its handle via $N. v1 ceiling: no init/post clauses — use upsert_decl/set_body for a classic three-clause loop

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
condstringcondition expression; mutually exclusive with range
rangestringa full range clause, e.g. “k, v := range coll”; mutually exclusive with cond

Example:

[
  {
    "op": "add_for",
    "at": "n2",
    "where": "after",
    "cond": "h(0) \u003e 0"
  }
]

add_switch

empty body; extend with add_case

argtyperequireddescription
atstringyeshandle (or $N) to place the statement relative to
wherestringyesbefore | after | first | last
tagstringswitch tag expression; omit for a tagless switch

Example:

[
  {
    "op": "add_switch",
    "at": "n2",
    "where": "after",
    "tag": "h(1)"
  }
]

add_case

always appends as the last clause (v1 has no argument for placing a case among existing ones); returns the new case’s body handle via $N

argtyperequireddescription
atstringyeshandle (or $N) of the switch statement to extend
exprs[]stringcase expressions; mutually exclusive with default
defaultboolmake this the default clause; mutually exclusive with exprs

Example:

[
  {
    "op": "add_switch",
    "at": "n2",
    "where": "after",
    "tag": "h(1)"
  },
  {
    "op": "add_case",
    "at": "$1",
    "exprs": [
      "1",
      "2"
    ]
  }
]

set_cond

a case clause’s whole expression list is replaced as one; v1 has no per-element case-expr addressing

argtyperequireddescription
atstringyeshandle (or $N) of the if/for/case to retarget
exprstringyesreplacement condition

Example:

[
  {
    "op": "add_if",
    "at": "n2",
    "where": "after",
    "cond": "h == nil"
  },
  {
    "op": "set_cond",
    "at": "$1",
    "expr": "h != nil"
  }
]

replace_expr

v1 ceiling: an if/for/case condition or a whole expression statement only; per-argument sub-expression handles are future work

argtyperequireddescription
atstringyeshandle (or $N) of the target node
exprstringyesreplacement expression

Example:

[
  {
    "op": "add_call",
    "at": "n2",
    "where": "after",
    "expr": "h(1)"
  },
  {
    "op": "replace_expr",
    "at": "$1",
    "expr": "h(2)"
  }
]

wrap_stmts

from/to must be direct siblings, in order, of the same statement list; returns the new node’s handle via $N

argtyperequireddescription
fromstringyeshandle (or $N) of the first statement to enclose
tostringyeshandle (or $N) of the last statement to enclose
withstringyesif | for | block
condstringcondition; required for with=if/for, forbidden for with=block

Example:

[
  {
    "op": "wrap_stmts",
    "from": "n1",
    "to": "n2",
    "with": "if",
    "cond": "helper(1) \u003e 0"
  }
]

wrap_error

the Go idiom automated end to end: binds err, inserts “if err != nil { return …, fmt.Errorf(…) }”. v1 ceiling: a bare expression-statement call resolves its return arity only for a same-package function identifier

argtyperequireddescription
atstringyeshandle (or $N) of the assignment or expression-statement call to wrap
messagestringyescontext prefix for fmt.Errorf(“…: %w”, err)

Example:

[
  {
    "op": "wrap_error",
    "at": "n1",
    "message": "fetch"
  }
]

add_test

scaffolds a table-driven test: case struct derived from the target’s signature, rows slice, range+t.Run loop. v1 targets a plain function, not a method; address the generated test by name in follow-up ops

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
targetstringyesthe function under test; defaults to the envelope’s sym when omitted
namestringtest function name; defaults to Test<Target>

Example:

[
  {
    "op": "add_test",
    "target": "Double"
  }
]

add_test_case

values are expression atoms, typechecked against the case struct at end-of-list typecheck

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
teststringtest function name (from add_test); defaults to the envelope’s sym
namestringyescase row name
args[]stringargument expressions, in target parameter order
want[]stringexpected-result expressions, in result order (wantErr last, if the target returns error)

Example:

[
  {
    "op": "add_test",
    "target": "Double"
  },
  {
    "op": "add_test_case",
    "test": "TestDouble",
    "name": "positive",
    "args": [
      "2"
    ],
    "want": [
      "4"
    ]
  }
]

set_test_case

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
teststringtest function name; defaults to the envelope’s sym
casestringyesexisting row’s current name
namestringnew row name; defaults to case (no rename)
args[]stringreplacement argument expressions
want[]stringreplacement expected-result expressions

Example:

[
  {
    "op": "set_test_case",
    "test": "TestScale",
    "case": "one",
    "args": [
      "3",
      "3"
    ],
    "want": [
      "9"
    ]
  }
]

remove_test_case

argtyperequireddescription
pkgstringpackage import path; defaults to the envelope’s pkg
teststringtest function name; defaults to the envelope’s sym
casestringyesrow name to remove

Example:

[
  {
    "op": "remove_test_case",
    "test": "TestScale",
    "case": "one"
  }
]