Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 26 additions & 0 deletions build_tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,32 @@
3) Run `build_tools/worker.py` to generate artifacts (uses an agent).
4) Run `build_tools/compile.py` to build `cartographer_out/db/knowledge.sqlite`.

`compile.py` also loads `internal/cli/resource_map.json` into these tables:
- `resources`
- `resource_fields`
- `resource_field_targets`

It creates a `command_resources` view that links list/show commands to their
resource by parsing `commands.full_path`. Example query:

```
SELECT c.full_path, cr.resource, cr.verb, r.label_fields
FROM command_resources cr
JOIN commands c ON c.id = cr.command_id
JOIN resources r ON r.name = cr.resource
ORDER BY c.full_path;
```

## Resource map pipeline (for CLI --fields)
1) Run `build_tools/resource_map_dispatcher.py` to create resource batches.
2) Run `build_tools/resource_map_worker.py` (or `resource_map_swarm.py`) to generate artifacts.
3) Run `build_tools/resource_map_compile.py` to merge into `internal/cli/resource_map.json`.

## Deterministic show backfill
Use this to backfill show-command artifacts without an agent (uses CLI help + resource_map):
1) Ensure `internal/cli/resource_map.json` is up to date.
2) Run `python3 build_tools/deterministic_show_backfill.py`.

## Bootstrap (Python deps)
If your system Python is externally managed (PEP 668), run:
`bash build_tools/bootstrap.sh`
Expand Down
116 changes: 116 additions & 0 deletions build_tools/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,58 @@ def init_db(db_path: Path) -> sqlite3.Connection:
)
"""
)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS resources (
name TEXT PRIMARY KEY,
label_fields TEXT
)
"""
)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS resource_fields (
resource TEXT NOT NULL,
name TEXT NOT NULL,
kind TEXT NOT NULL,
description TEXT,
is_label INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (resource, name),
FOREIGN KEY(resource) REFERENCES resources(name)
)
"""
)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS resource_field_targets (
resource TEXT NOT NULL,
field TEXT NOT NULL,
target_resource TEXT NOT NULL,
PRIMARY KEY (resource, field, target_resource),
FOREIGN KEY(resource) REFERENCES resources(name)
)
"""
)
conn.execute(
"""
CREATE VIEW IF NOT EXISTS command_resources AS
SELECT
id AS command_id,
full_path,
CASE
WHEN full_path LIKE 'view % list'
THEN substr(full_path, 6, instr(substr(full_path, 6), ' ') - 1)
WHEN full_path LIKE 'view % show'
THEN substr(full_path, 6, instr(substr(full_path, 6), ' ') - 1)
END AS resource,
CASE
WHEN full_path LIKE 'view % list' THEN 'list'
WHEN full_path LIKE 'view % show' THEN 'show'
END AS verb
FROM commands
WHERE full_path LIKE 'view % list' OR full_path LIKE 'view % show';
"""
)
return conn


Expand Down Expand Up @@ -107,17 +159,81 @@ def upsert_artifact(conn: sqlite3.Connection, artifact: CommandArtifact) -> None
)


def load_resource_map(resource_map_path: Path) -> dict:
if not resource_map_path.exists():
raise FileNotFoundError(f"resource_map.json not found: {resource_map_path}")
return json.loads(resource_map_path.read_text(encoding="utf-8"))


def upsert_resource_map(conn: sqlite3.Connection, resource_map: dict) -> None:
conn.execute("DELETE FROM resource_field_targets")
conn.execute("DELETE FROM resource_fields")
conn.execute("DELETE FROM resources")

resources = resource_map.get("resources", {})
relationships = resource_map.get("relationships", {})

for resource_name, data in resources.items():
label_fields = data.get("label_fields", [])
conn.execute(
"INSERT INTO resources (name, label_fields) VALUES (?, ?)",
(resource_name, json.dumps(label_fields)),
)

for attr in data.get("attributes", []):
conn.execute(
"""
INSERT INTO resource_fields (resource, name, kind, description, is_label)
VALUES (?, ?, ?, ?, ?)
""",
(
resource_name,
attr,
"attribute",
None,
1 if attr in label_fields else 0,
),
)

for resource_name, rels in relationships.items():
for rel_name, rel_info in rels.items():
conn.execute(
"""
INSERT OR IGNORE INTO resource_fields (resource, name, kind, description, is_label)
VALUES (?, ?, ?, ?, ?)
""",
(resource_name, rel_name, "relationship", None, 0),
)
for target in rel_info.get("resources", []):
conn.execute(
"""
INSERT OR IGNORE INTO resource_field_targets (resource, field, target_resource)
VALUES (?, ?, ?)
""",
(resource_name, rel_name, target),
)


def main() -> None:
parser = argparse.ArgumentParser(description="Compile Cartographer artifacts into SQLite")
parser.add_argument("--config", default="config.yaml", help="Path to config.yaml")
parser.add_argument(
"--resource-map",
default=None,
help="Path to resource_map.json (defaults to internal/cli/resource_map.json)",
)
args = parser.parse_args()

config = load_config(args.config)
root_out = project_root(config)
artifacts_root = root_out / "artifacts"
db_path = root_out / "db" / "knowledge.sqlite"
repo_root = Path(__file__).resolve().parents[1]
resource_map_path = Path(args.resource_map) if args.resource_map else repo_root / "internal/cli/resource_map.json"

conn = init_db(db_path)
resource_map = load_resource_map(resource_map_path)
upsert_resource_map(conn, resource_map)
inserted = 0
skipped = 0

Expand Down
Loading