claude-code-tools 1.0.6__py3-none-any.whl → 1.4.6__py3-none-any.whl

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.
Files changed (33) hide show
  1. claude_code_tools/__init__.py +1 -1
  2. claude_code_tools/action_rpc.py +16 -10
  3. claude_code_tools/aichat.py +793 -51
  4. claude_code_tools/claude_continue.py +4 -0
  5. claude_code_tools/codex_continue.py +48 -0
  6. claude_code_tools/export_session.py +94 -11
  7. claude_code_tools/find_claude_session.py +36 -12
  8. claude_code_tools/find_codex_session.py +33 -18
  9. claude_code_tools/find_session.py +30 -16
  10. claude_code_tools/gdoc2md.py +220 -0
  11. claude_code_tools/md2gdoc.py +549 -0
  12. claude_code_tools/search_index.py +119 -15
  13. claude_code_tools/session_menu_cli.py +1 -1
  14. claude_code_tools/session_utils.py +3 -3
  15. claude_code_tools/smart_trim.py +18 -8
  16. claude_code_tools/smart_trim_core.py +4 -2
  17. claude_code_tools/tmux_cli_controller.py +35 -25
  18. claude_code_tools/trim_session.py +28 -2
  19. claude_code_tools-1.4.6.dist-info/METADATA +1112 -0
  20. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/RECORD +31 -24
  21. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/entry_points.txt +2 -0
  22. docs/linked-in-20260102.md +32 -0
  23. docs/local-llm-setup.md +286 -0
  24. docs/reddit-aichat-resume-v2.md +80 -0
  25. docs/reddit-aichat-resume.md +29 -0
  26. docs/reddit-aichat.md +79 -0
  27. docs/rollover-details.md +67 -0
  28. node_ui/action_config.js +3 -3
  29. node_ui/menu.js +67 -113
  30. claude_code_tools/session_tui.py +0 -516
  31. claude_code_tools-1.0.6.dist-info/METADATA +0 -685
  32. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/WHEEL +0 -0
  33. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ gdoc2md: Download Google Docs as Markdown files.
4
+
5
+ This tool uses the Google Drive API to export Google Docs as Markdown,
6
+ using Google's native markdown export (same as File → Download → Markdown).
7
+
8
+ Prerequisites:
9
+ - First run: Will open browser for OAuth authentication (one-time setup)
10
+ - Credentials stored in .gdoc-credentials.json (local) or ~/.config/md2gdoc/
11
+ """
12
+
13
+ import argparse
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ from rich.console import Console
19
+ from rich.panel import Panel
20
+
21
+ console = Console()
22
+
23
+ # Import shared utilities from md2gdoc
24
+ from claude_code_tools.md2gdoc import (
25
+ SCOPES,
26
+ check_dependencies,
27
+ get_credentials,
28
+ get_drive_service,
29
+ find_folder_id,
30
+ )
31
+
32
+
33
+ def find_doc_by_name(
34
+ service, folder_id: Optional[str], doc_name: str
35
+ ) -> Optional[dict]:
36
+ """Find a Google Doc by name in a folder. Returns file metadata or None."""
37
+ parent = folder_id if folder_id else "root"
38
+ query = (
39
+ f"name = '{doc_name}' and "
40
+ f"'{parent}' in parents and "
41
+ f"mimeType = 'application/vnd.google-apps.document' and "
42
+ f"trashed = false"
43
+ )
44
+ results = (
45
+ service.files()
46
+ .list(q=query, fields="files(id, name)", pageSize=1)
47
+ .execute()
48
+ )
49
+ files = results.get("files", [])
50
+ return files[0] if files else None
51
+
52
+
53
+ def list_docs_in_folder(service, folder_id: Optional[str]) -> list[dict]:
54
+ """List all Google Docs in a folder."""
55
+ parent = folder_id if folder_id else "root"
56
+ query = (
57
+ f"'{parent}' in parents and "
58
+ f"mimeType = 'application/vnd.google-apps.document' and "
59
+ f"trashed = false"
60
+ )
61
+ results = (
62
+ service.files()
63
+ .list(q=query, fields="files(id, name)", pageSize=100, orderBy="name")
64
+ .execute()
65
+ )
66
+ return results.get("files", [])
67
+
68
+
69
+ def download_doc_as_markdown(service, file_id: str) -> Optional[str]:
70
+ """Download a Google Doc as Markdown content."""
71
+ try:
72
+ # Export as markdown
73
+ content = (
74
+ service.files()
75
+ .export(fileId=file_id, mimeType="text/markdown")
76
+ .execute()
77
+ )
78
+ # Content is returned as bytes
79
+ if isinstance(content, bytes):
80
+ return content.decode("utf-8")
81
+ return content
82
+ except Exception as e:
83
+ console.print(f"[red]Export error:[/red] {e}")
84
+ return None
85
+
86
+
87
+ def main() -> None:
88
+ parser = argparse.ArgumentParser(
89
+ description="Download Google Docs as Markdown files.",
90
+ formatter_class=argparse.RawDescriptionHelpFormatter,
91
+ epilog="""
92
+ Examples:
93
+ gdoc2md "My Document" # Download from root
94
+ gdoc2md "My Document" --folder "OTA/Reports" # Download from folder
95
+ gdoc2md "My Document" -o report.md # Save with custom name
96
+ gdoc2md --list --folder OTA # List docs in folder
97
+
98
+ Credentials (in order of precedence):
99
+ 1. .gdoc-token.json in current directory (project-specific)
100
+ 2. ~/.config/md2gdoc/token.json (global)
101
+ 3. Application Default Credentials (gcloud)
102
+ """,
103
+ )
104
+
105
+ parser.add_argument(
106
+ "doc_name",
107
+ type=str,
108
+ nargs="?",
109
+ help="Name of the Google Doc to download",
110
+ )
111
+
112
+ parser.add_argument(
113
+ "--folder",
114
+ "-f",
115
+ type=str,
116
+ default="",
117
+ help="Folder in Google Drive (e.g., 'OTA/Reports')",
118
+ )
119
+
120
+ parser.add_argument(
121
+ "--output",
122
+ "-o",
123
+ type=str,
124
+ default="",
125
+ help="Output filename (default: <doc_name>.md)",
126
+ )
127
+
128
+ parser.add_argument(
129
+ "--list",
130
+ "-l",
131
+ action="store_true",
132
+ help="List Google Docs in the folder instead of downloading",
133
+ )
134
+
135
+ args = parser.parse_args()
136
+
137
+ # Check dependencies
138
+ if not check_dependencies():
139
+ sys.exit(1)
140
+
141
+ # Need either doc_name or --list
142
+ if not args.doc_name and not args.list:
143
+ parser.print_help()
144
+ sys.exit(1)
145
+
146
+ # Get Drive service
147
+ service = get_drive_service()
148
+ if not service:
149
+ sys.exit(1)
150
+
151
+ # Find folder if specified
152
+ folder_id = None
153
+ if args.folder:
154
+ console.print(f"[dim]Finding folder: {args.folder}[/dim]")
155
+ folder_id = find_folder_id(service, args.folder, create_if_missing=False)
156
+ if folder_id is None:
157
+ console.print(f"[red]Error:[/red] Folder not found: {args.folder}")
158
+ sys.exit(1)
159
+
160
+ # List mode
161
+ if args.list:
162
+ docs = list_docs_in_folder(service, folder_id)
163
+ if not docs:
164
+ console.print("[yellow]No Google Docs found in this folder.[/yellow]")
165
+ sys.exit(0)
166
+
167
+ console.print(f"\n[bold]Google Docs in {args.folder or 'My Drive'}:[/bold]\n")
168
+ for doc in docs:
169
+ console.print(f" • {doc['name']}")
170
+ console.print(f"\n[dim]Total: {len(docs)} document(s)[/dim]")
171
+ sys.exit(0)
172
+
173
+ # Download mode
174
+ console.print(f"[dim]Looking for: {args.doc_name}[/dim]")
175
+ doc = find_doc_by_name(service, folder_id, args.doc_name)
176
+
177
+ if not doc:
178
+ console.print(f"[red]Error:[/red] Document not found: {args.doc_name}")
179
+ # Suggest listing
180
+ console.print(f"[dim]Use --list to see available documents[/dim]")
181
+ sys.exit(1)
182
+
183
+ # Download as markdown
184
+ console.print(f"[cyan]Downloading[/cyan] {doc['name']} → Markdown...")
185
+ content = download_doc_as_markdown(service, doc["id"])
186
+
187
+ if content is None:
188
+ sys.exit(1)
189
+
190
+ # Determine output filename
191
+ if args.output:
192
+ output_path = Path(args.output)
193
+ else:
194
+ # Use doc name, sanitize for filesystem
195
+ safe_name = "".join(c if c.isalnum() or c in "._- " else "_" for c in doc["name"])
196
+ output_path = Path(f"{safe_name}.md")
197
+
198
+ # Check if file exists
199
+ if output_path.exists():
200
+ console.print(
201
+ f"[yellow]Warning:[/yellow] {output_path} already exists, overwriting"
202
+ )
203
+
204
+ # Write file
205
+ output_path.write_text(content, encoding="utf-8")
206
+
207
+ console.print()
208
+ console.print(
209
+ Panel(
210
+ f"[green]Successfully downloaded![/green]\n\n"
211
+ f"[dim]Document:[/dim] {doc['name']}\n"
212
+ f"[dim]Saved to:[/dim] {output_path}",
213
+ title="Done",
214
+ border_style="green",
215
+ )
216
+ )
217
+
218
+
219
+ if __name__ == "__main__":
220
+ main()