Wire an existing source/format connector into the neocarta CLI as a
neocarta <source> <verb> command. This file is the operational loop; the
full prose standard lives in
connector-cli-command-contract.md
(read it before designing a command — it's self-contained). Drive the whole loop
with the driver:
.claude/skills/neocarta-add-connector-cli-command/scripts/driver.py, run through uv.
All paths below are relative to the repo root.
Scope
- Connectors only. This is for source/format connector commands. The CLI will also grow enrichment/embedding commands and MCP-tool commands — those are separate skills, not this one.
- Follow-up PR, not the connector itself. The library connector under
neocarta/connectors/is built and verified by theneocarta-add-source-connectorskill, which defers CLI wiring to here. Build the connector first; this skill wraps it.
Prerequisites
The managed environment, with the CLI extras and dev groups installed:
uv sync --all-groups --all-extras
(click/rich/pydantic-settings are the cli extra, not a dependency
group, so plain uv sync --all-groups will not install the CLI — use
--all-extras too.)
Run (agent path) — the driver
# Gap map: every connector and whether it already has a CLI command:
uv run .claude/skills/neocarta-add-connector-cli-command/scripts/driver.py list
# Scaffold a command module + unit test and wire it into main.py.
# <source> is the CLI group name (== command-module stem). By default the
# connector package is the same name and the first exported *Connector is used:
uv run .claude/skills/neocarta-add-connector-cli-command/scripts/driver.py scaffold databricks
# Point at a specific package / class, and/or give multiple verbs:
uv run .claude/skills/neocarta-add-connector-cli-command/scripts/driver.py \
scaffold bigquery --connector-pkg bigquery --connector-class BigQuerySchemaConnector \
--verb schema --verb logs
# Verify a command against the contract (import, registration, agent-context,
# --help, ruff, and its unit test):
uv run .claude/skills/neocarta-add-connector-cli-command/scripts/driver.py verify databricks
scaffold writes a conformant skeleton — neocarta/_cli/commands/<source>.py
(group + one ingest-shaped command per verb, with the dry-run / JSON / error
plumbing already correct) and tests/unit/_cli/test_<source>.py — and wires the
import + cli.add_command into main.py alphabetically (idempotent). The
skeleton's --help and --dry-run work and the generated test passes as-is
once the wrapped connector class is importable. You then fill the TODOs for the
connector's real constructor and ingest() signature.
verify imports the module, checks the group is registered on cli, confirms it
appears in neocarta agent-context, runs --help for each verb, runs ruff check on the module, and runs the unit test. It exits non-zero on any failure.
Typical workflow
listto confirm the connector exists and has no CLI command yet.scaffold <source>(add--connector-pkg/--connector-class/ repeated--verbas needed).- Fill the TODOs in
neocarta/_cli/commands/<source>.py: the connector's real constructor args andingest()(orexport()) call, and the success/dry-run payload fields. - Promote the
--sourceinput to aCLISettingsfield + anENV_VARSentry in neocarta/_cli/config.py (see contract §6), and resolve fromsettingsinstead ofos.environ. - Flesh out the unit test for connector-specific behaviour beyond the skeleton.
verify <source>until green.- Run the full CLI suite + ruff, update
CHANGELOG.md.
Test
make test-cli # CLI unit tests — NOTE: make test-unit ignores tests/unit/_cli
make fmt && make lint # ruff format + lint (select = ["ALL"]) — must be clean before PR
make test-it # integration: real ingest into a Neo4j testcontainer (Docker)
Gotchas
make test-unitdoes not run the CLI tests. They live undertests/unit/_cliwhich that target ignores; usemake test-cli(the driver'sverifyruns the single new test file directly).- CLI deps are an extra, not a group.
uv sync --all-groupsalone omitsclick/rich; the CLI then fails to import. Sync with--all-extras. - The scaffold reads its input from
os.environ, deliberately. That keeps the generated command + test self-contained and green before you touchconfig.py. Promote it to aCLISettingsfield (step 4) for the real PR — the house pattern resolves inputs fromsettings, notos.environ. agent-contextis generated, never hand-edited. It introspects the live command tree; a registered command with itsENV_VARSentry shows up automatically. If it's missing, the command isn't registered inmain.py.- Multiple connector classes / non-ingest shapes need hand-editing. The
scaffold emits one ingest-shaped command per
--verb. Aschema/logssplit (likebigquery) or anexportverb (likeosi) starts from the scaffold, then adapt againstbigquery.py/osi.py. - Don't unwrap the Neo4j password into a named local. Use the
_common.pyhelpers (_require_neo4j_settings,_neo4j_driver) as-is; they keep the secret off CodeQL's logging-sink radar.
Troubleshooting
verifyFAIL:cannot import neocarta.connectors.<pkg>— the library connector isn't built/installed yet, or--connector-pkgis wrong. Build the connector first (theneocarta-add-source-connectorskill).verifyFAIL:<source>is not registered in main.py — re-runscaffold(the wiring step is idempotent) or add the import +cli.add_command(<source>)by hand, alphabetically.verifyFAIL: ruff — runmake fmtthenuv run ruff check neocarta/_cli/commands/<source>.py; the generated skeleton is clean, so a failure is in a TODO you filled.scaffoldWARN:exports no *Connector— the package has no connector class in__all__yet (e.g.databricks,collibraare work-in-progress). Pass--connector-classexplicitly or finish the library connector first.