Files
stickman/tools/sync_doc/sync_sources.py
T
ryan 00abacc7b2 Add regression tests for prop attachment, centering, and editor UX
- Implemented test_phase9a_prop_attach.gd to validate anchor schema, alignment math, and preview isolation.
- Implemented test_phase9b_prop_centering.gd to check canonical origin presentation, bounds, centering, and save threshold.
- Implemented test_phase9c_prop_editor_ux.gd to assess the user experience in the prop editor, including anchor renaming and UI responsiveness.
- Added corresponding .uid files for each test.
- Created sync_sources.py script to manage markdown file synchronization with the notebook.
- Added .gitignore for virtual environment files.
2026-09-21 16:48:24 -04:00

98 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
from pathlib import Path
from notebooklm import NotebookLMClient
RELATIVE_TARGETS = [
"../../AGENTS.md",
"../../README.md",
"../../plans",
"../../docs"
]
NOTEBOOK_TITLE = "Stickman Studio"
def gather_markdown_files(base_path: Path):
"""Recursively collect all .md files from specified relative paths."""
files_to_upload = []
for rel_path in RELATIVE_TARGETS:
target = (base_path / rel_path).resolve()
if target.is_file() and target.suffix.lower() in [".md", ".markdown"]:
files_to_upload.append(target)
elif target.is_dir():
for md_file in target.rglob("*.md"):
files_to_upload.append(md_file)
else:
print(f"⚠️ Target path not found: {target}")
return list(dict.fromkeys(files_to_upload))
async def main():
script_dir = Path(__file__).parent.resolve()
async with await NotebookLMClient.from_storage() as client:
# 1. Locate 'Stickman Studio'
print(f"🔍 Searching for notebook: '{NOTEBOOK_TITLE}'...")
notebooks = await client.notebooks.list()
target_notebook = next(
(nb for nb in notebooks if getattr(nb, 'title', None) == NOTEBOOK_TITLE or getattr(nb, 'display_name', None) == NOTEBOOK_TITLE),
None
)
if not target_notebook:
print(f"❌ Could not find notebook named '{NOTEBOOK_TITLE}'.")
return
notebook_id = getattr(target_notebook, 'id', None) or getattr(target_notebook, 'name', '')
print(f"✅ Found notebook ID: {notebook_id}")
# 2. Safely filter and delete ONLY non-chat file sources
existing_sources = await client.sources.list(notebook_id=notebook_id)
sources_to_delete = []
for source in existing_sources:
# Check source properties to identify saved chats
mime_type = getattr(source, 'original_mime_type', None) or getattr(source, 'originalMimeType', '')
source_type = getattr(source, 'source_type', None) or getattr(source, 'type', '')
# SAFEGUARD: Keep any source identified as a chat note
is_chat = 'chat' in str(mime_type).lower() or 'chat' in str(source_type).lower()
if is_chat:
source_title = getattr(source, 'title', None) or getattr(source, 'display_name', 'Chat')
print(f"🛡️ Preserving past chat: '{source_title}'")
else:
sources_to_delete.append(source)
if sources_to_delete:
print(f"\n🗑️ Removing {len(sources_to_delete)} file sources (keeping chats intact)...")
for source in sources_to_delete:
source_id = getattr(source, 'id', None) or getattr(source, 'name', '')
source_title = getattr(source, 'title', None) or getattr(source, 'display_name', 'File Source')
await client.sources.delete(notebook_id=notebook_id, source_id=source_id)
print(f" - Removed: {source_title}")
else:
print("ℹ️ No old file sources to remove.")
# 3. Collect target Markdown files
md_files = gather_markdown_files(script_dir)
print(f"\n📁 Found {len(md_files)} local markdown files to attach.")
# 4. Upload Markdown files
for md_path in md_files:
rel_display = md_path.relative_to(script_dir.parent.parent)
print(f" ⬆️ Uploading: {rel_display}")
await client.sources.add_file(
notebook_id=notebook_id,
file_path=str(md_path)
)
print(f"\n🎉 Sync complete! All local docs updated without touching your chat notes.")
if __name__ == "__main__":
asyncio.run(main())