-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmint.py
More file actions
250 lines (202 loc) · 7.22 KB
/
mint.py
File metadata and controls
250 lines (202 loc) · 7.22 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#!/usr/bin/env python3
"""
mint-cli - Project scaffolding tool
Suggests project names, creates the directory, initializes git,
and opens WHY.md for you to document your vision and intent.
"""
import os
import sys
import subprocess
from datetime import date
from dotenv import load_dotenv
from config import BASE_PATH, EDITOR, NUM_SUGGESTIONS
# Load env vars from .env if present
load_dotenv()
# Optional: OpenAI client for name suggestions
client = None
api_key = os.getenv("DIGITAL_OCEAN_MODEL_ACCESS_KEY")
if api_key:
try:
from openai import OpenAI
client = OpenAI(
base_url="https://inference.do-ai.run/v1/",
api_key=api_key,
)
except ImportError:
pass
def suggest_names(description: str) -> list[str] | None:
"""Call DO inference API and return list of name suggestions. Returns None if unavailable."""
if not client:
return None
try:
prompt = f"""
I am starting a new side project.
What I'm trying to build:
{description}
Suggest {NUM_SUGGESTIONS} short, memorable project names.
Guidelines:
- Prefer 1–2 words
- Always lowercase with hyphens (e.g., book-shelf, read-track, quick-note)
- Easy to type
- No explanations, just the names
- One name per line
"""
resp = client.chat.completions.create(
model="llama3-8b-instruct",
messages=[
{
"role": "system",
"content": "You help developers come up with good project names."
},
{
"role": "user",
"content": prompt
}
],
temperature=0.9,
)
# Parse response into list of names
raw = resp.choices[0].message.content.strip()
names = []
for line in raw.split("\n"):
# Clean up numbering like "1. Name" or "1) Name" or just "Name"
line = line.strip()
if not line:
continue
# Remove leading numbers and punctuation
if line[0].isdigit():
# Skip past number and any following punctuation/space
i = 0
while i < len(line) and (line[i].isdigit() or line[i] in ".)- "):
i += 1
line = line[i:].strip()
if line:
names.append(line)
return names if names else None
except Exception:
return None
def select_name(names: list[str]) -> str | None:
"""Display numbered list, get user selection or custom name. Returns None to regenerate."""
print("\nProject name ideas:\n")
print(" r. [Regenerate names]")
print(" 0. [Type your own name]")
for i, name in enumerate(names, 1):
print(f" {i}. {name}")
while True:
choice = input("\nPick a number (r to regenerate, 0 to type your own): ").strip().lower()
if choice == "r":
return None # Signal to regenerate
if choice == "0":
custom = input("Enter your project name: ").strip()
if custom:
return custom
print("Name cannot be empty.")
continue
try:
idx = int(choice)
if 1 <= idx <= len(names):
return names[idx - 1]
print(f"Please enter a number between 0 and {len(names)}.")
except ValueError:
# User typed a name directly instead of a number
if choice:
return choice
print("Please enter a valid choice.")
def create_project(name: str) -> str:
"""Create directory, git init, create WHY.md and DECISIONS.md. Return path."""
project_path = os.path.join(BASE_PATH, name)
# Check if directory already exists
if os.path.exists(project_path):
print(f"\nError: Directory already exists: {project_path}")
raise SystemExit(1)
# Create directory
os.makedirs(project_path)
# Git init
subprocess.run(["git", "init"], cwd=project_path, capture_output=True)
# Create docs directory
docs_path = os.path.join(project_path, "docs")
os.makedirs(docs_path)
# Create WHY.md
today = date.today().strftime("%Y-%m-%d")
why_content = f"""# {name}
> Document your vision and intent before writing code.
> Created {today} with [mint-cli](https://github.com/ajotwani/mint-cli)
## I'm building this because:
-
## I'm intentionally not solving:
-
## This will be done when:
-
"""
why_path = os.path.join(docs_path, "WHY.md")
with open(why_path, "w") as f:
f.write(why_content)
# Create DECISIONS.md
decisions_content = f"""# {name} - Decisions
> Log the decisions you make as you build.
> Created {today} with [mint-cli](https://github.com/ajotwani/mint-cli)
---
"""
decisions_path = os.path.join(docs_path, "DECISIONS.md")
with open(decisions_path, "w") as f:
f.write(decisions_content)
# Print summary
print(f"\n✅ Created {project_path}")
print("✅ Initialized git repository")
print("✅ Created docs/WHY.md")
print("✅ Created docs/DECISIONS.md")
return project_path
def open_in_editor(filepath: str):
"""Open file in configured editor."""
print(f"\nOpening in {EDITOR}...")
subprocess.run([EDITOR, filepath])
def main():
print("\n✨ mint-cli - Project Scaffolding\n")
# Check if project name was passed as argument
if len(sys.argv) > 1:
name = sys.argv[1]
full_path = os.path.join(BASE_PATH, name)
confirm = input(f"Create '{full_path}'? (y/n): ").strip().lower()
if confirm != 'y':
print("Cancelled.")
return
selected = name
else:
description = input("What are you trying to build?\n> ").strip()
if not description:
print("Please describe what you're building.")
return
# Try to generate name suggestions
selected = None
print("\nGenerating name ideas...")
names = suggest_names(description)
if names:
# Got suggestions, let user pick
while selected is None:
selected = select_name(names)
if selected is None:
# User wants to regenerate
print("\nRegenerating...")
names = suggest_names(description)
if not names:
print("Could not regenerate. Enter a name manually.")
selected = input("Project name: ").strip()
if not selected:
print("Name cannot be empty.")
return
else:
# No API available, ask for name directly
print("(Name suggestions unavailable)")
selected = input("Project name: ").strip()
if not selected:
print("Name cannot be empty.")
return
project_path = create_project(selected)
why_file = os.path.join(project_path, "docs", "WHY.md")
open_in_editor(why_file)
# Write path to temp file for shell wrapper to read
mint_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(mint_dir, ".last_project"), "w") as f:
f.write(project_path)
if __name__ == "__main__":
main()