feat: v0.8.0 — Claude Code subagent capture, own Joplin notebook, git-root repo tags, multi-root scanning

- Subagents: fold Task-tool transcripts (subagents/*.jsonl) inline as
  collapsible <details> blocks at the spawn point; their own tool traffic
  collapses under the same policy; nesting handled via toolUseId matching
- Joplin: Claude Code gets its own top-level AI-ClaudeCode notebook;
  update_note sets parent_id so notes self-heal/relocate on re-sync
- Repo [tags] in titles via git-root detection (nearest .git ancestor),
  home-wide and cross-workspace; CLAUDE_CODE_REPO_TAG_IGNORE escape hatch
- Multi-root scanning: CLAUDE_CODE_DIR as os.pathsep list + CLAUDE_CONFIG_DIR;
  merged by launch-folder, newer-mtime wins on UUID collision
- 298 tests passing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
JesseMarkowitz
2026-07-06 05:04:41 -04:00
co-authored by Claude Opus 4.8
parent bbcb29c856
commit 1f5a445ada
12 changed files with 779 additions and 71 deletions
+21
View File
@@ -48,6 +48,9 @@ class TestNotebookPath:
def test_claude_provider(self):
assert notebook_path("claude", "budget-tracker") == ("AI-Claude", "Budget Tracker")
def test_claude_code_gets_own_top_level_notebook(self):
assert notebook_path("claude-code", "services") == ("AI-ClaudeCode", "Services")
def test_multi_word_project(self):
assert notebook_path("claude", "ai-research-notes") == ("AI-Claude", "Ai Research Notes")
@@ -61,6 +64,24 @@ class TestNotebookPath:
# ---------------------------------------------------------------------------
class TestUpdateNote:
def test_parent_id_moves_note(self):
client = _make_client()
client._put = MagicMock()
client.update_note("nid", "Title", "body", parent_id="nb1")
args, _ = client._put.call_args
assert args[0] == "/notes/nid"
assert args[1]["parent_id"] == "nb1"
assert args[1]["title"] == "Title"
def test_no_parent_id_omits_field(self):
client = _make_client()
client._put = MagicMock()
client.update_note("nid", "Title", "body")
args, _ = client._put.call_args
assert "parent_id" not in args[1]
class TestPing:
def test_ping_success(self):
client = _make_client()