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.
- htmlgraph/__init__.py +1 -1
- htmlgraph/agent_detection.py +41 -2
- htmlgraph/analytics/cli.py +86 -20
- htmlgraph/cli.py +519 -87
- htmlgraph/collections/base.py +68 -4
- 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/git_events.py +61 -7
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +61 -0
- htmlgraph/operations/analytics.py +338 -0
- htmlgraph/operations/events.py +243 -0
- htmlgraph/operations/hooks.py +349 -0
- htmlgraph/operations/server.py +302 -0
- htmlgraph/orchestration/__init__.py +39 -0
- htmlgraph/orchestration/headless_spawner.py +566 -0
- htmlgraph/orchestration/model_selection.py +323 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +47 -0
- htmlgraph/parser.py +56 -1
- htmlgraph/sdk.py +529 -7
- htmlgraph/server.py +153 -60
- {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/METADATA +3 -1
- {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/RECORD +40 -19
- /htmlgraph/{orchestration.py → orchestration/task_coordination.py} +0 -0
- {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.21.0.data → htmlgraph-0.23.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.21.0.dist-info → htmlgraph-0.23.0.dist-info}/WHEEL +0 -0
- {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.
|
|
119
|
+
from htmlgraph.operations import start_server
|
|
115
120
|
|
|
116
|
-
|
|
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
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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.
|
|
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
|
-
|
|
2202
|
-
index = AnalyticsIndex(db_path)
|
|
2445
|
+
result = rebuild_index(graph_dir=graph_dir)
|
|
2203
2446
|
|
|
2204
|
-
|
|
2205
|
-
result
|
|
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)
|