Operations
Operations:
create_node, delete_node, set_label, set_view_label, set_parent, set_kind, set_tags, set_props, add_port, remove_port, set_rows, connect, disconnect, set_edge_labels, move, resize, reorder, unpin, pin_port, set_edge_hint, rename_id, create_view, update_view, delete_view, show_node, hide_node, add_decoration, update_decoration, delete_decoration, promote_decoration, batch
Every change dg makes to a project’s files is an operation: the editor sends them, dg op runs
them from the command line, and agents call them through dg op or the MCP
op tool. An operation edits the HCL files in place, keeping their comments and formatting, and
rewrites the layout files it touches.
mkdir my-diagram
dg op create_node -C my-diagram --kind service --label "API"
dg op create_node -C my-diagram --kind database --id db --label "Orders"
dg op connect -C my-diagram --from api --to db --label "SQL" --json
dg op move -C my-diagram --view main --id db --x 200 --y 0
The first operation generates the ID api from the label; --id names one. The model goes to
model.hcl and the position to views/main.layout.yaml.
Flags and JSON
Each field of an operation is a flag of the same name. Lists such as --tags, --include and
--order are repeated, one value each, and --props, --compartments, --overlays, --styles
and --decoration take JSON. The exceptions are the fields that hold objects, which several flags
fill:
| JSON field | Keys | Flags |
|---|---|---|
from, to |
node, port?, row? |
--from, --from-port, --from-row, --to, --to-port, --to-row |
labels |
start?, mid?, end? |
--start-label, --label, --end-label |
hint |
from_side?, to_side?, via?, label_pos? |
--from-side, --to-side, --via x,y repeated, --label-pos |
As JSON, an operation is an object whose op field names it and whose other fields are its
arguments. An unknown or missing field is an error that lists the fields it takes, and an unknown
key in a nested field is an error that gives that field’s keys. dg op batch reads a list of
operations, or one, from standard input or --file, and applies them in order as one change:
echo '[{"op": "set_label", "id": "db", "label": "Orders DB"},
{"op": "set_tags", "id": "api-db", "type": "edge", "tags": ["hot"]}]' |
dg op batch -C my-diagram
Results
dg op prints each operation with the ID it created or changed (after a rename, the new ID) and,
for move and resize, the values it stored, then each file it wrote. With --json it prints
{"results": [...], "files": [...], "warnings": [...]}, the warnings being those the operation
added (warnings is left out when there are none), or {"error": ..., "diagnostics": [...]} when it was refused.
An operation, a batch included, is applied completely or not at all:
- It is refused, writing nothing, when it would add a validation error (such as
set_kindto a kind that is not a container while the node has children), when it would change aread_onlyfile (the error names it), whiledg.hclhas errors or an HCL file does not parse, or when it would edit a layout file that has errors. - Every file it changes is written together, through temporary files.
- If a file changed since it was read, such as a save in a text editor, the operation is applied again to the new content, and refused if it no longer applies.
- Writers of one project take turns: an operation waits up to 10 seconds for another to finish.
New blocks go at the end of the file that holds the node they relate to (the parent, or the
from node) when it is writable, else of the first writable model file, else of a new
model.hcl.
dg op has no geometry: move pins only the node it names, at coordinates relative to the
node’s frame. dg export --format json gives every node’s frame and its
drawn position rel in it, which move takes; to keep a whole automatic layout, pin every node
at its rel in one batch, as the editor’s first drag does. A child of a stack or grid container
is marked ordered_in in the export: use reorder, not move, for it.
dg op has no undo: git is its undo. The editor keeps an undo history for its session.
The operations
The rest of this page is generated from dg’s definitions of the operations.
create_node
Append a node block. With view, also make the view show it (adding node.<id> to its include if needed) and, with x and y, pin it there; where a stack or grid container places it in the view, it goes in the container’s order, before before or last.
| Flag | Value | Meaning |
|---|---|---|
--before |
string | with view, where a stack or grid container places the new node: the sibling it goes before in the container’s order (default: last) |
--id |
string | ID of the new node (generated from the label, or the kind, when omitted) |
--kind |
string | (required) kind of the node (dg vocabulary lists them) |
--label |
string | label of the node; a newline character breaks a line (in a shell, $’a\nb’) |
--parent |
string | ID of the container node |
--view |
string | view to show the node in |
-x |
integer | x relative to the node’s frame (its nearest container drawn in the view, else the canvas), rounded to the project grid (an integer) |
-y |
integer | y relative to the node’s frame, rounded to the project grid (an integer) |
As JSON, with the required fields: {"op": "create_node", "kind": "service"}
Fields (? marks the optional ones): kind, id?, label?, parent?, view?, x?, y?, before?
delete_node
Remove a node with its edges, layout entries, decorations attached to it or placed in it, view selectors and node.<id> view rules. Refused while a view has of on it.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--recursive |
also delete the node’s descendants; otherwise its children move to its parent, unpinned |
As JSON, with the required fields: {"op": "delete_node", "id": "api"}
Fields (? marks the optional ones): id, recursive?
set_label
Change a node’s label. A node created in the same batch without a label or ID takes a new ID from this first label (spec 6.3).
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--label |
string | (required) label of the node; a newline character breaks a line (in a shell, $’a\nb’) |
As JSON, with the required fields: {"op": "set_label", "id": "api", "label": "API"}
Fields (? marks the optional ones): id, label
set_view_label
Set the label a view draws a node with instead of the model’s; without label, remove it, so the view draws the model’s again.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--label |
string | label of the node in this view; a newline character breaks a line (in a shell, $’a\nb’) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "set_view_label", "view": "main", "id": "api"}
Fields (? marks the optional ones): view, id, label?
set_parent
Set or remove a node’s parent.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--parent |
string | ID of the container node; empty or omitted removes the parent |
As JSON, with the required fields: {"op": "set_parent", "id": "api"}
Fields (? marks the optional ones): id, parent?
set_kind
Change the kind of a node or edge.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node, or of the edge with type edge |
--kind |
string | (required) kind of the node, or edge kind of the edge |
--type |
string | what id names: node (the default) or edge |
As JSON, with the required fields: {"op": "set_kind", "id": "api", "kind": "service"}
Fields (? marks the optional ones): id, kind, type?
set_tags
Replace the tags of a node or edge.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node, or of the edge with type edge |
--tags |
string | (required) tags (simple IDs); an empty list removes them (repeatable) |
--type |
string | what id names: node (the default) or edge |
As JSON, with the required fields: {"op": "set_tags", "id": "api", "tags": ["critical"]}
Fields (? marks the optional ones): id, tags, type?
set_props
Replace a node’s properties.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--props |
JSON | (required) property values by name, of the types the kind declares; an empty object removes them, as JSON |
As JSON, with the required fields: {"op": "set_props", "id": "api", "props": {"replicas": 3}}
Fields (? marks the optional ones): id, props
add_port
Add an instance port block to a node.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--name |
string | (required) port name |
--side |
string | side of the port: top, right, bottom, left or auto |
As JSON, with the required fields: {"op": "add_port", "id": "api", "name": "grpc"}
Fields (? marks the optional ones): id, name, side?
remove_port
Remove an instance port block, and its pinned positions. Refused while an edge uses the port.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--name |
string | (required) port name |
As JSON, with the required fields: {"op": "remove_port", "id": "api", "name": "grpc"}
Fields (? marks the optional ones): id, name
set_rows
Replace a node’s rows and compartments.
| Flag | Value | Meaning |
|---|---|---|
--compartments |
JSON | (required) compartments of rows ({name, rows: [{name, type?, key?}]}); one compartment without a name holds rows declared directly in the node; an empty list removes every row, as JSON |
--id |
string | (required) ID of the node |
As JSON, with the required fields: {"op": "set_rows", "id": "api", "compartments": [{"rows": [{"name": "id", "type": "uuid", "key": "pk"}]}]}
Fields (? marks the optional ones): id, compartments: [{name?, rows: [{name, type?, key?}]}]
connect
Append an edge block. With view, make the view’s edges filter show it (adding edge.<id>) if needed.
| Flag | Value | Meaning |
|---|---|---|
--end-label |
string | label at the edge’s end |
--from |
string | (required) node the edge starts at |
--from-port |
string | port of the start node |
--from-row |
string | row of the start node |
--id |
string | ID of the new edge (<from>-<to> when omitted) |
--kind |
string | edge kind |
--label |
string | label at the edge’s middle |
--start-label |
string | label at the edge’s start |
--tags |
string | tags (simple IDs); an empty list removes them (repeatable) |
--to |
string | (required) node the edge ends at |
--to-port |
string | port of the end node |
--to-row |
string | row of the end node |
--view |
string | view to show the edge in |
As JSON, with the required fields: {"op": "connect", "from": {"node": "api"}, "to": {"node": "db"}}
Fields (? marks the optional ones): from: {node, port?, row?}, to: {node, port?, row?}, id?, kind?, labels?: {start?, mid?, end?}, tags?, view?
disconnect
Remove an edge with its hints and view references.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the edge |
As JSON, with the required fields: {"op": "disconnect", "id": "api-db"}
Fields (? marks the optional ones): id
set_edge_labels
Replace an edge’s labels; none removes them.
| Flag | Value | Meaning |
|---|---|---|
--end-label |
string | label at the edge’s end |
--id |
string | (required) ID of the edge |
--label |
string | label at the edge’s middle |
--start-label |
string | label at the edge’s start |
As JSON, with the required fields: {"op": "set_edge_labels", "id": "api-db"}
Fields (? marks the optional ones): id, labels?: {start?, mid?, end?}
move
Pin a node in a view at x, y relative to its frame. Only this node is pinned: dg op computes no geometry; dg export –format json gives every node’s frame and rel, the coordinates move takes. Refused for a node a stack or grid container places (ordered_in in the export); reorder it instead.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--view |
string | (required) view ID |
-x |
integer | (required) x relative to the node’s frame (its nearest container drawn in the view, else the canvas), rounded to the project grid (an integer) |
-y |
integer | (required) y relative to the node’s frame, rounded to the project grid (an integer) |
As JSON, with the required fields: {"op": "move", "view": "main", "id": "api", "x": 100, "y": 40}
Fields (? marks the optional ones): view, id, x, y
resize
Set the size of a node pinned in a view, or of a child of a stack or grid container.
| Flag | Value | Meaning |
|---|---|---|
-h |
integer | (required) height, positive, rounded to the project grid (at least one unit) (an integer) |
--id |
string | (required) ID of the node |
--view |
string | (required) view ID |
-w |
integer | (required) width, positive, rounded to the project grid (at least one unit) (an integer) |
As JSON, with the required fields: {"op": "resize", "view": "main", "id": "api", "w": 160, "h": 80}
Fields (? marks the optional ones): view, id, w, h
reorder
Set the order in which a stack or grid container places its children in a view; children the order leaves out follow in sorted ID order.
| Flag | Value | Meaning |
|---|---|---|
--container |
string | (required) ID of the stack or grid container, drawn expanded in the view or the view’s of |
--order |
string | IDs of the container’s children the view draws, in order; empty or omitted removes the order of any node, even one that no longer places its children in order (repeatable) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "reorder", "view": "main", "container": "lanes"}
Fields (? marks the optional ones): view, container, order?
unpin
Remove a node’s position, size and pinned ports from a view; a container keeps its order.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "unpin", "view": "main", "id": "api"}
Fields (? marks the optional ones): view, id
pin_port
Pin a port of a node pinned in a view to a side, at an offset along it.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--offset |
number | position along the side, 0 to 1, kept to two decimals (a number) |
--port |
string | (required) port name |
--side |
string | (required) side: top, right, bottom or left |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "pin_port", "view": "main", "id": "api", "port": "grpc", "side": "right"}
Fields (? marks the optional ones): view, id, port, side, offset?
set_edge_hint
Replace an edge’s routing hint in a view.
| Flag | Value | Meaning |
|---|---|---|
--from-side |
string | side the edge leaves its start node by: top, right, bottom or left |
--id |
string | (required) ID of the edge, or lift:<from>:<to> for a lifted edge (with %, : and # in those IDs written %25, %3A and %23) |
--label-pos |
string | position of the middle label along the route, 0 to 1 |
--to-side |
string | side the edge enters its end node by |
--via |
string | waypoint x,y in canvas coordinates (repeatable) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "set_edge_hint", "view": "main", "id": "api-db", "hint": {"to_side": "top"}}
Fields (? marks the optional ones): view, id, hint: {from_side?, to_side?, via?: [[x, y]], label_pos?}
rename_id
Rename a node, edge or view everywhere it is referenced: model files, views.hcl, layout files (and the layout file’s name).
| Flag | Value | Meaning |
|---|---|---|
--new |
string | (required) new ID |
--old |
string | (required) current ID |
--type |
string | (required) what old names: node, edge or view |
As JSON, with the required fields: {"op": "rename_id", "type": "node", "old": "api", "new": "gateway"}
Fields (? marks the optional ones): type, old, new
create_view
Append a view block to views.hcl.
| Flag | Value | Meaning |
|---|---|---|
--collapse |
string | selectors of containers the view draws collapsed (repeatable) |
--direction |
string | the way the layers of the view’s first layout run: down, right, up or left (default: down, or right when an edge ends at a row) |
--edges |
string | selectors of the edges the view shows: edge_kind.<kind>, tag.<tag>, edge.<id> (default: every edge whose ends are shown) (repeatable) |
--exclude |
string | selectors of nodes the view leaves out (repeatable) |
--id |
string | (required) ID of the new view (a simple ID) |
--include |
string | selectors of the nodes the view shows, as in views.hcl: all, node.<id> or node[“<id>”], kind.<kind>, tag.<tag>, children.<id>, descendants.<id> (default [all]) (repeatable) |
--of |
string | ID of the container whose inside the view shows (drill-down) |
--overlays |
JSON | overlays [{id, members: [selectors], style?, label?}], as JSON |
--styles |
JSON | view style rules [{type?: style (default) or edge_style, label: a kind, tag.<tag>, node.<id> or edge.<id>, props: {property: value}}], as JSON |
--title |
string | view title |
As JSON, with the required fields: {"op": "create_view", "id": "overview"}
Fields (? marks the optional ones): id, title?, include?, exclude?, collapse?, edges?, of?, direction?, overlays?: [{id, members, style?, label?}], styles?: [{type?, label, props}]
update_view
Change attributes and overlays of a view in views.hcl: the fields given replace the view’s, unset removes them.
| Flag | Value | Meaning |
|---|---|---|
--collapse |
string | selectors of containers the view draws collapsed (repeatable) |
--direction |
string | the way the layers of the view’s first layout run: down, right, up or left (default: down, or right when an edge ends at a row) |
--edges |
string | selectors of the edges the view shows: edge_kind.<kind>, tag.<tag>, edge.<id> (default: every edge whose ends are shown) (repeatable) |
--exclude |
string | selectors of nodes the view leaves out (repeatable) |
--id |
string | (required) view ID |
--include |
string | selectors of the nodes the view shows, as in views.hcl: all, node.<id> or node[“<id>”], kind.<kind>, tag.<tag>, children.<id>, descendants.<id> (default [all]) (repeatable) |
--of |
string | ID of the container whose inside the view shows (drill-down) |
--overlays |
JSON | overlays [{id, members: [selectors], style?, label?}]; replaces every overlay of the view, keeping unchanged ones as they are written, as JSON |
--styles |
JSON | view style rules [{type?: style (default) or edge_style, label: a kind, tag.<tag>, node.<id> or edge.<id>, props: {property: value}}]; replaces every style rule of the view, keeping unchanged ones as they are written, as JSON |
--title |
string | view title |
--unset |
string | attributes to remove: title, include, exclude, collapse, edges, of, direction, overlays, styles (repeatable) |
As JSON, with the required fields: {"op": "update_view", "id": "overview"}
Fields (? marks the optional ones): id, title?, include?, exclude?, collapse?, edges?, of?, direction?, overlays?: [{id, members, style?, label?}], styles?: [{type?, label, props}], unset?
delete_view
Remove a view from views.hcl, and its layout file.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) view ID |
As JSON, with the required fields: {"op": "delete_view", "id": "overview"}
Fields (? marks the optional ones): id
show_node
Make a view show an existing node: remove node.<id> from its exclude and, when its selectors still do not show the node, add node.<id> to its include. Refused when the view still does not show it (excluded by another selector, or inside a collapsed container).
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "show_node", "view": "main", "id": "api"}
Fields (? marks the optional ones): view, id
hide_node
Make a view stop showing a node: remove node.<id> from its include and, when its selectors still show the node, add node.<id> to its exclude.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the node |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "hide_node", "view": "main", "id": "api"}
Fields (? marks the optional ones): view, id
add_decoration
Add a decoration to a view’s layout file (spec 5.7).
| Flag | Value | Meaning |
|---|---|---|
--decoration |
JSON | (required) decoration fields as in layout files: type (text, rect, ellipse, band, image, arrow, freehand), in or attach, x and y (in the frame) or dx and dy (from the attached node’s center), w, h, z, text, src, points, path, fill, stroke, stroke_width, stroke_dash, opacity, text_color, font_size; type is required, as JSON |
--id |
string | ID of the new decoration (generated from its type when omitted) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "add_decoration", "view": "main", "decoration": {"type": "text", "x": 0, "y": -60, "text": "Title"}}
Fields (? marks the optional ones): view, id?, decoration: {type, in?, attach?, x?, y?, dx?, dy?, w?, h?, z?, text?, src?, points?, path?, fill?, stroke?, stroke_width?, stroke_dash?, opacity?, text_color?, font_size?}
update_decoration
Change fields of a decoration of a view: the fields given replace its, unset removes them.
| Flag | Value | Meaning |
|---|---|---|
--decoration |
JSON | decoration fields as in layout files: type (text, rect, ellipse, band, image, arrow, freehand), in or attach, x and y (in the frame) or dx and dy (from the attached node’s center), w, h, z, text, src, points, path, fill, stroke, stroke_width, stroke_dash, opacity, text_color, font_size, as JSON |
--id |
string | (required) ID of the decoration |
--unset |
string | decoration fields to remove, such as attach, dx and dy (repeatable) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "update_decoration", "view": "main", "id": "note"}
Fields (? marks the optional ones): view, id, decoration?: {type?, in?, attach?, x?, y?, dx?, dy?, w?, h?, z?, text?, src?, points?, path?, fill?, stroke?, stroke_width?, stroke_dash?, opacity?, text_color?, font_size?}, unset?
delete_decoration
Remove a decoration from a view.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the decoration |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "delete_decoration", "view": "main", "id": "note"}
Fields (? marks the optional ones): view, id
promote_decoration
Replace a decoration of a view with a model node pinned where the decoration is, at its size; a text decoration’s text becomes the label, and a decoration in a frame becomes a child of that container. Refused for attached decorations, arrows and freehand lines.
| Flag | Value | Meaning |
|---|---|---|
--id |
string | (required) ID of the decoration |
--kind |
string | (required) kind of the node (dg vocabulary lists them) |
--label |
string | label of the new node (default: the text of a text decoration) |
--new |
string | ID of the new node (generated from the label, or the kind, when omitted) |
--view |
string | (required) view ID |
As JSON, with the required fields: {"op": "promote_decoration", "view": "main", "id": "note", "kind": "service"}
Fields (? marks the optional ones): view, id, kind, new?, label?
batch
Apply operations in order as one change: all files are written together, or none.
| Flag | Value | Meaning |
|---|---|---|
--file |
string | file holding the operations as JSON (default standard input) |
As JSON, with the required fields: {"op": "batch", "ops": [{"op": "create_node", "kind": "service"}]}