graph-lcb 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. graph_lcb-0.1.0/LICENSE +21 -0
  2. graph_lcb-0.1.0/PKG-INFO +605 -0
  3. graph_lcb-0.1.0/README.md +594 -0
  4. graph_lcb-0.1.0/cli/__init__.py +244 -0
  5. graph_lcb-0.1.0/commands/__init__.py +1 -0
  6. graph_lcb-0.1.0/commands/admin.py +103 -0
  7. graph_lcb-0.1.0/commands/common.py +167 -0
  8. graph_lcb-0.1.0/commands/mutations.py +85 -0
  9. graph_lcb-0.1.0/commands/queries.py +69 -0
  10. graph_lcb-0.1.0/commands/records.py +129 -0
  11. graph_lcb-0.1.0/commands/visualization-templates/graphs.template.html +821 -0
  12. graph_lcb-0.1.0/commands/visualization.py +44 -0
  13. graph_lcb-0.1.0/graph_cli.py +15 -0
  14. graph_lcb-0.1.0/graph_lcb.egg-info/PKG-INFO +605 -0
  15. graph_lcb-0.1.0/graph_lcb.egg-info/SOURCES.txt +31 -0
  16. graph_lcb-0.1.0/graph_lcb.egg-info/dependency_links.txt +1 -0
  17. graph_lcb-0.1.0/graph_lcb.egg-info/entry_points.txt +2 -0
  18. graph_lcb-0.1.0/graph_lcb.egg-info/requires.txt +3 -0
  19. graph_lcb-0.1.0/graph_lcb.egg-info/top_level.txt +5 -0
  20. graph_lcb-0.1.0/graphs/engine.py +754 -0
  21. graph_lcb-0.1.0/graphs/types/__init__.py +105 -0
  22. graph_lcb-0.1.0/pyproject.toml +26 -0
  23. graph_lcb-0.1.0/schemas/__init__.py +1 -0
  24. graph_lcb-0.1.0/schemas/edges.schema.json +91 -0
  25. graph_lcb-0.1.0/schemas/entities.schema.json +76 -0
  26. graph_lcb-0.1.0/setup.cfg +4 -0
  27. graph_lcb-0.1.0/tests/test_case_insensitive_types.py +128 -0
  28. graph_lcb-0.1.0/tests/test_cli.py +123 -0
  29. graph_lcb-0.1.0/tests/test_engine_and_commands.py +404 -0
  30. graph_lcb-0.1.0/tests/test_graph_output.py +119 -0
  31. graph_lcb-0.1.0/tests/test_init.py +94 -0
  32. graph_lcb-0.1.0/tests/test_traversal.py +97 -0
  33. graph_lcb-0.1.0/tests/test_validate.py +182 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,605 @@
1
+ Metadata-Version: 2.4
2
+ Name: graph-lcb
3
+ Version: 0.1.0
4
+ Summary: A local CLI for managing JSON-backed graphs with customizable entity and edge types.
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=9.2.0; extra == "test"
10
+ Dynamic: license-file
11
+
12
+ # graph-cli
13
+
14
+ A local command-line tool for managing a JSON-backed graph with customizable
15
+ entity and edge types. The workspace is stored in `.graph/` under the current
16
+ working directory.
17
+
18
+ ![](docs/assets/1.png)
19
+ ![](docs/assets/2.png)
20
+ ![](docs/assets/3.png)
21
+
22
+
23
+ ## Install
24
+
25
+ Requires Python 3.10 or newer and Git. Install the CLI directly from GitHub with:
26
+
27
+ ```sh
28
+ pip install graphcli
29
+ ```
30
+
31
+ This installs the `graph` command. To install the test dependencies and run the
32
+ test suite from a local development checkout instead:
33
+
34
+ ```sh
35
+ python -m pip install -e ".[test]"
36
+ python -m pytest
37
+ ```
38
+
39
+ ## Workspace and files
40
+
41
+ Initialize a project explicitly:
42
+
43
+ ```sh
44
+ graph init
45
+ ```
46
+
47
+ This creates missing starter contract files and copies the bundled JSON
48
+ Schemas into `.graph/schemas/`. Existing contracts, schemas, and graph data
49
+ are preserved. Initialization does not create `graph.json`; the graph store
50
+ is written when a record is first added.
51
+
52
+ Graph operations do not initialize a workspace implicitly. If `.graph/` or
53
+ either contract file is missing, graph operations report that the workspace
54
+ must be initialized. `graph validate` reports an uninitialized workspace
55
+ without creating files. `graph contract` can display the starter contracts
56
+ without creating a workspace.
57
+
58
+ | Path | Purpose |
59
+ | --- | --- |
60
+ | `.graph/entities.json` | Entity type definitions and allowed node properties |
61
+ | `.graph/edges.json` | Edge type definitions, endpoint constraints, and allowed edge properties |
62
+ | `.graph/graph.json` | Persisted `nodes` and `edges` arrays |
63
+ | `.graph/schemas/entities.schema.json` | Bundled editor-facing JSON Schema for entity contracts |
64
+ | `.graph/schemas/edges.schema.json` | Bundled editor-facing JSON Schema for edge contracts |
65
+ | `.graph/view.html` | Generated visualization written by `graph view` |
66
+
67
+ The contracts and graph store are resolved relative to the process's current
68
+ working directory, not the location of the installed CLI. Run commands from
69
+ the project whose `.graph/` you intend to access.
70
+
71
+ ## Contract format
72
+
73
+ An entity contract maps each entity type to a definition with a `properties`
74
+ object. An edge contract additionally defines the allowed source and target
75
+ entity types. The starter contracts created by `graph init` are:
76
+
77
+ ```json
78
+ {
79
+ "$schema": "./schemas/entities.schema.json",
80
+ "API": {
81
+ "description": "An API or application service",
82
+ "properties": {
83
+ "language": { "type": "string", "required": true }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ ```json
90
+ {
91
+ "$schema": "./schemas/edges.schema.json",
92
+ "CALLS": {
93
+ "description": "A service synchronously invokes another service or API.",
94
+ "source": ["API"],
95
+ "target": ["API"],
96
+ "properties": {
97
+ "protocol": { "type": "string", "required": false }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ You can add domain-specific types and properties. For example, an entity
104
+ contract may define:
105
+
106
+ ```json
107
+ {
108
+ "API": {
109
+ "description": "An API or application service",
110
+ "properties": {
111
+ "language": { "type": "string", "required": true },
112
+ "framework": "string",
113
+ "base_url": { "type": "string", "required": false }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ And an edge contract may define:
120
+
121
+ ```json
122
+ {
123
+ "CALLS": {
124
+ "description": "One service invokes another.",
125
+ "source": ["API", "WORKER"],
126
+ "target": ["API"],
127
+ "properties": {
128
+ "method": { "type": "string", "required": false }
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ Contract rules:
135
+
136
+ - Each top-level type name maps to a JSON object. Type names are
137
+ case-insensitive when used by commands and must be unique ignoring case.
138
+ The spelling in the contract is canonical for newly created records.
139
+ - Each entity requires a `properties` object. Each edge requires `source`,
140
+ `target`, and `properties`. `source` and `target` are lists of entity type
141
+ names; each referenced type must exist. The bundled JSON Schema expects these
142
+ lists to be non-empty.
143
+ - `description` is optional at runtime. It describes a type, not an individual
144
+ record. Edge records have their own optional free-form description.
145
+ - A property definition is either a shorthand type string, such as
146
+ `"framework": "string"`, or an object with `type` and optional `required`.
147
+ Supported types are `string`, `number`, `integer`, and `boolean`.
148
+ `required` defaults to `false`.
149
+ - `number` accepts JSON integers and decimal numbers, but not booleans.
150
+ `integer` accepts integers, but not booleans. Null, arrays, and objects are
151
+ not supported property types.
152
+ - The engine checks contract structure, type references, and property rules
153
+ during graph operations and `validate`. The bundled JSON Schema files support
154
+ editor validation and completion; `$schema` is metadata and is omitted from
155
+ `graph contract` output.
156
+
157
+ ## Command conventions
158
+
159
+ ```text
160
+ graph <command> [arguments] [options]
161
+ ```
162
+
163
+ - Long options use either `--name value` or `--name=value`. Short options such
164
+ as `-n` are not supported.
165
+ - Options with comma-separated values accept one or more items without spaces,
166
+ for example `--type=API,WORKER`.
167
+ - JSON option values must be valid JSON. The examples below use single quotes
168
+ around JSON, as is customary in POSIX shells; use quoting appropriate for
169
+ your shell.
170
+ - `--help` or `-h` prints brief command help. `graph help` prints the command
171
+ list; `graph help COMMAND` prints that command's synopsis. Detailed option
172
+ behavior is documented below.
173
+ - `--` stops option parsing. Arguments after it are treated as positional
174
+ values.
175
+ - Commands requiring a graph workspace expect `graph init` to have been run
176
+ first. Help, `init`, `contract`, and `validate` do not require initialization.
177
+
178
+ ## Command reference
179
+
180
+ ### `graph --help`, `graph help`
181
+
182
+ ```text
183
+ graph --help
184
+ graph help [COMMAND]
185
+ ```
186
+
187
+ Print the registered commands, or brief help for one command. These forms do
188
+ not read or create graph files.
189
+
190
+ ### `graph init`
191
+
192
+ ```text
193
+ graph init
194
+ ```
195
+
196
+ Create `.graph/`, starter `entities.json` and `edges.json` files, and missing
197
+ bundled schema files under `.graph/schemas/`.
198
+
199
+ - Takes no positional arguments or options.
200
+ - Existing contracts and schemas are never overwritten. Existing
201
+ `.graph/graph.json` is not modified.
202
+ - Safe to run again: it fills missing files but preserves existing ones.
203
+ - Does not create an empty `graph.json`; a graph store is written when a node
204
+ or edge is added.
205
+ - Prints `{"initialized":true,"directory":".graph"}` on success.
206
+
207
+ ### `graph contract`
208
+
209
+ ```text
210
+ graph contract [entities|edges] [--compact]
211
+ ```
212
+
213
+ Show contract definitions. This command can be used before initialization;
214
+ when no workspace contract files exist, it displays the built-in starter
215
+ contracts without writing them.
216
+
217
+ | Parameter | Meaning |
218
+ | --- | --- |
219
+ | `entities` | Show only entity contracts. `entity` is also accepted. |
220
+ | `edges` | Show only edge contracts. `edge` is also accepted. |
221
+ | Omitted category | Show both sections. |
222
+ | `--compact` | Render text summaries containing type descriptions, edge endpoint types, and property types/required markers instead of JSON. Takes no value. |
223
+
224
+ Default output is compact single-line JSON: when showing both categories the
225
+ top-level keys are `entities` and `edges`; when one category is selected, the
226
+ output is that contract object. `$schema` is omitted from this output.
227
+ Compact output is plain text, not JSON.
228
+
229
+ ### `graph validate`
230
+
231
+ ```text
232
+ graph validate
233
+ ```
234
+
235
+ Read-only validation of the current contracts and graph store. It does not
236
+ create, rewrite, or repair files. It reports all detected contract and record
237
+ violations in one response.
238
+
239
+ The JSON response has this shape:
240
+
241
+ ```json
242
+ {
243
+ "valid": false,
244
+ "errors": [
245
+ {
246
+ "kind": "node",
247
+ "id": "orders-api",
248
+ "code": "missing_required_property",
249
+ "message": "missing required property: language"
250
+ }
251
+ ],
252
+ "summary": {
253
+ "nodes": 1,
254
+ "edges": 0,
255
+ "errors": 1
256
+ }
257
+ }
258
+ ```
259
+
260
+ `kind` identifies the contract, store, node, or edge involved. When an issue
261
+ belongs to a record, the issue may include its `id`; some errors may include
262
+ additional details such as valid types or properties. `summary.nodes` and
263
+ `summary.edges` count records in the store arrays, and `summary.errors` counts
264
+ reported issues.
265
+
266
+ If the store file does not exist, validation treats it as an empty graph. A
267
+ missing workspace or unreadable contract/store is reported in JSON with exit
268
+ code `4`; validation does not initialize or repair anything.
269
+
270
+ ### `graph add-node`
271
+
272
+ ```text
273
+ graph add-node ID NAME TYPE [--properties JSON] [--evidences JSON_ARRAY]
274
+ ```
275
+
276
+ Create a node and append it to `.graph/graph.json`.
277
+
278
+ | Parameter | Meaning |
279
+ | --- | --- |
280
+ | `ID` | Required, unique node identifier. Must match `^[a-z0-9][a-z0-9._-]*$`: the first character is a lowercase ASCII letter or digit; later characters may also include `.`, `_`, and `-`. |
281
+ | `NAME` | Required display name, stored as supplied. |
282
+ | `TYPE` | Required entity type from `entities.json`; lookup is case-insensitive and the stored type uses the contract's canonical spelling. |
283
+ | `--properties JSON` | Complete properties object. Defaults to `{}`. Supplied keys must be defined in the entity contract, values must have the declared JSON type, and all required properties must be present. |
284
+ | `--evidences JSON_ARRAY` | Optional JSON array in which every item must be an object. Defaults to `[]`; evidence fields and nested values are user-defined. |
285
+
286
+ The node's metadata receives UTC `created_at` and `updated_at` timestamps.
287
+ The output is the complete node record, including `id`, `type`, `name`,
288
+ `properties`, `metadata`, and `evidences`.
289
+
290
+ Example:
291
+
292
+ ```sh
293
+ graph add-node orders-api "Orders API" API \
294
+ --properties '{"language":"Python"}' \
295
+ --evidences '[{"source":"src/orders/api.py","locator":{"start_line":12,"end_line":28},"note":"API implementation"}]'
296
+ ```
297
+
298
+ ### `graph add-edge`
299
+
300
+ ```text
301
+ graph add-edge SOURCE_ID EDGE_TYPE TARGET_ID
302
+ [--description TEXT]
303
+ [--properties JSON]
304
+ [--evidences JSON_ARRAY]
305
+ ```
306
+
307
+ Create a directed edge between existing nodes.
308
+
309
+ | Parameter | Meaning |
310
+ | --- | --- |
311
+ | `SOURCE_ID` | Required existing source node ID. |
312
+ | `EDGE_TYPE` | Required edge type from `edges.json`, case-insensitive; the stored spelling is canonical. |
313
+ | `TARGET_ID` | Required existing target node ID. |
314
+ | `--description TEXT` | Optional free-form relationship description; defaults to the empty string. If there is already any edge between the same endpoints, the new edge must have a non-empty description. |
315
+ | `--properties JSON` | Complete properties object. Defaults to `{}` and is checked against the edge type's property contract. |
316
+ | `--evidences JSON_ARRAY` | Optional array of evidence objects; defaults to `[]`. |
317
+
318
+ The edge contract must allow the source node's entity type in `source` and the
319
+ target node's entity type in `target`. An edge ID is generated as a UUID, and
320
+ the edge receives UTC creation/update timestamps.
321
+
322
+ Two edges with the same type, endpoints, and properties are rejected as
323
+ duplicates even if their descriptions differ. Any additional edge between the
324
+ same endpoints must have a non-empty description; for the same edge type,
325
+ properties must also differ to distinguish the edge. The output is the full
326
+ edge record, with `target_type` derived from the target node for CLI output.
327
+
328
+ Example:
329
+
330
+ ```sh
331
+ graph add-edge orders-api CALLS stock-api \
332
+ --description "Checks stock availability" \
333
+ --properties '{"method":"GET"}' \
334
+ --evidences '[{"source":"src/orders/client.py","locator":{"start_line":20,"end_line":35}}]'
335
+ ```
336
+
337
+ ### `graph nodes`
338
+
339
+ ```text
340
+ graph nodes [ID]
341
+ [--type TYPE[,TYPE...]]
342
+ [--properties JSON]
343
+ [--prefix TEXT] [--suffix TEXT] [--contains TEXT]
344
+ [--limit N] [--fields FIELD[,FIELD...]]
345
+ ```
346
+
347
+ Get one node by ID or list/filter nodes.
348
+
349
+ | Parameter | Meaning |
350
+ | --- | --- |
351
+ | `ID` | Optional exact node ID. Returns the complete record directly, not a list envelope. It cannot be combined with `--properties` or `--fields`. |
352
+ | `--type TYPE[,TYPE...]` | Keep nodes whose entity type matches any listed type, case-insensitively. |
353
+ | `--properties JSON` | Keep nodes whose properties match every supplied key/value. Values are compared for equality; extra properties on a node are allowed. |
354
+ | `--prefix TEXT` | Case-insensitive prefix match against node `name`. |
355
+ | `--suffix TEXT` | Case-insensitive suffix match against node `name`. |
356
+ | `--contains TEXT` | Case-insensitive substring match against node `name`. |
357
+ | `--limit N` | Optional positive integer. Applied after filtering; `1` or greater. |
358
+ | `--fields FIELD[,FIELD...]` | Return only the selected fields, in the requested order. Allowed fields: `id`, `type`, `name`, `properties`, `metadata`, `evidences`. Not valid with an ID lookup. |
359
+
360
+ All list filters combine with AND, except multiple values within `--type`,
361
+ which combine with OR. List output is an object containing `rows`, `total`,
362
+ and `truncated`. `total` is the count after filtering but before the limit;
363
+ `truncated` is true if the limit omitted matches. Without `--fields`, rows
364
+ contain full node records. Node list order follows the order in `graph.json`.
365
+
366
+ Examples:
367
+
368
+ ```sh
369
+ graph nodes
370
+ graph nodes orders-api
371
+ graph nodes --type=API,WORKER --contains=order --properties '{"language":"Python"}'
372
+ graph nodes --prefix=order --limit=10 --fields=id,name,type
373
+ ```
374
+
375
+ ### `graph edges`
376
+
377
+ ```text
378
+ graph edges [EDGE_ID]
379
+ [--type TYPE[,TYPE...]]
380
+ [--properties JSON]
381
+ [--from SOURCE_ID] [--to TARGET_ID]
382
+ [--limit N]
383
+ [--fields FIELD[,FIELD...]]
384
+ ```
385
+
386
+ Get one edge by its generated ID or list/filter edges.
387
+
388
+ | Parameter | Meaning |
389
+ | --- | --- |
390
+ | `EDGE_ID` | Optional exact edge ID. Returns the complete edge record directly and cannot be combined with filters. |
391
+ | `--type TYPE[,TYPE...]` | Keep edges whose type matches any listed contract type, case-insensitively. Each requested type must exist. |
392
+ | `--properties JSON` | Keep edges matching every supplied property key/value. |
393
+ | `--from SOURCE_ID` | Keep edges whose source is this existing node ID. |
394
+ | `--to TARGET_ID` | Keep edges whose target is this existing node ID. |
395
+ | `--limit N` | Optional positive integer. Applied after filtering; `1` or greater. |
396
+ | `--fields FIELD[,FIELD...]` | Return objects with only selected fields, in requested order. Allowed fields: `id`, `type`, `source`, `target`, `target_type`, `description`, `properties`, `metadata`, `evidences`. |
397
+
398
+ Filters combine with AND; multiple `--type` values combine with OR.
399
+ `--from` and `--to` can be used together. An unknown endpoint ID is an error.
400
+ List output uses the same `rows`, `total`, `truncated` envelope as `nodes`.
401
+ Without `--fields`, each row is the compact tuple
402
+ `[source_id, type, target_id, target_type]`; `target_type` is derived from the
403
+ target node, not stored on the edge. Rows are sorted by source ID, type, target
404
+ ID, then edge ID.
405
+
406
+ Examples:
407
+
408
+ ```sh
409
+ graph edges --type=CALLS --from=orders-api --limit=10
410
+ graph edges --to=stock-api --properties '{"method":"GET"}' \
411
+ --fields=id,type,source,target,target_type,description,properties
412
+ graph edges 6c7f7d2c-22f0-49d2-a7f3-37d9b8cb0988
413
+ ```
414
+
415
+ ### `graph edit`
416
+
417
+ ```text
418
+ graph edit node ID
419
+ [--name TEXT] [--properties JSON] [--evidences JSON_ARRAY]
420
+
421
+ graph edit edge EDGE_ID
422
+ [--description TEXT] [--properties JSON] [--evidences JSON_ARRAY]
423
+ ```
424
+
425
+ Update a node or edge in place. IDs, node/edge types, and edge endpoints are
426
+ not editable.
427
+
428
+ For nodes:
429
+
430
+ - `ID` is the existing node ID.
431
+ - `--name TEXT` replaces the display name.
432
+ - `--properties JSON` replaces the entire properties object, not just selected
433
+ keys. The resulting object must satisfy the current entity contract.
434
+ - `--evidences JSON_ARRAY` replaces the complete evidence list; `[]` clears it.
435
+
436
+ For edges:
437
+
438
+ - `EDGE_ID` is the existing generated edge ID.
439
+ - `--description TEXT` replaces the description.
440
+ - `--properties JSON` replaces the entire properties object and must satisfy
441
+ the current edge contract.
442
+ - `--evidences JSON_ARRAY` replaces the complete evidence list; `[]` clears it.
443
+
444
+ At least one editable option is required. Omitted fields remain unchanged.
445
+ Metadata `updated_at` is refreshed after a successful edit. Edge edits also
446
+ recheck duplicate-edge and parallel-edge description rules. The command prints
447
+ the updated full record.
448
+
449
+ Examples:
450
+
451
+ ```sh
452
+ graph edit node orders-api --name "Orders service"
453
+ graph edit node orders-api --properties '{"language":"Python","framework":"FastAPI"}'
454
+ graph edit edge 6c7f7d2c-22f0-49d2-a7f3-37d9b8cb0988 \
455
+ --description "Uses the stock lookup endpoint" --properties '{"method":"GET"}'
456
+ ```
457
+
458
+ ### `graph delete`
459
+
460
+ ```text
461
+ graph delete node ID
462
+ graph delete edge EDGE_ID
463
+ graph delete edge SOURCE_ID EDGE_TYPE TARGET_ID
464
+ ```
465
+
466
+ Delete a node or edge:
467
+
468
+ - `delete node ID` removes the node and every edge connected to it. The output
469
+ contains `deleted_node` and the number of removed `deleted_edges`.
470
+ - `delete edge EDGE_ID` deletes exactly one edge by ID.
471
+ - `delete edge SOURCE_ID EDGE_TYPE TARGET_ID` selects edges by type and
472
+ endpoints. If exactly one matches, it is deleted. If several match, none are
473
+ deleted; the CLI displays the candidate records and IDs so one can be
474
+ deleted by ID.
475
+
476
+ The tuple form requires both endpoint nodes to exist. Edge type lookup is
477
+ case-insensitive. Node and edge IDs cannot be supplied with `--id`; they are
478
+ positional. A successful deletion prints the deleted record. An ambiguous
479
+ edge selection returns exit code `2` and leaves the graph unchanged.
480
+
481
+ Examples:
482
+
483
+ ```sh
484
+ graph delete node orders-api
485
+ graph delete edge 6c7f7d2c-22f0-49d2-a7f3-37d9b8cb0988
486
+ graph delete edge orders-api CALLS stock-api
487
+ ```
488
+
489
+ ### `graph traverse`
490
+
491
+ ```text
492
+ graph traverse NODE_ID
493
+ [--depth N]
494
+ [--direction in|out|both]
495
+ [--edge TYPE[,TYPE...]]
496
+ [--limit N]
497
+ ```
498
+
499
+ Perform a breadth-first traversal from one existing node.
500
+
501
+ | Parameter | Meaning |
502
+ | --- | --- |
503
+ | `NODE_ID` | Required anchor node; it is always included in the output. |
504
+ | `--depth N` | Maximum number of edge hops; positive integer, default `1`. |
505
+ | `--direction in|out|both` | Direction to follow at every hop; default `both`. `in` follows incoming edges to their source; `out` follows outgoing edges to their target. |
506
+ | `--edge TYPE[,TYPE...]` | Optional set of allowed edge types. Names are case-insensitive and must exist in the edge contract. |
507
+ | `--limit N` | Optional positive maximum number of reached nodes, excluding the anchor. Applied as nodes are reached. |
508
+
509
+ The result is compact JSON:
510
+
511
+ ```json
512
+ {
513
+ "nodes": [["orders-api", "API", "Orders API"], ["stock-api", "API", "Stock API"]],
514
+ "edges": [["orders-api", "CALLS", "stock-api", "API"]],
515
+ "truncated": false
516
+ }
517
+ ```
518
+
519
+ Node tuples are `[id, type, name]`; edge tuples are
520
+ `[source_id, type, target_id, target_type]`. Edge tuples preserve the stored
521
+ source-to-target orientation even when traversing `in`. The anchor is first;
522
+ other nodes and edges are ordered deterministically. The edge list can include
523
+ cross-links or cycle-closing edges encountered during expansion, not only the
524
+ edges of a traversal tree.
525
+
526
+ `truncated` is true only when the node limit excludes reachable nodes; merely
527
+ reaching `--depth` does not set it. If the limit cuts off a frontier, node IDs
528
+ are accepted in lexical order.
529
+
530
+ Example:
531
+
532
+ ```sh
533
+ graph traverse orders-api --depth=2 --direction=out --edge=CALLS,PUBLISHES --limit=20
534
+ ```
535
+
536
+ ### `graph path`
537
+
538
+ ```text
539
+ graph path SOURCE_ID TARGET_ID
540
+ [--direction out|in|both]
541
+ [--edge TYPE[,TYPE...]]
542
+ [--max-depth N]
543
+ [--limit N]
544
+ ```
545
+
546
+ Find simple paths between two existing nodes. A simple path does not visit a
547
+ node more than once.
548
+
549
+ | Parameter | Meaning |
550
+ | --- | --- |
551
+ | `SOURCE_ID` | Required starting node ID. |
552
+ | `TARGET_ID` | Required destination node ID. |
553
+ | `--direction [out,in,both]` | Direction in which to follow edges; default `out`. `in` traverses incoming edges backward; `both` follows either orientation. Output edges retain their stored orientation. |
554
+ | `--edge TYPE[,TYPE...]` | Optional set of allowed edge types. Names are case-insensitive and must exist. |
555
+ | `--max-depth N` | Optional non-negative maximum hop count. If omitted, the search allows up to the number of graph nodes minus one hops. `0` permits only a zero-hop path when source and target are the same node. |
556
+ | `--limit N` | Positive maximum number of paths to return; default `1`. |
557
+
558
+ Paths are ordered shortest first with deterministic tie-breaking. The result
559
+ has `paths` and `truncated` fields. Each path contains `nodes` as
560
+ `[id, type, name]` tuples, `edges` as
561
+ `[edge_id, source_id, type, target_id, target_type]` tuples, and `hops`.
562
+ If there is no route, `paths` is empty. When source and target are the same
563
+ node, a zero-hop path is valid. `truncated` means another path was found
564
+ beyond the requested limit; it does not indicate that search work was capped.
565
+ Parallel edges can produce distinct paths.
566
+
567
+ Example:
568
+
569
+ ```sh
570
+ graph path orders-api database --direction=both --edge=CALLS,READS \
571
+ --max-depth=6 --limit=5
572
+ ```
573
+
574
+ Path search can enumerate many candidate simple paths in a dense graph. Use
575
+ `--max-depth`, `--edge`, and a modest `--limit` to narrow a query.
576
+
577
+ ### `graph view`
578
+
579
+ ```text
580
+ graph view [--no-open]
581
+ ```
582
+
583
+ Generate a self-contained HTML visualization at `.graph/view.html`. The page
584
+ includes the graph records and contract descriptions and supports graph
585
+ exploration in a browser.
586
+
587
+ - Requires an initialized workspace.
588
+ - Overwrites the generated `view.html`; it does not modify `graph.json`.
589
+ - By default, asks the system browser to open the generated page.
590
+ - `--no-open` skips the browser-open attempt. It takes no value.
591
+ - Prints a JSON object containing the absolute output `path`, node and edge
592
+ counts, and `opened` status.
593
+
594
+ ## Exit codes
595
+
596
+ | Code | Meaning |
597
+ | --- | --- |
598
+ | `0` | Command completed successfully; `validate` found no violations. |
599
+ | `2` | Usage or operation error, such as invalid arguments, invalid data for an operation, a missing initialized workspace, or ambiguous edge deletion. |
600
+ | `3` | A requested graph record was not found, or `validate` found contract/store violations. |
601
+ | `4` | `validate` could not run because the workspace, contract, or graph store was unavailable or unreadable. |
602
+
603
+ `validate` always prints a JSON result when it can handle the failure, including
604
+ for invalid or unavailable workspaces. Other errors are printed to standard
605
+ error.