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
+16 -5
View File
@@ -178,19 +178,28 @@ class JoplinClient:
logger.info("[joplin] Note created: %r%s", title, note_id)
return note_id
def update_note(self, note_id: str, title: str, body: str) -> None:
def update_note(
self, note_id: str, title: str, body: str, parent_id: str | None = None
) -> None:
"""Update the title and body of an existing note.
Args:
note_id: Joplin note ID.
title: New note title.
body: New note body (Markdown).
parent_id: If given, also move the note into this notebook. Joplin's
``PUT /notes/:id`` relocates a note when ``parent_id`` is set —
this is how notes self-heal to a new notebook mapping (e.g. the
Claude Code AI-Claude → AI-ClaudeCode migration) on re-sync.
"""
logger.debug(
"[joplin] Updating note %s: %r (%d chars)",
note_id, title, len(body),
)
self._put(f"/notes/{note_id}", {"title": title, "body": body})
data: dict = {"title": title, "body": body}
if parent_id:
data["parent_id"] = parent_id
self._put(f"/notes/{note_id}", data)
logger.info("[joplin] Note updated: %r (%s)", title, note_id)
# ------------------------------------------------------------------
@@ -395,9 +404,11 @@ def upload_media_and_rewrite(
_PROVIDER_DISPLAY = {
"chatgpt": "AI-ChatGPT",
"claude": "AI-Claude",
# Decision 2026-06-12: Claude Code coding projects nest under the same
# AI-Claude parent, alongside Claude web projects.
"claude-code": "AI-Claude",
# Decision 2026-07-06: Claude Code coding sessions get their own top-level
# notebook (was nested under AI-Claude, which made them hard to find,
# intermixed with Claude web projects and named by dev folder). Existing
# notes self-heal into here on the next sync — update_note moves them.
"claude-code": "AI-ClaudeCode",
}