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.
- htmlgraph/__init__.py +1 -1
- htmlgraph/analytics/__init__.py +3 -1
- htmlgraph/analytics/cross_session.py +612 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/cli.py +510 -0
- htmlgraph/converter.py +39 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +161 -0
- htmlgraph/learning.py +121 -97
- htmlgraph/models.py +53 -1
- htmlgraph/sdk.py +4 -1
- {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/METADATA +1 -1
- {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/RECORD +33 -16
- {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.9.data → htmlgraph-0.22.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.9.dist-info → htmlgraph-0.22.0.dist-info}/WHEEL +0 -0
- {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
|