Describe the bug
marimo pair prompt --claude prints "could not be found" when the skill is missing, then
emits the pair prompt anyway and exits 0. So the documented usage
claude "$(uvx marimo@latest pair prompt --url http://localhost:2718 --claude)"
launches an agent session whose first instruction is "Use the /marimo-pair skill" for a
skill that is not installed. The warning goes to stderr, where command substitution leaves
it easy to miss, and the agent starts with no way to do what it was asked.
This looks like a source change that went missing rather than a design decision. #9029
("fix: exit with non-zero code when marimo-pair skill is missing") describes two changes:
The prompt command printed an error when the skill was not found but continued executing
and exited successfully. Now it raises SystemExit(1) so callers can detect the failure.
Also fixes the test assertions to match the actual error message ("could not be found").
The second half landed and was clearly correct — the tests were asserting a stale
"not installed" string that the source no longer produced. But the merged commit
2bb4d538c touches only tests/_cli/test_cli_pair.py:
- assert result.exit_code != 0, flag
- assert "not installed" in result.output, flag
+ assert result.exit_code == 0, flag
+ assert "could not be found" in result.output, flag
- assert result.exit_code != 0
- assert "not installed" in result.output
+ assert result.exit_code == 0
+ assert "could not be found" in result.output
Both tests were red before that commit, failing on the exit code as well as the message:
$ git checkout 2bb4d538c~1
$ pytest tests/_cli/test_cli_pair.py -q
> assert result.exit_code != 0
E assert 0 != 0
FAILED tests/_cli/test_cli_pair.py::TestPairPrompt::test_prompt_skill_missing
FAILED tests/_cli/test_cli_pair.py::TestPairPromptWithToken::test_with_token_and_skill_missing_fails
2 failed, 14 passed
So the exit-code assertions were green-lit against the unchanged source instead of the
source being changed to satisfy them. The SystemExit(1) never reached
marimo/_cli/pair/commands.py, and has never existed anywhere in the package:
$ git log --all -S 'SystemExit' -- marimo/_cli/pair/
$ grep -rnE 'SystemExit|sys\.exit|raise click|Abort' marimo/_cli/pair/
(both empty)
The net effect is that the tests now assert the behaviour #9029 set out to remove, which
makes it read as intentional. test_with_token_and_skill_missing_fails is named for a
failure it asserts does not happen — worth renaming alongside any fix.
Interaction with #10704
Please consider sequencing this after #10704. Today the validator result is cosmetic, so a
false negative only costs a spurious warning. Once a missing skill exits non-zero, any false
negative becomes a hard failure.
_claude_skill_dirs() currently produces one for a project-scoped install: it resolves
.claude from Path.cwd() alone, so a skill at <repo>/.claude/skills/marimo-pair/ is
invisible from any subdirectory. (A global ~/.claude/skills install is unaffected — that
path is checked directly.) #10704 fixes the project-scoped case. Landing this one first
would turn that warning into a hard blocker for anyone with a project-scoped install whose
notebooks live below the repository root.
Will you submit a PR?
Two questions worth settling first, since both affect callers:
- Should a missing skill be fatal for all agent flags, or only when the prompt is being
piped into an agent? A user reading the output themselves may reasonably want the prompt.
- Should
--with-token still write the token file before exiting?
Environment
Reproducible from source; not version-specific. Confirmed on main at 1269547a7, and the
behaviour is present in the released 0.24.0.
marimo: 0.24.0 (released) / main @ 1269547a7
OS: Darwin 25.6.0 (arm64)
Python: 3.13 / 3.14
Code to reproduce
Exit code is 0 and the prompt is emitted even with the skill absent:
from unittest.mock import patch
from click.testing import CliRunner
from marimo._cli.cli import main
from marimo._cli.pair.commands import AgentConfig
with patch.object(AgentConfig, "has_skill", return_value=False):
result = CliRunner().invoke(
main, ["pair", "prompt", "--url", "http://localhost:2718", "--claude"]
)
print("exit_code =", result.exit_code) # 0
print("warned =", "could not be found" in result.output) # True
print("prompt emitted =", "Use the /marimo-pair skill" in result.output) # True
Or from a shell, with no skill installed:
$ cd "$(mktemp -d)"
$ marimo pair prompt --url http://localhost:2718 --claude >/tmp/prompt.txt
The marimo-pair skill for Claude Code could not be found.
...
$ echo $?
0
$ head -1 /tmp/prompt.txt
Use the /marimo-pair skill to pair-program on a running marimo notebook.
Describe the bug
marimo pair prompt --claudeprints "could not be found" when the skill is missing, thenemits the pair prompt anyway and exits
0. So the documented usageclaude "$(uvx marimo@latest pair prompt --url http://localhost:2718 --claude)"launches an agent session whose first instruction is "Use the /marimo-pair skill" for a
skill that is not installed. The warning goes to stderr, where command substitution leaves
it easy to miss, and the agent starts with no way to do what it was asked.
This looks like a source change that went missing rather than a design decision. #9029
("fix: exit with non-zero code when marimo-pair skill is missing") describes two changes:
The second half landed and was clearly correct — the tests were asserting a stale
"not installed"string that the source no longer produced. But the merged commit2bb4d538ctouches onlytests/_cli/test_cli_pair.py:Both tests were red before that commit, failing on the exit code as well as the message:
So the exit-code assertions were green-lit against the unchanged source instead of the
source being changed to satisfy them. The
SystemExit(1)never reachedmarimo/_cli/pair/commands.py, and has never existed anywhere in the package:The net effect is that the tests now assert the behaviour #9029 set out to remove, which
makes it read as intentional.
test_with_token_and_skill_missing_failsis named for afailure it asserts does not happen — worth renaming alongside any fix.
Interaction with #10704
Please consider sequencing this after #10704. Today the validator result is cosmetic, so a
false negative only costs a spurious warning. Once a missing skill exits non-zero, any false
negative becomes a hard failure.
_claude_skill_dirs()currently produces one for a project-scoped install: it resolves.claudefromPath.cwd()alone, so a skill at<repo>/.claude/skills/marimo-pair/isinvisible from any subdirectory. (A global
~/.claude/skillsinstall is unaffected — thatpath is checked directly.) #10704 fixes the project-scoped case. Landing this one first
would turn that warning into a hard blocker for anyone with a project-scoped install whose
notebooks live below the repository root.
Will you submit a PR?
Two questions worth settling first, since both affect callers:
piped into an agent? A user reading the output themselves may reasonably want the prompt.
--with-tokenstill write the token file before exiting?Environment
Reproducible from source; not version-specific. Confirmed on
mainat1269547a7, and thebehaviour is present in the released
0.24.0.Code to reproduce
Exit code is
0and the prompt is emitted even with the skill absent:Or from a shell, with no skill installed: