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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -19
  3. package/agents/frontend-specialist.md +2 -2
  4. package/agents/mobile-developer.md +4 -4
  5. package/bin/cli.js +370 -118
  6. package/package.json +48 -25
  7. package/rules/GEMINI.md +5 -3
  8. package/scripts/init.py +317 -122
  9. package/skills/desktop-design/SKILL.md +25 -0
  10. package/skills/react-frontend/SKILL.md +25 -0
  11. package/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  12. package/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  13. package/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  14. package/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  15. package/.shared/ui-ux-pro-max/data/products.csv +0 -97
  16. package/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  17. package/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  18. package/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  19. package/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  20. package/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  21. package/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  22. package/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  23. package/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  24. package/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  25. package/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  26. package/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  27. package/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  28. package/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  29. package/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  30. package/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  31. package/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  32. package/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  33. package/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  34. package/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  35. package/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  36. package/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  37. package/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  38. package/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  39. package/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  40. package/ARCHITECTURE.md +0 -288
  41. package/CLAUDE.md +0 -90
  42. 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` | `mobile-design` |
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 / Next.js | `nextjs-react-expert` |
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 / Tauri | `rust-pro` |
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
- from pathlib import Path
16
- from datetime import datetime
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": ["nextjs-react-expert", "frontend-design"],
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", "mobile-design"],
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
- - Follow skills in .claude/skills/ (or skills/ if using Antigravity).
258
- - Tests are mandatory. Verify before claiming done.
259
- - Max {data.get('max_file_lines', '300')} lines per file.
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
- args = parser.parse_args()
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
- data = {}
301
-
302
- # ── Project Info ──────────────────────────────────────────────────────
303
- section("Project Info")
304
- data["project_name"] = ask("Project name", default=Path.cwd().name)
305
- data["description"] = ask("Short description", default="A software project")
306
- data["project_type"] = ask("Project type",
307
- default="web-app",
308
- choices=["web-app", "api", "mobile", "desktop", "cli", "library", "fullstack"])
309
-
310
- # ── Tech Stack ────────────────────────────────────────────────────────
311
- section("Tech Stack")
312
-
313
- LANGUAGES = ["typescript", "javascript", "python", "go", "php", "rust", "java", "swift", "kotlin", "csharp", "c", "cpp"]
314
- data["primary_language"] = ask("Primary language",
315
- default="typescript",
316
- choices=LANGUAGES)
317
-
318
- FRAMEWORKS = ["vue", "react", "next", "nuxt", "svelte", "angular",
319
- "laravel", "express", "fastapi", "django", "gin", "fiber", "nestjs",
320
- "tauri", "flutter", "react-native",
321
- "none"]
322
- frameworks = ask_multi("Frameworks (select all that apply)", FRAMEWORKS, default=None)
323
- data["frameworks"] = frameworks or []
324
-
325
- DATABASES = ["postgres", "mysql", "sqlite", "mongodb", "redis", "dynamodb", "supabase", "prisma", "none"]
326
- data["database"] = ask("Database / ORM", default="postgres", choices=DATABASES)
327
-
328
- PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun", "pip", "uv", "poetry", "go mod", "cargo", "composer", "auto-detect"]
329
- data["package_manager"] = ask("Package manager", default="auto-detect", choices=PACKAGE_MANAGERS)
330
-
331
- # ── Infrastructure ────────────────────────────────────────────────────
332
- section("Infrastructure")
333
-
334
- data["uses_docker"] = ask_bool("Uses Docker / Docker Compose?", default=True)
335
- CLOUDS = ["aws", "gcp", "azure", "vercel", "fly", "railway", "digitalocean", "none"]
336
- cloud = ask("Cloud provider", default="none", choices=CLOUDS)
337
- data["cloud"] = cloud
338
-
339
- infrastructure = []
340
- if data["uses_docker"]: infrastructure.append("docker")
341
- if cloud != "none": infrastructure.append(cloud)
342
- data["infrastructure"] = infrastructure
343
-
344
- CI_CD = ["github-actions", "gitlab-ci", "circleci", "jenkins", "none"]
345
- data["ci_cd"] = ask("CI/CD", default="github-actions", choices=CI_CD)
346
-
347
- # ── Testing ───────────────────────────────────────────────────────────
348
- section("Testing")
349
-
350
- TEST_FRAMEWORKS = ["jest", "vitest", "pytest", "go test", "phpunit", "pest", "rspec", "junit", "auto-detect"]
351
- data["test_framework"] = ask("Test framework", default="auto-detect", choices=TEST_FRAMEWORKS)
352
-
353
- # ── Conventions ───────────────────────────────────────────────────────
354
- section("Coding Conventions")
355
-
356
- data["max_file_lines"] = ask("Max lines per file before splitting", default="300")
357
- data["code_style"] = ask("Code style / linter", default="project-standard",
358
- choices=["eslint", "prettier", "ruff", "gofmt", "pint", "standard", "project-standard"])
359
-
360
- # ── Optional fields ───────────────────────────────────────────────────
361
- section("Architecture (Optional — press Enter to skip)")
362
-
363
- data["architecture_notes"] = ask("Key architecture notes", default="<!-- Add notes -->")
364
- data["current_phase"] = ask("Current phase", default="Initial setup")
365
- data["next_phase"] = ask("Next planned phase", default="—")
366
- data["known_issues"] = ask("Known issues / tasks", default="<!-- None yet -->")
367
-
368
- # ── Generate files ────────────────────────────────────────────────────
369
- section("Generating Files")
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('Next steps:')}
395
- 1. Review {bold('PROJECT_CONTEXT.md')} and add architecture notes
396
- 2. Open project in your AI editor (Antigravity / Claude Code / Codex)
397
- 3. The agent will read PROJECT_CONTEXT.md on session start
398
-
399
- {dim(' Tip: Run with --platform all to also generate CLAUDE.md')}
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