htmlgraph 0.21.0__py3-none-any.whl → 0.23.0__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 (40) hide show
  1. htmlgraph/__init__.py +1 -1
  2. htmlgraph/agent_detection.py +41 -2
  3. htmlgraph/analytics/cli.py +86 -20
  4. htmlgraph/cli.py +519 -87
  5. htmlgraph/collections/base.py +68 -4
  6. htmlgraph/docs/__init__.py +77 -0
  7. htmlgraph/docs/docs_version.py +55 -0
  8. htmlgraph/docs/metadata.py +93 -0
  9. htmlgraph/docs/migrations.py +232 -0
  10. htmlgraph/docs/template_engine.py +143 -0
  11. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  12. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  13. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  14. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  15. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  16. htmlgraph/docs/version_check.py +161 -0
  17. htmlgraph/git_events.py +61 -7
  18. htmlgraph/operations/README.md +62 -0
  19. htmlgraph/operations/__init__.py +61 -0
  20. htmlgraph/operations/analytics.py +338 -0
  21. htmlgraph/operations/events.py +243 -0
  22. htmlgraph/operations/hooks.py +349 -0
  23. htmlgraph/operations/server.py +302 -0
  24. htmlgraph/orchestration/__init__.py +39 -0
  25. htmlgraph/orchestration/headless_spawner.py +566 -0
  26. htmlgraph/orchestration/model_selection.py +323 -0
  27. htmlgraph/orchestrator-system-prompt-optimized.txt +47 -0
  28. htmlgraph/parser.py +56 -1
  29. htmlgraph/sdk.py +529 -7
  30. htmlgraph/server.py +153 -60
  31. {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/METADATA +3 -1
  32. {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/RECORD +40 -19
  33. /htmlgraph/{orchestration.py → orchestration/task_coordination.py} +0 -0
  34. {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/dashboard.html +0 -0
  35. {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/styles.css +0 -0
  36. {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  37. {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  38. {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  39. {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/WHEEL +0 -0
  40. {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/entry_points.txt +0 -0
htmlgraph/cli.py CHANGED
@@ -8,6 +8,11 @@ Usage:
8
8
  htmlgraph status [--dir DIR]
9
9
  htmlgraph query SELECTOR [--dir DIR]
10
10
 
11
+ Claude Code Integration:
12
+ htmlgraph claude # Start Claude Code
13
+ htmlgraph claude --init # Start with orchestrator system prompt (recommended)
14
+ htmlgraph claude --continue # Resume last Claude Code session
15
+
11
16
  Session Management:
12
17
  htmlgraph session start [--id ID] [--agent AGENT]
13
18
  htmlgraph session end ID [--notes NOTES] [--recommend NEXT] [--blocker BLOCKER]
@@ -111,9 +116,9 @@ def cmd_install_gemini_extension(args: argparse.Namespace) -> None:
111
116
 
112
117
  def cmd_serve(args: argparse.Namespace) -> None:
113
118
  """Start the HtmlGraph server."""
114
- from htmlgraph.server import serve
119
+ from htmlgraph.operations import start_server
115
120
 
116
- serve(
121
+ start_server(
117
122
  port=args.port,
118
123
  graph_dir=args.graph_dir,
119
124
  static_dir=args.static_dir,
@@ -122,10 +127,14 @@ def cmd_serve(args: argparse.Namespace) -> None:
122
127
  auto_port=args.auto_port,
123
128
  )
124
129
 
130
+ # The start_server operation already handles all output and blocks
131
+ # No additional CLI formatting needed
132
+
125
133
 
126
134
  def cmd_init(args: argparse.Namespace) -> None:
127
135
  """Initialize a new .htmlgraph directory."""
128
136
  import shutil
137
+ from contextlib import nullcontext
129
138
 
130
139
  from htmlgraph.analytics_index import AnalyticsIndex
131
140
  from htmlgraph.server import HtmlGraphAPIHandler
@@ -160,38 +169,33 @@ def cmd_init(args: argparse.Namespace) -> None:
160
169
  agent_name = "claude"
161
170
  generate_docs = True # Always generate in non-interactive mode
162
171
 
163
- graph_dir = Path(args.dir) / ".htmlgraph"
164
- graph_dir.mkdir(parents=True, exist_ok=True)
165
-
166
- for collection in HtmlGraphAPIHandler.COLLECTIONS:
167
- (graph_dir / collection).mkdir(exist_ok=True)
168
-
169
- # Event stream directory (Git-friendly source of truth)
170
- events_dir = graph_dir / "events"
171
- events_dir.mkdir(exist_ok=True)
172
- if not args.no_events_keep:
173
- keep = events_dir / ".gitkeep"
174
- if not keep.exists():
175
- keep.write_text("", encoding="utf-8")
176
-
177
- # Copy stylesheet
178
- styles_src = Path(__file__).parent / "styles.css"
179
- styles_dest = graph_dir / "styles.css"
180
- if styles_src.exists() and not styles_dest.exists():
181
- styles_dest.write_text(styles_src.read_text())
182
-
183
- # Create default index.html if not exists
184
- index_path = Path(args.dir) / "index.html"
185
- if not index_path.exists():
186
- create_default_index(index_path)
187
-
188
- # Create analytics cache DB (rebuildable; typically gitignored)
189
- if not args.no_index:
172
+ def init_progress() -> tuple[Any | None, Any | None]:
173
+ if args.quiet or getattr(args, "format", "text") != "text":
174
+ return None, None
190
175
  try:
191
- AnalyticsIndex(graph_dir / "index.sqlite").ensure_schema()
176
+ from rich.console import Console
177
+ from rich.progress import (
178
+ BarColumn,
179
+ Progress,
180
+ SpinnerColumn,
181
+ TextColumn,
182
+ TimeElapsedColumn,
183
+ )
192
184
  except Exception:
193
- # Never fail init because of analytics cache.
194
- pass
185
+ return None, None
186
+ console = Console()
187
+ progress = Progress(
188
+ SpinnerColumn(),
189
+ TextColumn("{task.description}"),
190
+ BarColumn(),
191
+ TimeElapsedColumn(),
192
+ console=console,
193
+ transient=True,
194
+ )
195
+ return progress, console
196
+
197
+ graph_dir = Path(args.dir) / ".htmlgraph"
198
+ events_dir = graph_dir / "events"
195
199
 
196
200
  def ensure_gitignore_entries(project_dir: Path, lines: list[str]) -> None:
197
201
  if args.no_update_gitignore:
@@ -218,19 +222,12 @@ def cmd_init(args: argparse.Namespace) -> None:
218
222
  # Don't fail init on .gitignore issues.
219
223
  pass
220
224
 
221
- ensure_gitignore_entries(
222
- Path(args.dir),
223
- [
224
- ".htmlgraph/index.sqlite",
225
- ".htmlgraph/index.sqlite-wal",
226
- ".htmlgraph/index.sqlite-shm",
227
- ".htmlgraph/git-hook-errors.log",
228
- ],
229
- )
225
+ progress, progress_console = init_progress()
230
226
 
231
- # Ensure versioned hook scripts exist (installation into .git/hooks is optional)
232
- hooks_dir = graph_dir / "hooks"
233
- hooks_dir.mkdir(exist_ok=True)
227
+ def status_context(message: str) -> Any:
228
+ if progress_console is None:
229
+ return nullcontext()
230
+ return progress_console.status(message)
234
231
 
235
232
  # Hook templates (used when htmlgraph is installed without this repo layout).
236
233
  post_commit = """#!/bin/bash
@@ -443,26 +440,77 @@ fi
443
440
  exit 0
444
441
  """
445
442
 
446
- def ensure_hook_file(hook_name: str, hook_content: str) -> Path:
447
- hook_dest = hooks_dir / f"{hook_name}.sh"
448
- if not hook_dest.exists():
449
- hook_dest.write_text(hook_content)
450
- try:
451
- hook_dest.chmod(0o755)
452
- except Exception:
453
- pass
454
- return hook_dest
455
-
456
- hook_files = {
457
- "pre-commit": ensure_hook_file("pre-commit", pre_commit),
458
- "post-commit": ensure_hook_file("post-commit", post_commit),
459
- "post-checkout": ensure_hook_file("post-checkout", post_checkout),
460
- "post-merge": ensure_hook_file("post-merge", post_merge),
461
- "pre-push": ensure_hook_file("pre-push", pre_push),
462
- }
443
+ hook_files: dict[str, Path] = {}
463
444
 
464
- # Generate documentation files from templates
465
- if generate_docs:
445
+ def create_graph_dirs() -> None:
446
+ graph_dir.mkdir(parents=True, exist_ok=True)
447
+ for collection in HtmlGraphAPIHandler.COLLECTIONS:
448
+ (graph_dir / collection).mkdir(exist_ok=True)
449
+
450
+ def create_events_dir() -> None:
451
+ events_dir.mkdir(exist_ok=True)
452
+ if not args.no_events_keep:
453
+ keep = events_dir / ".gitkeep"
454
+ if not keep.exists():
455
+ keep.write_text("", encoding="utf-8")
456
+
457
+ def copy_assets() -> None:
458
+ styles_src = Path(__file__).parent / "styles.css"
459
+ styles_dest = graph_dir / "styles.css"
460
+ if styles_src.exists() and not styles_dest.exists():
461
+ styles_dest.write_text(styles_src.read_text())
462
+
463
+ index_path = Path(args.dir) / "index.html"
464
+ if not index_path.exists():
465
+ create_default_index(index_path)
466
+
467
+ def init_analytics_cache() -> None:
468
+ if args.no_index:
469
+ return
470
+ with status_context("Initializing analytics cache..."):
471
+ try:
472
+ AnalyticsIndex(graph_dir / "index.sqlite").ensure_schema()
473
+ except Exception:
474
+ # Never fail init because of analytics cache.
475
+ pass
476
+
477
+ def update_gitignore() -> None:
478
+ ensure_gitignore_entries(
479
+ Path(args.dir),
480
+ [
481
+ ".htmlgraph/index.sqlite",
482
+ ".htmlgraph/index.sqlite-wal",
483
+ ".htmlgraph/index.sqlite-shm",
484
+ ".htmlgraph/git-hook-errors.log",
485
+ ],
486
+ )
487
+
488
+ def create_hook_templates() -> None:
489
+ nonlocal hook_files
490
+ hooks_dir = graph_dir / "hooks"
491
+ hooks_dir.mkdir(exist_ok=True)
492
+
493
+ def ensure_hook_file(hook_name: str, hook_content: str) -> Path:
494
+ hook_dest = hooks_dir / f"{hook_name}.sh"
495
+ if not hook_dest.exists():
496
+ hook_dest.write_text(hook_content)
497
+ try:
498
+ hook_dest.chmod(0o755)
499
+ except Exception:
500
+ pass
501
+ return hook_dest
502
+
503
+ hook_files = {
504
+ "pre-commit": ensure_hook_file("pre-commit", pre_commit),
505
+ "post-commit": ensure_hook_file("post-commit", post_commit),
506
+ "post-checkout": ensure_hook_file("post-checkout", post_checkout),
507
+ "post-merge": ensure_hook_file("post-merge", post_merge),
508
+ "pre-push": ensure_hook_file("pre-push", pre_push),
509
+ }
510
+
511
+ def generate_docs_step() -> None:
512
+ if not generate_docs:
513
+ return
466
514
 
467
515
  def render_template(
468
516
  template_path: Path, replacements: dict[str, str]
@@ -483,7 +531,7 @@ exit 0
483
531
  from htmlgraph import __version__
484
532
 
485
533
  version = __version__
486
- except:
534
+ except Exception:
487
535
  version = "unknown"
488
536
 
489
537
  replacements = {
@@ -519,17 +567,9 @@ exit 0
519
567
  gemini_dest.write_text(content, encoding="utf-8")
520
568
  print(f"✓ Generated: {gemini_dest}")
521
569
 
522
- print(f"\nInitialized HtmlGraph in {graph_dir}")
523
- print(f"Collections: {', '.join(HtmlGraphAPIHandler.COLLECTIONS)}")
524
- print("\nStart server with: htmlgraph serve")
525
- if not args.no_index:
526
- print(
527
- f"Analytics cache: {graph_dir / 'index.sqlite'} (rebuildable; typically gitignored)"
528
- )
529
- print(f"Events: {events_dir}/ (append-only JSONL)")
530
-
531
- # Install Git hooks if requested
532
- if args.install_hooks:
570
+ def install_hooks_step() -> None:
571
+ if not args.install_hooks:
572
+ return
533
573
  git_dir = Path(args.dir) / ".git"
534
574
  if not git_dir.exists():
535
575
  print("\n⚠️ Warning: No .git directory found. Git hooks not installed.")
@@ -606,6 +646,46 @@ fi
606
646
 
607
647
  print("\nGit events will now be logged to HtmlGraph automatically.")
608
648
 
649
+ steps: list[tuple[str, Any]] = [
650
+ ("Create .htmlgraph directories", create_graph_dirs),
651
+ ("Create event log directory", create_events_dir),
652
+ ("Update .gitignore", update_gitignore),
653
+ ("Prepare git hook templates", create_hook_templates),
654
+ ("Copy default assets", copy_assets),
655
+ ]
656
+ if not args.no_index:
657
+ steps.append(("Initialize analytics cache", init_analytics_cache))
658
+ if generate_docs:
659
+ steps.append(("Generate documentation", generate_docs_step))
660
+ if args.install_hooks:
661
+ steps.append(("Install git hooks", install_hooks_step))
662
+
663
+ def run_steps(step_list: list[tuple[str, Any]]) -> None:
664
+ if progress is None:
665
+ for _, fn in step_list:
666
+ fn()
667
+ return
668
+
669
+ with progress:
670
+ task_id = progress.add_task(
671
+ "Initializing HtmlGraph...", total=len(step_list)
672
+ )
673
+ for description, fn in step_list:
674
+ progress.update(task_id, description=description)
675
+ fn()
676
+ progress.advance(task_id)
677
+
678
+ run_steps(steps)
679
+
680
+ print(f"\nInitialized HtmlGraph in {graph_dir}")
681
+ print(f"Collections: {', '.join(HtmlGraphAPIHandler.COLLECTIONS)}")
682
+ print("\nStart server with: htmlgraph serve")
683
+ if not args.no_index:
684
+ print(
685
+ f"Analytics cache: {graph_dir / 'index.sqlite'} (rebuildable; typically gitignored)"
686
+ )
687
+ print(f"Events: {events_dir}/ (append-only JSONL)")
688
+
609
689
 
610
690
  def cmd_install_hooks(args: argparse.Namespace) -> None:
611
691
  """Install Git hooks for automatic tracking."""
@@ -2164,6 +2244,173 @@ def cmd_track(args: argparse.Namespace) -> None:
2164
2244
  print(f" Drift warning: {entry.drift_score:.2f}")
2165
2245
 
2166
2246
 
2247
+ # =============================================================================
2248
+ # Documentation Version Management Commands
2249
+ # =============================================================================
2250
+
2251
+
2252
+ def cmd_docs_version(args: argparse.Namespace) -> None:
2253
+ """Check documentation version compatibility."""
2254
+ from htmlgraph.docs import check_docs_version
2255
+
2256
+ htmlgraph_dir = Path(args.graph_dir)
2257
+
2258
+ if not htmlgraph_dir.exists():
2259
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2260
+ print(" Run `htmlgraph init` first")
2261
+ sys.exit(1)
2262
+
2263
+ compatible, message = check_docs_version(htmlgraph_dir)
2264
+
2265
+ if compatible and not message:
2266
+ print("✅ Documentation is up to date")
2267
+ elif compatible and message:
2268
+ print(message)
2269
+ print("\n💡 Run `htmlgraph docs upgrade` to update to latest version")
2270
+ else:
2271
+ print(message)
2272
+ print("\n❌ Documentation version is incompatible")
2273
+ print(" Run `htmlgraph docs upgrade` to migrate")
2274
+ sys.exit(1)
2275
+
2276
+
2277
+ def cmd_docs_upgrade(args: argparse.Namespace) -> None:
2278
+ """Upgrade documentation to latest version."""
2279
+ from htmlgraph.docs import upgrade_docs_interactive
2280
+ from htmlgraph.docs.docs_version import get_current_doc_version
2281
+ from htmlgraph.docs.metadata import DocsMetadata
2282
+ from htmlgraph.docs.migrations import get_migration
2283
+
2284
+ htmlgraph_dir = Path(args.graph_dir)
2285
+
2286
+ if not htmlgraph_dir.exists():
2287
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2288
+ print(" Run `htmlgraph init` first")
2289
+ sys.exit(1)
2290
+
2291
+ if args.auto:
2292
+ # Auto-migrate without prompts
2293
+ metadata = DocsMetadata.load(htmlgraph_dir)
2294
+ current_version = get_current_doc_version()
2295
+
2296
+ if metadata.schema_version == current_version:
2297
+ print("✅ Documentation is already up to date")
2298
+ return
2299
+
2300
+ migration = get_migration(metadata.schema_version, current_version)
2301
+ if not migration:
2302
+ print(
2303
+ f"❌ No migration available from v{metadata.schema_version} to v{current_version}"
2304
+ )
2305
+ sys.exit(1)
2306
+
2307
+ backup_dir = htmlgraph_dir / ".docs-backups"
2308
+ backup_dir.mkdir(exist_ok=True)
2309
+
2310
+ print("🚀 Starting auto-migration...")
2311
+ success = migration.migrate(htmlgraph_dir, backup_dir)
2312
+
2313
+ if success:
2314
+ print("✅ Migration complete!")
2315
+ print(f"📦 Backup saved to {backup_dir}")
2316
+ else:
2317
+ print("❌ Migration failed")
2318
+ sys.exit(1)
2319
+ else:
2320
+ # Interactive upgrade
2321
+ upgrade_docs_interactive(htmlgraph_dir)
2322
+
2323
+
2324
+ def cmd_docs_diff(args: argparse.Namespace) -> None:
2325
+ """Show migration diff preview."""
2326
+ htmlgraph_dir = Path(args.graph_dir)
2327
+
2328
+ if not htmlgraph_dir.exists():
2329
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2330
+ print(" Run `htmlgraph init` first")
2331
+ sys.exit(1)
2332
+
2333
+ print("📊 Showing migration preview...")
2334
+ print("⚠️ Diff preview not yet implemented")
2335
+ print(" Use `htmlgraph docs upgrade` instead")
2336
+
2337
+
2338
+ def cmd_docs_rollback(args: argparse.Namespace) -> None:
2339
+ """Rollback to previous documentation version."""
2340
+ from htmlgraph.docs.metadata import DocsMetadata
2341
+ from htmlgraph.docs.migrations import get_migration
2342
+
2343
+ htmlgraph_dir = Path(args.graph_dir)
2344
+
2345
+ if not htmlgraph_dir.exists():
2346
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2347
+ print(" Run `htmlgraph init` first")
2348
+ sys.exit(1)
2349
+
2350
+ backup_dir = htmlgraph_dir / ".docs-backups"
2351
+ if not backup_dir.exists() or not list(backup_dir.glob("v*")):
2352
+ print("❌ No backups found")
2353
+ print(" Nothing to rollback to")
2354
+ sys.exit(1)
2355
+
2356
+ # Get target version
2357
+ metadata = DocsMetadata.load(htmlgraph_dir)
2358
+ target_version = int(args.version) if args.version else metadata.schema_version - 1
2359
+
2360
+ if target_version < 1:
2361
+ print("❌ Invalid version")
2362
+ sys.exit(1)
2363
+
2364
+ # Get migration script
2365
+ migration = get_migration(target_version, metadata.schema_version)
2366
+ if not migration:
2367
+ print(
2368
+ f"❌ Cannot rollback from v{metadata.schema_version} to v{target_version}"
2369
+ )
2370
+ sys.exit(1)
2371
+
2372
+ print(f"🔄 Rolling back to v{target_version}...")
2373
+ try:
2374
+ migration.rollback(htmlgraph_dir, backup_dir)
2375
+ print("✅ Rollback complete")
2376
+ except Exception as e:
2377
+ print(f"❌ Rollback failed: {e}")
2378
+ sys.exit(1)
2379
+
2380
+
2381
+ def cmd_docs_generate(args: argparse.Namespace) -> None:
2382
+ """Generate documentation from templates with user customizations."""
2383
+ from htmlgraph.docs import sync_docs_to_file
2384
+
2385
+ htmlgraph_dir = Path(args.graph_dir)
2386
+ output_file = Path(args.output) if args.output else Path("AGENTS.md")
2387
+ platform = args.platform
2388
+
2389
+ if not htmlgraph_dir.exists():
2390
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2391
+ print(" Run `htmlgraph init` first")
2392
+ sys.exit(1)
2393
+
2394
+ try:
2395
+ print(f"📝 Generating documentation for platform: {platform}")
2396
+ print(f" Output: {output_file}")
2397
+
2398
+ result_path = sync_docs_to_file(htmlgraph_dir, output_file, platform)
2399
+
2400
+ print(f"✅ Documentation generated: {result_path}")
2401
+ print("\n💡 To customize:")
2402
+ print(f" 1. Create {htmlgraph_dir}/docs/templates/agents.md.j2")
2403
+ print(" 2. Extend base template: {% extends 'base_agents.md.j2' %}")
2404
+ print(" 3. Override blocks: header, introduction, custom_workflows, etc.")
2405
+
2406
+ except Exception as e:
2407
+ print(f"❌ Generation failed: {e}")
2408
+ import traceback
2409
+
2410
+ traceback.print_exc()
2411
+ sys.exit(1)
2412
+
2413
+
2167
2414
  # =============================================================================
2168
2415
  # Events & Index Commands
2169
2416
  # =============================================================================
@@ -2191,22 +2438,15 @@ def cmd_events_export(args: argparse.Namespace) -> None:
2191
2438
 
2192
2439
  def cmd_index_rebuild(args: argparse.Namespace) -> None:
2193
2440
  """Rebuild the SQLite analytics index from JSONL event logs."""
2194
- from htmlgraph.analytics_index import AnalyticsIndex
2195
- from htmlgraph.event_log import JsonlEventLog
2441
+ from htmlgraph.operations import rebuild_index
2196
2442
 
2197
2443
  graph_dir = Path(args.graph_dir)
2198
- events_dir = graph_dir / "events"
2199
- db_path = graph_dir / "index.sqlite"
2200
2444
 
2201
- log = JsonlEventLog(events_dir)
2202
- index = AnalyticsIndex(db_path)
2445
+ result = rebuild_index(graph_dir=graph_dir)
2203
2446
 
2204
- events = (event for _, event in log.iter_events())
2205
- result = index.rebuild_from_events(events)
2206
-
2207
- print(f"DB: {db_path}")
2208
- print(f"Inserted: {result['inserted']}")
2209
- print(f"Skipped: {result['skipped']}")
2447
+ print(f"DB: {result.db_path}")
2448
+ print(f"Inserted: {result.inserted}")
2449
+ print(f"Skipped: {result.skipped}")
2210
2450
 
2211
2451
 
2212
2452
  def cmd_watch(args: argparse.Namespace) -> None:
@@ -3345,6 +3585,106 @@ def create_default_index(path: Path) -> None:
3345
3585
  )
3346
3586
 
3347
3587
 
3588
+ def cmd_claude(args: argparse.Namespace) -> None:
3589
+ """Start Claude Code with orchestrator prompt."""
3590
+ import textwrap
3591
+
3592
+ try:
3593
+ if args.init:
3594
+ # Load optimized orchestrator system prompt
3595
+ prompt_file = Path(__file__).parent / "orchestrator_system_prompt.txt"
3596
+
3597
+ if prompt_file.exists():
3598
+ system_prompt = prompt_file.read_text(encoding="utf-8")
3599
+ else:
3600
+ # Fallback: provide minimal orchestrator guidance
3601
+ system_prompt = textwrap.dedent(
3602
+ """
3603
+ You are an AI orchestrator for HtmlGraph project development.
3604
+
3605
+ CRITICAL DIRECTIVES:
3606
+ 1. DELEGATE to subagents - do not implement directly
3607
+ 2. CREATE work items before delegating (features, bugs, spikes)
3608
+ 3. USE SDK for tracking - all work must be tracked in .htmlgraph/
3609
+ 4. RESPECT dependencies - check blockers before starting
3610
+
3611
+ Key Rules:
3612
+ - Implementation work → delegate to general-purpose subagent
3613
+ - Research/exploration → delegate to explorer subagent
3614
+ - Testing/validation → delegate to test-runner subagent
3615
+ - Complex analysis → delegate to appropriate specialist
3616
+
3617
+ Always use:
3618
+ from htmlgraph import SDK
3619
+ sdk = SDK(agent='orchestrator')
3620
+
3621
+ See CLAUDE.md for complete orchestrator directives.
3622
+ """
3623
+ )
3624
+
3625
+ if args.quiet or args.format == "json":
3626
+ # Non-interactive: directly launch Claude with system prompt
3627
+ cmd = ["claude", "--append-system-prompt", system_prompt]
3628
+ else:
3629
+ # Interactive: show summary first
3630
+ print("=" * 60)
3631
+ print("🤖 HtmlGraph Orchestrator Mode")
3632
+ print("=" * 60)
3633
+ print("\nStarting Claude Code with orchestrator system prompt...")
3634
+ print("Key directives:")
3635
+ print(" ✓ Delegate implementation to subagents")
3636
+ print(" ✓ Create work items before delegating")
3637
+ print(" ✓ Track all work in .htmlgraph/")
3638
+ print(" ✓ Respect dependency chains")
3639
+ print()
3640
+
3641
+ cmd = ["claude", "--append-system-prompt", system_prompt]
3642
+
3643
+ try:
3644
+ subprocess.run(cmd, check=False)
3645
+ except FileNotFoundError:
3646
+ print("Error: 'claude' command not found.", file=sys.stderr)
3647
+ print(
3648
+ "Please install Claude Code CLI: https://code.claude.com",
3649
+ file=sys.stderr,
3650
+ )
3651
+ sys.exit(1)
3652
+
3653
+ elif args.continue_session:
3654
+ # Resume last Claude Code session
3655
+ if args.quiet or args.format == "json":
3656
+ cmd = ["claude", "--resume"]
3657
+ else:
3658
+ print("Resuming last Claude Code session...")
3659
+ cmd = ["claude", "--resume"]
3660
+
3661
+ try:
3662
+ subprocess.run(cmd, check=False)
3663
+ except FileNotFoundError:
3664
+ print("Error: 'claude' command not found.", file=sys.stderr)
3665
+ print(
3666
+ "Please install Claude Code CLI: https://code.claude.com",
3667
+ file=sys.stderr,
3668
+ )
3669
+ sys.exit(1)
3670
+
3671
+ else:
3672
+ # Default: start normal Claude Code session
3673
+ try:
3674
+ subprocess.run(["claude"], check=False)
3675
+ except FileNotFoundError:
3676
+ print("Error: 'claude' command not found.", file=sys.stderr)
3677
+ print(
3678
+ "Please install Claude Code CLI: https://code.claude.com",
3679
+ file=sys.stderr,
3680
+ )
3681
+ sys.exit(1)
3682
+
3683
+ except Exception as e:
3684
+ print(f"Error: Failed to start Claude Code: {e}", file=sys.stderr)
3685
+ sys.exit(1)
3686
+
3687
+
3348
3688
  def main() -> None:
3349
3689
  parser = argparse.ArgumentParser(
3350
3690
  description="HtmlGraph - HTML is All You Need",
@@ -4428,6 +4768,64 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4428
4768
  "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4429
4769
  )
4430
4770
 
4771
+ # =========================================================================
4772
+ # Documentation Version Management
4773
+ # =========================================================================
4774
+
4775
+ docs_parser = subparsers.add_parser("docs", help="Documentation version management")
4776
+ docs_subparsers = docs_parser.add_subparsers(
4777
+ dest="docs_command", help="Docs command"
4778
+ )
4779
+
4780
+ docs_version = docs_subparsers.add_parser(
4781
+ "version", help="Check documentation version compatibility"
4782
+ )
4783
+ docs_version.add_argument(
4784
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4785
+ )
4786
+
4787
+ docs_upgrade = docs_subparsers.add_parser(
4788
+ "upgrade", help="Upgrade documentation to latest version"
4789
+ )
4790
+ docs_upgrade.add_argument(
4791
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4792
+ )
4793
+ docs_upgrade.add_argument(
4794
+ "--auto", action="store_true", help="Auto-migrate without prompts"
4795
+ )
4796
+
4797
+ docs_diff = docs_subparsers.add_parser("diff", help="Show migration diff preview")
4798
+ docs_diff.add_argument(
4799
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4800
+ )
4801
+
4802
+ docs_rollback = docs_subparsers.add_parser(
4803
+ "rollback", help="Rollback to previous version"
4804
+ )
4805
+ docs_rollback.add_argument(
4806
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4807
+ )
4808
+ docs_rollback.add_argument(
4809
+ "version", nargs="?", help="Version to rollback to (default: latest backup)"
4810
+ )
4811
+
4812
+ docs_generate = docs_subparsers.add_parser(
4813
+ "generate", help="Generate documentation from templates"
4814
+ )
4815
+ docs_generate.add_argument(
4816
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4817
+ )
4818
+ docs_generate.add_argument(
4819
+ "--platform",
4820
+ "-p",
4821
+ default="claude",
4822
+ choices=["claude", "gemini", "api", "cli"],
4823
+ help="Platform to generate docs for",
4824
+ )
4825
+ docs_generate.add_argument(
4826
+ "--output", "-o", help="Output file (default: AGENTS.md)"
4827
+ )
4828
+
4431
4829
  # =========================================================================
4432
4830
  # Events & Analytics Index
4433
4831
  # =========================================================================
@@ -4692,6 +5090,24 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4692
5090
  help="Install the Gemini CLI extension from the bundled package",
4693
5091
  )
4694
5092
 
5093
+ # claude - Start Claude Code with orchestrator support
5094
+ claude_parser = subparsers.add_parser(
5095
+ "claude", help="Start Claude Code with HtmlGraph integration"
5096
+ )
5097
+ claude_group = claude_parser.add_mutually_exclusive_group()
5098
+ claude_group.add_argument(
5099
+ "--init",
5100
+ action="store_true",
5101
+ help="Start with orchestrator system prompt (recommended)",
5102
+ )
5103
+ claude_group.add_argument(
5104
+ "--continue",
5105
+ dest="continue_session",
5106
+ action="store_true",
5107
+ help="Resume last Claude Code session",
5108
+ )
5109
+ claude_parser.set_defaults(func=cmd_claude)
5110
+
4695
5111
  args = parser.parse_args()
4696
5112
 
4697
5113
  if args.command == "serve":
@@ -4840,6 +5256,20 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4840
5256
  from htmlgraph.analytics.cli import cmd_analytics
4841
5257
 
4842
5258
  cmd_analytics(args)
5259
+ elif args.command == "docs":
5260
+ if args.docs_command == "version":
5261
+ cmd_docs_version(args)
5262
+ elif args.docs_command == "upgrade":
5263
+ cmd_docs_upgrade(args)
5264
+ elif args.docs_command == "diff":
5265
+ cmd_docs_diff(args)
5266
+ elif args.docs_command == "rollback":
5267
+ cmd_docs_rollback(args)
5268
+ elif args.docs_command == "generate":
5269
+ cmd_docs_generate(args)
5270
+ else:
5271
+ docs_parser.print_help()
5272
+ sys.exit(1)
4843
5273
  elif args.command == "events":
4844
5274
  if args.events_command == "export-sessions":
4845
5275
  cmd_events_export(args)
@@ -4915,6 +5345,8 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4915
5345
  sys.exit(1)
4916
5346
  elif args.command == "install-gemini-extension":
4917
5347
  cmd_install_gemini_extension(args)
5348
+ elif args.command == "claude":
5349
+ cmd_claude(args)
4918
5350
  else:
4919
5351
  parser.print_help()
4920
5352
  sys.exit(1)