code2docs 3.0.12__tar.gz → 3.0.14__tar.gz
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.
- {code2docs-3.0.12 → code2docs-3.0.14}/PKG-INFO +3 -3
- {code2docs-3.0.12 → code2docs-3.0.14}/README.md +2 -2
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/__init__.py +1 -1
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/analyzers/dependency_scanner.py +130 -2
- code2docs-3.0.14/code2docs/generators/contributing_gen.py +231 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/getting_started_gen.py +38 -9
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/readme_gen.py +17 -3
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/PKG-INFO +3 -3
- {code2docs-3.0.12 → code2docs-3.0.14}/pyproject.toml +1 -1
- code2docs-3.0.12/code2docs/generators/contributing_gen.py +0 -137
- {code2docs-3.0.12 → code2docs-3.0.14}/LICENSE +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/__main__.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/analyzers/__init__.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/analyzers/docstring_extractor.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/analyzers/endpoint_detector.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/analyzers/project_scanner.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/base.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/cli.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/config.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/examples/advanced_usage.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/examples/quickstart.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/formatters/__init__.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/formatters/badges.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/formatters/markdown.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/formatters/toc.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/__init__.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/_registry_adapters.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/_source_links.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/api_changelog_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/api_reference_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/architecture_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/changelog_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/code2llm_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/config_docs_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/coverage_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/depgraph_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/examples_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/mkdocs_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/module_docs_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/generators/org_readme_gen.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/llm_helper.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/registry.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/sync/__init__.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/sync/differ.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/sync/updater.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/sync/watcher.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/api_module.md.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/architecture.md.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/example_usage.py.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/index.md.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/module_doc.md.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs/templates/readme.md.j2 +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/SOURCES.txt +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/dependency_links.txt +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/entry_points.txt +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/requires.txt +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/code2docs.egg-info/top_level.txt +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/setup.cfg +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_analyzers.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_cli.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_code2docs.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_config.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_formatters.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_generators.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_llm_helper.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_registry.py +0 -0
- {code2docs-3.0.12 → code2docs-3.0.14}/tests/test_sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2docs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.14
|
|
4
4
|
Summary: Auto-generate and sync project documentation from source code analysis
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|
|
|
51
51
|
# code2docs
|
|
52
52
|
|
|
53
|
-
  
|
|
54
54
|
|
|
55
55
|
> Auto-generate and sync project documentation from source code analysis.
|
|
56
56
|
|
|
@@ -190,7 +190,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
190
190
|
```markdown
|
|
191
191
|
<!-- code2docs:start --># code2docs
|
|
192
192
|
|
|
193
|
-
   
|
|
194
194
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
195
195
|
|
|
196
196
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# code2docs
|
|
2
2
|
|
|
3
|
-
  
|
|
4
4
|
|
|
5
5
|
> Auto-generate and sync project documentation from source code analysis.
|
|
6
6
|
|
|
@@ -140,7 +140,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
140
140
|
```markdown
|
|
141
141
|
<!-- code2docs:start --># code2docs
|
|
142
142
|
|
|
143
|
-
   
|
|
144
144
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
145
145
|
|
|
146
146
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -5,7 +5,7 @@ Uses code2llm's AnalysisResult to produce human-readable documentation:
|
|
|
5
5
|
README.md, API references, module docs, examples, and architecture diagrams.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "3.0.
|
|
8
|
+
__version__ = "3.0.14"
|
|
9
9
|
__author__ = "Tom Sapletta"
|
|
10
10
|
|
|
11
11
|
from .config import Code2DocsConfig
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Scan project dependencies from requirements.txt, pyproject.toml, setup.py."""
|
|
1
|
+
"""Scan project dependencies from requirements.txt, pyproject.toml, setup.py, package.json, Cargo.toml, go.mod."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from dataclasses import dataclass, field
|
|
@@ -26,7 +26,9 @@ class DependencyInfo:
|
|
|
26
26
|
@dataclass
|
|
27
27
|
class ProjectDependencies:
|
|
28
28
|
"""All detected project dependencies."""
|
|
29
|
+
language: str = "python"
|
|
29
30
|
python_version: str = ""
|
|
31
|
+
runtime_version: str = ""
|
|
30
32
|
dependencies: List[DependencyInfo] = field(default_factory=list)
|
|
31
33
|
dev_dependencies: List[DependencyInfo] = field(default_factory=list)
|
|
32
34
|
optional_groups: Dict[str, List[DependencyInfo]] = field(default_factory=dict)
|
|
@@ -47,7 +49,31 @@ class DependencyScanner:
|
|
|
47
49
|
project = Path(project_path)
|
|
48
50
|
deps = ProjectDependencies()
|
|
49
51
|
|
|
50
|
-
#
|
|
52
|
+
# Detect language from dependency files
|
|
53
|
+
# Priority: pyproject.toml > setup.py > requirements.txt (Python)
|
|
54
|
+
# package.json (JS/TS) > Cargo.toml (Rust) > go.mod (Go)
|
|
55
|
+
package_json = project / "package.json"
|
|
56
|
+
if package_json.exists():
|
|
57
|
+
deps = self._parse_package_json(package_json)
|
|
58
|
+
deps.source_file = "package.json"
|
|
59
|
+
# Detect TypeScript
|
|
60
|
+
tsconfig = project / "tsconfig.json"
|
|
61
|
+
if tsconfig.exists():
|
|
62
|
+
deps.language = "typescript"
|
|
63
|
+
return deps
|
|
64
|
+
|
|
65
|
+
cargo_toml = project / "Cargo.toml"
|
|
66
|
+
if cargo_toml.exists():
|
|
67
|
+
deps = self._parse_cargo_toml(cargo_toml)
|
|
68
|
+
deps.source_file = "Cargo.toml"
|
|
69
|
+
return deps
|
|
70
|
+
|
|
71
|
+
go_mod = project / "go.mod"
|
|
72
|
+
if go_mod.exists():
|
|
73
|
+
deps = self._parse_go_mod(go_mod)
|
|
74
|
+
deps.source_file = "go.mod"
|
|
75
|
+
return deps
|
|
76
|
+
|
|
51
77
|
pyproject = project / "pyproject.toml"
|
|
52
78
|
if pyproject.exists():
|
|
53
79
|
deps = self._parse_pyproject(pyproject)
|
|
@@ -159,6 +185,108 @@ class DependencyScanner:
|
|
|
159
185
|
deps.install_command = "pip install -r requirements.txt"
|
|
160
186
|
return deps
|
|
161
187
|
|
|
188
|
+
def _parse_package_json(self, path: Path) -> ProjectDependencies:
|
|
189
|
+
"""Parse package.json for dependencies."""
|
|
190
|
+
import json
|
|
191
|
+
deps = ProjectDependencies(language="javascript")
|
|
192
|
+
try:
|
|
193
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
194
|
+
except (json.JSONDecodeError, OSError):
|
|
195
|
+
return deps
|
|
196
|
+
|
|
197
|
+
deps.version = data.get("version", "")
|
|
198
|
+
name = data.get("name", "")
|
|
199
|
+
|
|
200
|
+
# Node engine version
|
|
201
|
+
engines = data.get("engines", {})
|
|
202
|
+
deps.runtime_version = engines.get("node", "")
|
|
203
|
+
|
|
204
|
+
for dep_name, ver in data.get("dependencies", {}).items():
|
|
205
|
+
deps.dependencies.append(DependencyInfo(name=dep_name, version_spec=ver))
|
|
206
|
+
|
|
207
|
+
for dep_name, ver in data.get("devDependencies", {}).items():
|
|
208
|
+
deps.dev_dependencies.append(DependencyInfo(name=dep_name, version_spec=ver, group="dev"))
|
|
209
|
+
|
|
210
|
+
# Detect install command
|
|
211
|
+
lock_yarn = path.parent / "yarn.lock"
|
|
212
|
+
lock_pnpm = path.parent / "pnpm-lock.yaml"
|
|
213
|
+
if lock_pnpm.exists():
|
|
214
|
+
deps.install_command = "pnpm install"
|
|
215
|
+
elif lock_yarn.exists():
|
|
216
|
+
deps.install_command = "yarn install"
|
|
217
|
+
else:
|
|
218
|
+
deps.install_command = "npm install"
|
|
219
|
+
|
|
220
|
+
if name:
|
|
221
|
+
deps.keywords = data.get("keywords", [])
|
|
222
|
+
|
|
223
|
+
return deps
|
|
224
|
+
|
|
225
|
+
def _parse_cargo_toml(self, path: Path) -> ProjectDependencies:
|
|
226
|
+
"""Parse Cargo.toml for Rust dependencies."""
|
|
227
|
+
deps = ProjectDependencies(language="rust")
|
|
228
|
+
content = path.read_text(encoding="utf-8")
|
|
229
|
+
|
|
230
|
+
# Version
|
|
231
|
+
ver_match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
|
|
232
|
+
if ver_match:
|
|
233
|
+
deps.version = ver_match.group(1)
|
|
234
|
+
|
|
235
|
+
# Dependencies section
|
|
236
|
+
in_deps = False
|
|
237
|
+
in_dev_deps = False
|
|
238
|
+
for line in content.splitlines():
|
|
239
|
+
stripped = line.strip()
|
|
240
|
+
if stripped == "[dependencies]":
|
|
241
|
+
in_deps, in_dev_deps = True, False
|
|
242
|
+
continue
|
|
243
|
+
elif stripped == "[dev-dependencies]":
|
|
244
|
+
in_deps, in_dev_deps = False, True
|
|
245
|
+
continue
|
|
246
|
+
elif stripped.startswith("["):
|
|
247
|
+
in_deps, in_dev_deps = False, False
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
dep_match = re.match(r'^([a-zA-Z0-9_-]+)\s*=\s*"?([^"\s]+)"?', stripped)
|
|
251
|
+
if dep_match:
|
|
252
|
+
info = DependencyInfo(name=dep_match.group(1), version_spec=dep_match.group(2))
|
|
253
|
+
if in_dev_deps:
|
|
254
|
+
info.group = "dev"
|
|
255
|
+
deps.dev_dependencies.append(info)
|
|
256
|
+
elif in_deps:
|
|
257
|
+
deps.dependencies.append(info)
|
|
258
|
+
|
|
259
|
+
deps.install_command = "cargo build"
|
|
260
|
+
return deps
|
|
261
|
+
|
|
262
|
+
def _parse_go_mod(self, path: Path) -> ProjectDependencies:
|
|
263
|
+
"""Parse go.mod for Go dependencies."""
|
|
264
|
+
deps = ProjectDependencies(language="go")
|
|
265
|
+
content = path.read_text(encoding="utf-8")
|
|
266
|
+
|
|
267
|
+
# Go version
|
|
268
|
+
go_ver = re.search(r'^go\s+(\S+)', content, re.MULTILINE)
|
|
269
|
+
if go_ver:
|
|
270
|
+
deps.runtime_version = go_ver.group(1)
|
|
271
|
+
|
|
272
|
+
# Require block
|
|
273
|
+
require_block = re.search(r'require\s*\((.*?)\)', content, re.DOTALL)
|
|
274
|
+
if require_block:
|
|
275
|
+
for line in require_block.group(1).splitlines():
|
|
276
|
+
line = line.strip()
|
|
277
|
+
if not line or line.startswith("//"):
|
|
278
|
+
continue
|
|
279
|
+
parts = line.split()
|
|
280
|
+
if len(parts) >= 2:
|
|
281
|
+
deps.dependencies.append(DependencyInfo(name=parts[0], version_spec=parts[1]))
|
|
282
|
+
|
|
283
|
+
# Single-line requires
|
|
284
|
+
for match in re.finditer(r'^require\s+(\S+)\s+(\S+)', content, re.MULTILINE):
|
|
285
|
+
deps.dependencies.append(DependencyInfo(name=match.group(1), version_spec=match.group(2)))
|
|
286
|
+
|
|
287
|
+
deps.install_command = "go mod download"
|
|
288
|
+
return deps
|
|
289
|
+
|
|
162
290
|
@staticmethod
|
|
163
291
|
def _parse_dep_string(dep_str: str) -> DependencyInfo:
|
|
164
292
|
"""Parse a dependency string like 'package>=1.0'."""
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""CONTRIBUTING.md generator from project tooling detection."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
from code2llm.api import AnalysisResult
|
|
6
|
+
|
|
7
|
+
from ..config import Code2DocsConfig
|
|
8
|
+
from ..analyzers.dependency_scanner import DependencyScanner
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ContributingGenerator:
|
|
12
|
+
"""Generate CONTRIBUTING.md by detecting dev tools from pyproject.toml."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, config: Code2DocsConfig, result: AnalysisResult):
|
|
15
|
+
self.config = config
|
|
16
|
+
self.result = result
|
|
17
|
+
|
|
18
|
+
def generate(self) -> str:
|
|
19
|
+
"""Generate CONTRIBUTING.md content."""
|
|
20
|
+
project = self.config.project_name or "Project"
|
|
21
|
+
tools = self._detect_dev_tools()
|
|
22
|
+
lines = [
|
|
23
|
+
f"# Contributing to {project}\n",
|
|
24
|
+
self._render_setup(tools),
|
|
25
|
+
"",
|
|
26
|
+
self._render_development(tools),
|
|
27
|
+
"",
|
|
28
|
+
self._render_testing(tools),
|
|
29
|
+
"",
|
|
30
|
+
self._render_code_style(tools),
|
|
31
|
+
"",
|
|
32
|
+
self._render_pull_request(),
|
|
33
|
+
"",
|
|
34
|
+
]
|
|
35
|
+
return "\n".join(lines)
|
|
36
|
+
|
|
37
|
+
def _detect_dev_tools(self) -> Dict[str, bool]:
|
|
38
|
+
"""Detect which dev tools are configured in the project."""
|
|
39
|
+
scanner = DependencyScanner()
|
|
40
|
+
deps = scanner.scan(self.result.project_path)
|
|
41
|
+
all_deps = {d.name.lower() for d in deps.dependencies}
|
|
42
|
+
all_deps |= {d.name.lower() for d in deps.dev_dependencies}
|
|
43
|
+
return {
|
|
44
|
+
"language": deps.language,
|
|
45
|
+
"install_command": deps.install_command,
|
|
46
|
+
# Python tools
|
|
47
|
+
"pytest": "pytest" in all_deps,
|
|
48
|
+
"black": "black" in all_deps,
|
|
49
|
+
"ruff": "ruff" in all_deps,
|
|
50
|
+
"mypy": "mypy" in all_deps,
|
|
51
|
+
"flake8": "flake8" in all_deps,
|
|
52
|
+
"isort": "isort" in all_deps,
|
|
53
|
+
"pre-commit": "pre-commit" in all_deps,
|
|
54
|
+
"tox": "tox" in all_deps,
|
|
55
|
+
# JS/TS tools
|
|
56
|
+
"eslint": "eslint" in all_deps,
|
|
57
|
+
"prettier": "prettier" in all_deps,
|
|
58
|
+
"jest": "jest" in all_deps,
|
|
59
|
+
"vitest": "vitest" in all_deps,
|
|
60
|
+
"typescript": "typescript" in all_deps,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def _render_setup(self, tools: Dict[str, bool]) -> str:
|
|
64
|
+
"""Render development setup instructions."""
|
|
65
|
+
project = self.config.project_name or "project"
|
|
66
|
+
repo_url = self.config.repo_url or "<repository-url>"
|
|
67
|
+
lang = tools.get("language", "python")
|
|
68
|
+
install_cmd = tools.get("install_command", "pip install -e .")
|
|
69
|
+
|
|
70
|
+
lines = [
|
|
71
|
+
"## Development Setup\n",
|
|
72
|
+
"```bash",
|
|
73
|
+
f"git clone {repo_url}",
|
|
74
|
+
f"cd {project}",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
if lang in ("javascript", "typescript"):
|
|
78
|
+
lines.append(install_cmd)
|
|
79
|
+
elif lang == "rust":
|
|
80
|
+
lines.append("cargo build")
|
|
81
|
+
elif lang == "go":
|
|
82
|
+
lines.append("go mod download")
|
|
83
|
+
lines.append("go build ./...")
|
|
84
|
+
else:
|
|
85
|
+
lines.extend([
|
|
86
|
+
"python -m venv .venv",
|
|
87
|
+
"source .venv/bin/activate # or .venv\\Scripts\\activate on Windows",
|
|
88
|
+
'pip install -e ".[dev]"',
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
lines.append("```")
|
|
92
|
+
return "\n".join(lines)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _render_development(tools: Dict[str, bool]) -> str:
|
|
96
|
+
"""Render development workflow."""
|
|
97
|
+
lines = [
|
|
98
|
+
"## Development Workflow\n",
|
|
99
|
+
"1. Create a feature branch from `main`",
|
|
100
|
+
"2. Make your changes",
|
|
101
|
+
"3. Add or update tests",
|
|
102
|
+
"4. Run the test suite",
|
|
103
|
+
"5. Submit a pull request",
|
|
104
|
+
]
|
|
105
|
+
return "\n".join(lines)
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _render_testing(tools: Dict[str, bool]) -> str:
|
|
109
|
+
"""Render testing instructions."""
|
|
110
|
+
lang = tools.get("language", "python")
|
|
111
|
+
lines = ["## Testing\n"]
|
|
112
|
+
|
|
113
|
+
if lang in ("javascript", "typescript"):
|
|
114
|
+
if tools.get("vitest"):
|
|
115
|
+
lines.extend([
|
|
116
|
+
"```bash",
|
|
117
|
+
"# Run all tests",
|
|
118
|
+
"npx vitest",
|
|
119
|
+
"",
|
|
120
|
+
"# Run with coverage",
|
|
121
|
+
"npx vitest --coverage",
|
|
122
|
+
"",
|
|
123
|
+
"# Watch mode",
|
|
124
|
+
"npx vitest --watch",
|
|
125
|
+
"```",
|
|
126
|
+
])
|
|
127
|
+
elif tools.get("jest"):
|
|
128
|
+
lines.extend([
|
|
129
|
+
"```bash",
|
|
130
|
+
"# Run all tests",
|
|
131
|
+
"npx jest",
|
|
132
|
+
"",
|
|
133
|
+
"# Run with coverage",
|
|
134
|
+
"npx jest --coverage",
|
|
135
|
+
"```",
|
|
136
|
+
])
|
|
137
|
+
else:
|
|
138
|
+
lines.extend([
|
|
139
|
+
"```bash",
|
|
140
|
+
"npm test",
|
|
141
|
+
"```",
|
|
142
|
+
])
|
|
143
|
+
elif lang == "rust":
|
|
144
|
+
lines.extend([
|
|
145
|
+
"```bash",
|
|
146
|
+
"# Run all tests",
|
|
147
|
+
"cargo test",
|
|
148
|
+
"",
|
|
149
|
+
"# Run a specific test",
|
|
150
|
+
"cargo test test_name",
|
|
151
|
+
"```",
|
|
152
|
+
])
|
|
153
|
+
elif lang == "go":
|
|
154
|
+
lines.extend([
|
|
155
|
+
"```bash",
|
|
156
|
+
"# Run all tests",
|
|
157
|
+
"go test ./...",
|
|
158
|
+
"",
|
|
159
|
+
"# Run with coverage",
|
|
160
|
+
"go test -cover ./...",
|
|
161
|
+
"```",
|
|
162
|
+
])
|
|
163
|
+
elif tools.get("pytest"):
|
|
164
|
+
lines.extend([
|
|
165
|
+
"```bash",
|
|
166
|
+
"# Run all tests",
|
|
167
|
+
"pytest",
|
|
168
|
+
"",
|
|
169
|
+
"# Run with coverage",
|
|
170
|
+
"pytest --cov --cov-report=term-missing",
|
|
171
|
+
"",
|
|
172
|
+
"# Run a specific test file",
|
|
173
|
+
"pytest tests/test_specific.py -v",
|
|
174
|
+
"```",
|
|
175
|
+
])
|
|
176
|
+
else:
|
|
177
|
+
lines.extend([
|
|
178
|
+
"```bash",
|
|
179
|
+
"python -m unittest discover tests/",
|
|
180
|
+
"```",
|
|
181
|
+
])
|
|
182
|
+
return "\n".join(lines)
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def _render_code_style(tools: Dict[str, bool]) -> str:
|
|
186
|
+
"""Render code style guidelines."""
|
|
187
|
+
lang = tools.get("language", "python")
|
|
188
|
+
lines = ["## Code Style\n"]
|
|
189
|
+
|
|
190
|
+
if lang in ("javascript", "typescript"):
|
|
191
|
+
if tools.get("eslint"):
|
|
192
|
+
lines.append("- **Linting:** [ESLint](https://eslint.org/) — `npx eslint .`")
|
|
193
|
+
if tools.get("prettier"):
|
|
194
|
+
lines.append("- **Formatting:** [Prettier](https://prettier.io/) — `npx prettier --write .`")
|
|
195
|
+
if tools.get("typescript"):
|
|
196
|
+
lines.append("- **Type checking:** TypeScript — `npx tsc --noEmit`")
|
|
197
|
+
if not any(tools.get(t) for t in ("eslint", "prettier")):
|
|
198
|
+
lines.append("Follow the project's ESLint/Prettier configuration.")
|
|
199
|
+
elif lang == "rust":
|
|
200
|
+
lines.append("- **Formatting:** `cargo fmt`")
|
|
201
|
+
lines.append("- **Linting:** `cargo clippy`")
|
|
202
|
+
elif lang == "go":
|
|
203
|
+
lines.append("- **Formatting:** `gofmt -w .`")
|
|
204
|
+
lines.append("- **Linting:** `go vet ./...`")
|
|
205
|
+
else:
|
|
206
|
+
if tools.get("black"):
|
|
207
|
+
lines.append("- **Formatting:** [Black](https://black.readthedocs.io/) — `black .`")
|
|
208
|
+
if tools.get("ruff"):
|
|
209
|
+
lines.append("- **Linting:** [Ruff](https://docs.astral.sh/ruff/) — `ruff check .`")
|
|
210
|
+
if tools.get("mypy"):
|
|
211
|
+
lines.append("- **Type checking:** [mypy](https://mypy.readthedocs.io/) — `mypy .`")
|
|
212
|
+
if tools.get("flake8"):
|
|
213
|
+
lines.append("- **Linting:** [flake8](https://flake8.pycqa.org/) — `flake8 .`")
|
|
214
|
+
if tools.get("isort"):
|
|
215
|
+
lines.append("- **Imports:** [isort](https://pycqa.github.io/isort/) — `isort .`")
|
|
216
|
+
if not any(tools.get(t) for t in ("black", "ruff", "mypy", "flake8", "isort")):
|
|
217
|
+
lines.append("Follow PEP 8 conventions.")
|
|
218
|
+
return "\n".join(lines)
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _render_pull_request() -> str:
|
|
222
|
+
"""Render pull request guidelines."""
|
|
223
|
+
lines = [
|
|
224
|
+
"## Pull Request Guidelines\n",
|
|
225
|
+
"- Keep PRs focused — one feature or fix per PR",
|
|
226
|
+
"- Include tests for new functionality",
|
|
227
|
+
"- Update documentation if needed",
|
|
228
|
+
"- Ensure all tests pass before submitting",
|
|
229
|
+
"- Use descriptive commit messages",
|
|
230
|
+
]
|
|
231
|
+
return "\n".join(lines)
|
|
@@ -44,12 +44,29 @@ class GettingStartedGenerator:
|
|
|
44
44
|
"""Render prerequisites section."""
|
|
45
45
|
dep_scanner = DependencyScanner()
|
|
46
46
|
deps = dep_scanner.scan(self.result.project_path)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
lines = ["## Prerequisites\n"]
|
|
48
|
+
|
|
49
|
+
lang = deps.language
|
|
50
|
+
if lang in ("javascript", "typescript"):
|
|
51
|
+
node_ver = deps.runtime_version or ">=18"
|
|
52
|
+
lines.append(f"- Node.js {node_ver}")
|
|
53
|
+
if "pnpm" in deps.install_command:
|
|
54
|
+
lines.append("- pnpm")
|
|
55
|
+
elif "yarn" in deps.install_command:
|
|
56
|
+
lines.append("- yarn")
|
|
57
|
+
else:
|
|
58
|
+
lines.append("- npm")
|
|
59
|
+
elif lang == "rust":
|
|
60
|
+
lines.append("- Rust toolchain (rustup)")
|
|
61
|
+
lines.append("- cargo")
|
|
62
|
+
elif lang == "go":
|
|
63
|
+
go_ver = deps.runtime_version or ">=1.21"
|
|
64
|
+
lines.append(f"- Go {go_ver}")
|
|
65
|
+
else:
|
|
66
|
+
py_ver = deps.python_version or ">=3.9"
|
|
67
|
+
lines.append(f"- Python {py_ver}")
|
|
68
|
+
lines.append("- pip (or your preferred package manager)")
|
|
69
|
+
|
|
53
70
|
if deps.dependencies:
|
|
54
71
|
lines.append(f"- {len(deps.dependencies)} dependencies (installed automatically)")
|
|
55
72
|
return "\n".join(lines)
|
|
@@ -60,6 +77,9 @@ class GettingStartedGenerator:
|
|
|
60
77
|
deps = dep_scanner.scan(self.result.project_path)
|
|
61
78
|
cmd = deps.install_command or f"pip install {self.config.project_name or '.'}"
|
|
62
79
|
repo_url = self.config.repo_url or "<repository-url>"
|
|
80
|
+
project = self.config.project_name or "project"
|
|
81
|
+
lang = deps.language
|
|
82
|
+
|
|
63
83
|
lines = [
|
|
64
84
|
"## Installation\n",
|
|
65
85
|
"```bash",
|
|
@@ -68,10 +88,19 @@ class GettingStartedGenerator:
|
|
|
68
88
|
"To install from source:\n",
|
|
69
89
|
"```bash",
|
|
70
90
|
f"git clone {repo_url}",
|
|
71
|
-
f"cd {
|
|
72
|
-
"pip install -e .",
|
|
73
|
-
"```",
|
|
91
|
+
f"cd {project}",
|
|
74
92
|
]
|
|
93
|
+
|
|
94
|
+
if lang in ("javascript", "typescript"):
|
|
95
|
+
lines.append(cmd)
|
|
96
|
+
elif lang == "rust":
|
|
97
|
+
lines.append("cargo build --release")
|
|
98
|
+
elif lang == "go":
|
|
99
|
+
lines.append("go build ./...")
|
|
100
|
+
else:
|
|
101
|
+
lines.append("pip install -e .")
|
|
102
|
+
|
|
103
|
+
lines.append("```")
|
|
75
104
|
return "\n".join(lines)
|
|
76
105
|
|
|
77
106
|
def _render_first_usage(self) -> str:
|
|
@@ -371,7 +371,16 @@ class ReadmeGenerator:
|
|
|
371
371
|
if not deps or not deps.install_command:
|
|
372
372
|
return ""
|
|
373
373
|
parts = ["## Installation\n", f"```bash\n{deps.install_command}\n```\n"]
|
|
374
|
-
|
|
374
|
+
lang = getattr(deps, 'language', 'python')
|
|
375
|
+
if lang in ('javascript', 'typescript'):
|
|
376
|
+
runtime_ver = getattr(deps, 'runtime_version', '')
|
|
377
|
+
if runtime_ver:
|
|
378
|
+
parts.append(f"Requires Node.js {runtime_ver}\n")
|
|
379
|
+
elif lang == 'go':
|
|
380
|
+
runtime_ver = getattr(deps, 'runtime_version', '')
|
|
381
|
+
if runtime_ver:
|
|
382
|
+
parts.append(f"Requires Go {runtime_ver}\n")
|
|
383
|
+
elif deps.python_version:
|
|
375
384
|
parts.append(f"Requires Python {deps.python_version}\n")
|
|
376
385
|
return "\n".join(parts)
|
|
377
386
|
|
|
@@ -381,8 +390,13 @@ class ReadmeGenerator:
|
|
|
381
390
|
parts = ["## Quick Start\n"]
|
|
382
391
|
entry_points = context.get("entry_points", [])
|
|
383
392
|
if entry_points:
|
|
384
|
-
|
|
385
|
-
|
|
393
|
+
deps = context.get("dependencies")
|
|
394
|
+
lang = getattr(deps, 'language', 'python') if deps else 'python'
|
|
395
|
+
lang_map = {'python': 'python', 'javascript': 'javascript', 'typescript': 'typescript',
|
|
396
|
+
'rust': 'rust', 'go': 'go'}
|
|
397
|
+
code_lang = lang_map.get(lang, lang)
|
|
398
|
+
parts.append(f"```{code_lang}")
|
|
399
|
+
parts.append(f"// Entry points: {', '.join(entry_points[:3])}")
|
|
386
400
|
parts.append("```\n")
|
|
387
401
|
return "\n".join(parts)
|
|
388
402
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2docs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.14
|
|
4
4
|
Summary: Auto-generate and sync project documentation from source code analysis
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|
|
|
51
51
|
# code2docs
|
|
52
52
|
|
|
53
|
-
  
|
|
54
54
|
|
|
55
55
|
> Auto-generate and sync project documentation from source code analysis.
|
|
56
56
|
|
|
@@ -190,7 +190,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
190
190
|
```markdown
|
|
191
191
|
<!-- code2docs:start --># code2docs
|
|
192
192
|
|
|
193
|
-
   
|
|
194
194
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
195
195
|
|
|
196
196
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
"""CONTRIBUTING.md generator from project tooling detection."""
|
|
2
|
-
|
|
3
|
-
from typing import Dict, List
|
|
4
|
-
|
|
5
|
-
from code2llm.api import AnalysisResult
|
|
6
|
-
|
|
7
|
-
from ..config import Code2DocsConfig
|
|
8
|
-
from ..analyzers.dependency_scanner import DependencyScanner
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ContributingGenerator:
|
|
12
|
-
"""Generate CONTRIBUTING.md by detecting dev tools from pyproject.toml."""
|
|
13
|
-
|
|
14
|
-
def __init__(self, config: Code2DocsConfig, result: AnalysisResult):
|
|
15
|
-
self.config = config
|
|
16
|
-
self.result = result
|
|
17
|
-
|
|
18
|
-
def generate(self) -> str:
|
|
19
|
-
"""Generate CONTRIBUTING.md content."""
|
|
20
|
-
project = self.config.project_name or "Project"
|
|
21
|
-
tools = self._detect_dev_tools()
|
|
22
|
-
lines = [
|
|
23
|
-
f"# Contributing to {project}\n",
|
|
24
|
-
self._render_setup(tools),
|
|
25
|
-
"",
|
|
26
|
-
self._render_development(tools),
|
|
27
|
-
"",
|
|
28
|
-
self._render_testing(tools),
|
|
29
|
-
"",
|
|
30
|
-
self._render_code_style(tools),
|
|
31
|
-
"",
|
|
32
|
-
self._render_pull_request(),
|
|
33
|
-
"",
|
|
34
|
-
]
|
|
35
|
-
return "\n".join(lines)
|
|
36
|
-
|
|
37
|
-
def _detect_dev_tools(self) -> Dict[str, bool]:
|
|
38
|
-
"""Detect which dev tools are configured in the project."""
|
|
39
|
-
scanner = DependencyScanner()
|
|
40
|
-
deps = scanner.scan(self.result.project_path)
|
|
41
|
-
all_deps = {d.name.lower() for d in deps.dependencies}
|
|
42
|
-
all_deps |= {d.name.lower() for d in deps.dev_dependencies}
|
|
43
|
-
return {
|
|
44
|
-
"pytest": "pytest" in all_deps,
|
|
45
|
-
"black": "black" in all_deps,
|
|
46
|
-
"ruff": "ruff" in all_deps,
|
|
47
|
-
"mypy": "mypy" in all_deps,
|
|
48
|
-
"flake8": "flake8" in all_deps,
|
|
49
|
-
"isort": "isort" in all_deps,
|
|
50
|
-
"pre-commit": "pre-commit" in all_deps,
|
|
51
|
-
"tox": "tox" in all_deps,
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
def _render_setup(self, tools: Dict[str, bool]) -> str:
|
|
55
|
-
"""Render development setup instructions."""
|
|
56
|
-
project = self.config.project_name or "project"
|
|
57
|
-
repo_url = self.config.repo_url or "<repository-url>"
|
|
58
|
-
lines = [
|
|
59
|
-
"## Development Setup\n",
|
|
60
|
-
"```bash",
|
|
61
|
-
f"git clone {repo_url}",
|
|
62
|
-
f"cd {project}",
|
|
63
|
-
"python -m venv .venv",
|
|
64
|
-
"source .venv/bin/activate # or .venv\\Scripts\\activate on Windows",
|
|
65
|
-
"pip install -e \".[dev]\"",
|
|
66
|
-
"```",
|
|
67
|
-
]
|
|
68
|
-
return "\n".join(lines)
|
|
69
|
-
|
|
70
|
-
@staticmethod
|
|
71
|
-
def _render_development(tools: Dict[str, bool]) -> str:
|
|
72
|
-
"""Render development workflow."""
|
|
73
|
-
lines = [
|
|
74
|
-
"## Development Workflow\n",
|
|
75
|
-
"1. Create a feature branch from `main`",
|
|
76
|
-
"2. Make your changes",
|
|
77
|
-
"3. Add or update tests",
|
|
78
|
-
"4. Run the test suite",
|
|
79
|
-
"5. Submit a pull request",
|
|
80
|
-
]
|
|
81
|
-
return "\n".join(lines)
|
|
82
|
-
|
|
83
|
-
@staticmethod
|
|
84
|
-
def _render_testing(tools: Dict[str, bool]) -> str:
|
|
85
|
-
"""Render testing instructions."""
|
|
86
|
-
lines = ["## Testing\n"]
|
|
87
|
-
if tools.get("pytest"):
|
|
88
|
-
lines.extend([
|
|
89
|
-
"```bash",
|
|
90
|
-
"# Run all tests",
|
|
91
|
-
"pytest",
|
|
92
|
-
"",
|
|
93
|
-
"# Run with coverage",
|
|
94
|
-
"pytest --cov --cov-report=term-missing",
|
|
95
|
-
"",
|
|
96
|
-
"# Run a specific test file",
|
|
97
|
-
"pytest tests/test_specific.py -v",
|
|
98
|
-
"```",
|
|
99
|
-
])
|
|
100
|
-
else:
|
|
101
|
-
lines.extend([
|
|
102
|
-
"```bash",
|
|
103
|
-
"python -m unittest discover tests/",
|
|
104
|
-
"```",
|
|
105
|
-
])
|
|
106
|
-
return "\n".join(lines)
|
|
107
|
-
|
|
108
|
-
@staticmethod
|
|
109
|
-
def _render_code_style(tools: Dict[str, bool]) -> str:
|
|
110
|
-
"""Render code style guidelines."""
|
|
111
|
-
lines = ["## Code Style\n"]
|
|
112
|
-
if tools.get("black"):
|
|
113
|
-
lines.append("- **Formatting:** [Black](https://black.readthedocs.io/) — `black .`")
|
|
114
|
-
if tools.get("ruff"):
|
|
115
|
-
lines.append("- **Linting:** [Ruff](https://docs.astral.sh/ruff/) — `ruff check .`")
|
|
116
|
-
if tools.get("mypy"):
|
|
117
|
-
lines.append("- **Type checking:** [mypy](https://mypy.readthedocs.io/) — `mypy .`")
|
|
118
|
-
if tools.get("flake8"):
|
|
119
|
-
lines.append("- **Linting:** [flake8](https://flake8.pycqa.org/) — `flake8 .`")
|
|
120
|
-
if tools.get("isort"):
|
|
121
|
-
lines.append("- **Imports:** [isort](https://pycqa.github.io/isort/) — `isort .`")
|
|
122
|
-
if not any(tools.get(t) for t in ("black", "ruff", "mypy", "flake8", "isort")):
|
|
123
|
-
lines.append("Follow PEP 8 conventions.")
|
|
124
|
-
return "\n".join(lines)
|
|
125
|
-
|
|
126
|
-
@staticmethod
|
|
127
|
-
def _render_pull_request() -> str:
|
|
128
|
-
"""Render pull request guidelines."""
|
|
129
|
-
lines = [
|
|
130
|
-
"## Pull Request Guidelines\n",
|
|
131
|
-
"- Keep PRs focused — one feature or fix per PR",
|
|
132
|
-
"- Include tests for new functionality",
|
|
133
|
-
"- Update documentation if needed",
|
|
134
|
-
"- Ensure all tests pass before submitting",
|
|
135
|
-
"- Use descriptive commit messages",
|
|
136
|
-
]
|
|
137
|
-
return "\n".join(lines)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|