-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_docs.py
More file actions
182 lines (135 loc) · 5.82 KB
/
generate_docs.py
File metadata and controls
182 lines (135 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import os
from typing import List, Set
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "your-default-api-key-here")
COMMANDS_DIR: str = "structix/commands"
DOCS_DIR: str = "docs/docs/cli-commands"
client = OpenAI(api_key=OPENAI_API_KEY)
def list_command_files(base_dir: str) -> List[str]:
"""List all .py command files except __init__.py."""
command_files: List[str] = []
for root, dirs, files in os.walk(base_dir):
for file in files:
if file.endswith(".py") and file != "__init__.py":
relative_path = os.path.relpath(
os.path.join(root, file), base_dir
)
command_files.append(relative_path)
return command_files
def build_prompt(command_path: str) -> str:
"""Build the prompt to send to OpenAI, including file content."""
base_command: str = (
command_path.replace(".py", "").replace("/", " ").replace("\\", " ")
)
full_command: str = f"structix {base_command}"
full_path: str = os.path.join(COMMANDS_DIR, command_path)
try:
with open(full_path, "r", encoding="utf-8") as f:
file_content: str = f.read()
except Exception as e:
print(f"⚠️ Warning: Failed to read {full_path}: {e}")
file_content = ""
return f"""
You are writing professional CLI documentation.
Command: `{full_command}`
Below is the source code of the command:
```
{file_content}
```
When writing usage examples or showing how to use the command, always:
- Start the example with a bash code block using triple backticks and the language "bash", like this: ```bash
- Write the command exactly as `{full_command}`.
- Then close the code block correctly with triple backticks.
- Never omit the `bash` annotation.
Please generate a documentation page in Markdown with:
- A short description (what it does).
- A Usage section showing how the command would be used.
- A Arguments section with all arguments and their descriptions.
- An Options section (say "This command currently has no options." if none are available).
- An Examples section with at least one realistic usage example.
Extra Context:
- The code is a Python script that uses the Click library for command-line interfaces.
- The arguments are defined using the `@click.argument` decorator.
- The options are defined using the `@click.option` decorator.
Do not include options in usage examples.
Do not use tables for the Arguments and Options sections.
Do not include a title or 'Documentation for' at the beginning; that will be handled externally.
Return only pure markdown content.
EXAMPLE OUTPUT:
Add a new Helm chart microservice.
## Usage
```bash
structix ops add microservice <name> <image>
```
## Arguments
- `name`: The name of the microservice to be created.
- `image`: The Docker image for the microservice.
## Options
- `--db`: Optional database to be used with the microservice. Choices are `postgres`, `mysql`, `mongo`, or `redis`.
- `--port`: Port the service will expose. Default is `80`.
- `--replicas`: Number of replicas for the deployment. Default is `1`.
- `--deploy`: Deploy the Helm chart into your current Kubernetes cluster. This is a flag option.
- `--with-ingress`: Include an Ingress resource for the microservice. This is a flag option.
## Examples
```bash
structix ops add microservice my-service my-image:latest --db postgres --port 8080 --replicas 3 --deploy --with-ingress
```
"""
def generate_markdown(prompt: str) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "You are a professional technical writer for CLI documentation.",
},
{"role": "user", "content": prompt},
],
temperature=0.5,
)
return str(response.choices[0].message.content)
def format_title(command_path: str) -> str:
"""Format the title based on the command path."""
parts: List[str] = command_path.replace(".py", "").split(os.sep)
last_part: str = parts[-1]
title: str = last_part.replace("_", " ").capitalize()
return title
def main() -> None:
"""Main execution function."""
commands: List[str] = list_command_files(COMMANDS_DIR)
existing_docs: Set[str] = set()
for root, dirs, files in os.walk(DOCS_DIR):
for file in files:
if file.endswith(".md"):
relative_path: str = os.path.relpath(
os.path.join(root, file), DOCS_DIR
)
existing_docs.add(relative_path.replace(".md", ".py"))
for command in commands:
doc_path: str = os.path.join(DOCS_DIR, command.replace(".py", ".md"))
if command not in existing_docs:
os.makedirs(os.path.dirname(doc_path), exist_ok=True)
print(f"📄 Generating documentation for {command}...")
prompt: str = build_prompt(command)
markdown_content: str = generate_markdown(prompt)
title: str = format_title(command)
first_lines: str = (
f"# {title}\n\n"
f"Documentation for `structix {command.replace('.py', '').replace(os.sep, ' ')}` command.\n\n"
)
with open(doc_path, "w", encoding="utf-8") as f:
f.write(first_lines)
f.write(markdown_content)
else:
print(f"✅ Documentation already exists for {command}")
for existing_doc in existing_docs:
if existing_doc not in commands and existing_doc != "overview.py":
doc_md_path: str = os.path.join(
DOCS_DIR, existing_doc.replace(".py", ".md")
)
print(f"🗑 Removing orphan documentation {doc_md_path}...")
os.remove(doc_md_path)
if __name__ == "__main__":
main()