refactor(cli): one ArtifactType layer and per-session import plumbing#133
Conversation
Unify harness/artifact typing: ArtifactType is the single enum naming artifact sources (absorbing HarnessArg), Harness stays as the harness-only layer share/resume take, SessionRow generalizes to ArtifactRow. Every import flow now loops per-session derive helpers (derive_*_session_with, injectable managers) — breaking: p import pi --all emits one Path per session; p import gemini loses --include-thinking (thinking is always ingested). Claude derives source path.base from the session's recorded cwd instead of the lossy dir slug. Library groundwork: toolpath-gemini 0.6.1 (list_session_entries, bounded peek_session_id), toolpath-claude 0.12.1 (session_chain public).
|
🔍 Preview deployed: https://b68c92d7.toolpath.pages.dev |
akesling
left a comment
There was a problem hiding this comment.
Generally looks good. Let's clean up the dependencies/ownership for Harness and ArtifactType in the CLI, though.
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, clap::ValueEnum)] | ||
| #[value(rename_all = "lower")] | ||
| pub enum HarnessArg { | ||
| pub enum Harness { |
There was a problem hiding this comment.
Why does cmd_share own Harness and this impl ArtifactType when they're used by other commands, etc.? Why do we have this impl here anyway when ArtifactType is owned by sync?
Would it make sense to push this to some common types/helpers module and then have everything depend on that?
There was a problem hiding this comment.
Extracted to src/artifact.rs and src/harness.rs.
ArtifactType lived in sync.rs and Harness/HarnessBundle in cmd_share.rs while being consumed across import, share, resume, and cache. Give the shared layers their own modules: artifact.rs holds ArtifactType, harness.rs holds Harness, ArtifactType::harness(), HarnessBundle, and the provider is_not_found_* probes. Command modules keep only their own logic.
…oc links The old sync module was pub, so its ArtifactType doc links resolved as public items. Keep that visibility for the split-out modules, and refer to the pub(crate) HarnessBundle without a link.
…ommands The document-cache store (cache_path/write_cached/cache_ref/list/ remove/make_id) and single-session derivation (DerivedDoc, the derive_*_session helpers, the Pathbase fetch) were owned by cmd_cache.rs and cmd_import.rs while being called from share, resume, export, and query. Give them their own modules; the command files keep only their arg surfaces and flows.
The cursor and opencode session helpers are cfg'd off on emscripten (SQLite), and cmd_import's remaining make_id/ArtifactType/doc_inner_id uses all sit in gated code — the unconditional imports broke or warned on the site's wasm build, which quality_gates.sh doesn't cover.
|
additionally fixed that antipattern across the cli crate, extracted logic into derive.rs and cache.rs instead of exporting logic from cmd files. |
akesling
left a comment
There was a problem hiding this comment.
A couple things to improve, but LGTM
| ArtifactType::Cursor => "cursor ", | ||
| ArtifactType::Pi => "pi ", | ||
| ArtifactType::Copilot => "copilot ", | ||
| ArtifactType::Git => "git ", |
There was a problem hiding this comment.
What are all of these spaces?
There was a problem hiding this comment.
this was column padding for fzf. replaced with a width constant and a padding function.
| /// The trailing UUID of a codex rollout filename stem | ||
| /// (`rollout-<timestamp>-<uuid>`), or the whole stem when it doesn't end | ||
| /// in one. Codex's `read_session` resolves either form. | ||
| fn codex_artifact_id(stem: &str) -> &str { |
There was a problem hiding this comment.
Why is this even inspecting the ID? Shouldn't functionality related to this be owned by the codex crate? Maybe we need extract ID creation out to some helper indirection for all the harnesses?
There was a problem hiding this comment.
Codex --all now works like the other providers
| @@ -45,9 +45,9 @@ use clap::Args; | |||
| use std::path::PathBuf; | |||
|
|
|||
| /// Re-exported so external callers (integration tests, future consumers) | |||
There was a problem hiding this comment.
Let's remove this comment.
| // re-fetches; `--no-cache` skips both the probe AND the post- | ||
| // fetch write (still useful for ephemeral environments). | ||
| let cache_id = crate::cmd_import::pathbase_cache_id_of(u, args.url.as_deref())?; | ||
| let cache_id = crate::derive::pathbase_cache_id_of(u, args.url.as_deref())?; |
There was a problem hiding this comment.
I think cache ID management probably be owned by the cache module.
| } | ||
| } | ||
|
|
||
| pub(crate) fn is_not_found_claude(err: &toolpath_claude::ConvoError) -> bool { |
There was a problem hiding this comment.
We can leave this be here, but definitely calling out this manual reification smell as something we should maybe revisit in the future.
…h, pathbase key in cache - Replace the hand-padded symbol() literal table with padded_name() formatting over NAME_COLUMN_WIDTH; the picker row matcher compares the first token against name() instead of prefix-matching padding. - Delete codex_artifact_id: the codex --all import reads each enumerated rollout file directly via derive_codex_session_file instead of round-tripping filename -> id -> re-glob. - Drop the stale Harness re-export comment in cmd_resume along with the re-export itself; tests import path_cli::harness::Harness. - cache.rs owns the pathbase cache-key scheme (pathbase_cache_id); pathbase_cache_id_of is gone — resume parses the ref and computes the key in two visible steps.
Codex --all now lists session metadata and derives by id like every other provider, instead of enumerating rollout files and deriving by path (drops derive_codex_session_file; one derive helper per provider again). opencode and cursor --all now warn-and-skip unreadable sessions like the other five providers instead of aborting the batch.
…ll imports toolpath-codex 0.6.1: list_session_ids returns rollout filename stems from one directory walk with no file reads, and find_rollout_file resolves a full stem straight to its sessions/YYYY/MM/DD path before falling back to the tree walk. p import codex --all now lists ids that way, so the symmetric list-then-derive shape costs one parse per file and one walk total — cheaper than the old filename-parsing version.
First of a four-PR stack splitting up #131 into reviewable pieces, per review. This one is the harness/type groundwork; the sync engine, query auto-sync, and
--parent-dirscoping follow in the stack.One
ArtifactTypelayer.ArtifactType(inartifact.rs) becomes the single enum naming artifact sources — it absorbs the formerHarnessArg, and--harnessfilters and import cache-id prefixes all route through it. TheHarnessenum stays as the harness-only layer thatshare/resume--harnesstake (you can't resume into a git repo), mapping into the general enum viaHarness::artifact_type().SessionRowgeneralizes toArtifactRowwith an optionalmessage_count.Cross-command machinery gets its own modules (per review — thanks @akesling):
artifact.rsholdsArtifactType;harness.rsholdsHarness,HarnessBundle, and the provider not-found probes;cache.rsholds the document store (write_cached,cache_ref,list_cached,make_id, …);derive.rsholdsDerivedDoc, the per-sessionderive_*_sessionhelpers, and the Pathbase fetch.cmd_*.rsfiles keep only their command's argument surface and flow — nothing imports machinery out of another command's file anymore.Per-session import plumbing. Every import flow — explicit
--session, picker multi-select,--all, and the most-recent fallbacks — now loops per-session derive helpers (derive_*_session_with, one per provider, each usable with an injected provider manager).--allwarns-and-skips unreadable sessions instead of aborting the batch.Breaking (pre-1.0):
p import pi --allemits one Path document per session, consistent with every other provider (previously a single combined Graph).p import geminiloses--include-thinking— thinking is always ingested; the cache, uploads, andresumeall carry the full derivation.Claude
path.basefix. Claude derives leaveDeriveConfig.project_pathunset sopath.basecomes from the session's own recorded cwd rather than the lossy project-dir slug, falling back to the caller's--projectwhen no cwd was recorded.Library groundwork (consumed by the sync PR next in the stack):
toolpath-gemini0.6.1 addsPathResolver::list_session_entriesand boundspeek_session_idto a 4 KiB prefix scan with full-parse fallback;toolpath-claude0.12.1 makesClaudeConvo::session_chainpublic (rides the cached chain index, no extra IO after a listing).path-cli0.15.0 → 0.16.0.Tests:
cargo clippy -D warningsclean; 433 tests pass at this point in the stack.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Stack (review bottom-up): #133 types/imports → #134 cache sync → #135 query auto-sync → #136 parent-dir. Supersedes #131.