claude-artifact-owner
Keep Claude Code sessions working on their artifacts after cswap switches claude.ai accounts.
An artifact belongs to the claude.ai account that published it. cswap switches the login that all ordinary Claude Code sessions share, and they follow it at their next message. From then on, the artifacts a session published look deleted to it: reads return "artifact not found — it may have been deleted, or it has not been shared with you", and in auto mode the permission review can fail first ("could not look up whose artifact this is … HTTP 404"). Sessions then guess at what happened, or publish a copy under a new link.
claude-artifact-owner is a pair of Claude Code hooks, run by its artifact-owner command. It records which account
owns each artifact, and runs an Artifact call on an artifact another of your accounts owns as that account, in a
short headless helper session. The session gets the call's result back.
Experimental. Tested with Claude Code 2.1.308 to 2.1.310 and cswap 0.28, on Linux. Not affiliated with Anthropic.
Install
You need cswap with your accounts added, cswap run working for them (cswap's per-terminal session mode), and
Claude Code with the Artifact tool (a claude.ai login).
uv tool install claude-artifact-owner # or: pipx install claude-artifact-owner
artifact-owner refresh # record who owns your existing artifacts: one helper session per account
artifact-owner hooks # print the hook entries for ~/.claude/settings.json
To install from GitHub instead: uv tool install git+https://github.com/cskiraly/claude-artifact-owner.
Add the two entries that artifact-owner hooks prints to the hooks of ~/.claude/settings.json (next to any
PreToolUse and PostToolUse entries you already have). Then add this to ~/.claude/CLAUDE.md, so that sessions
trust what the hook hands back (without it, a session may report a routed result as unverified):
- **artifact-owner** is a hook I set up for cswap account switches. When an Artifact call is on an
artifact that one of my other claude.ai accounts owns, it runs the call as that account and hands
back the result as a "hook error" that names artifact-owner. When it says it ran the call, the
result is real and the call is done; when it says it couldn't, tell me.
Running sessions may need a restart, or a look at /hooks, before they use the new hooks.
How it works
- Record (PostToolUse on Artifact): stores the account that owns each artifact, taken from publishes, reads that
say
owned: true, andlistresults. For a publish it also stores the session and the file. Records go to~/.local/state/artifact-owner/owners.jsonl. - Route (PreToolUse on Artifact, ArtifactComments, ArtifactData): for a call that names a link, or a file this session published, whose owner isn't the account the session now runs as, it runs the same call in a helper session as the owner's cswap account (matched by email, and by organization when an email is in more than one account). It then denies the original call, with the helper's result as the reason ("the call is done"). A result over 9000 characters is cut there, and the whole of it goes to a file in the session's scratchpad, which the reason names. Later calls are routed the same way. A routed publish is recorded for the calling session, so a later republish that names only the file finds the link.
- Passes through:
open,pin,unpin,quickstart, calls without a link, and links with no recorded owner (runartifact-owner refreshwhen the log says "owner unknown"; it records up to 200 artifacts per account, the most recently opened or updated). Artifact deletes (thedeleteaction) are never routed: the session is told to leave them to you. Other changes, such as ArtifactData writes and comments, are routed and get the usual review. - Helper:
cswap run <owner> -- -p …with Haiku, in safe mode (no CLAUDE.md, skills, plugins, hooks or MCP servers), with only the Artifact tools and Read, at most 8 turns, no saved transcript, and access to the calling session's scratchpad and the file's folder (--add-dir; its tools only read there, except that a publish witheditssaves the edited file). It runs in auto mode with permission prompts off, so its calls get the usual review, and that review never lets it delete an artifact. A republish reads the live version first, as the tool requires. The result counts only if the helper made exactly the call asked for; any other call is logged. The Artifact tool is off in headless sessions; the helper turns it on withCLAUDE_CODE_ARTIFACT=1, a variable found in Claude Code itself and not documented, so a new version may change it. - Republishes: only the session that published a page republishes it through the hook (a publish record of that
session; what a transcript says doesn't count), or a session you allowed to. The helper doesn't know whether you
asked for a change, and its review takes the request for yours: from Claude Code 2.1.310 it lets another session's
republish of a private page through. So any other session is told that the page is in another account, and that
you can allow it, switch to that account (
cswap switch <n>), or have it publish a copy under a new link. The helper gets one auto-mode rule, republishing a private page you own is fine, since the review has refused those too (2.1.309). The review still looks the page up when the publish happens, so a shared page gets the plain review. - Allowing a republish: type
! artifact-owner allow <link>in the session that should republish the page; the session's refusal names the command. That session may then republish the page through the hook for 15 minutes (allow <link> 60for an hour,allow <link> 0withdraws it). The grant is tied to the session byCLAUDE_CODE_SESSION_ID, which Claude Code sets for the commands run in a session. The session is told the command is yours to run. If it ran it with its Bash tool anyway, that would go through its own permission check (a prompt, or in auto mode the review, which sees what you asked), unlike the routed publish. - Failure: the session is told the artifact isn't deleted, why the helper failed (the review refused it, the
owner account is at its limit, …), and the options: try again later,
cswap switch <owner>, or a copy with a new link.
A routed call takes about 6–40 seconds and, at list prices, a fraction of a cent, on the owner account's usage.
Usage
artifact-owner hook the PreToolUse/PostToolUse hook (hook JSON on stdin)
artifact-owner hooks print the hook entries for ~/.claude/settings.json
artifact-owner refresh record the owner of every artifact of every account
artifact-owner who URL print the recorded owner of an artifact
artifact-owner run ACCOUNT TOOL JSON run one call as ACCOUNT (cswap number or email), print the result
artifact-owner allow URL [MINUTES] let this Claude Code session republish a page it didn't publish, for
MINUTES (15; 0 withdraws): type it in the session, after a !
The log, ~/.local/state/artifact-owner/log, has one line per routing decision, helper run and error. The raw
output of the last helper run is in helper-last.out next to it, and that of failed ones in helper-failed-*.out.
Caveats
- The own-page rule is applied by the reviewer, a model, so it can still refuse; the session then tells you. A page's sharing can change between the review's lookup and the publish, a matter of seconds.
- Pages published before the hooks were installed have no record of the session that published them, so their republishes are refused until you allow them.
- Live notifications (comments sent to Claude, new versions) don't come back to a session that was switched away.
- An artifact shared with the current account as an editor is still routed to its owner. That costs a helper run, and another session's republish is refused until you allow it, though the current account could publish it directly.
- A switch between a publish and its PostToolUse hook records the wrong owner. cswap switches rarely, so this is rare.
- The helper runs the newest installed Claude Code, which can be newer than a running session's.
- A call whose input doesn't fit in one command-line argument (128 KiB on Linux), such as very large
edits, can't be routed; the session is told why.
The real fix belongs in Claude Code or claude.ai: a session should still reach the artifacts it published after the account changes.
Tests
uv run pytest: the logic, on helper output and hook input captured from Claude Code (accounts, ids, titles and paths replaced), with no sessions started.tests/e2e.sh: reads and republishes a throwaway page that another of your accounts owns, through real headless sessions; the header says how to set it up.
Uninstall
Remove the two hook entries from ~/.claude/settings.json (and the CLAUDE.md line), then:
uv tool uninstall claude-artifact-owner
rm -r ~/.local/state/artifact-owner
License
MIT
Metadata
Release files for claude-artifact-owner 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| claude_artifact_owner-0.1.0.tar.gz | 35.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claude_artifact_owner-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.7 kB
Release files / claude_artifact_owner-0.1.0.tar.gz
| Download URL | claude_artifact_owner-0.1.0.tar.gz |
|---|---|
| Size | 35.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c32decb84b4152356eb3d8d01ba3fc18e91a02b1c75bde5444e0b1e0c16e73d0
|
|
BLAKE2b-256 checksum How to use checksums |
9a2a998ddd9b0e5bdb844116232e98d3e2f9bb2015ad8ba193776aec2497a5ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / claude_artifact_owner-0.1.0-py3-none-any.whl
| Download URL | claude_artifact_owner-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9264d01683a4420b07318f198bf8576d8ccafdf228e1fce9ab23748f966df7ac
|
|
BLAKE2b-256 checksum How to use checksums |
45d1d98f575ffcbbccfd33bae3f97c587d8d7d172e9b247ae017ed1b50d8223f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for claude-artifact-owner 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.