Skip to content

feat(cli): import/export verbs + Pathbase round-trip#58

Open
eliothedeman wants to merge 2 commits intomainfrom
eliot/stupefied-kirch-1f6d2b
Open

feat(cli): import/export verbs + Pathbase round-trip#58
eliothedeman wants to merge 2 commits intomainfrom
eliot/stupefied-kirch-1f6d2b

Conversation

@eliothedeman
Copy link
Copy Markdown
Collaborator

Summary

Collapse the external-boundary CLI surface to two symmetric verbs, with Pathbase as a peer source/target to claude, gemini, codex, opencode, pi, git, and github — not a privileged verb pair.

  • path import <source> — ingest into ~/.toolpath/documents/<cache-id>.json
  • path export <target> — emit from toolpath into external formats
  • path cache ls | rm — list / remove cached documents

The Pathbase round-trip (asked for)

  • path import pathbase <id-or-url>GET /api/v1/traces/:id → cache
  • path export pathbase --input <ref>POST /api/v1/traces ← cache or file

<ref> accepted by export resolves as a bare cache id first, falling back to a filesystem path. Auth reuses the existing path auth login / ~/.toolpath/credentials.json flow.

Server endpoints don't exist yet. CLI surfaces a clean connection/404 error until they ship. Tracked separately.

New files

  • crates/toolpath-cli/src/cmd_pathbase.rs — shared HTTP/session plumbing (extracted from cmd_auth.rs), plus traces_post / traces_get / require_session
  • crates/toolpath-cli/src/cmd_cache.rs — cache directory (0700), file writes (0600), cache_ref resolver, ls/rm subcommands, slug generation
  • crates/toolpath-cli/src/cmd_import.rs — per-source handlers (was cmd_derive.rs), plus Pathbase source
  • crates/toolpath-cli/src/cmd_export.rsClaude + Pathbase targets (supersedes cmd_incept.rs + cmd_project.rs)

Deprecations

One-release overlap; each prints a stderr warning and delegates into the new handler:

  • path derivepath import (stdout preserved via implicit --no-cache)
  • path inceptpath export claude --project <dir>
  • path project claudepath export claude

Ergonomics

  • --no-cache on import sends JSON to stdout for shell composition with render | query | validate
  • --force to overwrite an existing cache entry; default is error-on-exists for uniformity across every source
  • $TOOLPATH_CONFIG_DIR overrides the cache root (already honored by the credentials helper)

Test plan

  • cargo build --workspace (excl. toolpath-desktop)
  • cargo test --workspace (excl. toolpath-desktop) — 33 binaries, 0 failures, 156 unit + 24 integration on toolpath-cli
  • cargo clippy --workspace --all-targets -- -D warnings — clean (includes fix for a pre-existing needless_borrow in toolpath-convo/src/lib.rs:1026 and an #[allow(deprecated)] for the pre-existing assert_cmd::cargo_bin in render_md_snapshots.rs)
  • All 12 example documents validate
  • End-to-end smoke:
    • path import git --repo . --branch main writes cache + prints path
    • path cache ls surfaces the entry; path cache rm removes it
    • path import git --no-cache | path render md composes
    • path derive git still works and warns
    • path export pathbase --input <ref> without login errors with Not logged in. Run \path auth login`.`
    • path import pathbase <id> without login errors the same way

Follow-ups (out of scope)

  • Server endpoints POST/GET /api/v1/traces[/:id]
  • Projectors for gemini / codex / opencode / pi targets (each needs a ConversationProjector impl in the respective crate)
  • Switch toolpath-desktop's upload stub to call cmd_pathbase::traces_post once the server is real
  • path cache gc — size-based pruning
  • Remove the derive / incept / project aliases one release later

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

🔍 Preview deployed: https://69e3b4ef.toolpath.pages.dev

Collapse the external-boundary CLI surface to `import` and `export`, with
Pathbase as a peer source/target to claude/gemini/codex/opencode/pi/git/github.

New commands:
- `path import <source>` — ingest into ~/.toolpath/documents/<cache-id>.json
- `path export <target>` — emit from toolpath into external formats
- `path cache ls | rm` — list / remove cached documents

New sources/targets:
- `import pathbase <id-or-url>` — GET /api/v1/traces/:id into the cache
- `export pathbase --input <ref>` — POST the document to /api/v1/traces

Shared plumbing:
- cmd_pathbase.rs — HTTP client, session storage, `traces_post` / `traces_get`
- cmd_cache.rs — cache dir (0700), file writes (0600), `cache_ref` (id or path)

`<ref>` accepted by export/upload resolves as a bare cache id first,
falling back to a filesystem path. `--no-cache` on import sends JSON to
stdout for shell composition.

Deprecated (hidden aliases, one-release overlap, stderr warning):
- `path derive` → `import`
- `path incept` / `path project claude` → `export claude`

Server endpoints `POST/GET /api/v1/traces[/:id]` don't exist yet — the
CLI surfaces a clean connection/404 error until they ship.
Blockers:
- Fix wasm/emscripten build: `cmd_cache` unconditionally imported from
  `cmd_pathbase` (which is cfg-gated off emscripten). Extract `config_dir`
  into a new un-gated `config` module; both `cmd_cache` and `cmd_pathbase`
  use it.
- Fix cache id collisions across repos on the same branch. Fold an 8-hex
  hash of the canonical repo path into git cache ids
  (`git-a1b2c3d4-path-main` vs `git-e5f6a7b8-path-main`). Replace the
  ad-hoc `cache_id_for_doc` with a composable `doc_inner_id` + `make_id`.

Robustness:
- `write_cached` now uses `OpenOptions::create_new` when `!force`, so
  concurrent imports can't silently stomp each other (was a TOCTOU race
  between the exists-check and the write).
- `make_id` strips a trailing `.json` so a trace id like `trc.json`
  doesn't produce `pathbase-trc.json` and then fail `cache_path`'s
  suffix validator.

UX:
- `export pathbase --url` warns when its host differs from the session's
  server — previously would just 401.
- `cache_ref` file-not-found branch hints at `path cache ls` + dropping
  the extension for typo recovery.

Tests:
- New `MockServer` (raw TcpListener) exercises `traces_post` / `traces_get`
  success, 401, 404, and 5xx with server-supplied error messages. No new
  dependencies.
- Two-repo cache-id integration test: import `main` from two distinct
  git fixtures, assert two cache entries instead of a collision.
- `--pretty` propagates through `--no-cache` to stdout.
- Multi-session claude import emits one cache entry per session.
- Total: 165 unit + 26 integration, zero failures.

Release:
- Bump toolpath-cli 0.4.0 → 0.5.0. CHANGELOG + site/_data/crates.json
  updated per the release checklist.
@eliothedeman eliothedeman force-pushed the eliot/stupefied-kirch-1f6d2b branch from 943387c to 1a3f081 Compare April 25, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant