htmlgraph 0.21.0__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 CHANGED
@@ -84,7 +84,7 @@ from htmlgraph.types import (
84
84
  )
85
85
  from htmlgraph.work_type_utils import infer_work_type, infer_work_type_from_id
86
86
 
87
- __version__ = "0.21.0"
87
+ __version__ = "0.22.0"
88
88
  __all__ = [
89
89
  # Exceptions
90
90
  "HtmlGraphError",
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
  # =============================================================================
@@ -4428,6 +4595,64 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4428
4595
  "--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
4429
4596
  )
4430
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
+
4431
4656
  # =========================================================================
4432
4657
  # Events & Analytics Index
4433
4658
  # =========================================================================
@@ -4840,6 +5065,20 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
4840
5065
  from htmlgraph.analytics.cli import cmd_analytics
4841
5066
 
4842
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)
4843
5082
  elif args.command == "events":
4844
5083
  if args.events_command == "export-sessions":
4845
5084
  cmd_events_export(args)
@@ -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
@@ -0,0 +1,55 @@
1
+ """
2
+ Documentation schema version management.
3
+
4
+ This module defines version compatibility rules for HtmlGraph documentation files.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+
9
+
10
+ @dataclass
11
+ class DocVersion:
12
+ """Documentation schema version."""
13
+
14
+ version: int # Schema version (1, 2, 3)
15
+ package_version: str # Minimum package version (e.g., "0.20.0")
16
+ breaking_changes: list[str]
17
+ migration_required: bool = False
18
+
19
+
20
+ # Version compatibility matrix
21
+ DOC_VERSIONS = {
22
+ 1: DocVersion(
23
+ version=1,
24
+ package_version="0.1.0",
25
+ breaking_changes=[],
26
+ migration_required=False,
27
+ ),
28
+ 2: DocVersion(
29
+ version=2,
30
+ package_version="0.20.0",
31
+ breaking_changes=[
32
+ "AGENTS.md structure changed to use Jinja2 templates",
33
+ "Removed root-level CLAUDE.md/GEMINI.md (moved to .htmlgraph/docs/)",
34
+ ],
35
+ migration_required=True,
36
+ ),
37
+ }
38
+
39
+
40
+ def get_current_doc_version() -> int:
41
+ """Get current documentation schema version for this package."""
42
+ return 2 # Current version
43
+
44
+
45
+ def is_compatible(user_version: int, package_version: int) -> bool:
46
+ """Check if user's doc version is compatible with package.
47
+
48
+ Args:
49
+ user_version: User's documentation schema version
50
+ package_version: Package's required documentation schema version
51
+
52
+ Returns:
53
+ True if compatible (supports N-1 versions)
54
+ """
55
+ return user_version >= package_version - 1 # Support N-1 versions
@@ -0,0 +1,93 @@
1
+ """
2
+ Documentation metadata storage and management.
3
+ """
4
+
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class DocsMetadata(BaseModel):
12
+ """Metadata for project documentation."""
13
+
14
+ schema_version: int = 2
15
+ last_updated: datetime = Field(default_factory=datetime.now)
16
+ customizations: list[str] = [] # List of user-customized sections
17
+ base_version_on_last_update: str = "0.21.0"
18
+
19
+ @classmethod
20
+ def load(cls, htmlgraph_dir: Path) -> "DocsMetadata":
21
+ """Load metadata from .docs-metadata.json.
22
+
23
+ Args:
24
+ htmlgraph_dir: Path to .htmlgraph directory
25
+
26
+ Returns:
27
+ DocsMetadata instance (default if file doesn't exist)
28
+ """
29
+ import json
30
+
31
+ metadata_file = htmlgraph_dir / ".docs-metadata.json"
32
+ if metadata_file.exists():
33
+ data = json.loads(metadata_file.read_text())
34
+ return cls(**data)
35
+ return cls() # Default
36
+
37
+ def save(self, htmlgraph_dir: Path) -> None:
38
+ """Save metadata to .docs-metadata.json.
39
+
40
+ Args:
41
+ htmlgraph_dir: Path to .htmlgraph directory
42
+ """
43
+ metadata_file = htmlgraph_dir / ".docs-metadata.json"
44
+ metadata_file.write_text(self.model_dump_json(indent=2))
45
+
46
+ @classmethod
47
+ def create_initial(
48
+ cls, htmlgraph_dir: Path, schema_version: int = 2
49
+ ) -> "DocsMetadata":
50
+ """Create initial metadata file.
51
+
52
+ Args:
53
+ htmlgraph_dir: Path to .htmlgraph directory
54
+ schema_version: Documentation schema version
55
+
56
+ Returns:
57
+ New DocsMetadata instance
58
+ """
59
+ from htmlgraph import __version__
60
+
61
+ metadata = cls(
62
+ schema_version=schema_version,
63
+ base_version_on_last_update=__version__,
64
+ customizations=[],
65
+ )
66
+ metadata.save(htmlgraph_dir)
67
+ return metadata
68
+
69
+ def add_customization(self, section_name: str) -> None:
70
+ """Mark a section as customized.
71
+
72
+ Args:
73
+ section_name: Name of the customized section
74
+ """
75
+ if section_name not in self.customizations:
76
+ self.customizations.append(section_name)
77
+
78
+ def remove_customization(self, section_name: str) -> None:
79
+ """Remove a customization marker.
80
+
81
+ Args:
82
+ section_name: Name of the section to unmark
83
+ """
84
+ if section_name in self.customizations:
85
+ self.customizations.remove(section_name)
86
+
87
+ def has_customizations(self) -> bool:
88
+ """Check if any customizations exist.
89
+
90
+ Returns:
91
+ True if user has customized documentation
92
+ """
93
+ return len(self.customizations) > 0