feat(slidev): update 3 4
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
||||
---
|
||||
name: project-knowledge-builder
|
||||
description: Use when a user asks “项目知识库”, “看项目”, “整理项目”, or otherwise wants to inspect a software project deeply, build/refresh a project knowledge base, create onboarding docs, architecture maps, domain glossaries, ADRs, generated API/config/database indexes, or RAG-ready repository documentation.
|
||||
---
|
||||
|
||||
# Project Knowledge Builder
|
||||
|
||||
## Overview
|
||||
|
||||
Build a source-grounded project knowledge base that helps humans and AI agents understand, extend, troubleshoot, and index a codebase. Treat the knowledge base as a maintained system artifact, not a README rewrite.
|
||||
|
||||
## Quick Use
|
||||
|
||||
Users should not need to remember commands, script paths, or `$project-knowledge-builder`.
|
||||
|
||||
The shortest trigger is enough:
|
||||
|
||||
- `项目知识库`
|
||||
|
||||
Treat the current repository as the target and `docs/project-knowledge/` as the output path unless the user says otherwise. Run the bundled inventory script yourself and write the docs. Only show manual commands when the user explicitly asks for command-line usage.
|
||||
|
||||
## Language And Audience Defaults
|
||||
|
||||
If the user writes in Chinese or asks for Chinese, write human-facing narrative docs in Chinese by default: README, context, architecture, runtime, backend, frontend, data, auth/permissions, extension, troubleshooting, RAG guidance and ADRs. Keep source paths, class names, commands, API paths, config keys, table names, permission codes and metadata enum values unchanged.
|
||||
|
||||
Treat beginners as a primary audience. A useful knowledge base should let a new maintainer answer four questions without extra coaching:
|
||||
|
||||
1. What is this project and where are the main modules?
|
||||
2. How do I run it locally or identify the active runtime mode?
|
||||
3. How do I find the backend endpoint, frontend page, database table and permission code for a feature?
|
||||
4. What must change when adding a normal feature, especially menu/button permissions?
|
||||
|
||||
Do not make generated indexes overly friendly at the cost of precision. Keep generated files machine-refreshable; make narrative docs readable.
|
||||
|
||||
## Core Rule
|
||||
|
||||
Every claim must be traceable to the repository or to an explicitly marked inference. Prefer source paths, commands, generated inventories, and unresolved-question lists over confident prose.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Set scope.** Confirm or assume the output location. Default to `docs/project-knowledge/`. If the user mentions RAG, Feishu/Lark, Dify, Notion, onboarding, architecture review, or API docs, tailor the output contract.
|
||||
2. **Read local instructions first.** Check root docs plus `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.codex/`, `.agents/`, package manifests, build files, env examples, Docker files, and module READMEs.
|
||||
3. **Generate an inventory.** Run `scripts/project_inventory.py` from this skill against the repo. Use its output as a factual base, then inspect important files manually.
|
||||
4. **Map contexts.** Identify bounded contexts, runtime modes, major modules, owners implied by paths, external systems, and data stores. Use `CONTEXT.md` and `CONTEXT-MAP.md` style docs.
|
||||
5. **Extract generated indexes.** Produce machine-refreshable docs where possible: endpoints, frontend routes/views, config keys, env variables, database tables, permission codes, scheduled jobs, queues, and extension points.
|
||||
6. **Write human guides.** Produce onboarding, architecture, local run/deploy, data model, auth/permission, extension, troubleshooting, and glossary docs. Match the user's language for narrative docs; for Chinese users, use Chinese section titles such as `已观察来源`, `核心事实`, `操作影响`, and `未知与风险`.
|
||||
7. **Create ADRs for real decisions.** Record important architecture decisions already embodied in the code. Do not invent future decisions unless marked as proposed.
|
||||
8. **Add RAG metadata guidance.** Include chunking, source-path metadata, stale-data policy, and exclusion rules.
|
||||
9. **Verify.** Re-run the inventory, scan docs for placeholders, broken relative links, stale paths, and unsupported claims. Report what was verified and what remains uncertain.
|
||||
|
||||
## Output Contract
|
||||
|
||||
Read `references/output-contract.md` when creating or upgrading a full knowledge base.
|
||||
|
||||
Minimum useful set:
|
||||
|
||||
```text
|
||||
docs/project-knowledge/
|
||||
├── README.md
|
||||
├── CONTEXT.md
|
||||
├── CONTEXT-MAP.md
|
||||
├── 01-architecture.md
|
||||
├── 02-runtime.md
|
||||
├── 03-backend.md
|
||||
├── 04-frontend.md
|
||||
├── 05-data-and-migrations.md
|
||||
├── 06-auth-permissions.md
|
||||
├── 07-extension-guide.md
|
||||
├── 08-troubleshooting.md
|
||||
├── 09-rag-indexing.md
|
||||
├── adr/
|
||||
│ └── 0001-current-runtime-architecture.md
|
||||
└── generated/
|
||||
├── project-inventory.md
|
||||
├── backend-endpoints.md
|
||||
├── frontend-routes.md
|
||||
├── config-reference.md
|
||||
├── database-dictionary.md
|
||||
└── permission-codes.md
|
||||
```
|
||||
|
||||
Scale down only if the repo is small or the user requests a lighter artifact.
|
||||
|
||||
## Source-Grounding Standards
|
||||
|
||||
- Cite source paths inline for non-obvious claims.
|
||||
- Distinguish **Observed** from **Inferred** and **Unknown**.
|
||||
- Do not summarize generated code, dependencies, or configs from memory. Read them.
|
||||
- If external docs are needed for unstable facts, browse primary sources and link them.
|
||||
- Preserve project-specific conventions. If a repo requires trace comments, ADR format, or issue IDs, document and follow that.
|
||||
|
||||
## Cross-Platform Compatibility
|
||||
|
||||
Read `references/compatibility.md` when the user asks about macOS, Windows, Linux, CI, portability, or installing/running the skill across machines. The bundled inventory script is intended to run on Python 3.9+ with only the standard library.
|
||||
|
||||
## Inventory Script
|
||||
|
||||
Use this only for manual refreshes or when the user asks for the command. Run from the target repo:
|
||||
|
||||
macOS/Linux:
|
||||
|
||||
```sh
|
||||
python3 /path/to/skill/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
py -3 C:\path\to\skill\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
|
||||
```
|
||||
|
||||
Useful options:
|
||||
|
||||
```sh
|
||||
python3 /path/to/skill/scripts/project_inventory.py --root . --out ./kb-inventory --json
|
||||
python3 /path/to/skill/scripts/project_inventory.py --root . --out docs/project-knowledge/generated --max-files 20000
|
||||
```
|
||||
|
||||
The script is a starting point, not a replacement for reading core files. Inspect representative files before writing narrative docs.
|
||||
|
||||
## Quality Gates
|
||||
|
||||
Before final response:
|
||||
|
||||
- List generated docs with a platform-appropriate command, such as `find docs/project-knowledge -type f | sort` on macOS/Linux or `Get-ChildItem docs/project-knowledge -Recurse -File` on PowerShell.
|
||||
- `rg -n "TODO|TBD|待补|unknown|your/path|foo|bar" docs/project-knowledge`
|
||||
- Re-run the inventory script after edits.
|
||||
- Check that top-level README links resolve.
|
||||
- If the user is Chinese, scan narrative docs for English-only structural headings such as `Observed Sources`, `Core Facts`, `Operational Implications`, `Unknowns/Risks`, `Architecture`, `Runtime`, `Backend`, `Frontend`, `Troubleshooting`, and translate them unless they are code, metadata, or generated output.
|
||||
- Check newcomer usefulness: README gives a reading order; runtime doc names local/Docker/cloud modes; auth doc explains menu/button permission flow; extension doc explains backend CRUD, frontend page and permission changes.
|
||||
- `git status --short` to report changed files.
|
||||
|
||||
If the user asked for “deep” or “落地”, do not stop at a single overview. Deliver generated indexes plus human guides.
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "项目知识库"
|
||||
short_description: "深度整理代码仓库知识库"
|
||||
default_prompt: "Use $project-knowledge-builder 为当前项目生成 docs/project-knowledge 项目知识库。"
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
# Cross-Platform Compatibility
|
||||
|
||||
Use this reference when running or installing the skill on macOS, Windows, Linux, or CI.
|
||||
|
||||
## Compatibility Summary
|
||||
|
||||
| Area | macOS | Linux | Windows |
|
||||
| --- | --- | --- | --- |
|
||||
| `SKILL.md` workflow | Supported | Supported | Supported |
|
||||
| `project_inventory.py` | Supported with Python 3.9+ | Supported with Python 3.9+ | Supported with Python 3.9+ |
|
||||
| Python dependencies | Standard library only | Standard library only | Standard library only |
|
||||
| Path handling | `pathlib` | `pathlib` | `pathlib` |
|
||||
| Shell dependency | None in script | None in script | None in script |
|
||||
| Generated Markdown paths | POSIX-style relative paths | POSIX-style relative paths | POSIX-style relative paths |
|
||||
|
||||
The script intentionally emits repository-relative paths with `/` separators so generated Markdown is stable across platforms and friendlier for URLs, GitHub, and RAG metadata.
|
||||
|
||||
## Python Invocation
|
||||
|
||||
macOS and Linux usually have one of these:
|
||||
|
||||
```sh
|
||||
python3 skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
|
||||
python skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
|
||||
```
|
||||
|
||||
Windows PowerShell usually uses:
|
||||
|
||||
```powershell
|
||||
py -3 skills\project-knowledge-builder\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
|
||||
python skills\project-knowledge-builder\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
|
||||
```
|
||||
|
||||
CI should prefer an explicit Python:
|
||||
|
||||
```sh
|
||||
python -m py_compile skills/project-knowledge-builder/scripts/project_inventory.py
|
||||
python skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated --json
|
||||
```
|
||||
|
||||
## Command Substitutions
|
||||
|
||||
| Task | macOS/Linux | Windows PowerShell |
|
||||
| --- | --- | --- |
|
||||
| List docs | `find docs/project-knowledge -type f \| sort` | `Get-ChildItem docs/project-knowledge -Recurse -File \| Sort-Object FullName` |
|
||||
| Search placeholders | `rg -n "TODO|TBD" docs/project-knowledge` | `rg -n "TODO|TBD" docs/project-knowledge` |
|
||||
| Show git status | `git status --short` | `git status --short` |
|
||||
| Remove temp output | `rm -rf ./kb-inventory` | `Remove-Item .\kb-inventory -Recurse -Force` |
|
||||
|
||||
`rg` is preferred where available. If `rg` is not installed, use the platform search available in the environment.
|
||||
|
||||
## Avoid In Skill Examples
|
||||
|
||||
- Hard-coded `/tmp` paths when the command should work on Windows.
|
||||
- POSIX-only command chains such as `cmd1 && cmd2` in user-facing instructions when a PowerShell equivalent is needed.
|
||||
- Shell glob assumptions that behave differently between zsh, bash, PowerShell, and cmd.
|
||||
- Python code that depends on executable shebang behavior; Windows users should call `py -3 script.py`.
|
||||
|
||||
## Known Limits
|
||||
|
||||
- Java endpoint extraction is regex-based and intentionally conservative. It does not fully resolve constants, composed annotations, or inherited controller mappings.
|
||||
- SQL table extraction looks for `CREATE TABLE`; tables created dynamically or through migrations in another DSL need manual documentation.
|
||||
- Frontend route extraction scans route-like literals and view files; runtime-generated route data from APIs still requires backend menu/permission data or database export.
|
||||
- Very large repositories may need `--max-files` tuning or additional ignore directories.
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
# Project Knowledge Builder Output Contract
|
||||
|
||||
Use this reference when producing a full codebase knowledge base.
|
||||
|
||||
## Language Policy
|
||||
|
||||
Match the user's language for human-facing narrative docs. If the user writes in Chinese or asks for Chinese, write README, context, architecture, runtime, backend, frontend, data, auth/permissions, extension, troubleshooting, RAG guidance and ADRs in Chinese.
|
||||
|
||||
Keep technical anchors unchanged: source paths, commands, class names, method names, API paths, config keys, table names, permission codes, JSON keys and metadata enum values.
|
||||
|
||||
Generated indexes may remain technical and source-like. Do not hand-translate generated values if it reduces refreshability.
|
||||
|
||||
## Document Roles
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `README.md` | Entry point, reading order, high-value links, freshness note. For Chinese output, title and usage guidance should be Chinese. |
|
||||
| `CONTEXT.md` | Domain language, business capabilities, user roles, core nouns, invariants. |
|
||||
| `CONTEXT-MAP.md` | Bounded contexts, module ownership, upstream/downstream dependencies, runtime boundaries. |
|
||||
| `01-architecture.md` | System shape, module graph, request/data/control flows, extension seams. |
|
||||
| `02-runtime.md` | Local run, build, env vars, Docker, deployment modes, process topology. Must identify local, Docker standalone and cloud modes when present. |
|
||||
| `03-backend.md` | Backend modules, entrypoints, API conventions, service/mapper/entity patterns. |
|
||||
| `04-frontend.md` | Frontend bootstrap, routing, state, API client, component/page conventions. |
|
||||
| `05-data-and-migrations.md` | Databases, migrations, schema sources, seed data, table families. |
|
||||
| `06-auth-permissions.md` | Authentication, authorization, token/session flow, permission-code lifecycle. Must explain menu/button permission flow when the repo has backend-driven menus. |
|
||||
| `07-extension-guide.md` | How to add a feature end-to-end, with source-grounded examples. Must cover backend CRUD, frontend page and permission/menu data changes when applicable. |
|
||||
| `08-troubleshooting.md` | Symptom-driven runbook with commands, files, and likely causes. |
|
||||
| `09-rag-indexing.md` | Chunking, metadata, exclusions, refresh cadence, stale-data policy. |
|
||||
| `adr/*.md` | Architecture decisions observed in the current code. |
|
||||
| `generated/*.md` | Data-derived indexes that can be regenerated. |
|
||||
|
||||
## Required Sections
|
||||
|
||||
Each narrative doc should include:
|
||||
|
||||
- **Observed Sources:** concrete files or commands used.
|
||||
- **Core Facts:** concise source-backed statements.
|
||||
- **Operational Implications:** what developers must do differently because of those facts.
|
||||
- **Unknowns/Risks:** gaps that need runtime access, DB export, product owner input, or external docs.
|
||||
|
||||
For Chinese output, use equivalent section labels:
|
||||
|
||||
- **已观察来源**
|
||||
- **核心事实**
|
||||
- **操作影响**
|
||||
- **未知与风险**
|
||||
|
||||
## Newcomer Usefulness Gate
|
||||
|
||||
Before considering the knowledge base useful for handoff, verify that a new maintainer can answer:
|
||||
|
||||
1. What is the product/project, and which directories matter first?
|
||||
2. How do I start or identify the active runtime mode?
|
||||
3. Where are backend endpoints, frontend routes/pages, config, tables and permission codes indexed?
|
||||
4. How do menu, button and backend permission checks connect?
|
||||
5. When adding a normal feature, which backend, frontend, database and permission artifacts usually change?
|
||||
|
||||
If the answer is not obvious from README plus the narrative docs, add or revise human-facing guidance before finishing. Do not solve this by making generated indexes longer.
|
||||
|
||||
## Generated Indexes
|
||||
|
||||
### `project-inventory.md`
|
||||
|
||||
Include:
|
||||
|
||||
- repository roots
|
||||
- build/package manifests
|
||||
- primary languages
|
||||
- major directories
|
||||
- scripts
|
||||
- Docker/compose files
|
||||
- env files
|
||||
- test files
|
||||
|
||||
### `backend-endpoints.md`
|
||||
|
||||
Include route method, path, class/file, approximate line, auth annotations, and notes. Do not invent full route paths when class-level and method-level mappings require Java expression resolution; show observed fragments and mark composition limitations.
|
||||
|
||||
### `frontend-routes.md`
|
||||
|
||||
Include static router files, dynamic route loaders, `src/views` index, route guard files, and external route package loaders.
|
||||
|
||||
### `config-reference.md`
|
||||
|
||||
Include env files, YAML/properties files, relevant keys, default values, and risk notes. Redact secrets.
|
||||
|
||||
### `database-dictionary.md`
|
||||
|
||||
Include SQL files, migration files, table names, table families, and whether the table is initialized, migrated, or inferred. Avoid loading huge SQL into prose; summarize and link.
|
||||
|
||||
### `permission-codes.md`
|
||||
|
||||
Include backend annotations, frontend permission helpers/usages, menu/permission SQL occurrences, and auth lifecycle notes.
|
||||
|
||||
## ADR Template
|
||||
|
||||
```markdown
|
||||
# ADR NNNN: Title
|
||||
|
||||
Date: YYYY-MM-DD
|
||||
Status: Observed
|
||||
|
||||
## Context
|
||||
|
||||
Source-backed facts that show the decision exists.
|
||||
|
||||
## Decision
|
||||
|
||||
The architecture decision embodied by the current code.
|
||||
|
||||
## Consequences
|
||||
|
||||
Benefits, tradeoffs, operational constraints, and migration risks.
|
||||
|
||||
## Sources
|
||||
|
||||
- `path/to/file`
|
||||
```
|
||||
|
||||
## RAG Metadata
|
||||
|
||||
Recommended metadata per chunk:
|
||||
|
||||
```json
|
||||
{
|
||||
"project": "name",
|
||||
"snapshot_date": "YYYY-MM-DD",
|
||||
"source_path": "path",
|
||||
"doc_type": "architecture|runtime|generated|adr|runbook",
|
||||
"domain": "backend|frontend|data|auth|deploy",
|
||||
"freshness": "manual|generated",
|
||||
"confidence": "observed|inferred|unknown"
|
||||
}
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- A long essay without generated indexes.
|
||||
- “Best practice” advice unrelated to the repo.
|
||||
- Copying README marketing text as architecture documentation.
|
||||
- Claiming an endpoint, table, permission, or runtime mode exists without a source path.
|
||||
- Hiding unknowns instead of listing them.
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a source-grounded project inventory for knowledge-base work."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
IGNORE_DIRS = {
|
||||
".git",
|
||||
".idea",
|
||||
".vscode",
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
"target",
|
||||
".gradle",
|
||||
".mvn/wrapper",
|
||||
"coverage",
|
||||
".next",
|
||||
".nuxt",
|
||||
"__pycache__",
|
||||
}
|
||||
|
||||
MANIFEST_NAMES = {
|
||||
"package.json",
|
||||
"pnpm-lock.yaml",
|
||||
"yarn.lock",
|
||||
"package-lock.json",
|
||||
"pom.xml",
|
||||
"build.gradle",
|
||||
"settings.gradle",
|
||||
"Cargo.toml",
|
||||
"pyproject.toml",
|
||||
"requirements.txt",
|
||||
"go.mod",
|
||||
"composer.json",
|
||||
"Gemfile",
|
||||
}
|
||||
|
||||
CONFIG_PATTERNS = (
|
||||
re.compile(r".*\.env(\..*)?$"),
|
||||
re.compile(r".*application[-\w]*\.(yml|yaml|properties)$"),
|
||||
re.compile(r".*\.(ya?ml|toml|ini|properties|conf)$"),
|
||||
)
|
||||
|
||||
HTTP_MAPPING_RE = re.compile(
|
||||
r"@(RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping)\s*(?:\((.*?)\))?"
|
||||
)
|
||||
PERMISSION_RE = re.compile(r"@(?:RequiresPermissions|RequiresRoles)\s*\((.*?)\)")
|
||||
TABLE_NAME_RE = re.compile(
|
||||
r"\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?([A-Za-z0-9_.$-]+)`?",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
VUE_ROUTE_RE = re.compile(r"\b(path|name|component)\s*:\s*['\"]([^'\"]+)['\"]")
|
||||
|
||||
|
||||
def is_ignored(path: Path, root: Path) -> bool:
|
||||
rel_parts = path.relative_to(root).parts
|
||||
for i in range(len(rel_parts)):
|
||||
joined = "/".join(rel_parts[: i + 1])
|
||||
if joined in IGNORE_DIRS or rel_parts[i] in IGNORE_DIRS:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def iter_files(root: Path, max_files: int) -> Iterable[Path]:
|
||||
count = 0
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
current = Path(dirpath)
|
||||
dirnames[:] = [d for d in dirnames if not is_ignored(current / d, root)]
|
||||
for name in filenames:
|
||||
path = current / name
|
||||
if is_ignored(path, root):
|
||||
continue
|
||||
count += 1
|
||||
if count > max_files:
|
||||
return
|
||||
yield path
|
||||
|
||||
|
||||
def rel(path: Path, root: Path) -> str:
|
||||
return path.relative_to(root).as_posix()
|
||||
|
||||
|
||||
def read_text(path: Path, limit: int = 400_000) -> str:
|
||||
try:
|
||||
data = path.read_bytes()
|
||||
except OSError:
|
||||
return ""
|
||||
if len(data) > limit:
|
||||
data = data[:limit]
|
||||
try:
|
||||
return data.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
return data.decode("utf-8", errors="ignore")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def detect_config(path: Path) -> bool:
|
||||
name = path.name
|
||||
return any(pattern.fullmatch(name) for pattern in CONFIG_PATTERNS)
|
||||
|
||||
|
||||
def extract_mapping_value(raw: str | None) -> str:
|
||||
if not raw:
|
||||
return ""
|
||||
value_match = re.search(r"['\"]([^'\"]+)['\"]", raw)
|
||||
if value_match:
|
||||
return value_match.group(1)
|
||||
return raw.strip().replace("\n", " ")[:120]
|
||||
|
||||
|
||||
def scan(root: Path, max_files: int) -> dict:
|
||||
files = list(iter_files(root, max_files))
|
||||
ext_counts = Counter(path.suffix.lower() or "[none]" for path in files)
|
||||
manifests = []
|
||||
configs = []
|
||||
docker = []
|
||||
tests = []
|
||||
java_endpoints = []
|
||||
permission_codes = []
|
||||
vue_routes = []
|
||||
views = []
|
||||
sql_tables = []
|
||||
major_dirs = defaultdict(int)
|
||||
|
||||
for path in files:
|
||||
r = rel(path, root)
|
||||
parts = Path(r).parts
|
||||
if parts:
|
||||
major_dirs[parts[0]] += 1
|
||||
if path.name in MANIFEST_NAMES:
|
||||
manifests.append(r)
|
||||
lower = path.name.lower()
|
||||
if lower.startswith("dockerfile") or "docker-compose" in lower:
|
||||
docker.append(r)
|
||||
if detect_config(path):
|
||||
configs.append(r)
|
||||
if re.search(r"(^|/)(test|tests|__tests__)(/|$)", r) or re.search(r"\.(test|spec)\.[A-Za-z0-9]+$", r):
|
||||
tests.append(r)
|
||||
if "/src/views/" in r and path.suffix.lower() in {".vue", ".tsx", ".ts"}:
|
||||
views.append(r)
|
||||
|
||||
suffix = path.suffix.lower()
|
||||
if suffix not in {".java", ".ts", ".tsx", ".vue", ".js", ".jsx", ".sql", ".yml", ".yaml", ".properties"}:
|
||||
continue
|
||||
text = read_text(path)
|
||||
if not text:
|
||||
continue
|
||||
|
||||
if suffix == ".java":
|
||||
lines = text.splitlines()
|
||||
class_mapping = ""
|
||||
class_name = ""
|
||||
pending_annotations: list[tuple[int, str, str]] = []
|
||||
for idx, line in enumerate(lines, start=1):
|
||||
class_match = re.search(r"\bclass\s+([A-Za-z0-9_]+)", line)
|
||||
if class_match:
|
||||
class_name = class_match.group(1)
|
||||
for match in HTTP_MAPPING_RE.finditer(line):
|
||||
value = extract_mapping_value(match.group(2))
|
||||
annotation = match.group(1)
|
||||
pending_annotations.append((idx, annotation, value))
|
||||
if annotation == "RequestMapping" and not class_mapping and ("class " not in line):
|
||||
class_mapping = value
|
||||
if re.search(r"\b(public|private|protected)\b.*\(", line) and pending_annotations:
|
||||
method = line.strip()[:160]
|
||||
for ann_line, annotation, value in pending_annotations:
|
||||
java_endpoints.append(
|
||||
{
|
||||
"file": r,
|
||||
"line": ann_line,
|
||||
"class": class_name,
|
||||
"annotation": annotation,
|
||||
"class_mapping": class_mapping,
|
||||
"mapping": value,
|
||||
"method_signature": method,
|
||||
}
|
||||
)
|
||||
pending_annotations = []
|
||||
for perm in PERMISSION_RE.finditer(line):
|
||||
permission_codes.append({"file": r, "line": idx, "value": perm.group(1).strip()})
|
||||
|
||||
if suffix in {".ts", ".tsx", ".vue", ".js", ".jsx"}:
|
||||
for idx, line in enumerate(text.splitlines(), start=1):
|
||||
if "path:" in line or "component:" in line or "name:" in line:
|
||||
for match in VUE_ROUTE_RE.finditer(line):
|
||||
vue_routes.append({"file": r, "line": idx, "key": match.group(1), "value": match.group(2)})
|
||||
|
||||
if suffix == ".sql":
|
||||
for match in TABLE_NAME_RE.finditer(text):
|
||||
sql_tables.append({"file": r, "table": match.group(1)})
|
||||
|
||||
return {
|
||||
"root": str(root),
|
||||
"file_count": len(files),
|
||||
"extensions": dict(ext_counts.most_common(40)),
|
||||
"major_dirs": dict(sorted(major_dirs.items(), key=lambda item: (-item[1], item[0]))),
|
||||
"manifests": sorted(manifests),
|
||||
"configs": sorted(configs),
|
||||
"docker": sorted(docker),
|
||||
"tests": sorted(tests),
|
||||
"views": sorted(views),
|
||||
"java_endpoints": java_endpoints,
|
||||
"permission_codes": permission_codes,
|
||||
"vue_routes": vue_routes,
|
||||
"sql_tables": sql_tables,
|
||||
}
|
||||
|
||||
|
||||
def write_md(data: dict, out_dir: Path) -> None:
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def bullet(items: list[str], limit: int | None = None) -> str:
|
||||
selected = items if limit is None else items[:limit]
|
||||
if not selected:
|
||||
return "- None found\n"
|
||||
body = "".join(f"- `{item}`\n" for item in selected)
|
||||
if limit is not None and len(items) > limit:
|
||||
body += f"- ... {len(items) - limit} more\n"
|
||||
return body
|
||||
|
||||
inventory = [
|
||||
"# Project Inventory\n\n",
|
||||
f"Root: `{data['root']}`\n\n",
|
||||
f"Files scanned: {data['file_count']}\n\n",
|
||||
"## Major Directories\n\n",
|
||||
]
|
||||
inventory.extend(f"- `{name}`: {count} files\n" for name, count in data["major_dirs"].items())
|
||||
inventory.extend(["\n## Extensions\n\n"])
|
||||
inventory.extend(f"- `{name}`: {count}\n" for name, count in data["extensions"].items())
|
||||
inventory.extend(["\n## Manifests\n\n", bullet(data["manifests"])])
|
||||
inventory.extend(["\n## Config Files\n\n", bullet(data["configs"], 200)])
|
||||
inventory.extend(["\n## Docker Files\n\n", bullet(data["docker"])])
|
||||
inventory.extend(["\n## Test Files\n\n", bullet(data["tests"], 200)])
|
||||
(out_dir / "project-inventory.md").write_text("".join(inventory), encoding="utf-8")
|
||||
|
||||
endpoints = ["# Backend Endpoints\n\n"]
|
||||
if data["java_endpoints"]:
|
||||
endpoints.append("| File | Line | Class | Annotation | Class Mapping | Mapping | Method |\n")
|
||||
endpoints.append("| --- | ---: | --- | --- | --- | --- | --- |\n")
|
||||
for item in data["java_endpoints"]:
|
||||
method_signature = item["method_signature"].replace("|", "\\|")
|
||||
endpoints.append(
|
||||
f"| `{item['file']}` | {item['line']} | `{item['class']}` | `{item['annotation']}` | "
|
||||
f"`{item['class_mapping']}` | `{item['mapping']}` | `{method_signature}` |\n"
|
||||
)
|
||||
else:
|
||||
endpoints.append("No Java endpoint annotations found.\n")
|
||||
(out_dir / "backend-endpoints.md").write_text("".join(endpoints), encoding="utf-8")
|
||||
|
||||
routes = ["# Frontend Routes And Views\n\n", "## Views\n\n", bullet(data["views"], 400), "\n## Route-like Literals\n\n"]
|
||||
if data["vue_routes"]:
|
||||
routes.append("| File | Line | Key | Value |\n| --- | ---: | --- | --- |\n")
|
||||
for item in data["vue_routes"][:1000]:
|
||||
routes.append(f"| `{item['file']}` | {item['line']} | `{item['key']}` | `{item['value']}` |\n")
|
||||
if len(data["vue_routes"]) > 1000:
|
||||
routes.append(f"\nTruncated: {len(data['vue_routes']) - 1000} more route-like literals.\n")
|
||||
else:
|
||||
routes.append("No route-like literals found.\n")
|
||||
(out_dir / "frontend-routes.md").write_text("".join(routes), encoding="utf-8")
|
||||
|
||||
configs = ["# Config Reference\n\n", bullet(data["configs"], 500)]
|
||||
(out_dir / "config-reference.md").write_text("".join(configs), encoding="utf-8")
|
||||
|
||||
tables = ["# Database Dictionary\n\n"]
|
||||
if data["sql_tables"]:
|
||||
tables.append("| Table | Source |\n| --- | --- |\n")
|
||||
for item in sorted(data["sql_tables"], key=lambda x: (x["table"], x["file"])):
|
||||
tables.append(f"| `{item['table']}` | `{item['file']}` |\n")
|
||||
else:
|
||||
tables.append("No SQL CREATE TABLE statements found in scanned files.\n")
|
||||
(out_dir / "database-dictionary.md").write_text("".join(tables), encoding="utf-8")
|
||||
|
||||
perms = ["# Permission Codes\n\n"]
|
||||
if data["permission_codes"]:
|
||||
perms.append("| File | Line | Value |\n| --- | ---: | --- |\n")
|
||||
for item in data["permission_codes"]:
|
||||
permission_value = item["value"].replace("|", "\\|")
|
||||
perms.append(f"| `{item['file']}` | {item['line']} | `{permission_value}` |\n")
|
||||
else:
|
||||
perms.append("No permission annotations found.\n")
|
||||
(out_dir / "permission-codes.md").write_text("".join(perms), encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", default=".", help="Repository root to scan")
|
||||
parser.add_argument("--out", required=True, help="Output directory for generated Markdown")
|
||||
parser.add_argument("--json", action="store_true", help="Also write inventory.json")
|
||||
parser.add_argument("--max-files", type=int, default=30000, help="Maximum files to scan")
|
||||
args = parser.parse_args()
|
||||
|
||||
root = Path(args.root).resolve()
|
||||
out_dir = Path(args.out).resolve()
|
||||
data = scan(root, args.max_files)
|
||||
write_md(data, out_dir)
|
||||
if args.json:
|
||||
(out_dir / "inventory.json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"Wrote project inventory to {out_dir}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user