Skip to content

Use the public contree-client library - #13

Merged
mosquito merged 10 commits into
masterfrom
feature/use-contree-client
Jul 27, 2026
Merged

Use the public contree-client library#13
mosquito merged 10 commits into
masterfrom
feature/use-contree-client

Conversation

@mosquito

Copy link
Copy Markdown
Collaborator

Summary

Switches the CLI onto the public contree-client package (>=0.1.3) and reworks skill installation semantics.

contree-client adoption

  • contree-client becomes the only runtime dependency: HTTP transport, typed API methods, models, retries, operation helpers, image reference resolution, profile parsing, and stream decoding now come from the library.
  • client.py shrinks to CLI-side glue: CliClient announcing the CLI identity in the User-Agent, client_from_profile() with the CLI retry policy, and version helpers.
  • config.py keeps only the write side of profile management (atomic 0600 save, switch, delete with session-DB cleanup) on top of contree_client.profiles.
  • uv.lock now resolves the package from PyPI instead of an editable sibling checkout; docs and tests updated across the board.

skill install/remove rework

  • Directory specs mean a project root: every kind resolves to its project location under it (.claude/skills/contree, .agents/skills/contree, .claude/agents/*.md, ...). Paths pointing at a skill artifact (SKILL.md inside, contree basename, .md suffix, tool markers) stay literal.
  • Codex skills move to .agents/skills (project) / ~/.agents/skills (global), matching current Codex discovery; rules stay in $CODEX_HOME.
  • Install writes only the files it owns instead of replacing the target directory; remove deletes only those files and prunes empty layout dirs. Installing into a repository can never wipe foreign content (regression coverage included).
  • The Codex sandbox section renders the resolved CONTREE_HOME into writable_roots, so overridden data directories get correct sandbox permissions.
  • Claude subagent frontmatter uses the tools field, agent template uses the documented comma-separated form, and stale registry rows are forgotten on remove.

Test plan

  • make tests (ruff + mypy strict + pytest): 1869 passed, 4 skipped against contree-client 0.1.3 from PyPI.
  • Live smoke: uv run contree skill install codex:PATH lands in PATH/.agents/skills/contree; raw-dir install/remove cycle leaves the project byte-identical.

mosquito added 3 commits July 17, 2026 12:48
…e sandbox

Directory specs now mean a project root: every kind resolves to its
project location under it (.claude/skills/contree, .agents/skills/contree,
.claude/agents/*.md, ...); paths pointing at a skill artifact stay literal.
Codex skills move to .agents/skills (project) and ~/.agents/skills (global),
rules remain in CODEX_HOME. Install writes only the files it owns instead
of replacing the target directory, remove deletes only those files and
prunes empty layout dirs, so installing into a repository can never wipe
foreign content. The Codex sandbox section renders the resolved
CONTREE_HOME into writable_roots. Subagent frontmatter uses the tools
field, the workflow numbering is owned by the template, and stale registry
rows are forgotten on remove.
…as POSIX

skills_from_spec treated any spec containing a colon as kind:HINT, so
C:\path never expanded as a project root. The Codex sandbox rendered
CONTREE_HOME with backslashes, which are escape sequences inside TOML
basic strings; render with forward slashes instead. Path assertions in
the CONTREE_HOME tests no longer assume the pytest tmp dir lives
outside the user home.
Comment thread contree_cli/client.py Outdated
Comment thread contree_cli/docker/kw_from.py
Comment thread contree_cli/skill.py
Comment thread contree_cli/cli/export.py Outdated
mosquito added 4 commits July 17, 2026 23:59
…fing

The library yields the body exactly as served with compressed=True and
transparently decompressed tar with compressed=False, so the CLI writes
the stream as-is and drops the magic-byte passthrough.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates contree-cli from its internal HTTP/DTO layer to the public contree-client package, and updates CLI semantics, docs, and tests to match the new typed-client surface (including skill install layout changes and new export/build behaviors).

Changes:

  • Replace ad-hoc HTTP calls with contree-client typed methods/models and shared runtime helpers (errors, stream decoding, pagination/iterators).
  • Rework skill install/remove semantics (project-root specs, owned-file writes/removals, Codex layout alignment) and update docs/examples for renamed global flags (-o/--format/--output, etc.).
  • Add/expand end-to-end and doc-lint tests (entrypoint lifecycle, export command, documentation example validation).

Reviewed changes

Copilot reviewed 83 out of 84 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_use.py Update use tests for typed client mocks
tests/test_url_fetch.py Switch URL fetch tests to ensure_file/models
tests/test_tag.py Update tag tests for typed tag APIs/errors
tests/test_show.py Update show tests for typed operation responses
tests/test_shell_repl.py Align shell error handling + -o flag
tests/test_shell_parser.py Update parser tests for -o global flag
tests/test_shell_completer.py Update completion fixtures to typed models
tests/test_session.py Update session path tests to new helper
tests/test_session_cmd.py Add typed operation helpers for session cmd tests
tests/test_main.py New: assert client context-manager lifecycle in main
tests/test_ls.py Update ls tests for typed inspect + raw RequestSpec path
tests/test_kill.py Update cancel/kill tests for typed operations
tests/test_export.py New: coverage for export archive streaming
tests/test_doc_examples.py New: validate documented CLI examples against parser/help
tests/test_cp.py Update cp tests for streaming download API + progress
tests/test_config.py Migrate config tests to library Profile + constants
tests/test_cd.py Update cd validation tests to typed inspect/list errors
tests/test_cat.py Update cat tests to typed download API + call assertions
tests/test_auth.py Update auth tests to typed whoami parsing behavior
tests/conftest.py Replace fake HTTP stack with contree_client.testing client
README.md Update docs for dependencies + -o flag + workflow tweaks
pyproject.toml Add runtime dependency contree-client>=0.1.3
docs/tutorial/workflows.md Update examples to -o global output flag
docs/tutorial/shell.md Update REPL format flag docs to -o
docs/tutorial/installation.md Note contree-client dependency
docs/tutorial/images.md Update tag delete example to -U
docs/tutorial/configuration.md Update examples to -o global output flag
docs/index.md Update landing page examples + dependency messaging
docs/commands/show.md Update show docs for -o (needs --raw wording fix)
docs/commands/shell.md Update shell docs to -o global output flag
docs/commands/ps.md Fix kind flag example to -k
docs/commands/operation.md Update operation docs to -o global output flag
docs/commands/ls.md Update ls docs to -o global output flag
docs/commands/images.md Update images docs (include untagged via -a, uuid example)
docs/commands/file.md Update file docs to -o global output flag
docs/commands/cd.md Update cd semantics docs (print cwd; validate via inspect API)
docs/commands/auth.md Update auth docs to -o global output flag
contree_cli/types.py Update flag aliases (-o, --output, force -f) + add --decompress
contree_cli/skill_body.md Refresh skill text (sandbox section templated, -o examples)
contree_cli/shell/sources.py Use typed list APIs + server-side tag prefix filtering
contree_cli/shell/repl.py Handle ContreeAPIError + -o/--output format builtin
contree_cli/shell/completer.py Update completion builtins/flags for -o/--output
contree_cli/shell/argmap.py Add completion mapping for export args
contree_cli/session.py Add SessionStore.select_session helper
contree_cli/manual.md Update manual examples to -o global output flag
contree_cli/log.py Mirror CLI log level into contree_client logger
contree_cli/docker/url_fetch.py Use ensure_file; close HTTPError in HEAD helper
contree_cli/docker/kw_run.py Use typed spawn/status helpers + stream decoding from library
contree_cli/docker/kw_from.py Implement multistage sealing + typed import/wait/cancel
contree_cli/docker/kw_copy.py Implement COPY --from via archive export + extraction RUN (bug noted)
contree_cli/docker/kw_add.py Reject unsupported ADD --from explicitly
contree_cli/docker/context.py Update build context typing + stage tracking
contree_cli/config.py Refactor config around contree_client.profiles + session DB helpers
contree_cli/cli/use.py Use client.resolve_image; reuse SessionStore via select_session
contree_cli/cli/tag.py Use typed tag APIs + resolve-on-delete behavior
contree_cli/cli/skill.py Switch to multi-skill spec expansion + registry cleanup on missing
contree_cli/cli/show.py Use typed operation status + terminal-status constants
contree_cli/cli/session.py Use typed polling; raw RequestSpec for session_key filtering
contree_cli/cli/operation.py Switch to typed iterators + typed cancel/status; progress logs
contree_cli/cli/ls.py Use typed inspect list + raw RequestSpec for ?text=1
contree_cli/cli/images.py Use typed iterators/import helpers + credential model
contree_cli/cli/file.py Use typed streaming download + ensure_file + file iterators
contree_cli/cli/export.py New: export archive streaming command
contree_cli/cli/cp.py Use streaming download API; support DEST directory targets
contree_cli/cli/cd.py Validate dirs via typed inspect/list + typed errors
contree_cli/cli/cat.py Use typed download; preserve cache behavior
contree_cli/cli/build.py Multistage notes; use session_db_path + typed tag update
contree_cli/cli/auth.py Use typed whoami + stricter parsing/error handling
contree_cli/arguments.py Register new export command
contree_cli/agent.md Update agent guide examples and include export
contree_cli/main.py Drive client via context manager using ExitStack
contree_cli/init.py Update contextvar types to contree-client Profile/CliClient

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread contree_cli/docker/kw_copy.py
Comment thread contree_cli/cli/export.py
Comment thread docs/commands/show.md
mosquito added 3 commits July 27, 2026 17:05
…onest --raw docs

export: write to a temp file and rename into place only after the stream
completes; on NotFoundError only the temp file is removed, so a
pre-existing destination is no longer truncated then deleted.

kw_copy: pass compressed=False explicitly to inspect_image_archive so the
plain-tar contract COPY --from relies on is documented at the call site
instead of depending on the library default.

docs: show --raw round-trips through the typed operation model, so it
drops unknown fields -- stop calling it verbatim/full server payload.
Fixes eight regressions found in a broader review of this branch:

- cp: stream to a temp file and rename atomically, so a missing source
  or network error no longer truncates-then-deletes an existing
  destination (same pattern already applied to export).
- docker: FROM now reuses a sealed stage's image directly instead of
  re-resolving/importing it externally; COPY --from falls back to
  import_image on a resolve miss, matching FROM; the COPY --from
  scratch directory is randomised per invocation so its cleanup can
  never delete a path that pre-existed in the source image; stage
  sealing runs as root regardless of the stage's active USER.
- use --new: validates the image before switching to a new session key,
  so a failed resolve no longer strands the store on an empty,
  unvalidated session.
- skill: a project directory that merely happens to be named `contree`
  is no longer mistaken for an installed skill artifact unless it sits
  under a `skills/` directory; non-force installs now refuse when any
  owned file (not just SKILL.md) already exists.
- ls/session: the raw `client.request()` escape hatches now go through
  `client.call()` with `idempotent=True`, so transient 410/425/5xx
  responses are retried instead of failing immediately.
- __main__: catch urllib3/httpx transport exceptions (neither inherits
  from OSError) alongside the stdlib ones, so a non-requests backend
  reports a friendly network error instead of a bare traceback.
- operation/show: reworded --raw help text and comments that still
  claimed the output is the server payload "verbatim" -- it round-trips
  through the typed model and drops unknown fields.
- tests: isolate CODEX_HOME globally so skill-install tests can no
  longer write to a developer's real ~/.codex.
skills_from_spec() only includes the Claude-based kinds when
default_claude_home() exists, so the new
test_contree_basename_elsewhere_expands_as_project_root only saw all 7
kinds locally because ~/.claude happened to exist -- on the CI runner
it doesn't, so the test flaked to 4. Mock default_claude_home() like
the sibling test_raw_dir_expands_to_all_kinds already does.
@mosquito

Copy link
Copy Markdown
Collaborator Author

Pushed two more commits addressing a broader pass over the branch (8 additional findings not tied to an existing thread here), on top of the fixes above:

  • cp: same destination-truncation bug as export had — fixed with the identical atomic-temp-file pattern.
  • docker build: FROM builder AS next now reuses the sealed builder stage locally instead of trying to resolve/import it as an external image; COPY --from=<external image> now falls back to import_image on a resolve miss (previously only FROM had that fallback); the COPY --from scratch directory is now randomised per invocation so its rm -rf can never delete something the base image already had at /.contree-build; stage sealing now runs as root regardless of the sealed stage's active USER.
  • use --new: validates the image before switching to the new session key, so a bad tag/network error no longer strands the store on an empty, unvalidated session.
  • skill: a project directory that happens to be named contree is no longer treated as a skill artifact unless it actually sits under a skills/ directory; non-force install now checks all three owned files, not just SKILL.md, before refusing.
  • ls/session: the two raw client.request() escape hatches now go through client.call() with idempotent=True, so they get retried on transient 410/425/5xx like every typed call already does.
  • main: now also catches urllib3/httpx transport exceptions (neither subclasses OSError) so a non-requests backend reports a friendly network error instead of a bare traceback.
  • tests: CODEX_HOME is now isolated globally in conftest.py — a skill-install test was writing a rules file to the real ~/.codex on any machine without an explicit per-test patch.

Full details in the commit messages (18889be, a25b6c4). All 1881 tests pass, ruff/mypy clean.

@mosquito
mosquito merged commit 749b459 into master Jul 27, 2026
20 checks passed
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.

3 participants