thachvd-kit 1.0.0 → 1.0.2
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.
- package/LICENSE +21 -0
- package/README.md +30 -19
- package/agents/frontend-specialist.md +2 -2
- package/agents/mobile-developer.md +4 -4
- package/bin/cli.js +370 -118
- package/package.json +48 -25
- package/rules/GEMINI.md +5 -3
- package/scripts/init.py +317 -122
- package/skills/desktop-design/SKILL.md +25 -0
- package/skills/react-frontend/SKILL.md +25 -0
- package/.shared/ui-ux-pro-max/data/charts.csv +0 -26
- package/.shared/ui-ux-pro-max/data/colors.csv +0 -97
- package/.shared/ui-ux-pro-max/data/icons.csv +0 -101
- package/.shared/ui-ux-pro-max/data/landing.csv +0 -31
- package/.shared/ui-ux-pro-max/data/products.csv +0 -97
- package/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
- package/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
- package/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
- package/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
- package/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
- package/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
- package/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
- package/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
- package/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
- package/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
- package/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
- package/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
- package/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
- package/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
- package/.shared/ui-ux-pro-max/data/styles.csv +0 -59
- package/.shared/ui-ux-pro-max/data/typography.csv +0 -58
- package/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
- package/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
- package/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
- package/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.shared/ui-ux-pro-max/scripts/core.py +0 -258
- package/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
- package/.shared/ui-ux-pro-max/scripts/search.py +0 -106
- package/ARCHITECTURE.md +0 -288
- package/CLAUDE.md +0 -90
- package/mcp_config.json +0 -24
package/rules/GEMINI.md
CHANGED
|
@@ -53,7 +53,7 @@ trigger: always_on
|
|
|
53
53
|
| Web frontend (any framework) | `frontend-specialist` | `frontend-design` + framework skill from PROJECT_CONTEXT |
|
|
54
54
|
| Backend API (any language) | `backend-specialist` | language skill + `api-design` |
|
|
55
55
|
| Database / Schema | `database-architect` | `database-design` |
|
|
56
|
-
| Mobile / Desktop (Tauri, RN, Flutter) | `mobile-developer` |
|
|
56
|
+
| Mobile / Desktop (Tauri, RN, Flutter) | `mobile-developer` | platform skill from PROJECT_CONTEXT |
|
|
57
57
|
| DevOps / Cloud / CI | `devops-engineer` | `docker-patterns`, `deployment-procedures` |
|
|
58
58
|
| Security audit | `security-auditor` | `vulnerability-scanner` |
|
|
59
59
|
| Debug / RCA | `debugger` | `systematic-debugging` |
|
|
@@ -65,12 +65,14 @@ trigger: always_on
|
|
|
65
65
|
| Framework/Language | Load Skill |
|
|
66
66
|
|-------------------|-----------|
|
|
67
67
|
| Vue | `frontend-design` (+ Vue-specific if available) |
|
|
68
|
-
| React
|
|
68
|
+
| React | `react-frontend` |
|
|
69
|
+
| Next.js | `nextjs-react-expert` |
|
|
69
70
|
| Laravel | `laravel-patterns` + `laravel-security` |
|
|
70
71
|
| Node.js | `nodejs-best-practices` |
|
|
71
72
|
| Python | `python-patterns` |
|
|
72
73
|
| Go | `golang-patterns` |
|
|
73
|
-
| Rust
|
|
74
|
+
| Rust | `rust-pro` |
|
|
75
|
+
| Tauri | `desktop-design` + `rust-pro` |
|
|
74
76
|
| Docker | `docker-patterns` |
|
|
75
77
|
|
|
76
78
|
> If stack not in mapping → use agent's general skill set + `clean-code`.
|
package/scripts/init.py
CHANGED
|
@@ -9,11 +9,12 @@ Usage:
|
|
|
9
9
|
python scripts/init.py --platform claude # Only CLAUDE.md
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import sys
|
|
13
|
-
import os
|
|
14
|
-
import argparse
|
|
15
|
-
|
|
16
|
-
from
|
|
12
|
+
import sys
|
|
13
|
+
import os
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from datetime import datetime
|
|
17
18
|
|
|
18
19
|
# ─── Color helpers ──────────────────────────────────────────────────────────
|
|
19
20
|
|
|
@@ -53,13 +54,17 @@ def ask_multi(prompt, choices, default=None):
|
|
|
53
54
|
selected.append(part) # allow free-text
|
|
54
55
|
return selected
|
|
55
56
|
|
|
56
|
-
def ask_bool(prompt, default=True):
|
|
57
|
+
def ask_bool(prompt, default=True):
|
|
57
58
|
"""Yes/no prompt."""
|
|
58
59
|
default_str = "Y/n" if default else "y/N"
|
|
59
60
|
val = input(f" {bold('→')} {prompt} [{dim(default_str)}]: ").strip().lower()
|
|
60
|
-
if not val:
|
|
61
|
-
return default
|
|
62
|
-
return val.startswith("y")
|
|
61
|
+
if not val:
|
|
62
|
+
return default
|
|
63
|
+
return val.startswith("y")
|
|
64
|
+
|
|
65
|
+
def ask_select(prompt, choices, default=None):
|
|
66
|
+
default = default or choices[0]
|
|
67
|
+
return ask(prompt, default=default, choices=choices)
|
|
63
68
|
|
|
64
69
|
def section(title):
|
|
65
70
|
print(f"\n{bold(cyan('━━ ' + title + ' '))}")
|
|
@@ -79,10 +84,10 @@ LANG_SKILLS = {
|
|
|
79
84
|
"csharp": ["api-design"],
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
FRAMEWORK_SKILLS = {
|
|
83
|
-
"vue": ["frontend-design", "tailwind-patterns"],
|
|
84
|
-
"react": ["
|
|
85
|
-
"next": ["nextjs-react-expert", "frontend-design"],
|
|
87
|
+
FRAMEWORK_SKILLS = {
|
|
88
|
+
"vue": ["frontend-design", "tailwind-patterns"],
|
|
89
|
+
"react": ["react-frontend", "frontend-design"],
|
|
90
|
+
"next": ["nextjs-react-expert", "frontend-design"],
|
|
86
91
|
"nuxt": ["frontend-design"],
|
|
87
92
|
"svelte": ["frontend-design"],
|
|
88
93
|
"laravel": ["laravel-patterns", "laravel-security", "laravel-tdd"],
|
|
@@ -91,12 +96,12 @@ FRAMEWORK_SKILLS = {
|
|
|
91
96
|
"django": ["python-patterns", "api-design"],
|
|
92
97
|
"gin": ["golang-patterns", "golang-testing"],
|
|
93
98
|
"fiber": ["golang-patterns", "golang-testing"],
|
|
94
|
-
"tauri": ["rust-pro", "
|
|
95
|
-
"flutter": ["mobile-design"],
|
|
96
|
-
"react-native": ["mobile-design"],
|
|
97
|
-
}
|
|
99
|
+
"tauri": ["rust-pro", "desktop-design"],
|
|
100
|
+
"flutter": ["mobile-design"],
|
|
101
|
+
"react-native": ["mobile-design"],
|
|
102
|
+
}
|
|
98
103
|
|
|
99
|
-
INFRA_SKILLS = {
|
|
104
|
+
INFRA_SKILLS = {
|
|
100
105
|
"docker": ["docker-patterns"],
|
|
101
106
|
"aws": ["deployment-procedures", "docker-patterns"],
|
|
102
107
|
"gcp": ["deployment-procedures", "docker-patterns"],
|
|
@@ -120,7 +125,157 @@ def resolve_skills(data):
|
|
|
120
125
|
if key in infra_lower:
|
|
121
126
|
skills.update(vals)
|
|
122
127
|
skills.update(["clean-code", "systematic-debugging"])
|
|
123
|
-
return sorted(skills)
|
|
128
|
+
return sorted(skills)
|
|
129
|
+
|
|
130
|
+
FRAMEWORKS_ALL = ["vue", "react", "next", "nuxt", "svelte", "angular",
|
|
131
|
+
"laravel", "express", "fastapi", "django", "gin", "fiber", "nestjs",
|
|
132
|
+
"tauri", "flutter", "react-native"]
|
|
133
|
+
LANGUAGES_ALL = ["typescript", "javascript", "python", "go", "php", "rust", "java", "swift", "kotlin", "csharp", "c", "cpp"]
|
|
134
|
+
PROJECT_TYPES_ALL = ["web-app", "api", "mobile", "desktop", "cli", "library", "fullstack"]
|
|
135
|
+
DATABASES_ALL = ["postgres", "mysql", "sqlite", "mongodb", "redis", "dynamodb", "supabase", "prisma", "none"]
|
|
136
|
+
PACKAGE_MANAGERS_ALL = ["npm", "pnpm", "yarn", "bun", "pip", "uv", "poetry", "go mod", "cargo", "composer", "auto-detect"]
|
|
137
|
+
TEST_FRAMEWORKS_ALL = ["jest", "vitest", "pytest", "go test", "phpunit", "pest", "rspec", "junit", "auto-detect"]
|
|
138
|
+
CLOUDS_ALL = ["aws", "gcp", "azure", "vercel", "fly", "railway", "digitalocean", "none"]
|
|
139
|
+
|
|
140
|
+
def read_text_if_exists(*parts):
|
|
141
|
+
path = Path.cwd().joinpath(*parts)
|
|
142
|
+
try:
|
|
143
|
+
return path.read_text(encoding="utf-8") if path.exists() else ""
|
|
144
|
+
except Exception:
|
|
145
|
+
return ""
|
|
146
|
+
|
|
147
|
+
def read_json_if_exists(*parts):
|
|
148
|
+
path = Path.cwd().joinpath(*parts)
|
|
149
|
+
try:
|
|
150
|
+
return json.loads(path.read_text(encoding="utf-8")) if path.exists() else None
|
|
151
|
+
except Exception:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def detect_package_manager():
|
|
155
|
+
cwd = Path.cwd()
|
|
156
|
+
if (cwd / "pnpm-lock.yaml").exists(): return "pnpm"
|
|
157
|
+
if (cwd / "yarn.lock").exists(): return "yarn"
|
|
158
|
+
if (cwd / "bun.lockb").exists() or (cwd / "bun.lock").exists(): return "bun"
|
|
159
|
+
if (cwd / "package-lock.json").exists(): return "npm"
|
|
160
|
+
if (cwd / "uv.lock").exists(): return "uv"
|
|
161
|
+
if (cwd / "poetry.lock").exists(): return "poetry"
|
|
162
|
+
if (cwd / "requirements.txt").exists() or (cwd / "pyproject.toml").exists(): return "pip"
|
|
163
|
+
if (cwd / "go.mod").exists(): return "go mod"
|
|
164
|
+
if (cwd / "Cargo.toml").exists(): return "cargo"
|
|
165
|
+
if (cwd / "composer.json").exists(): return "composer"
|
|
166
|
+
return "auto-detect"
|
|
167
|
+
|
|
168
|
+
def uniq(values):
|
|
169
|
+
return list(dict.fromkeys(v for v in values if v))
|
|
170
|
+
|
|
171
|
+
def scan_project():
|
|
172
|
+
pkg = read_json_if_exists("package.json") or {}
|
|
173
|
+
deps = {}
|
|
174
|
+
for key in ("dependencies", "devDependencies", "peerDependencies"):
|
|
175
|
+
deps.update(pkg.get(key, {}) or {})
|
|
176
|
+
dep_names = set(deps.keys())
|
|
177
|
+
frameworks = []
|
|
178
|
+
infrastructure = []
|
|
179
|
+
primary_language = ""
|
|
180
|
+
project_type = ""
|
|
181
|
+
database = "none"
|
|
182
|
+
test_framework = "auto-detect"
|
|
183
|
+
cloud = "none"
|
|
184
|
+
ci_cd = "none"
|
|
185
|
+
|
|
186
|
+
def add_framework(name, condition):
|
|
187
|
+
if condition:
|
|
188
|
+
frameworks.append(name)
|
|
189
|
+
|
|
190
|
+
requirements = read_text_if_exists("requirements.txt")
|
|
191
|
+
go_mod = read_text_if_exists("go.mod")
|
|
192
|
+
pubspec = read_text_if_exists("pubspec.yaml")
|
|
193
|
+
|
|
194
|
+
add_framework("next", "next" in dep_names)
|
|
195
|
+
add_framework("react", "react" in dep_names)
|
|
196
|
+
add_framework("vue", "vue" in dep_names)
|
|
197
|
+
add_framework("nuxt", "nuxt" in dep_names)
|
|
198
|
+
add_framework("svelte", "svelte" in dep_names or "@sveltejs/kit" in dep_names)
|
|
199
|
+
add_framework("angular", "@angular/core" in dep_names)
|
|
200
|
+
add_framework("express", "express" in dep_names)
|
|
201
|
+
add_framework("nestjs", "@nestjs/core" in dep_names)
|
|
202
|
+
add_framework("fastapi", "fastapi" in requirements.lower())
|
|
203
|
+
add_framework("django", "django" in requirements.lower())
|
|
204
|
+
add_framework("tauri", "@tauri-apps/api" in dep_names or Path("src-tauri/tauri.conf.json").exists() or Path("src-tauri/tauri.conf.json5").exists())
|
|
205
|
+
add_framework("flutter", "flutter:" in pubspec.lower())
|
|
206
|
+
add_framework("react-native", "react-native" in dep_names or "expo" in dep_names)
|
|
207
|
+
add_framework("laravel", Path("artisan").exists())
|
|
208
|
+
add_framework("gin", "github.com/gin-gonic/gin" in go_mod.lower())
|
|
209
|
+
add_framework("fiber", "github.com/gofiber/fiber" in go_mod.lower())
|
|
210
|
+
|
|
211
|
+
if Path("tsconfig.json").exists() or "typescript" in dep_names: primary_language = "typescript"
|
|
212
|
+
elif Path("Cargo.toml").exists() or Path("src-tauri/Cargo.toml").exists(): primary_language = "rust"
|
|
213
|
+
elif Path("go.mod").exists(): primary_language = "go"
|
|
214
|
+
elif Path("requirements.txt").exists() or Path("pyproject.toml").exists(): primary_language = "python"
|
|
215
|
+
elif Path("composer.json").exists(): primary_language = "php"
|
|
216
|
+
elif dep_names or Path("package.json").exists(): primary_language = "javascript"
|
|
217
|
+
|
|
218
|
+
if "tauri" in frameworks: project_type = "desktop"
|
|
219
|
+
elif "flutter" in frameworks or "react-native" in frameworks: project_type = "mobile"
|
|
220
|
+
elif any(f in frameworks for f in ["express", "fastapi", "django", "laravel", "gin", "fiber", "nestjs"]):
|
|
221
|
+
project_type = "fullstack" if any(f in frameworks for f in ["react", "next", "vue", "nuxt", "svelte", "angular"]) else "api"
|
|
222
|
+
elif any(f in frameworks for f in ["react", "next", "vue", "nuxt", "svelte", "angular"]):
|
|
223
|
+
project_type = "web-app"
|
|
224
|
+
elif Path("package.json").exists() and pkg.get("bin"):
|
|
225
|
+
project_type = "cli"
|
|
226
|
+
|
|
227
|
+
if "pg" in dep_names or "postgres" in dep_names or "@prisma/client" in dep_names or "postgres" in json.dumps(pkg).lower(): database = "postgres"
|
|
228
|
+
elif "mysql" in dep_names or "mysql2" in dep_names: database = "mysql"
|
|
229
|
+
elif "sqlite3" in dep_names or "better-sqlite3" in dep_names or "rusqlite" in dep_names: database = "sqlite"
|
|
230
|
+
elif "mongodb" in dep_names or "mongoose" in dep_names: database = "mongodb"
|
|
231
|
+
elif "redis" in dep_names or "ioredis" in dep_names: database = "redis"
|
|
232
|
+
elif "@aws-sdk/client-dynamodb" in dep_names: database = "dynamodb"
|
|
233
|
+
|
|
234
|
+
if "vitest" in dep_names: test_framework = "vitest"
|
|
235
|
+
elif "jest" in dep_names: test_framework = "jest"
|
|
236
|
+
elif "pytest" in dep_names or "pytest" in requirements.lower(): test_framework = "pytest"
|
|
237
|
+
elif "phpunit" in dep_names: test_framework = "phpunit"
|
|
238
|
+
elif "pest" in dep_names: test_framework = "pest"
|
|
239
|
+
elif Path("go.mod").exists(): test_framework = "go test"
|
|
240
|
+
|
|
241
|
+
if Path("Dockerfile").exists() or Path("docker-compose.yml").exists() or Path("docker-compose.yaml").exists():
|
|
242
|
+
infrastructure.append("docker")
|
|
243
|
+
|
|
244
|
+
if Path("vercel.json").exists():
|
|
245
|
+
cloud = "vercel"
|
|
246
|
+
infrastructure.append("vercel")
|
|
247
|
+
elif Path("fly.toml").exists():
|
|
248
|
+
cloud = "fly"
|
|
249
|
+
infrastructure.append("fly")
|
|
250
|
+
elif Path("railway.json").exists() or Path("railway.toml").exists():
|
|
251
|
+
cloud = "railway"
|
|
252
|
+
infrastructure.append("railway")
|
|
253
|
+
|
|
254
|
+
if Path(".github/workflows").exists(): ci_cd = "github-actions"
|
|
255
|
+
elif Path(".gitlab-ci.yml").exists(): ci_cd = "gitlab-ci"
|
|
256
|
+
elif Path(".circleci/config.yml").exists(): ci_cd = "circleci"
|
|
257
|
+
elif Path("Jenkinsfile").exists(): ci_cd = "jenkins"
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
"project_name": Path.cwd().name,
|
|
261
|
+
"description": pkg.get("description", "A software project"),
|
|
262
|
+
"project_type": project_type if project_type in PROJECT_TYPES_ALL else "web-app",
|
|
263
|
+
"primary_language": primary_language if primary_language in LANGUAGES_ALL else "typescript",
|
|
264
|
+
"frameworks": uniq(frameworks),
|
|
265
|
+
"database": database if database in DATABASES_ALL else "none",
|
|
266
|
+
"package_manager": detect_package_manager(),
|
|
267
|
+
"uses_docker": "docker" in infrastructure,
|
|
268
|
+
"cloud": cloud,
|
|
269
|
+
"infrastructure": uniq(infrastructure),
|
|
270
|
+
"ci_cd": ci_cd,
|
|
271
|
+
"test_framework": test_framework if test_framework in TEST_FRAMEWORKS_ALL else "auto-detect",
|
|
272
|
+
"max_file_lines": "300",
|
|
273
|
+
"code_style": "project-standard",
|
|
274
|
+
"architecture_notes": "<!-- Add notes -->",
|
|
275
|
+
"current_phase": "Initial setup",
|
|
276
|
+
"next_phase": "—",
|
|
277
|
+
"known_issues": "<!-- None yet -->",
|
|
278
|
+
}
|
|
124
279
|
|
|
125
280
|
def resolve_agent_table(data):
|
|
126
281
|
rows = []
|
|
@@ -252,11 +407,12 @@ def generate_claude_md(data):
|
|
|
252
407
|
{data['description']}
|
|
253
408
|
Stack: {data['primary_language']} | {', '.join(data.get('frameworks', []))}
|
|
254
409
|
|
|
255
|
-
## Rules
|
|
256
|
-
- Read PROJECT_CONTEXT.md first for stack details and agent routing.
|
|
257
|
-
-
|
|
258
|
-
-
|
|
259
|
-
-
|
|
410
|
+
## Rules
|
|
411
|
+
- Read PROJECT_CONTEXT.md first for stack details and agent routing.
|
|
412
|
+
- If PROJECT_CONTEXT.md is missing or stale, create/update it before major work.
|
|
413
|
+
- Follow skills in .claude/skills/ (or skills/ if using Antigravity).
|
|
414
|
+
- Tests are mandatory. Verify before claiming done.
|
|
415
|
+
- Max {data.get('max_file_lines', '300')} lines per file.
|
|
260
416
|
|
|
261
417
|
## Key Skills
|
|
262
418
|
- Plan: `plan-writing` → `executing-plans`
|
|
@@ -275,19 +431,20 @@ See PROJECT_CONTEXT.md for full agent routing table.
|
|
|
275
431
|
|
|
276
432
|
# ─── Main wizard ─────────────────────────────────────────────────────────────
|
|
277
433
|
|
|
278
|
-
def main():
|
|
279
|
-
parser = argparse.ArgumentParser(description="thachvd-kit project init wizard")
|
|
280
|
-
parser.add_argument("--platform", choices=["antigravity", "claude", "all"], default="antigravity",
|
|
281
|
-
help="Which platform config to generate (default: antigravity)")
|
|
282
|
-
parser.add_argument("--yes", "-y", action="store_true", help="Use defaults without prompting")
|
|
283
|
-
|
|
434
|
+
def main():
|
|
435
|
+
parser = argparse.ArgumentParser(description="thachvd-kit project init wizard")
|
|
436
|
+
parser.add_argument("--platform", choices=["antigravity", "claude", "all"], default="antigravity",
|
|
437
|
+
help="Which platform config to generate (default: antigravity)")
|
|
438
|
+
parser.add_argument("--yes", "-y", action="store_true", help="Use defaults without prompting")
|
|
439
|
+
parser.add_argument("--scan", action="store_true", help="Scan the current project to prefill or auto-generate PROJECT_CONTEXT.md")
|
|
440
|
+
args = parser.parse_args()
|
|
284
441
|
|
|
285
442
|
print(f"""
|
|
286
|
-
{bold(cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))}
|
|
287
|
-
{bold('thachvd-kit')} — Project Init Wizard
|
|
288
|
-
Generates PROJECT_CONTEXT.md for smart agent routing
|
|
289
|
-
{bold(cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))}
|
|
290
|
-
""")
|
|
443
|
+
{bold(cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))}
|
|
444
|
+
{bold('thachvd-kit')} — Project Init Wizard
|
|
445
|
+
Generates PROJECT_CONTEXT.md for smart agent routing{' (scan-assisted)' if args.scan else ''}
|
|
446
|
+
{bold(cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))}
|
|
447
|
+
""")
|
|
291
448
|
|
|
292
449
|
# Check if PROJECT_CONTEXT.md already exists
|
|
293
450
|
output_path = Path("PROJECT_CONTEXT.md")
|
|
@@ -297,85 +454,118 @@ def main():
|
|
|
297
454
|
print(f"\n {yellow('Aborted.')} Existing PROJECT_CONTEXT.md unchanged.\n")
|
|
298
455
|
sys.exit(0)
|
|
299
456
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
457
|
+
use_scan = args.scan
|
|
458
|
+
if not args.scan and not args.yes:
|
|
459
|
+
section("Setup Mode")
|
|
460
|
+
project_setup = ask_select("Project already exists or are you starting fresh",
|
|
461
|
+
["existing", "new"], default="existing")
|
|
462
|
+
if project_setup == "existing":
|
|
463
|
+
existing_mode = ask_select("How should thachvd-kit build PROJECT_CONTEXT.md",
|
|
464
|
+
["scan", "manual"], default="scan")
|
|
465
|
+
use_scan = existing_mode == "scan"
|
|
466
|
+
|
|
467
|
+
scanned = scan_project() if use_scan else None
|
|
468
|
+
if use_scan:
|
|
469
|
+
print(f" {dim('Scan mode: detecting stack from current project files...')}")
|
|
470
|
+
print(f" {bold('Scan result:')} {scanned['primary_language']} | {', '.join(scanned['frameworks']) or 'no framework'}")
|
|
471
|
+
print(f" {bold('Detected infra:')} {', '.join(scanned['infrastructure']) or 'none'}\n")
|
|
472
|
+
elif not args.yes:
|
|
473
|
+
print(f" {dim('Manual mode: asking for project context explicitly.')}\n")
|
|
474
|
+
|
|
475
|
+
if args.yes and scanned:
|
|
476
|
+
data = dict(scanned)
|
|
477
|
+
else:
|
|
478
|
+
data = {}
|
|
479
|
+
|
|
480
|
+
if not args.yes:
|
|
481
|
+
# ── Project Info ──────────────────────────────────────────────────────
|
|
482
|
+
section("Project Info")
|
|
483
|
+
data["project_name"] = ask("Project name", default=(scanned or {}).get("project_name", Path.cwd().name))
|
|
484
|
+
data["description"] = ask("Short description", default=(scanned or {}).get("description", "A software project"))
|
|
485
|
+
data["project_type"] = ask("Project type",
|
|
486
|
+
default=(scanned or {}).get("project_type", "web-app"),
|
|
487
|
+
choices=PROJECT_TYPES_ALL)
|
|
488
|
+
|
|
489
|
+
# ── Tech Stack ────────────────────────────────────────────────────────
|
|
490
|
+
section("Tech Stack")
|
|
491
|
+
|
|
492
|
+
data["primary_language"] = ask("Primary language",
|
|
493
|
+
default=(scanned or {}).get("primary_language", "typescript"),
|
|
494
|
+
choices=LANGUAGES_ALL)
|
|
495
|
+
|
|
496
|
+
frameworks = ask_multi("Frameworks (select all that apply)", FRAMEWORKS_ALL, default=", ".join((scanned or {}).get("frameworks", [])) or None)
|
|
497
|
+
data["frameworks"] = frameworks or []
|
|
498
|
+
|
|
499
|
+
data["database"] = ask("Database / ORM", default=(scanned or {}).get("database", "postgres"), choices=DATABASES_ALL)
|
|
500
|
+
|
|
501
|
+
data["package_manager"] = ask("Package manager", default=(scanned or {}).get("package_manager", "auto-detect"), choices=PACKAGE_MANAGERS_ALL)
|
|
502
|
+
|
|
503
|
+
# ── Infrastructure ────────────────────────────────────────────────────
|
|
504
|
+
section("Infrastructure")
|
|
505
|
+
|
|
506
|
+
data["uses_docker"] = ask_bool("Uses Docker / Docker Compose?", default=(scanned or {}).get("uses_docker", True))
|
|
507
|
+
cloud = ask("Cloud provider", default=(scanned or {}).get("cloud", "none"), choices=CLOUDS_ALL)
|
|
508
|
+
data["cloud"] = cloud
|
|
509
|
+
|
|
510
|
+
infrastructure = []
|
|
511
|
+
if data["uses_docker"]: infrastructure.append("docker")
|
|
512
|
+
if cloud != "none": infrastructure.append(cloud)
|
|
513
|
+
data["infrastructure"] = infrastructure
|
|
514
|
+
|
|
515
|
+
CI_CD = ["github-actions", "gitlab-ci", "circleci", "jenkins", "none"]
|
|
516
|
+
data["ci_cd"] = ask("CI/CD", default=(scanned or {}).get("ci_cd", "github-actions"), choices=CI_CD)
|
|
517
|
+
|
|
518
|
+
# ── Testing ───────────────────────────────────────────────────────────
|
|
519
|
+
section("Testing")
|
|
520
|
+
|
|
521
|
+
data["test_framework"] = ask("Test framework", default=(scanned or {}).get("test_framework", "auto-detect"), choices=TEST_FRAMEWORKS_ALL)
|
|
522
|
+
|
|
523
|
+
# ── Conventions ───────────────────────────────────────────────────────
|
|
524
|
+
section("Coding Conventions")
|
|
525
|
+
|
|
526
|
+
data["max_file_lines"] = ask("Max lines per file before splitting", default=(scanned or {}).get("max_file_lines", "300"))
|
|
527
|
+
data["code_style"] = ask("Code style / linter", default=(scanned or {}).get("code_style", "project-standard"),
|
|
528
|
+
choices=["eslint", "prettier", "ruff", "gofmt", "pint", "standard", "project-standard"])
|
|
529
|
+
|
|
530
|
+
# ── Optional fields ───────────────────────────────────────────────────
|
|
531
|
+
section("Architecture (Optional — press Enter to skip)")
|
|
532
|
+
|
|
533
|
+
data["architecture_notes"] = ask("Key architecture notes", default=(scanned or {}).get("architecture_notes", "<!-- Add notes -->"))
|
|
534
|
+
data["current_phase"] = ask("Current phase", default=(scanned or {}).get("current_phase", "Initial setup"))
|
|
535
|
+
data["next_phase"] = ask("Next planned phase", default=(scanned or {}).get("next_phase", "—"))
|
|
536
|
+
data["known_issues"] = ask("Known issues / tasks", default=(scanned or {}).get("known_issues", "<!-- None yet -->"))
|
|
537
|
+
|
|
538
|
+
data["project_name"] = data.get("project_name", (scanned or {}).get("project_name", Path.cwd().name))
|
|
539
|
+
data["description"] = data.get("description", (scanned or {}).get("description", "A software project"))
|
|
540
|
+
data["project_type"] = data.get("project_type", (scanned or {}).get("project_type", "web-app"))
|
|
541
|
+
data["primary_language"] = data.get("primary_language", (scanned or {}).get("primary_language", "typescript"))
|
|
542
|
+
data["frameworks"] = data.get("frameworks", (scanned or {}).get("frameworks", []))
|
|
543
|
+
data["database"] = data.get("database", (scanned or {}).get("database", "none"))
|
|
544
|
+
data["package_manager"] = data.get("package_manager", (scanned or {}).get("package_manager", "auto-detect"))
|
|
545
|
+
data["uses_docker"] = data.get("uses_docker", (scanned or {}).get("uses_docker", True))
|
|
546
|
+
data["cloud"] = data.get("cloud", (scanned or {}).get("cloud", "none"))
|
|
547
|
+
data["test_framework"] = data.get("test_framework", (scanned or {}).get("test_framework", "auto-detect"))
|
|
548
|
+
data["max_file_lines"] = data.get("max_file_lines", (scanned or {}).get("max_file_lines", "300"))
|
|
549
|
+
data["code_style"] = data.get("code_style", (scanned or {}).get("code_style", "project-standard"))
|
|
550
|
+
data["architecture_notes"] = data.get("architecture_notes", (scanned or {}).get("architecture_notes", "<!-- Add notes -->"))
|
|
551
|
+
data["current_phase"] = data.get("current_phase", (scanned or {}).get("current_phase", "Initial setup"))
|
|
552
|
+
data["next_phase"] = data.get("next_phase", (scanned or {}).get("next_phase", "—"))
|
|
553
|
+
data["known_issues"] = data.get("known_issues", (scanned or {}).get("known_issues", "<!-- None yet -->"))
|
|
554
|
+
data["infrastructure"] = []
|
|
555
|
+
if data["uses_docker"]: data["infrastructure"].append("docker")
|
|
556
|
+
if data["cloud"] != "none": data["infrastructure"].append(data["cloud"])
|
|
557
|
+
|
|
558
|
+
# ── Generate files ────────────────────────────────────────────────────
|
|
559
|
+
section("Generating Files")
|
|
370
560
|
|
|
371
561
|
# PROJECT_CONTEXT.md (always)
|
|
372
|
-
context_content = generate_project_context(data)
|
|
373
|
-
output_path.write_text(context_content, encoding="utf-8")
|
|
374
|
-
print(f" {green('✓')} {bold('PROJECT_CONTEXT.md')} generated")
|
|
375
|
-
|
|
376
|
-
# CLAUDE.md (for Claude Code / Codex)
|
|
377
|
-
if args.platform in ("claude", "all"):
|
|
378
|
-
claude_path = Path("CLAUDE.md")
|
|
562
|
+
context_content = generate_project_context(data)
|
|
563
|
+
output_path.write_text(context_content, encoding="utf-8")
|
|
564
|
+
print(f" {green('✓')} {bold('PROJECT_CONTEXT.md')} generated")
|
|
565
|
+
|
|
566
|
+
# CLAUDE.md (for Claude Code / Codex)
|
|
567
|
+
if args.platform in ("claude", "all"):
|
|
568
|
+
claude_path = Path("CLAUDE.md")
|
|
379
569
|
claude_path.write_text(generate_claude_md(data), encoding="utf-8")
|
|
380
570
|
print(f" {green('✓')} {bold('CLAUDE.md')} generated (Claude Code / Codex)")
|
|
381
571
|
|
|
@@ -388,16 +578,21 @@ def main():
|
|
|
388
578
|
{bold('Stack:')} {data['primary_language']} | {', '.join(data['frameworks']) or 'no framework'}
|
|
389
579
|
{bold('Infra:')} {', '.join(data['infrastructure']) or 'none'}
|
|
390
580
|
|
|
391
|
-
{bold('Auto-resolved skills:')}
|
|
392
|
-
{chr(10).join(f' · {s}' for s in auto_skills)}
|
|
393
|
-
|
|
394
|
-
{bold('
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
{
|
|
400
|
-
|
|
581
|
+
{bold('Auto-resolved skills:')}
|
|
582
|
+
{chr(10).join(f' · {s}' for s in auto_skills)}
|
|
583
|
+
|
|
584
|
+
{bold('Generated files:')}
|
|
585
|
+
· {bold(str(output_path.resolve()))}
|
|
586
|
+
· {bold(str(Path("CLAUDE.md").resolve())) if args.platform in ("claude", "all") else 'CLAUDE.md not generated for this platform'}
|
|
587
|
+
|
|
588
|
+
{bold('Next steps:')}
|
|
589
|
+
1. Review {bold('PROJECT_CONTEXT.md')} first and add architecture notes
|
|
590
|
+
2. Instruct the agent to read/update {bold('PROJECT_CONTEXT.md')} before major tasks
|
|
591
|
+
3. Open project in your AI editor (Antigravity / Claude Code / Codex)
|
|
592
|
+
4. The agent will read PROJECT_CONTEXT.md on session start
|
|
593
|
+
|
|
594
|
+
{dim(' Tip: Run with --platform all to also generate CLAUDE.md')}
|
|
595
|
+
""")
|
|
401
596
|
|
|
402
597
|
if __name__ == "__main__":
|
|
403
598
|
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: desktop-design
|
|
3
|
+
description: Desktop and cross-platform app design guidance for Tauri and similar desktop shells. Use when building desktop-first interfaces, windowed workflows, keyboard-heavy UX, or cross-platform desktop UI behavior.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Desktop Design
|
|
8
|
+
|
|
9
|
+
This skill covers desktop-first product thinking where `mobile-design` is the wrong default.
|
|
10
|
+
|
|
11
|
+
Use it for Tauri and other desktop-oriented apps with window management, keyboard shortcuts, dense layouts, or multi-panel workflows.
|
|
12
|
+
|
|
13
|
+
## Guidance
|
|
14
|
+
|
|
15
|
+
- Design for pointer + keyboard, not touch-first.
|
|
16
|
+
- Use information density deliberately; desktop screens can support richer sidebars and multi-column layouts.
|
|
17
|
+
- Respect platform conventions for menus, shortcuts, dialogs, file pickers, and drag/drop.
|
|
18
|
+
- Assume resizing, multiple windows, and long-running sessions are normal.
|
|
19
|
+
- Pair with `rust-pro` for Tauri backend/native concerns and `frontend-design` for UI execution.
|
|
20
|
+
|
|
21
|
+
## Related Skills
|
|
22
|
+
|
|
23
|
+
- `mobile-design` for touch-first mobile products
|
|
24
|
+
- `frontend-design` for visual and interaction design
|
|
25
|
+
- `rust-pro` for Rust and Tauri implementation details
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-frontend
|
|
3
|
+
description: React-focused frontend guidance for component architecture, state flow, rendering boundaries, and maintainable UI systems. Use for plain React apps that are not specifically Next.js-first.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Frontend
|
|
8
|
+
|
|
9
|
+
This skill is the React-first counterpart to `nextjs-react-expert`.
|
|
10
|
+
|
|
11
|
+
Use it when the project uses React without needing Next.js-specific assumptions such as SSR, App Router, or server components.
|
|
12
|
+
|
|
13
|
+
## Guidance
|
|
14
|
+
|
|
15
|
+
- Prefer the existing project architecture over introducing a new pattern.
|
|
16
|
+
- Keep state ownership shallow and explicit.
|
|
17
|
+
- Separate presentational UI from data-fetching and side effects when it improves maintainability.
|
|
18
|
+
- Optimize rendering only after confirming an actual bottleneck.
|
|
19
|
+
- Pair with `frontend-design` for UI/UX decisions.
|
|
20
|
+
|
|
21
|
+
## Related Skills
|
|
22
|
+
|
|
23
|
+
- `nextjs-react-expert` for Next.js-specific performance and routing concerns
|
|
24
|
+
- `frontend-design` for layout, visual hierarchy, and responsive design
|
|
25
|
+
- `clean-code` for naming, file boundaries, and readability
|