gaia pkg
Install, publish, and bootstrap packages.
gaia pkg add <package> Install a registered package from the registry
gaia pkg add --lkm-index <id> --lkm-paper <paper-id>
Materialize an LKM paper as a local package
gaia pkg add --lkm-index <id> --lkm-claim <claim-id>
Resolve an LKM claim to its backing paper package
gaia pkg add lkm:<index>:paper:<paper-id>
Materialize a canonical LKM paper source ref
gaia pkg add lkm:<index>:claim:<claim-id>
Materialize the backing paper for a claim source ref
gaia pkg add-import --from <m> Insert a sibling/module import into a package file
gaia pkg add-module --name <m> Scaffold a sibling Python module
gaia pkg register [path] Submit a package to the official registry
gaia pkg scaffold --target <p> Bootstrap a fresh -gaia package directory layout
| Verb | Purpose |
|---|---|
add |
Resolve a registry entry to a SHA-pinned git URL, add as dependency, optionally cache dep_beliefs/<name>.json; also accepts LKM paper source refs/flags, materializes the paper graph as a project-local Gaia package, compiles it, and adds it as an editable dependency |
add-import |
Insert an idempotent from <module> import <names> line into __init__.py or another package source file |
add-module |
Create src/<import_name>/<module>.py with an optional docstring, optional seeded DSL imports, and a literal empty __all__ |
register |
Submit a package to the registry: emit Package/Versions/Deps TOML, exports/premises/holes/bridges/beliefs JSON, and (optionally) push + open a registry PR |
scaffold |
Write the minimal -gaia package skeleton (pyproject.toml with [tool.gaia], src/<import_name>/__init__.py importing claim, .gaia/.gitkeep). Counterpart to gaia author <verb> — bootstraps the package an agent then authors into. See gaia author. |
The historical flat verbs map to grouped paths where applicable
(gaia register --create-pr → gaia pkg register --create-pr). The
add-import, add-module, and scaffold verbs are v0.5 additions. See
CLI Commands for workflow examples and
use gaia pkg <verb> --help for the executable option surface.
For LKM search results, gaia pkg add accepts both the friendly action form
and canonical source refs:
gaia pkg add --lkm-index bohrium --lkm-paper 811827932371615744
gaia pkg add --lkm-index bohrium --lkm-claim gcn_579430355a0e4bbd
gaia pkg add lkm:bohrium:paper:811827932371615744
gaia pkg add lkm:bohrium:claim:gcn_579430355a0e4bbd
gaia pkg add lkm:paper:811827932371615744
The short lkm:paper:<id> / lkm:claim:<id> forms are default-index
compatibility aliases; Gaia emits canonical refs with the explicit index id.
The paper form fetches /papers/graph, writes a generated Gaia package under
.gaia/lkm_packages/<package-name>/, compiles it so dependency manifests exist,
and runs uv add --editable <generated-package>. The claim form first fetches
graph-shaped claim reasoning, resolves the backing paper:<id>, then performs
the same paper package materialization. If the reasoning response points to
multiple backing papers, Gaia refuses to guess; inspect the raw reasoning
response and add the intended paper explicitly.
Generated packages are normal Python Gaia packages. Their distribution name is
title-first and id-backed, for example
lkm-bohrium-controlling-phase-and-morphology-811827932371615744-gaia; their
[tool.gaia.source] metadata records the stable source ref
lkm:<index>:paper:<paper-id>. LKM paper factors are generated as
depends_on(...) scaffold records by default. This is the unformalized
authoring counterpart of derive(...): it preserves the premise-conclusion
shape, but it does not enter IR/BP until a user reviews and materializes it as
formal Gaia reasoning.
For LKM logic-graph responses, Gaia builds the scaffold from graph edges: a
factor's concludes edge identifies the conclusion, while incoming claim edges
such as previous_conclusion_of, weakpoint_of, and highlight_of are all
treated as premise claims in given=[...]; other incoming claim edges to the
same factor are treated the same way. addressed_problems and open_questions
are generated as paper-context question nodes, not as depends_on(...)
premises. The generated scaffold metadata preserves the original LKM edge types
for auditability.
Downstream source can import generated claims directly, for example:
from lkm_bohrium_controlling_phase_and_morphology_811827932371615744 import conclusion_1
gaia pkg add still does not install standalone LKM claim nodes. Claim refs are
convenience handles for installing the backing paper package.
LKM index URLs are resolver configuration, not package names. bohrium is the
built-in index id for https://open.bohrium.com/openapi/v1/lkm; custom indexes
can be supplied by environment, for example
GAIA_LKM_INDEX_PRIVATE_URL=https://example.test/lkm and
gaia pkg add --lkm-index private --lkm-paper <paper-id>. Access keys still
come from the LKM credential flow or GAIA_LKM_ACCESS_KEY / LKM_ACCESS_KEY.
The engine-side helpers (loading, compilation, prior application) live at
gaia.engine.packaging.
Implementation
gaia.cli.commands.add
gaia pkg add — install registered or LKM-backed Gaia packages.
LKMSourceRef
dataclass
LKMSourceRef(index_id: str, kind: str, provider_id: str)
Stable source identity for an LKM-backed package candidate.
ref
property
ref: str
Return the canonical LKM source ref.
add_command
add_command(package: str | None = typer.Argument(None, help='Package name (e.g., galileo-falling-bodies-gaia) or LKM ref (lkm:<index>:paper:<id> / lkm:<index>:claim:<id>).'), version: str | None = typer.Option(None, '--version', '-v', help='Specific version'), registry: str = typer.Option(DEFAULT_REGISTRY, '--registry', help='Registry GitHub repo'), lkm_index: str = typer.Option(DEFAULT_LKM_INDEX_ID, '--lkm-index', '--lkm-server', help='Configured LKM index id for --lkm-paper / --lkm-claim.'), lkm_paper: str | None = typer.Option(None, '--lkm-paper', help='Materialize this LKM paper id as a local Gaia package and add it.'), lkm_claim: str | None = typer.Option(None, '--lkm-claim', help='Resolve this LKM claim id to its backing paper package.'), target: str = typer.Option('.', '--target', help='Path to the Gaia knowledge package to add the dependency to (default: cwd). Matches the --target convention used by `gaia author` verbs so the whole package lifecycle runs from one place.')) -> None
Install a registered or LKM-backed Gaia knowledge package.
Resolves <package> against the gaia registry (default:
SiliconEinstein/gaia-registry on GitHub), runs uv add on the
resolved git+<repo>@<sha> spec, and best-effort downloads the
upstream beliefs.json into .gaia/dep_beliefs/<import_name>.json
so foreign-node priors flow into local inference. Must be run from
within a Gaia knowledge package (pyproject.toml carrying a
tool.gaia table).
LKM paper refs/flags fetch the paper graph, generate a local Gaia package
under .gaia/lkm_packages/, compile it, and add it as an editable
dependency with uv add --editable.
--version pins a specific release; omit to take the latest
registered version.
Example:
.. code-block:: bash
gaia pkg add galileo-falling-bodies-gaia
gaia pkg add mendel-v0-5-gaia --version 0.1.0
gaia pkg add --lkm-index bohrium --lkm-paper 811827932371615744
gaia pkg add --lkm-index bohrium --lkm-claim gcn_579430355a0e4bbd
gaia pkg add lkm:bohrium:paper:811827932371615744
Source code in gaia/cli/commands/add.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
gaia.cli.commands.pkg.add_import
gaia pkg add-import — inject from <module> import <names> into a target file.
Plain-Python imports between sibling modules of a Gaia knowledge package
are a precondition the author verbs can't handle on their own. When
gaia author variable --value DOMINANT_COUNT is invoked against
__init__.py, the cli has no way to know DOMINANT_COUNT lives in
./probabilities.py — the per-verb sibling_imports machinery
short-circuits whenever the target file is __init__.py since a
package can't re-import from itself.
This verb closes that gap with a small, explicit utility: it inserts
from .<module> import <names> (or from <dotted.module> import
<names>) into the target file, idempotently. Each name that already
appears in any matching from line is silently skipped; everything
new is folded into a single import line, alphabetically sorted, placed
after the docstring + from __future__ block.
The verb covers the "plain Python data plumbing" gap surfaced when
reproducing example packages whose sibling modules carry plain numeric
constants (probabilities.py) or NamedTuple helpers — content that
isn't DSL and so isn't author-verb material, yet still needs to be
referenced from __init__.py.
add_import_command
add_import_command(from_: str = typer.Option(..., '--from', help="Module to import from. A bare identifier (`probabilities`) or leading-dot form (`.probabilities`) resolves against the target package's import name as `<import_name>.probabilities`. A dotted absolute form (`other_pkg.helpers`) is used verbatim."), names: str = typer.Option(..., '--names', help='Comma-separated Python identifiers to import. Each must be a valid identifier; duplicates and entries already imported are silently skipped.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str = typer.Option('__init__.py', '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`. The file must already exist; use `gaia pkg add-module` first if you need to seed a fresh sibling.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None
Inject from <module> import <names> into a Gaia package file.
Example:
.. code-block:: bash
gaia pkg add-import --from probabilities \
--names DOMINANT_COUNT,RECESSIVE_COUNT,TOTAL_COUNT
Source code in gaia/cli/commands/pkg/add_import.py
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
gaia.cli.commands.pkg.add_module
gaia pkg add-module — scaffold a fresh sibling Python module.
Sibling files in a Gaia knowledge package (e.g. priors.py,
probabilities.py) need a precondition step before
gaia author <verb> --file <relative> can write into them. This verb
fills the gap by:
- Validating the target package is a Gaia knowledge package (via the existing pre-write invariant (a) machinery).
- Creating
src/<import_name>/<module>.pywith a minimal header and an empty__all__list. - Optionally seeding
from gaia.engine.lang import <verbs>based on the--importsflag (CSV).
The verb emits the same uniform JSON envelope as the rest of the
gaia author / gaia pkg scaffold family so an agent consumer can
chain it into authoring pipelines.
Why a separate sub-verb instead of letting the author verbs auto-create their target file? Two reasons:
-
Pre-write hygiene. Author verbs assume the target file already exists so the (c) collision-and-reference scan has stable inputs; letting them silently mint a fresh file would diverge their pre-write invariants.
-
Explicit intent. Adding a sibling module is a structural choice about package organisation. Splitting it from the per-statement verb keeps the agent's actions auditable.
add_module_command
add_module_command(name: str = typer.Option(..., '--name', help='Module name relative to the package source root. Accepts a bare identifier (`priors`) or a filename (`priors.py`). Required.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), imports: str | None = typer.Option(None, '--imports', help="Comma-separated DSL verbs to seed the module's imports (e.g. `register_prior,note`). Default: no imports beyond `from __future__ import annotations`."), docstring: str | None = typer.Option(None, '--docstring', help='Module docstring for the generated sibling file. Wrapped in triple quotes at line 1. Default: no docstring.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None
Scaffold a sibling Python module under src/<import_name>/authored/.
The module is created inside the composed authored/ submodule so
a later gaia author <verb> --file <name>.py (which routes into
authored/) can write into it.
Example:
.. code-block:: bash
gaia pkg add-module --name priors --imports register_prior \
--target ./mypkg-gaia --docstring "Priors module."
Source code in gaia/cli/commands/pkg/add_module.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | |
gaia.cli.commands.register
gaia pkg register -- prepare or submit a registry registration for a Gaia package.
register_command
register_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), tag: str | None = typer.Option(None, help='Git tag to register. Defaults to v<version>.'), repo: str | None = typer.Option(None, help='GitHub repository URL. Defaults to the git origin remote.'), registry_dir: str | None = typer.Option(None, help='Path to a local checkout of the official registry repository.'), registry_repo: str = typer.Option('SiliconEinstein/gaia-registry', help='Registry GitHub repo slug for PR creation.'), create_pr: bool = typer.Option(False, help='Push the registry branch and open a GitHub PR.')) -> None
Prepare or submit a registration for a tagged GitHub-backed Gaia package.
Reads the freshly-compiled package, validates git state (clean
worktree, tag pointing at HEAD, tag pushed to origin), builds the
registry-side Package.toml / Versions.toml / Deps.toml +
per-release manifests + beliefs.json, and either prints the
registration plan as JSON (default) or writes it into a local
checkout of the registry repo and opens a PR.
Preconditions checked: [project].name ends with -gaia,
[tool.gaia].uuid set to a valid UUID, .gaia/ir_hash matches
the freshly-compiled IR (run gaia build compile first), git
worktree clean, tag v<version> (or --tag) on HEAD and
pushed.
Example:
.. code-block:: bash
# Dry run — print the registration plan as JSON:
gaia pkg register .
# Write a registry branch into a local checkout and open a PR:
gaia pkg register . --registry-dir ~/dev/gaia-registry --create-pr
Source code in gaia/cli/commands/register.py
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | |
gaia.cli.commands.pkg.scaffold
gaia pkg scaffold — initialise a fresh Gaia knowledge package.
Maps the agent-first authoring story to package initialisation: given a
target directory and a package name, lay down the minimal layout that
the rest of the gaia author cycle needs (pyproject.toml with
[tool.gaia] block, src/<import_name>/__init__.py template,
.gaia/ artifact directory).
Why a new verb instead of reusing the legacy gaia init /
gaia build init? Two reasons:
- Agent-first envelope.
gaia initwrites human-oriented text to stdout and shells out touv;gaia pkg scaffoldemits the same uniform{status, code, verb, payload, warnings, diagnostics}shape thegaia author <verb>family does, so an LLM agent can parse one envelope schema across the whole package lifecycle. - Independent pre-validation surface.
scaffoldknows about-gaianaming, namespace defaults, and--checkintegration that runsgaia build checkagainst the freshly created package.
The scaffold verb writes its own pre-validation pipeline (target
directory absence + name ending + import-name validity) rather than
reusing :mod:gaia.cli.commands.author._prewrite — its 4 invariants
assume a pre-existing Gaia package, while scaffold creates one
from scratch. The JSON envelope is shared verbatim.
scaffold_command
scaffold_command(target: str = typer.Option(..., '--target', help='Path to the directory to initialise (must be empty or non-existent). Author verbs are run from outside the package, with --target <path>.'), name: str | None = typer.Option(None, '--name', help="Package name (must end with '-gaia'); defaults to target dir name."), namespace: str | None = typer.Option(None, '--namespace', help='Package namespace; defaults to the import name.'), description: str | None = typer.Option(None, '--description', help='Short description for pyproject.toml.'), with_uuid: bool = typer.Option(False, '--with-uuid', help='Generate a [tool.gaia].uuid for the package. Default is to omit the field (matches the shipping example packages).'), docstring: str | None = typer.Option(None, '--docstring', help='Module docstring for the generated src/<import_name>/__init__.py. Wrapped in triple quotes at line 1. Default: no docstring.'), check: bool = typer.Option(False, '--check/--no-check', help='Run post-write `gaia build check` on the freshly created package. Default off — a fresh scaffold has no declarations yet, which the engine treats as an error. Re-run with `gaia build check` once author commands have added statements.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (no-op for scaffold — it emits no warnings).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None
Scaffold a fresh Gaia knowledge package.
Example:
.. code-block:: bash
gaia pkg scaffold --target ./mypkg-gaia --name mypkg-gaia \
--docstring "My package."
Source code in gaia/cli/commands/pkg/scaffold.py
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | |