swarph-cli 0.7.4__tar.gz → 0.7.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.7.4/src/swarph_cli.egg-info → swarph_cli-0.7.5}/PKG-INFO +1 -1
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/pyproject.toml +1 -1
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/spawn.py +36 -1
- {swarph_cli-0.7.4 → swarph_cli-0.7.5/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_spawn_command.py +89 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/LICENSE +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/README.md +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/setup.cfg +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli.egg-info/SOURCES.txt +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_import_command.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_main.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.7.4 → swarph_cli-0.7.5}/tests/test_watchdog.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.5
|
|
4
4
|
Summary: The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED).
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.5"
|
|
8
8
|
description = "The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -27,6 +27,7 @@ import argparse
|
|
|
27
27
|
import os
|
|
28
28
|
import shutil
|
|
29
29
|
import sys
|
|
30
|
+
from pathlib import Path
|
|
30
31
|
from typing import Optional
|
|
31
32
|
|
|
32
33
|
from swarph_cli import __version__
|
|
@@ -200,6 +201,33 @@ def _resolve_cell(args: argparse.Namespace) -> tuple[Cell, Optional[str]]:
|
|
|
200
201
|
return load_cell(path), requested_role
|
|
201
202
|
|
|
202
203
|
|
|
204
|
+
def _session_state_exists(session_id: str) -> bool:
|
|
205
|
+
"""True if Claude Code already has on-disk session state for this UUID.
|
|
206
|
+
|
|
207
|
+
Closes v0.7.4 spawn-bug surfaced 2026-05-14 post-reboot (DM #1255):
|
|
208
|
+
`claude --session-id <UUID>` rejects with "Session ID <UUID> is already
|
|
209
|
+
in use" when session-state files exist on disk, even after reboot
|
|
210
|
+
(files persist; the in-use check is filesystem-based not runtime-lock-
|
|
211
|
+
based). Switching to `claude --resume <UUID>` is the correct semantic
|
|
212
|
+
when the UUID's state already exists.
|
|
213
|
+
|
|
214
|
+
Probes the three filesystem locations Claude Code stores per-session
|
|
215
|
+
state in: ~/.claude/file-history/<UUID>, ~/.claude/session-env/<UUID>,
|
|
216
|
+
and ~/.claude/projects/<project-hash>/<UUID>.jsonl (the latter
|
|
217
|
+
discovered via glob since project-hash varies).
|
|
218
|
+
"""
|
|
219
|
+
claude_dir = Path.home() / ".claude"
|
|
220
|
+
if (claude_dir / "file-history" / session_id).exists():
|
|
221
|
+
return True
|
|
222
|
+
if (claude_dir / "session-env" / session_id).exists():
|
|
223
|
+
return True
|
|
224
|
+
projects_dir = claude_dir / "projects"
|
|
225
|
+
if projects_dir.exists():
|
|
226
|
+
for _ in projects_dir.glob(f"*/{session_id}.jsonl"):
|
|
227
|
+
return True
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
203
231
|
def _build_claude_argv(
|
|
204
232
|
cell: Cell,
|
|
205
233
|
session_id: str,
|
|
@@ -208,7 +236,14 @@ def _build_claude_argv(
|
|
|
208
236
|
effective_role: Optional[str] = None,
|
|
209
237
|
) -> list[str]:
|
|
210
238
|
name_value = effective_role if effective_role is not None else cell.role
|
|
211
|
-
|
|
239
|
+
# v0.7.5: auto-detect existing session state and switch from --session-id
|
|
240
|
+
# (create-new-with-pinned-UUID semantic) to --resume (attach-to-existing
|
|
241
|
+
# semantic). Both pass the same UUID; the verb determines whether claude
|
|
242
|
+
# treats it as fresh-create vs resume-existing.
|
|
243
|
+
if _session_state_exists(session_id):
|
|
244
|
+
argv: list[str] = ["claude", "--name", name_value, "--resume", session_id]
|
|
245
|
+
else:
|
|
246
|
+
argv = ["claude", "--name", name_value, "--session-id", session_id]
|
|
212
247
|
|
|
213
248
|
if not no_starter:
|
|
214
249
|
starter = read_starter_prompt(cell)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.5
|
|
4
4
|
Summary: The `swarph` binary — multi-LLM CLI with mesh-gateway integration. v0.7.0 ships Phase 7 substrate-doc R7 §11.1.7 operator-tooling layer in 5 increments: PR-A `--new-instance` flag (sibling-spawn case) + PR-B auto-suffix on collision (sibling-slot persistence) + PR-C SessionStart hook (closes bare-claude operator-paste gap) + watchdog (stranded-session recovery) + PR-D swarph-shared cell.yaml relocation (cell-yaml schema graduates to swarph-shared 0.3.0 kernel-tier; substrate-doc R7 §11.1.5 (O5) RESOLVED).
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -339,3 +339,92 @@ def test_run_spawn_dry_run_redacts_starter_prompt_in_command(
|
|
|
339
339
|
assert rc == 0
|
|
340
340
|
assert "redacted" not in captured.out # the literal word from the prompt
|
|
341
341
|
assert "starter prompt>" in captured.out # the redaction marker
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# ---------------------------------------------------------------------------
|
|
345
|
+
# v0.7.5 — _session_state_exists + --resume on existing session
|
|
346
|
+
# ---------------------------------------------------------------------------
|
|
347
|
+
#
|
|
348
|
+
# Closes the bug surfaced 2026-05-14 post-reboot: claude --session-id <UUID>
|
|
349
|
+
# rejects with "Session ID <UUID> is already in use" when on-disk session
|
|
350
|
+
# state exists, even after host reboot (files persist; check is filesystem-
|
|
351
|
+
# based not runtime-lock-based). Fix: detect existing state + switch from
|
|
352
|
+
# --session-id (create-new semantic) to --resume (attach-existing semantic).
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def test_session_state_exists_false_for_fresh_uuid(tmp_path, monkeypatch):
|
|
356
|
+
"""No filesystem state for the UUID = fresh; _build_claude_argv uses
|
|
357
|
+
--session-id (create-new semantic)."""
|
|
358
|
+
from swarph_cli.commands.spawn import _session_state_exists
|
|
359
|
+
|
|
360
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
361
|
+
fresh_uuid = "00000000-0000-0000-0000-000000000000"
|
|
362
|
+
assert _session_state_exists(fresh_uuid) is False
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_session_state_exists_true_when_file_history_present(tmp_path, monkeypatch):
|
|
366
|
+
"""File-history dir alone is enough to flip detection (any one of the
|
|
367
|
+
three location signals triggers)."""
|
|
368
|
+
from swarph_cli.commands.spawn import _session_state_exists
|
|
369
|
+
|
|
370
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
371
|
+
uuid = "a30e406c-8bae-4ea2-8cb2-fb0dff35a6f0"
|
|
372
|
+
(tmp_path / ".claude" / "file-history" / uuid).mkdir(parents=True)
|
|
373
|
+
assert _session_state_exists(uuid) is True
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def test_session_state_exists_true_when_session_env_present(tmp_path, monkeypatch):
|
|
377
|
+
from swarph_cli.commands.spawn import _session_state_exists
|
|
378
|
+
|
|
379
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
380
|
+
uuid = "a30e406c-8bae-4ea2-8cb2-fb0dff35a6f0"
|
|
381
|
+
(tmp_path / ".claude" / "session-env").mkdir(parents=True)
|
|
382
|
+
(tmp_path / ".claude" / "session-env" / uuid).write_text("")
|
|
383
|
+
assert _session_state_exists(uuid) is True
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def test_session_state_exists_true_when_project_jsonl_present(tmp_path, monkeypatch):
|
|
387
|
+
"""Projects path varies by project-hash; glob discovers any match."""
|
|
388
|
+
from swarph_cli.commands.spawn import _session_state_exists
|
|
389
|
+
|
|
390
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
391
|
+
uuid = "a30e406c-8bae-4ea2-8cb2-fb0dff35a6f0"
|
|
392
|
+
proj = tmp_path / ".claude" / "projects" / "-some-project-hash"
|
|
393
|
+
proj.mkdir(parents=True)
|
|
394
|
+
(proj / f"{uuid}.jsonl").write_text("{}\n")
|
|
395
|
+
assert _session_state_exists(uuid) is True
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def test_build_claude_argv_uses_session_id_when_fresh(fake_cell_yaml, tmp_path, monkeypatch):
|
|
399
|
+
"""No prior session state → --session-id (create-new) verb."""
|
|
400
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
401
|
+
cell = load_cell(fake_cell_yaml)
|
|
402
|
+
argv = _build_claude_argv(
|
|
403
|
+
cell=cell,
|
|
404
|
+
session_id="00000000-0000-0000-0000-000000000000",
|
|
405
|
+
no_starter=True,
|
|
406
|
+
passthrough=[],
|
|
407
|
+
)
|
|
408
|
+
assert "--session-id" in argv
|
|
409
|
+
assert "--resume" not in argv
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def test_build_claude_argv_uses_resume_when_state_exists(fake_cell_yaml, tmp_path, monkeypatch):
|
|
413
|
+
"""Prior session state exists → --resume (attach-existing) verb.
|
|
414
|
+
|
|
415
|
+
Closes the v0.7.4 spawn-after-reboot rejection class.
|
|
416
|
+
"""
|
|
417
|
+
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
|
418
|
+
uuid = "a30e406c-8bae-4ea2-8cb2-fb0dff35a6f0"
|
|
419
|
+
(tmp_path / ".claude" / "file-history" / uuid).mkdir(parents=True)
|
|
420
|
+
cell = load_cell(fake_cell_yaml)
|
|
421
|
+
argv = _build_claude_argv(
|
|
422
|
+
cell=cell,
|
|
423
|
+
session_id=uuid,
|
|
424
|
+
no_starter=True,
|
|
425
|
+
passthrough=[],
|
|
426
|
+
)
|
|
427
|
+
assert "--resume" in argv
|
|
428
|
+
assert "--session-id" not in argv
|
|
429
|
+
# UUID still passed (just as --resume's value not --session-id's)
|
|
430
|
+
assert uuid in argv
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|