htmlgraph 0.20.9__py3-none-any.whl → 0.22.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 (33) hide show
  1. htmlgraph/__init__.py +1 -1
  2. htmlgraph/analytics/__init__.py +3 -1
  3. htmlgraph/analytics/cross_session.py +612 -0
  4. htmlgraph/archive/__init__.py +24 -0
  5. htmlgraph/archive/bloom.py +234 -0
  6. htmlgraph/archive/fts.py +297 -0
  7. htmlgraph/archive/manager.py +583 -0
  8. htmlgraph/archive/search.py +244 -0
  9. htmlgraph/cli.py +510 -0
  10. htmlgraph/converter.py +39 -0
  11. htmlgraph/docs/__init__.py +77 -0
  12. htmlgraph/docs/docs_version.py +55 -0
  13. htmlgraph/docs/metadata.py +93 -0
  14. htmlgraph/docs/migrations.py +232 -0
  15. htmlgraph/docs/template_engine.py +143 -0
  16. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  17. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  18. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  19. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  20. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  21. htmlgraph/docs/version_check.py +161 -0
  22. htmlgraph/learning.py +121 -97
  23. htmlgraph/models.py +53 -1
  24. htmlgraph/sdk.py +4 -1
  25. {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/METADATA +1 -1
  26. {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/RECORD +33 -16
  27. {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/dashboard.html +0 -0
  28. {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/styles.css +0 -0
  29. {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  30. {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  31. {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  32. {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/WHEEL +0 -0
  33. {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/entry_points.txt +0 -0
htmlgraph/cli.py CHANGED
@@ -2164,6 +2164,173 @@ def cmd_track(args: argparse.Namespace) -> None:
2164
2164
  print(f" Drift warning: {entry.drift_score:.2f}")
2165
2165
 
2166
2166
 
2167
+ # =============================================================================
2168
+ # Documentation Version Management Commands
2169
+ # =============================================================================
2170
+
2171
+
2172
+ def cmd_docs_version(args: argparse.Namespace) -> None:
2173
+ """Check documentation version compatibility."""
2174
+ from htmlgraph.docs import check_docs_version
2175
+
2176
+ htmlgraph_dir = Path(args.graph_dir)
2177
+
2178
+ if not htmlgraph_dir.exists():
2179
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2180
+ print(" Run `htmlgraph init` first")
2181
+ sys.exit(1)
2182
+
2183
+ compatible, message = check_docs_version(htmlgraph_dir)
2184
+
2185
+ if compatible and not message:
2186
+ print("✅ Documentation is up to date")
2187
+ elif compatible and message:
2188
+ print(message)
2189
+ print("\n💡 Run `htmlgraph docs upgrade` to update to latest version")
2190
+ else:
2191
+ print(message)
2192
+ print("\n❌ Documentation version is incompatible")
2193
+ print(" Run `htmlgraph docs upgrade` to migrate")
2194
+ sys.exit(1)
2195
+
2196
+
2197
+ def cmd_docs_upgrade(args: argparse.Namespace) -> None:
2198
+ """Upgrade documentation to latest version."""
2199
+ from htmlgraph.docs import upgrade_docs_interactive
2200
+ from htmlgraph.docs.docs_version import get_current_doc_version
2201
+ from htmlgraph.docs.metadata import DocsMetadata
2202
+ from htmlgraph.docs.migrations import get_migration
2203
+
2204
+ htmlgraph_dir = Path(args.graph_dir)
2205
+
2206
+ if not htmlgraph_dir.exists():
2207
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2208
+ print(" Run `htmlgraph init` first")
2209
+ sys.exit(1)
2210
+
2211
+ if args.auto:
2212
+ # Auto-migrate without prompts
2213
+ metadata = DocsMetadata.load(htmlgraph_dir)
2214
+ current_version = get_current_doc_version()
2215
+
2216
+ if metadata.schema_version == current_version:
2217
+ print("✅ Documentation is already up to date")
2218
+ return
2219
+
2220
+ migration = get_migration(metadata.schema_version, current_version)
2221
+ if not migration:
2222
+ print(
2223
+ f"❌ No migration available from v{metadata.schema_version} to v{current_version}"
2224
+ )
2225
+ sys.exit(1)
2226
+
2227
+ backup_dir = htmlgraph_dir / ".docs-backups"
2228
+ backup_dir.mkdir(exist_ok=True)
2229
+
2230
+ print("🚀 Starting auto-migration...")
2231
+ success = migration.migrate(htmlgraph_dir, backup_dir)
2232
+
2233
+ if success:
2234
+ print("✅ Migration complete!")
2235
+ print(f"📦 Backup saved to {backup_dir}")
2236
+ else:
2237
+ print("❌ Migration failed")
2238
+ sys.exit(1)
2239
+ else:
2240
+ # Interactive upgrade
2241
+ upgrade_docs_interactive(htmlgraph_dir)
2242
+
2243
+
2244
+ def cmd_docs_diff(args: argparse.Namespace) -> None:
2245
+ """Show migration diff preview."""
2246
+ htmlgraph_dir = Path(args.graph_dir)
2247
+
2248
+ if not htmlgraph_dir.exists():
2249
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2250
+ print(" Run `htmlgraph init` first")
2251
+ sys.exit(1)
2252
+
2253
+ print("📊 Showing migration preview...")
2254
+ print("⚠️ Diff preview not yet implemented")
2255
+ print(" Use `htmlgraph docs upgrade` instead")
2256
+
2257
+
2258
+ def cmd_docs_rollback(args: argparse.Namespace) -> None:
2259
+ """Rollback to previous documentation version."""
2260
+ from htmlgraph.docs.metadata import DocsMetadata
2261
+ from htmlgraph.docs.migrations import get_migration
2262
+
2263
+ htmlgraph_dir = Path(args.graph_dir)
2264
+
2265
+ if not htmlgraph_dir.exists():
2266
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2267
+ print(" Run `htmlgraph init` first")
2268
+ sys.exit(1)
2269
+
2270
+ backup_dir = htmlgraph_dir / ".docs-backups"
2271
+ if not backup_dir.exists() or not list(backup_dir.glob("v*")):
2272
+ print("❌ No backups found")
2273
+ print(" Nothing to rollback to")
2274
+ sys.exit(1)
2275
+
2276
+ # Get target version
2277
+ metadata = DocsMetadata.load(htmlgraph_dir)
2278
+ target_version = int(args.version) if args.version else metadata.schema_version - 1
2279
+
2280
+ if target_version < 1:
2281
+ print("❌ Invalid version")
2282
+ sys.exit(1)
2283
+
2284
+ # Get migration script
2285
+ migration = get_migration(target_version, metadata.schema_version)
2286
+ if not migration:
2287
+ print(
2288
+ f"❌ Cannot rollback from v{metadata.schema_version} to v{target_version}"
2289
+ )
2290
+ sys.exit(1)
2291
+
2292
+ print(f"🔄 Rolling back to v{target_version}...")
2293
+ try:
2294
+ migration.rollback(htmlgraph_dir, backup_dir)
2295
+ print("✅ Rollback complete")
2296
+ except Exception as e:
2297
+ print(f"❌ Rollback failed: {e}")
2298
+ sys.exit(1)
2299
+
2300
+
2301
+ def cmd_docs_generate(args: argparse.Namespace) -> None:
2302
+ """Generate documentation from templates with user customizations."""
2303
+ from htmlgraph.docs import sync_docs_to_file
2304
+
2305
+ htmlgraph_dir = Path(args.graph_dir)
2306
+ output_file = Path(args.output) if args.output else Path("AGENTS.md")
2307
+ platform = args.platform
2308
+
2309
+ if not htmlgraph_dir.exists():
2310
+ print(f"❌ Directory not found: {htmlgraph_dir}")
2311
+ print(" Run `htmlgraph init` first")
2312
+ sys.exit(1)
2313
+
2314
+ try:
2315
+ print(f"📝 Generating documentation for platform: {platform}")
2316
+ print(f" Output: {output_file}")
2317
+
2318
+ result_path = sync_docs_to_file(htmlgraph_dir, output_file, platform)
2319
+
2320
+ print(f"✅ Documentation generated: {result_path}")
2321
+ print("\n💡 To customize:")
2322
+ print(f" 1. Create {htmlgraph_dir}/docs/templates/agents.md.j2")
2323
+ print(" 2. Extend base template: {% extends 'base_agents.md.j2' %}")
2324
+ print(" 3. Override blocks: header, introduction, custom_workflows, etc.")
2325
+
2326
+ except Exception as e:
2327
+ print(f"❌ Generation failed: {e}")
2328
+ import traceback
2329
+
2330
+ traceback.print_exc()
2331
+ sys.exit(1)
2332
+
2333
+
2167
2334
  # =============================================================================
2168
2335
  # Events & Index Commands
2169
2336
  # =============================================================================
@@ -4326,6 +4493,87 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4326
4493
  "--format", "-f", choices=["text", "json"], default="text", help="Output format"
4327
4494
  )
4328
4495
 
4496
+ # =========================================================================
4497
+ # Archive Management
4498
+ # =========================================================================
4499
+
4500
+ # archive (with subcommands)
4501
+ archive_parser = subparsers.add_parser(
4502
+ "archive", help="Archive management with optimized search"
4503
+ )
4504
+ archive_subparsers = archive_parser.add_subparsers(
4505
+ dest="archive_command", help="Archive command"
4506
+ )
4507
+
4508
+ # archive create
4509
+ archive_create = archive_subparsers.add_parser(
4510
+ "create", help="Create archive from old entities"
4511
+ )
4512
+ archive_create.add_argument(
4513
+ "--older-than",
4514
+ type=int,
4515
+ default=90,
4516
+ help="Archive entities older than N days (default: 90)",
4517
+ )
4518
+ archive_create.add_argument(
4519
+ "--period",
4520
+ choices=["quarter", "month", "year"],
4521
+ default="quarter",
4522
+ help="Archive grouping period (default: quarter)",
4523
+ )
4524
+ archive_create.add_argument(
4525
+ "--dry-run",
4526
+ action="store_true",
4527
+ help="Preview what would be archived without making changes",
4528
+ )
4529
+ archive_create.add_argument(
4530
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4531
+ )
4532
+
4533
+ # archive search
4534
+ archive_search = archive_subparsers.add_parser(
4535
+ "search", help="Search archived entities"
4536
+ )
4537
+ archive_search.add_argument("query", help="Search query")
4538
+ archive_search.add_argument(
4539
+ "--limit", "-l", type=int, default=10, help="Maximum results (default: 10)"
4540
+ )
4541
+ archive_search.add_argument(
4542
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4543
+ )
4544
+ archive_search.add_argument(
4545
+ "--format", "-f", choices=["text", "json"], default="text", help="Output format"
4546
+ )
4547
+
4548
+ # archive stats
4549
+ archive_stats = archive_subparsers.add_parser(
4550
+ "stats", help="Show archive statistics"
4551
+ )
4552
+ archive_stats.add_argument(
4553
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4554
+ )
4555
+ archive_stats.add_argument(
4556
+ "--format", "-f", choices=["text", "json"], default="text", help="Output format"
4557
+ )
4558
+
4559
+ # archive restore
4560
+ archive_restore = archive_subparsers.add_parser(
4561
+ "restore", help="Restore archived entity"
4562
+ )
4563
+ archive_restore.add_argument("entity_id", help="Entity ID to restore")
4564
+ archive_restore.add_argument(
4565
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4566
+ )
4567
+
4568
+ # archive list
4569
+ archive_list = archive_subparsers.add_parser("list", help="List all archive files")
4570
+ archive_list.add_argument(
4571
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4572
+ )
4573
+ archive_list.add_argument(
4574
+ "--format", "-f", choices=["text", "json"], default="text", help="Output format"
4575
+ )
4576
+
4329
4577
  # =========================================================================
4330
4578
  # Analytics
4331
4579
  # =========================================================================
@@ -4347,6 +4595,64 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4347
4595
  "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4348
4596
  )
4349
4597
 
4598
+ # =========================================================================
4599
+ # Documentation Version Management
4600
+ # =========================================================================
4601
+
4602
+ docs_parser = subparsers.add_parser("docs", help="Documentation version management")
4603
+ docs_subparsers = docs_parser.add_subparsers(
4604
+ dest="docs_command", help="Docs command"
4605
+ )
4606
+
4607
+ docs_version = docs_subparsers.add_parser(
4608
+ "version", help="Check documentation version compatibility"
4609
+ )
4610
+ docs_version.add_argument(
4611
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4612
+ )
4613
+
4614
+ docs_upgrade = docs_subparsers.add_parser(
4615
+ "upgrade", help="Upgrade documentation to latest version"
4616
+ )
4617
+ docs_upgrade.add_argument(
4618
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4619
+ )
4620
+ docs_upgrade.add_argument(
4621
+ "--auto", action="store_true", help="Auto-migrate without prompts"
4622
+ )
4623
+
4624
+ docs_diff = docs_subparsers.add_parser("diff", help="Show migration diff preview")
4625
+ docs_diff.add_argument(
4626
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4627
+ )
4628
+
4629
+ docs_rollback = docs_subparsers.add_parser(
4630
+ "rollback", help="Rollback to previous version"
4631
+ )
4632
+ docs_rollback.add_argument(
4633
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4634
+ )
4635
+ docs_rollback.add_argument(
4636
+ "version", nargs="?", help="Version to rollback to (default: latest backup)"
4637
+ )
4638
+
4639
+ docs_generate = docs_subparsers.add_parser(
4640
+ "generate", help="Generate documentation from templates"
4641
+ )
4642
+ docs_generate.add_argument(
4643
+ "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4644
+ )
4645
+ docs_generate.add_argument(
4646
+ "--platform",
4647
+ "-p",
4648
+ default="claude",
4649
+ choices=["claude", "gemini", "api", "cli"],
4650
+ help="Platform to generate docs for",
4651
+ )
4652
+ docs_generate.add_argument(
4653
+ "--output", "-o", help="Output file (default: AGENTS.md)"
4654
+ )
4655
+
4350
4656
  # =========================================================================
4351
4657
  # Events & Analytics Index
4352
4658
  # =========================================================================
@@ -4700,6 +5006,21 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4700
5006
  else:
4701
5007
  track_parser.print_help()
4702
5008
  sys.exit(1)
5009
+ elif args.command == "archive":
5010
+ # Archive management
5011
+ if args.archive_command == "create":
5012
+ cmd_archive_create(args)
5013
+ elif args.archive_command == "search":
5014
+ cmd_archive_search(args)
5015
+ elif args.archive_command == "stats":
5016
+ cmd_archive_stats(args)
5017
+ elif args.archive_command == "restore":
5018
+ cmd_archive_restore(args)
5019
+ elif args.archive_command == "list":
5020
+ cmd_archive_list(args)
5021
+ else:
5022
+ archive_parser.print_help()
5023
+ sys.exit(1)
4703
5024
  elif args.command == "work":
4704
5025
  # Work management with smart routing
4705
5026
  if args.work_command == "next":
@@ -4744,6 +5065,20 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4744
5065
  from htmlgraph.analytics.cli import cmd_analytics
4745
5066
 
4746
5067
  cmd_analytics(args)
5068
+ elif args.command == "docs":
5069
+ if args.docs_command == "version":
5070
+ cmd_docs_version(args)
5071
+ elif args.docs_command == "upgrade":
5072
+ cmd_docs_upgrade(args)
5073
+ elif args.docs_command == "diff":
5074
+ cmd_docs_diff(args)
5075
+ elif args.docs_command == "rollback":
5076
+ cmd_docs_rollback(args)
5077
+ elif args.docs_command == "generate":
5078
+ cmd_docs_generate(args)
5079
+ else:
5080
+ docs_parser.print_help()
5081
+ sys.exit(1)
4747
5082
  elif args.command == "events":
4748
5083
  if args.events_command == "export-sessions":
4749
5084
  cmd_events_export(args)
@@ -4971,5 +5306,180 @@ def cmd_sync_docs(args: argparse.Namespace) -> int:
4971
5306
  return 1 if any("⚠️" in c or "❌" in c for c in changes) else 0
4972
5307
 
4973
5308
 
5309
+ # =============================================================================
5310
+ # Archive Management Commands
5311
+ # =============================================================================
5312
+
5313
+
5314
+ def cmd_archive_create(args: argparse.Namespace) -> None:
5315
+ """Create archive from old entities."""
5316
+ from pathlib import Path
5317
+
5318
+ from htmlgraph.archive import ArchiveManager
5319
+
5320
+ htmlgraph_dir = Path(args.graph_dir).resolve()
5321
+
5322
+ if not htmlgraph_dir.exists():
5323
+ print(f"Error: Directory not found: {htmlgraph_dir}", file=sys.stderr)
5324
+ sys.exit(1)
5325
+
5326
+ manager = ArchiveManager(htmlgraph_dir)
5327
+
5328
+ # Run archive operation
5329
+ result = manager.archive_entities(
5330
+ older_than_days=args.older_than,
5331
+ period=args.period,
5332
+ dry_run=args.dry_run,
5333
+ )
5334
+
5335
+ if result["dry_run"]:
5336
+ print("\n🔍 DRY RUN - Preview (no changes made)\n")
5337
+ print(f"Would archive: {result['would_archive']} entities")
5338
+ print(f"Archive files: {len(result['archive_files'])}")
5339
+ print("\nDetails:")
5340
+ for archive_key, count in result["details"].items():
5341
+ print(f" {archive_key}: {count} entities")
5342
+ else:
5343
+ print(f"\n✅ Archived {result['archived_count']} entities")
5344
+ print(f"Created {len(result['archive_files'])} archive file(s):")
5345
+ for archive_file in result["archive_files"]:
5346
+ count = result["details"].get(archive_file.replace(".html", ""), 0)
5347
+ print(f" - {archive_file} ({count} entities)")
5348
+
5349
+ manager.close()
5350
+
5351
+
5352
+ def cmd_archive_search(args: argparse.Namespace) -> None:
5353
+ """Search archived entities."""
5354
+ import json
5355
+ from pathlib import Path
5356
+
5357
+ from htmlgraph.archive import ArchiveManager
5358
+
5359
+ htmlgraph_dir = Path(args.graph_dir).resolve()
5360
+
5361
+ if not htmlgraph_dir.exists():
5362
+ print(f"Error: Directory not found: {htmlgraph_dir}", file=sys.stderr)
5363
+ sys.exit(1)
5364
+
5365
+ manager = ArchiveManager(htmlgraph_dir)
5366
+
5367
+ # Search archives
5368
+ results = manager.search(args.query, limit=args.limit)
5369
+
5370
+ if args.format == "json":
5371
+ print(json.dumps({"query": args.query, "results": results}, indent=2))
5372
+ else:
5373
+ print(f"\n🔍 Search results for: '{args.query}'\n")
5374
+ print(f"Found {len(results)} result(s):\n")
5375
+
5376
+ for i, result in enumerate(results, 1):
5377
+ print(f"{i}. {result['entity_id']} ({result['entity_type']})")
5378
+ print(f" Archive: {result['archive_file']}")
5379
+ print(f" Status: {result['status']}")
5380
+ print(f" Title: {result['title_snippet']}")
5381
+ if result["description_snippet"]:
5382
+ print(f" Description: {result['description_snippet']}")
5383
+ print(f" Relevance: {result['rank']:.2f}")
5384
+ print()
5385
+
5386
+ manager.close()
5387
+
5388
+
5389
+ def cmd_archive_stats(args: argparse.Namespace) -> None:
5390
+ """Show archive statistics."""
5391
+ import json
5392
+ from pathlib import Path
5393
+
5394
+ from htmlgraph.archive import ArchiveManager
5395
+
5396
+ htmlgraph_dir = Path(args.graph_dir).resolve()
5397
+
5398
+ if not htmlgraph_dir.exists():
5399
+ print(f"Error: Directory not found: {htmlgraph_dir}", file=sys.stderr)
5400
+ sys.exit(1)
5401
+
5402
+ manager = ArchiveManager(htmlgraph_dir)
5403
+
5404
+ # Get statistics
5405
+ stats = manager.get_archive_stats()
5406
+
5407
+ if args.format == "json":
5408
+ print(json.dumps(stats, indent=2))
5409
+ else:
5410
+ print("\n📊 Archive Statistics\n")
5411
+ print(f"Archive files: {stats['archive_count']}")
5412
+ print(f"Archived entities: {stats['entity_count']}")
5413
+ print(f"Total size: {stats['total_size_mb']:.2f} MB")
5414
+ print(f"FTS5 index: {stats['fts_size_mb']:.2f} MB")
5415
+ print(
5416
+ f"Bloom filters: {stats['bloom_size_kb']:.2f} KB ({stats['bloom_count']} files)"
5417
+ )
5418
+
5419
+ manager.close()
5420
+
5421
+
5422
+ def cmd_archive_restore(args: argparse.Namespace) -> None:
5423
+ """Restore archived entity."""
5424
+ from pathlib import Path
5425
+
5426
+ from htmlgraph.archive import ArchiveManager
5427
+
5428
+ htmlgraph_dir = Path(args.graph_dir).resolve()
5429
+
5430
+ if not htmlgraph_dir.exists():
5431
+ print(f"Error: Directory not found: {htmlgraph_dir}", file=sys.stderr)
5432
+ sys.exit(1)
5433
+
5434
+ manager = ArchiveManager(htmlgraph_dir)
5435
+
5436
+ # Restore entity
5437
+ success = manager.unarchive(args.entity_id)
5438
+
5439
+ if success:
5440
+ print(f"✅ Restored {args.entity_id} from archive")
5441
+ else:
5442
+ print(f"❌ Entity not found in archives: {args.entity_id}", file=sys.stderr)
5443
+ sys.exit(1)
5444
+
5445
+ manager.close()
5446
+
5447
+
5448
+ def cmd_archive_list(args: argparse.Namespace) -> None:
5449
+ """List all archive files."""
5450
+ import json
5451
+ from pathlib import Path
5452
+
5453
+ htmlgraph_dir = Path(args.graph_dir).resolve()
5454
+
5455
+ if not htmlgraph_dir.exists():
5456
+ print(f"Error: Directory not found: {htmlgraph_dir}", file=sys.stderr)
5457
+ sys.exit(1)
5458
+
5459
+ archive_dir = htmlgraph_dir / "archives"
5460
+
5461
+ if not archive_dir.exists():
5462
+ print("No archives found")
5463
+ return
5464
+
5465
+ archive_files = sorted(archive_dir.glob("*.html"))
5466
+
5467
+ if args.format == "json":
5468
+ file_list = [
5469
+ {
5470
+ "filename": f.name,
5471
+ "size_kb": f.stat().st_size / 1024,
5472
+ "modified": f.stat().st_mtime,
5473
+ }
5474
+ for f in archive_files
5475
+ ]
5476
+ print(json.dumps({"archives": file_list}, indent=2))
5477
+ else:
5478
+ print(f"\n📦 Archive Files ({len(archive_files)})\n")
5479
+ for f in archive_files:
5480
+ size_kb = f.stat().st_size / 1024
5481
+ print(f" - {f.name} ({size_kb:.1f} KB)")
5482
+
5483
+
4974
5484
  if __name__ == "__main__":
4975
5485
  main()
htmlgraph/converter.py CHANGED
@@ -548,6 +548,45 @@ def html_to_session(filepath: Path | str) -> Session:
548
548
  # Activity log in HTML is reversed (newest first), so reverse back
549
549
  data["activity_log"] = list(reversed(activity_log))
550
550
 
551
+ # Parse detected patterns from table (if present)
552
+ detected_patterns = []
553
+ for tr in parser.query("section[data-detected-patterns] table tbody tr"):
554
+ # Extract pattern data from table row
555
+ pattern_type = tr.attrs.get("data-pattern-type", "neutral")
556
+
557
+ # Extract sequence from first <td class="sequence">
558
+ seq_td = tr.query_one("td.sequence")
559
+ sequence_str = seq_td.to_text().strip() if seq_td else ""
560
+ sequence = [s.strip() for s in sequence_str.split("→")] if sequence_str else []
561
+
562
+ # Extract count from third <td>
563
+ count_td = tr.query_all("td")[2] if len(tr.query_all("td")) > 2 else None
564
+ count_str = count_td.to_text().strip() if count_td else "0"
565
+ try:
566
+ count = int(count_str)
567
+ except (ValueError, TypeError):
568
+ count = 0
569
+
570
+ # Extract timestamps from fourth <td>
571
+ time_td = tr.query_all("td")[3] if len(tr.query_all("td")) > 3 else None
572
+ time_str = time_td.to_text().strip() if time_td else ""
573
+ times = time_str.split(" / ")
574
+ first_detected = times[0].strip() if len(times) > 0 else ""
575
+ last_detected = times[1].strip() if len(times) > 1 else ""
576
+
577
+ if sequence: # Only add if we have a valid sequence
578
+ detected_patterns.append(
579
+ {
580
+ "sequence": sequence,
581
+ "pattern_type": pattern_type,
582
+ "detection_count": count,
583
+ "first_detected": first_detected,
584
+ "last_detected": last_detected,
585
+ }
586
+ )
587
+
588
+ data["detected_patterns"] = detected_patterns
589
+
551
590
  return Session(**data)
552
591
 
553
592
 
@@ -0,0 +1,77 @@
1
+ """
2
+ Documentation version tracking and migration system with template-based user customization.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ from htmlgraph import __version__
8
+ from htmlgraph.docs.docs_version import (
9
+ DOC_VERSIONS,
10
+ DocVersion,
11
+ get_current_doc_version,
12
+ is_compatible,
13
+ )
14
+ from htmlgraph.docs.metadata import DocsMetadata
15
+ from htmlgraph.docs.migrations import MIGRATIONS, MigrationScript, get_migration
16
+ from htmlgraph.docs.template_engine import DocTemplateEngine
17
+ from htmlgraph.docs.version_check import check_docs_version, upgrade_docs_interactive
18
+
19
+ __all__ = [
20
+ "DOC_VERSIONS",
21
+ "DocVersion",
22
+ "get_current_doc_version",
23
+ "is_compatible",
24
+ "DocsMetadata",
25
+ "MigrationScript",
26
+ "MIGRATIONS",
27
+ "get_migration",
28
+ "check_docs_version",
29
+ "upgrade_docs_interactive",
30
+ "DocTemplateEngine",
31
+ "get_agents_md",
32
+ "sync_docs_to_file",
33
+ ]
34
+
35
+
36
+ def get_agents_md(htmlgraph_dir: Path, platform: str = "claude") -> str:
37
+ """Get AGENTS.md content with user customizations merged.
38
+
39
+ Args:
40
+ htmlgraph_dir: Path to .htmlgraph directory
41
+ platform: Platform name (claude, gemini, etc.)
42
+
43
+ Returns:
44
+ Merged documentation content
45
+
46
+ Example:
47
+ >>> from pathlib import Path
48
+ >>> content = get_agents_md(Path(".htmlgraph"), "claude")
49
+ """
50
+ engine = DocTemplateEngine(htmlgraph_dir)
51
+ return engine.render_agents_md(__version__, platform)
52
+
53
+
54
+ def sync_docs_to_file(
55
+ htmlgraph_dir: Path, output_file: Path, platform: str = "claude"
56
+ ) -> Path:
57
+ """Generate and write documentation to file.
58
+
59
+ Args:
60
+ htmlgraph_dir: Path to .htmlgraph directory
61
+ output_file: Path where documentation should be written
62
+ platform: Platform name (claude, gemini, etc.)
63
+
64
+ Returns:
65
+ Path to written file
66
+
67
+ Example:
68
+ >>> from pathlib import Path
69
+ >>> sync_docs_to_file(
70
+ ... Path(".htmlgraph"),
71
+ ... Path("AGENTS.md"),
72
+ ... "claude"
73
+ ... )
74
+ """
75
+ content = get_agents_md(htmlgraph_dir, platform)
76
+ output_file.write_text(content)
77
+ return output_file