docwright 0.1.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.
- ai_docgen/__init__.py +0 -0
- ai_docgen/analyzer.py +59 -0
- ai_docgen/built_in_templates/__init__.py +0 -0
- ai_docgen/built_in_templates/readme/__init__.py +0 -0
- ai_docgen/built_in_templates/readme/default.md.j2 +30 -0
- ai_docgen/built_in_templates/wiki/__init__.py +0 -0
- ai_docgen/built_in_templates/wiki/adr.md.j2 +27 -0
- ai_docgen/built_in_templates/wiki/api-contracts.md.j2 +20 -0
- ai_docgen/built_in_templates/wiki/architecture.md.j2 +25 -0
- ai_docgen/built_in_templates/wiki/data-model.md.j2 +22 -0
- ai_docgen/built_in_templates/wiki/db-schema.md.j2 +22 -0
- ai_docgen/built_in_templates/wiki/development-guide.md.j2 +22 -0
- ai_docgen/built_in_templates/wiki/integrations.md.j2 +22 -0
- ai_docgen/built_in_templates/wiki/operations.md.j2 +27 -0
- ai_docgen/built_in_templates/wiki/security.md.j2 +22 -0
- ai_docgen/built_in_templates/wiki/troubleshooting.md.j2 +22 -0
- ai_docgen/cli.py +136 -0
- ai_docgen/config.py +71 -0
- ai_docgen/engine.py +165 -0
- ai_docgen/outputs/__init__.py +0 -0
- ai_docgen/outputs/base.py +7 -0
- ai_docgen/outputs/direct.py +16 -0
- ai_docgen/outputs/factory.py +12 -0
- ai_docgen/outputs/pull_request.py +69 -0
- ai_docgen/providers/__init__.py +0 -0
- ai_docgen/providers/base.py +6 -0
- ai_docgen/providers/claude.py +22 -0
- ai_docgen/providers/factory.py +26 -0
- ai_docgen/providers/ollama.py +25 -0
- ai_docgen/providers/openai.py +20 -0
- ai_docgen/registry.py +125 -0
- ai_docgen/renderer.py +104 -0
- ai_docgen/reporters/__init__.py +0 -0
- ai_docgen/reporters/html.py +54 -0
- ai_docgen/reporters/terminal.py +31 -0
- ai_docgen/scaffolder.py +163 -0
- docwright-0.1.0.dist-info/METADATA +188 -0
- docwright-0.1.0.dist-info/RECORD +41 -0
- docwright-0.1.0.dist-info/WHEEL +4 -0
- docwright-0.1.0.dist-info/entry_points.txt +3 -0
- docwright-0.1.0.dist-info/licenses/LICENSE +21 -0
ai_docgen/__init__.py
ADDED
|
File without changes
|
ai_docgen/analyzer.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import fnmatch
|
|
4
|
+
import re
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class ChangedFile:
|
|
10
|
+
path: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DiffAnalyzer:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
diff_text: str,
|
|
17
|
+
trigger_paths: list[str],
|
|
18
|
+
ignore_paths: list[str],
|
|
19
|
+
) -> None:
|
|
20
|
+
self.diff_text = diff_text
|
|
21
|
+
self.trigger_paths = trigger_paths
|
|
22
|
+
self.ignore_paths = ignore_paths
|
|
23
|
+
|
|
24
|
+
def changed_files(self) -> list[ChangedFile]:
|
|
25
|
+
paths: list[str] = []
|
|
26
|
+
for line in self.diff_text.splitlines():
|
|
27
|
+
match = re.match(r"^\+\+\+ b/(.+)$", line)
|
|
28
|
+
if match:
|
|
29
|
+
paths.append(match.group(1))
|
|
30
|
+
return [ChangedFile(path=p) for p in paths]
|
|
31
|
+
|
|
32
|
+
def has_relevant_changes(self) -> bool:
|
|
33
|
+
for changed in self.changed_files():
|
|
34
|
+
if self.is_ignored(changed.path):
|
|
35
|
+
continue
|
|
36
|
+
if self.matches_trigger(changed.path):
|
|
37
|
+
return True
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def diff_summary(self) -> str:
|
|
41
|
+
files = self.changed_files()
|
|
42
|
+
if not files:
|
|
43
|
+
return "No changed files."
|
|
44
|
+
lines = ["Changed files:"]
|
|
45
|
+
for f in files:
|
|
46
|
+
lines.append(f" - {f.path}")
|
|
47
|
+
lines.append("")
|
|
48
|
+
lines.append(self.diff_text[:3000])
|
|
49
|
+
if len(self.diff_text) > 3000:
|
|
50
|
+
lines.append("... (truncated)")
|
|
51
|
+
return "\n".join(lines)
|
|
52
|
+
|
|
53
|
+
def is_ignored(self, path: str) -> bool:
|
|
54
|
+
return any(fnmatch.fnmatch(path, pattern) for pattern in self.ignore_paths)
|
|
55
|
+
|
|
56
|
+
def matches_trigger(self, path: str) -> bool:
|
|
57
|
+
if not self.trigger_paths:
|
|
58
|
+
return True
|
|
59
|
+
return any(fnmatch.fnmatch(path, pattern) for pattern in self.trigger_paths)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# {{ service_name }}
|
|
2
|
+
|
|
3
|
+
<!-- AUTO:overview -->
|
|
4
|
+
<!-- /AUTO:overview -->
|
|
5
|
+
|
|
6
|
+
## Getting Started
|
|
7
|
+
|
|
8
|
+
<!-- AUTO:getting_started -->
|
|
9
|
+
<!-- /AUTO:getting_started -->
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
<!-- AUTO:architecture -->
|
|
14
|
+
<!-- /AUTO:architecture -->
|
|
15
|
+
|
|
16
|
+
## API
|
|
17
|
+
|
|
18
|
+
<!-- AUTO:api -->
|
|
19
|
+
<!-- /AUTO:api -->
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
<!-- AUTO:development -->
|
|
24
|
+
<!-- /AUTO:development -->
|
|
25
|
+
|
|
26
|
+
<!-- MANUAL -->
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
Add contributing notes here.
|
|
30
|
+
<!-- /MANUAL -->
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Architecture Decision Records
|
|
2
|
+
|
|
3
|
+
## Recent Decisions
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:recent_decisions -->
|
|
6
|
+
<!-- /AUTO:recent_decisions -->
|
|
7
|
+
|
|
8
|
+
## Decision Index
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:decision_index -->
|
|
11
|
+
<!-- /AUTO:decision_index -->
|
|
12
|
+
|
|
13
|
+
<!-- MANUAL -->
|
|
14
|
+
## ADR Template
|
|
15
|
+
|
|
16
|
+
Use this format for new decisions:
|
|
17
|
+
|
|
18
|
+
### ADR-NNN: Title
|
|
19
|
+
|
|
20
|
+
**Status:** Proposed / Accepted / Deprecated / Superseded
|
|
21
|
+
|
|
22
|
+
**Context:** What prompted this decision.
|
|
23
|
+
|
|
24
|
+
**Decision:** What was decided.
|
|
25
|
+
|
|
26
|
+
**Consequences:** What changes as a result.
|
|
27
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# API Contracts
|
|
2
|
+
|
|
3
|
+
<!-- AUTO:endpoints -->
|
|
4
|
+
<!-- /AUTO:endpoints -->
|
|
5
|
+
|
|
6
|
+
## Authentication
|
|
7
|
+
|
|
8
|
+
<!-- AUTO:authentication -->
|
|
9
|
+
<!-- /AUTO:authentication -->
|
|
10
|
+
|
|
11
|
+
## Error Codes
|
|
12
|
+
|
|
13
|
+
<!-- AUTO:error_codes -->
|
|
14
|
+
<!-- /AUTO:error_codes -->
|
|
15
|
+
|
|
16
|
+
<!-- MANUAL -->
|
|
17
|
+
## Changelog
|
|
18
|
+
|
|
19
|
+
Document breaking changes here.
|
|
20
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
<!-- AUTO:overview -->
|
|
4
|
+
<!-- /AUTO:overview -->
|
|
5
|
+
|
|
6
|
+
## Components
|
|
7
|
+
|
|
8
|
+
<!-- AUTO:components -->
|
|
9
|
+
<!-- /AUTO:components -->
|
|
10
|
+
|
|
11
|
+
## Data Flow
|
|
12
|
+
|
|
13
|
+
<!-- AUTO:data_flow -->
|
|
14
|
+
<!-- /AUTO:data_flow -->
|
|
15
|
+
|
|
16
|
+
## Dependencies
|
|
17
|
+
|
|
18
|
+
<!-- AUTO:dependencies -->
|
|
19
|
+
<!-- /AUTO:dependencies -->
|
|
20
|
+
|
|
21
|
+
<!-- MANUAL -->
|
|
22
|
+
## Decision Log
|
|
23
|
+
|
|
24
|
+
Document architectural decisions here.
|
|
25
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Data Model
|
|
2
|
+
|
|
3
|
+
## Domain Entities
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:entities -->
|
|
6
|
+
<!-- /AUTO:entities -->
|
|
7
|
+
|
|
8
|
+
## Business Rules
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:business_rules -->
|
|
11
|
+
<!-- /AUTO:business_rules -->
|
|
12
|
+
|
|
13
|
+
## Relationships
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:relationships -->
|
|
16
|
+
<!-- /AUTO:relationships -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Glossary
|
|
20
|
+
|
|
21
|
+
Define domain terms here.
|
|
22
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Database Schema
|
|
2
|
+
|
|
3
|
+
## Tables
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:tables -->
|
|
6
|
+
<!-- /AUTO:tables -->
|
|
7
|
+
|
|
8
|
+
## Indexes
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:indexes -->
|
|
11
|
+
<!-- /AUTO:indexes -->
|
|
12
|
+
|
|
13
|
+
## Migrations
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:migrations -->
|
|
16
|
+
<!-- /AUTO:migrations -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Migration Runbook
|
|
20
|
+
|
|
21
|
+
Document manual migration steps and rollback procedures here.
|
|
22
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Development Guide
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:setup -->
|
|
6
|
+
<!-- /AUTO:setup -->
|
|
7
|
+
|
|
8
|
+
## Running Tests
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:testing -->
|
|
11
|
+
<!-- /AUTO:testing -->
|
|
12
|
+
|
|
13
|
+
## Code Style
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:code_style -->
|
|
16
|
+
<!-- /AUTO:code_style -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Team Conventions
|
|
20
|
+
|
|
21
|
+
Add team-specific conventions here.
|
|
22
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Integrations
|
|
2
|
+
|
|
3
|
+
## External Services
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:external_services -->
|
|
6
|
+
<!-- /AUTO:external_services -->
|
|
7
|
+
|
|
8
|
+
## Authentication & Credentials
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:auth_credentials -->
|
|
11
|
+
<!-- /AUTO:auth_credentials -->
|
|
12
|
+
|
|
13
|
+
## Data Exchange
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:data_exchange -->
|
|
16
|
+
<!-- /AUTO:data_exchange -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Integration Contacts
|
|
20
|
+
|
|
21
|
+
Document vendor contacts and SLA details here.
|
|
22
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Operations
|
|
2
|
+
|
|
3
|
+
## Deployment
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:deployment -->
|
|
6
|
+
<!-- /AUTO:deployment -->
|
|
7
|
+
|
|
8
|
+
## Monitoring
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:monitoring -->
|
|
11
|
+
<!-- /AUTO:monitoring -->
|
|
12
|
+
|
|
13
|
+
## Runbooks
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:runbooks -->
|
|
16
|
+
<!-- /AUTO:runbooks -->
|
|
17
|
+
|
|
18
|
+
## Incident Response
|
|
19
|
+
|
|
20
|
+
<!-- AUTO:incident_response -->
|
|
21
|
+
<!-- /AUTO:incident_response -->
|
|
22
|
+
|
|
23
|
+
<!-- MANUAL -->
|
|
24
|
+
## On-Call Contacts
|
|
25
|
+
|
|
26
|
+
Document on-call rotation and escalation paths here.
|
|
27
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Access Model
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:access_model -->
|
|
6
|
+
<!-- /AUTO:access_model -->
|
|
7
|
+
|
|
8
|
+
## Sensitive Data Handling
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:sensitive_data -->
|
|
11
|
+
<!-- /AUTO:sensitive_data -->
|
|
12
|
+
|
|
13
|
+
## Security Requirements
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:requirements -->
|
|
16
|
+
<!-- /AUTO:requirements -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Security Contacts
|
|
20
|
+
|
|
21
|
+
Document responsible disclosure process and security team contacts here.
|
|
22
|
+
<!-- /MANUAL -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Common Issues
|
|
4
|
+
|
|
5
|
+
<!-- AUTO:common_issues -->
|
|
6
|
+
<!-- /AUTO:common_issues -->
|
|
7
|
+
|
|
8
|
+
## Diagnostics
|
|
9
|
+
|
|
10
|
+
<!-- AUTO:diagnostics -->
|
|
11
|
+
<!-- /AUTO:diagnostics -->
|
|
12
|
+
|
|
13
|
+
## Known Limitations
|
|
14
|
+
|
|
15
|
+
<!-- AUTO:known_limitations -->
|
|
16
|
+
<!-- /AUTO:known_limitations -->
|
|
17
|
+
|
|
18
|
+
<!-- MANUAL -->
|
|
19
|
+
## Escalation Path
|
|
20
|
+
|
|
21
|
+
Document when and how to escalate unresolved issues here.
|
|
22
|
+
<!-- /MANUAL -->
|
ai_docgen/cli.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ai_docgen.config import Config
|
|
11
|
+
from ai_docgen.engine import DocsEngine
|
|
12
|
+
from ai_docgen.outputs.factory import build_output
|
|
13
|
+
from ai_docgen.providers.factory import build_provider
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_repo_root() -> Path:
|
|
17
|
+
return Path.cwd()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_engine(repo_root: Path) -> DocsEngine:
|
|
21
|
+
config = Config.load(repo_root)
|
|
22
|
+
provider = build_provider(config.provider)
|
|
23
|
+
output = build_output(config.output, repo_root)
|
|
24
|
+
return DocsEngine(repo_root=repo_root, provider=provider, output=output)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@click.group()
|
|
28
|
+
def cli() -> None:
|
|
29
|
+
"""AI-powered documentation agent."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@cli.command()
|
|
33
|
+
def init() -> None:
|
|
34
|
+
"""Generate documentation from scratch."""
|
|
35
|
+
engine = build_engine(get_repo_root())
|
|
36
|
+
asyncio.run(engine.init())
|
|
37
|
+
click.echo("Documentation initialized.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@cli.command()
|
|
41
|
+
@click.option("--dry-run", is_flag=True, help="Show what would change without writing files.")
|
|
42
|
+
def run(dry_run: bool) -> None:
|
|
43
|
+
"""Incrementally update documentation based on recent git diff."""
|
|
44
|
+
repo_root = get_repo_root()
|
|
45
|
+
base_sha = os.environ.get("AI_DOCGEN_BASE_SHA", "HEAD~1")
|
|
46
|
+
try:
|
|
47
|
+
diff_text = subprocess.check_output(
|
|
48
|
+
["git", "diff", f"{base_sha}..HEAD"], cwd=repo_root
|
|
49
|
+
).decode()
|
|
50
|
+
except subprocess.CalledProcessError:
|
|
51
|
+
diff_text = ""
|
|
52
|
+
if dry_run:
|
|
53
|
+
config = Config.load(repo_root)
|
|
54
|
+
triggers = config.triggers
|
|
55
|
+
from ai_docgen.analyzer import DiffAnalyzer
|
|
56
|
+
|
|
57
|
+
analyzer = DiffAnalyzer(
|
|
58
|
+
diff_text=diff_text,
|
|
59
|
+
trigger_paths=triggers.paths if triggers else [],
|
|
60
|
+
ignore_paths=triggers.ignore if triggers else [],
|
|
61
|
+
)
|
|
62
|
+
if analyzer.has_relevant_changes():
|
|
63
|
+
click.echo("Relevant changes detected — documentation would be updated.")
|
|
64
|
+
else:
|
|
65
|
+
click.echo("No relevant changes — documentation would be skipped.")
|
|
66
|
+
return
|
|
67
|
+
engine = build_engine(repo_root)
|
|
68
|
+
skipped = asyncio.run(engine.run(diff_text=diff_text))
|
|
69
|
+
click.echo(
|
|
70
|
+
"No relevant changes — documentation up to date." if skipped else "Documentation updated."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@cli.command()
|
|
75
|
+
def sync() -> None:
|
|
76
|
+
"""Force re-sync all documentation against current templates."""
|
|
77
|
+
engine = build_engine(get_repo_root())
|
|
78
|
+
asyncio.run(engine.sync())
|
|
79
|
+
click.echo("Documentation synced.")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@cli.command("install")
|
|
83
|
+
@click.option("--auto", is_flag=True, help="Non-interactive mode with auto-detected defaults.")
|
|
84
|
+
@click.option("--provider", default=None, type=click.Choice(["claude", "openai", "ollama"]))
|
|
85
|
+
@click.option("--output", "output_mode", default=None, type=click.Choice(["pr", "direct"]))
|
|
86
|
+
def install(auto: bool, provider: str | None, output_mode: str | None) -> None:
|
|
87
|
+
"""Bootstrap this repo with ai-docgen configuration."""
|
|
88
|
+
from ai_docgen.scaffolder import Scaffolder
|
|
89
|
+
|
|
90
|
+
repo_root = get_repo_root()
|
|
91
|
+
scaffolder = Scaffolder(repo_root=repo_root)
|
|
92
|
+
profile = scaffolder.detect_profile()
|
|
93
|
+
|
|
94
|
+
if auto:
|
|
95
|
+
final_provider = provider or "claude"
|
|
96
|
+
final_output = output_mode or "pr"
|
|
97
|
+
else:
|
|
98
|
+
click.echo(
|
|
99
|
+
f"Detected: {profile.language} project '{profile.service_name}', CI: {profile.ci}"
|
|
100
|
+
)
|
|
101
|
+
final_provider = provider or click.prompt(
|
|
102
|
+
"LLM provider",
|
|
103
|
+
type=click.Choice(["claude", "openai", "ollama"]),
|
|
104
|
+
default="claude",
|
|
105
|
+
)
|
|
106
|
+
final_output = output_mode or click.prompt(
|
|
107
|
+
"Output mode",
|
|
108
|
+
type=click.Choice(["pr", "direct"]),
|
|
109
|
+
default="pr",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
scaffolder.generate(profile, provider_type=final_provider, output_mode=final_output)
|
|
113
|
+
click.echo(f"Installed ai-docgen for '{profile.service_name}'.")
|
|
114
|
+
click.echo("Next: set your API key env var, then run 'make docs'.")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@cli.command()
|
|
118
|
+
@click.option("--registry", "registry_path", default=None, help="Path to registry.yml")
|
|
119
|
+
def dashboard(registry_path: str | None) -> None:
|
|
120
|
+
"""Show status of all registered projects."""
|
|
121
|
+
from ai_docgen.reporters.terminal import render_dashboard
|
|
122
|
+
|
|
123
|
+
path = Path(registry_path) if registry_path else Path.cwd() / ".ai-docgen" / "registry.yml"
|
|
124
|
+
render_dashboard(path)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@cli.command()
|
|
128
|
+
@click.option("--registry", "registry_path", default=None, help="Path to registry.yml")
|
|
129
|
+
@click.option("--output", "output_file", default="ai-docgen-report.html", help="Output HTML file")
|
|
130
|
+
def report(registry_path: str | None, output_file: str) -> None:
|
|
131
|
+
"""Generate a static HTML status report."""
|
|
132
|
+
from ai_docgen.reporters.html import render_html_report
|
|
133
|
+
|
|
134
|
+
reg_path = Path(registry_path) if registry_path else Path.cwd() / ".ai-docgen" / "registry.yml"
|
|
135
|
+
render_html_report(reg_path, Path(output_file))
|
|
136
|
+
click.echo(f"Report saved to {output_file}")
|
ai_docgen/config.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
CONFIG_DIR = ".ai-docgen"
|
|
10
|
+
CONFIG_FILE = "ai-docgen.yml"
|
|
11
|
+
INITIALIZED_MARKER = ".initialized"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProviderConfig(BaseModel):
|
|
15
|
+
type: Literal["claude", "openai", "ollama"]
|
|
16
|
+
model: str
|
|
17
|
+
api_key_env: str = "ANTHROPIC_API_KEY"
|
|
18
|
+
base_url: str | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OutputConfig(BaseModel):
|
|
22
|
+
mode: Literal["direct", "pr"] = "pr"
|
|
23
|
+
pr_title: str = "docs: auto-update documentation"
|
|
24
|
+
branch_prefix: str = "docs/auto-"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TemplatesConfig(BaseModel):
|
|
28
|
+
source: Literal["builtin", "local"] = "builtin"
|
|
29
|
+
local_path: str = ".ai-docgen/templates"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TriggersConfig(BaseModel):
|
|
33
|
+
paths: list[str] = Field(default_factory=lambda: ["app/**", "src/**"])
|
|
34
|
+
ignore: list[str] = Field(default_factory=lambda: ["tests/**", "**/*.md"])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DocumentConfig(BaseModel):
|
|
38
|
+
type: Literal["readme", "wiki"]
|
|
39
|
+
template: str
|
|
40
|
+
target: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class RegistryConfig(BaseModel):
|
|
44
|
+
path: str = "../.ai-docgen/registry.yml"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Config(BaseModel):
|
|
48
|
+
provider: ProviderConfig
|
|
49
|
+
output: OutputConfig = Field(default_factory=OutputConfig)
|
|
50
|
+
templates: TemplatesConfig = Field(default_factory=TemplatesConfig)
|
|
51
|
+
triggers: TriggersConfig | None = None
|
|
52
|
+
documents: list[DocumentConfig] = Field(default_factory=list)
|
|
53
|
+
registry: RegistryConfig = Field(default_factory=RegistryConfig)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def load(cls, repo_root: Path) -> Config:
|
|
57
|
+
config_path = repo_root / CONFIG_DIR / CONFIG_FILE
|
|
58
|
+
if not config_path.exists():
|
|
59
|
+
raise FileNotFoundError(f"Config not found: {config_path}")
|
|
60
|
+
data = yaml.safe_load(config_path.read_text())
|
|
61
|
+
return cls.model_validate(data)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def is_initialized(cls, repo_root: Path) -> bool:
|
|
65
|
+
return (repo_root / CONFIG_DIR / INITIALIZED_MARKER).exists()
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def mark_initialized(cls, repo_root: Path) -> None:
|
|
69
|
+
marker = repo_root / CONFIG_DIR / INITIALIZED_MARKER
|
|
70
|
+
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
marker.touch()
|