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/bin/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
- const prompts = require('prompts');
6
- const pc = require('picocolors');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const prompts = require('prompts');
6
+ const pc = require('picocolors');
7
7
 
8
8
  const sourceDir = path.resolve(__dirname, '..');
9
9
  const targetDir = process.cwd();
@@ -23,12 +23,12 @@ const LANG_SKILLS = {
23
23
  csharp: ["api-design"],
24
24
  };
25
25
 
26
- const FRAMEWORK_SKILLS = {
27
- vue: ["frontend-design", "tailwind-patterns"],
28
- react: ["nextjs-react-expert", "frontend-design"],
29
- next: ["nextjs-react-expert", "frontend-design"],
30
- nuxt: ["frontend-design"],
31
- svelte: ["frontend-design"],
26
+ const FRAMEWORK_SKILLS = {
27
+ vue: ["frontend-design", "tailwind-patterns"],
28
+ react: ["react-frontend", "frontend-design"],
29
+ next: ["nextjs-react-expert", "frontend-design"],
30
+ nuxt: ["frontend-design"],
31
+ svelte: ["frontend-design"],
32
32
  laravel: ["laravel-patterns", "laravel-security", "laravel-tdd"],
33
33
  express: ["nodejs-best-practices", "api-design"],
34
34
  fastapi: ["python-patterns", "api-design"],
@@ -36,12 +36,12 @@ const FRAMEWORK_SKILLS = {
36
36
  gin: ["golang-patterns", "golang-testing"],
37
37
  fiber: ["golang-patterns", "golang-testing"],
38
38
  nestjs: ["nodejs-best-practices", "api-design"],
39
- tauri: ["rust-pro", "mobile-design"],
40
- flutter: ["mobile-design"],
41
- "react-native": ["mobile-design"],
42
- };
39
+ tauri: ["rust-pro", "desktop-design"],
40
+ flutter: ["mobile-design"],
41
+ "react-native": ["mobile-design"],
42
+ };
43
43
 
44
- const INFRA_SKILLS = {
44
+ const INFRA_SKILLS = {
45
45
  docker: ["docker-patterns"],
46
46
  aws: ["deployment-procedures", "docker-patterns"],
47
47
  gcp: ["deployment-procedures", "docker-patterns"],
@@ -74,8 +74,190 @@ function resolveSkills(data) {
74
74
  skills.add("clean-code");
75
75
  skills.add("systematic-debugging");
76
76
 
77
- return Array.from(skills).sort();
78
- }
77
+ return Array.from(skills).sort();
78
+ }
79
+
80
+ const FRAMEWORK_CHOICES = [
81
+ 'vue', 'react', 'next', 'nuxt', 'svelte', 'angular',
82
+ 'laravel', 'express', 'fastapi', 'django', 'gin', 'fiber', 'nestjs',
83
+ 'tauri', 'flutter', 'react-native'
84
+ ];
85
+
86
+ const LANGUAGE_CHOICES = ['typescript', 'javascript', 'python', 'go', 'php', 'rust', 'java', 'swift', 'kotlin', 'csharp', 'cpp'];
87
+ const PROJECT_TYPE_CHOICES = ['web-app', 'api', 'mobile', 'desktop', 'cli', 'fullstack'];
88
+ const DATABASE_CHOICES = ['postgres', 'mysql', 'sqlite', 'mongodb', 'redis', 'dynamodb', 'none'];
89
+ const PACKAGE_MANAGER_CHOICES = ['npm', 'pnpm', 'yarn', 'bun', 'pip', 'uv', 'go mod', 'cargo', 'composer', 'auto-detect'];
90
+ const TEST_FRAMEWORK_CHOICES = ['jest', 'vitest', 'pytest', 'go test', 'phpunit', 'pest', 'rspec', 'junit', 'auto-detect'];
91
+ const CLOUD_CHOICES = ['aws', 'gcp', 'azure', 'vercel', 'fly', 'railway', 'none'];
92
+
93
+ function printHelp() {
94
+ console.log(`
95
+ ${pc.bold('Usage:')}
96
+ thachvd-kit init [--scan] [--yes]
97
+ thachvd-kit --help
98
+
99
+ ${pc.bold('Commands:')}
100
+ init Run the project init wizard
101
+
102
+ ${pc.bold('Options:')}
103
+ --scan Scan the current project to prefill PROJECT_CONTEXT.md
104
+ --yes Use detected/default values without prompting
105
+ --help Show this help message
106
+ `);
107
+ }
108
+
109
+ function fileExists(...parts) {
110
+ return fs.existsSync(path.join(targetDir, ...parts));
111
+ }
112
+
113
+ function readJsonIfExists(...parts) {
114
+ const fullPath = path.join(targetDir, ...parts);
115
+ if (!fs.existsSync(fullPath)) return null;
116
+ try {
117
+ return JSON.parse(fs.readFileSync(fullPath, 'utf8'));
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+
123
+ function readTextIfExists(...parts) {
124
+ const fullPath = path.join(targetDir, ...parts);
125
+ if (!fs.existsSync(fullPath)) return '';
126
+ try {
127
+ return fs.readFileSync(fullPath, 'utf8');
128
+ } catch {
129
+ return '';
130
+ }
131
+ }
132
+
133
+ function detectPackageManager() {
134
+ if (fileExists('pnpm-lock.yaml')) return 'pnpm';
135
+ if (fileExists('yarn.lock')) return 'yarn';
136
+ if (fileExists('bun.lockb') || fileExists('bun.lock')) return 'bun';
137
+ if (fileExists('package-lock.json')) return 'npm';
138
+ if (fileExists('uv.lock')) return 'uv';
139
+ if (fileExists('poetry.lock')) return 'pip';
140
+ if (fileExists('requirements.txt') || fileExists('pyproject.toml')) return 'pip';
141
+ if (fileExists('go.mod')) return 'go mod';
142
+ if (fileExists('Cargo.toml')) return 'cargo';
143
+ if (fileExists('composer.json')) return 'composer';
144
+ return 'auto-detect';
145
+ }
146
+
147
+ function uniq(values) {
148
+ return Array.from(new Set(values.filter(Boolean)));
149
+ }
150
+
151
+ function scanProject() {
152
+ const pkg = readJsonIfExists('package.json') || {};
153
+ const deps = {
154
+ ...(pkg.dependencies || {}),
155
+ ...(pkg.devDependencies || {}),
156
+ ...(pkg.peerDependencies || {})
157
+ };
158
+ const depNames = new Set(Object.keys(deps));
159
+ const frameworks = [];
160
+ const infrastructure = [];
161
+ let primaryLanguage = '';
162
+ let projectType = '';
163
+ let database = 'none';
164
+ let testFramework = 'auto-detect';
165
+ let cloud = 'none';
166
+ let ciCd = 'none';
167
+
168
+ const addFramework = (name, condition) => {
169
+ if (condition) frameworks.push(name);
170
+ };
171
+
172
+ addFramework('next', depNames.has('next'));
173
+ addFramework('react', depNames.has('react'));
174
+ addFramework('vue', depNames.has('vue'));
175
+ addFramework('nuxt', depNames.has('nuxt'));
176
+ addFramework('svelte', depNames.has('svelte') || depNames.has('@sveltejs/kit'));
177
+ addFramework('angular', depNames.has('@angular/core'));
178
+ addFramework('express', depNames.has('express'));
179
+ addFramework('nestjs', depNames.has('@nestjs/core'));
180
+ addFramework('fastapi', fileExists('requirements.txt') && /fastapi/i.test(readTextIfExists('requirements.txt')));
181
+ addFramework('django', fileExists('requirements.txt') && /django/i.test(readTextIfExists('requirements.txt')));
182
+ addFramework('tauri', depNames.has('@tauri-apps/api') || fileExists('src-tauri', 'tauri.conf.json') || fileExists('src-tauri', 'tauri.conf.json5'));
183
+ addFramework('flutter', fileExists('pubspec.yaml') && /flutter:/i.test(readTextIfExists('pubspec.yaml')));
184
+ addFramework('react-native', depNames.has('react-native') || depNames.has('expo'));
185
+ addFramework('laravel', fileExists('artisan') || depNames.has('laravel'));
186
+ addFramework('gin', fileExists('go.mod') && /\bgithub\.com\/gin-gonic\/gin\b/i.test(readTextIfExists('go.mod')));
187
+ addFramework('fiber', fileExists('go.mod') && /\bgithub\.com\/gofiber\/fiber\b/i.test(readTextIfExists('go.mod')));
188
+
189
+ if (fileExists('tsconfig.json') || depNames.has('typescript')) primaryLanguage = 'typescript';
190
+ else if (fileExists('Cargo.toml') || fileExists('src-tauri', 'Cargo.toml')) primaryLanguage = 'rust';
191
+ else if (fileExists('go.mod')) primaryLanguage = 'go';
192
+ else if (fileExists('requirements.txt') || fileExists('pyproject.toml')) primaryLanguage = 'python';
193
+ else if (fileExists('composer.json')) primaryLanguage = 'php';
194
+ else if (depNames.size > 0 || fileExists('package.json')) primaryLanguage = 'javascript';
195
+
196
+ if (frameworks.includes('tauri')) projectType = 'desktop';
197
+ else if (frameworks.includes('flutter') || frameworks.includes('react-native')) projectType = 'mobile';
198
+ else if (frameworks.some(f => ['express', 'fastapi', 'django', 'laravel', 'gin', 'fiber', 'nestjs'].includes(f))) {
199
+ projectType = frameworks.some(f => ['react', 'next', 'vue', 'nuxt', 'svelte', 'angular'].includes(f)) ? 'fullstack' : 'api';
200
+ } else if (frameworks.some(f => ['react', 'next', 'vue', 'nuxt', 'svelte', 'angular'].includes(f))) {
201
+ projectType = 'web-app';
202
+ } else if (fileExists('package.json') && pkg.bin) {
203
+ projectType = 'cli';
204
+ }
205
+
206
+ if (depNames.has('pg') || depNames.has('postgres') || depNames.has('@prisma/client') || /postgres/i.test(JSON.stringify(pkg))) database = 'postgres';
207
+ else if (depNames.has('mysql') || depNames.has('mysql2')) database = 'mysql';
208
+ else if (depNames.has('sqlite3') || depNames.has('better-sqlite3') || depNames.has('rusqlite')) database = 'sqlite';
209
+ else if (depNames.has('mongodb') || depNames.has('mongoose')) database = 'mongodb';
210
+ else if (depNames.has('redis') || depNames.has('ioredis')) database = 'redis';
211
+ else if (depNames.has('@aws-sdk/client-dynamodb')) database = 'dynamodb';
212
+
213
+ if (depNames.has('vitest')) testFramework = 'vitest';
214
+ else if (depNames.has('jest')) testFramework = 'jest';
215
+ else if (depNames.has('pytest') || /pytest/i.test(readTextIfExists('requirements.txt'))) testFramework = 'pytest';
216
+ else if (depNames.has('phpunit')) testFramework = 'phpunit';
217
+ else if (depNames.has('pest')) testFramework = 'pest';
218
+ else if (fileExists('go.mod')) testFramework = 'go test';
219
+
220
+ if (fileExists('Dockerfile') || fileExists('docker-compose.yml') || fileExists('docker-compose.yaml')) {
221
+ infrastructure.push('docker');
222
+ }
223
+
224
+ if (fileExists('vercel.json')) {
225
+ cloud = 'vercel';
226
+ infrastructure.push('vercel');
227
+ } else if (fileExists('fly.toml')) {
228
+ cloud = 'fly';
229
+ infrastructure.push('fly');
230
+ } else if (fileExists('railway.json') || fileExists('railway.toml')) {
231
+ cloud = 'railway';
232
+ infrastructure.push('railway');
233
+ }
234
+
235
+ if (fileExists('.github', 'workflows')) ciCd = 'github-actions';
236
+ else if (fileExists('.gitlab-ci.yml')) ciCd = 'gitlab-ci';
237
+ else if (fileExists('.circleci', 'config.yml')) ciCd = 'circleci';
238
+ else if (fileExists('Jenkinsfile')) ciCd = 'jenkins';
239
+
240
+ return {
241
+ project_name: path.basename(targetDir),
242
+ description: pkg.description || 'A software project',
243
+ project_type: PROJECT_TYPE_CHOICES.includes(projectType) ? projectType : 'web-app',
244
+ primary_language: LANGUAGE_CHOICES.includes(primaryLanguage) ? primaryLanguage : 'typescript',
245
+ frameworks: uniq(frameworks),
246
+ database: DATABASE_CHOICES.includes(database) ? database : 'none',
247
+ package_manager: PACKAGE_MANAGER_CHOICES.includes(detectPackageManager()) ? detectPackageManager() : 'auto-detect',
248
+ uses_docker: infrastructure.includes('docker'),
249
+ cloud,
250
+ infrastructure: uniq(infrastructure),
251
+ ci_cd: ciCd,
252
+ test_framework: TEST_FRAMEWORK_CHOICES.includes(testFramework) ? testFramework : 'auto-detect',
253
+ max_file_lines: '300',
254
+ code_style: 'project-standard',
255
+ architecture_notes: '<!-- Add notes -->',
256
+ current_phase: 'Initial setup',
257
+ next_phase: '—',
258
+ known_issues: '<!-- None yet -->'
259
+ };
260
+ }
79
261
 
80
262
  function resolveAgentTable(data) {
81
263
  const rows = [];
@@ -202,20 +384,21 @@ ${autoSkillsList}
202
384
  `;
203
385
  }
204
386
 
205
- function generateClaudeMd(data) {
206
- return `# CLAUDE.md — ${data.project_name}
207
- # Generated by thachvd-kit. See PROJECT_CONTEXT.md for full stack info.
208
- # Cross-platform: Claude Code · Codex · OpenCode
387
+ function generateClaudeMd(data) {
388
+ return `# CLAUDE.md — ${data.project_name}
389
+ # Generated by thachvd-kit. See PROJECT_CONTEXT.md for full stack info.
390
+ # Cross-platform: Claude Code · Codex · OpenCode
209
391
 
210
392
  ## Project
211
393
  ${data.description}
212
394
  Stack: ${data.primary_language} | ${(data.frameworks || []).join(', ')}
213
395
 
214
- ## Rules
215
- - Read PROJECT_CONTEXT.md first for stack details and agent routing.
216
- - Follow skills in .agent/skills/ (or .claude/skills/).
217
- - Tests are mandatory. Verify before claiming done.
218
- - Max ${data.max_file_lines || '300'} lines per file.
396
+ ## Rules
397
+ - Read PROJECT_CONTEXT.md first for stack details and agent routing.
398
+ - If PROJECT_CONTEXT.md is missing or stale, create/update it before major work.
399
+ - Follow skills in .agent/skills/ (or .claude/skills/).
400
+ - Tests are mandatory. Verify before claiming done.
401
+ - Max ${data.max_file_lines || '300'} lines per file.
219
402
 
220
403
  ## Key Skills
221
404
  - Plan: \`plan-writing\` → \`executing-plans\`
@@ -235,90 +418,152 @@ See PROJECT_CONTEXT.md for full agent routing table.
235
418
 
236
419
  // --- Main CLI ---
237
420
 
238
- async function main() {
239
- console.log(pc.bold(pc.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
240
- console.log(' ' + pc.bold('thachvd-kit') + ' — Project Init Wizard');
241
- console.log(' Generates .agent/ folder & smart PROJECT_CONTEXT.md');
242
- console.log(pc.bold(pc.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')) + '\n');
243
-
244
- if (fs.existsSync(path.join(targetDir, 'PROJECT_CONTEXT.md'))) {
245
- const res = await prompts({
246
- type: 'confirm',
247
- name: 'overwrite',
421
+ async function main() {
422
+ const rawArgs = process.argv.slice(2);
423
+ const command = rawArgs[0] && !rawArgs[0].startsWith('-') ? rawArgs[0] : 'init';
424
+ const args = command === 'init' ? rawArgs.slice(rawArgs[0] === 'init' ? 1 : 0) : rawArgs.slice(1);
425
+
426
+ if (rawArgs.includes('--help') || rawArgs.includes('-h')) {
427
+ printHelp();
428
+ return;
429
+ }
430
+
431
+ if (command !== 'init') {
432
+ console.error(pc.red(`Unknown command: ${command}`));
433
+ printHelp();
434
+ process.exit(1);
435
+ }
436
+
437
+ let useScan = args.includes('--scan');
438
+ const assumeYes = args.includes('--yes') || args.includes('-y');
439
+
440
+ console.log(pc.bold(pc.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')));
441
+ console.log(' ' + pc.bold('thachvd-kit') + ' — Project Init Wizard');
442
+ console.log(` Generates .agent/ folder & smart PROJECT_CONTEXT.md${useScan ? ' (scan-assisted)' : ''}`);
443
+ console.log(pc.bold(pc.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')) + '\n');
444
+
445
+ let projectSetup = 'new';
446
+ if (!useScan && !assumeYes) {
447
+ const modeAnswer = await prompts([
448
+ {
449
+ type: 'select',
450
+ name: 'project_setup',
451
+ message: 'Project already exists or are you starting fresh?',
452
+ choices: [
453
+ { title: 'Existing project', value: 'existing' },
454
+ { title: 'New project', value: 'new' }
455
+ ]
456
+ },
457
+ {
458
+ type: prev => prev === 'existing' ? 'select' : null,
459
+ name: 'existing_mode',
460
+ message: 'How should thachvd-kit build PROJECT_CONTEXT.md?',
461
+ choices: [
462
+ { title: 'Scan current project files', value: 'scan' },
463
+ { title: 'Enter context manually', value: 'manual' }
464
+ ],
465
+ initial: 0
466
+ }
467
+ ]);
468
+ projectSetup = modeAnswer.project_setup || 'new';
469
+ useScan = modeAnswer.existing_mode === 'scan';
470
+ }
471
+
472
+ if (useScan) {
473
+ console.log(pc.dim(' Scan mode: detecting stack from current project files...\n'));
474
+ } else if (projectSetup === 'new') {
475
+ console.log(pc.dim(' Manual mode: asking for project context explicitly.\n'));
476
+ }
477
+
478
+ if (fs.existsSync(path.join(targetDir, 'PROJECT_CONTEXT.md')) && !assumeYes) {
479
+ const res = await prompts({
480
+ type: 'confirm',
481
+ name: 'overwrite',
248
482
  message: 'PROJECT_CONTEXT.md already exists in this folder. Overwrite?',
249
483
  initial: false
250
484
  });
251
485
  if (!res.overwrite) {
252
486
  console.log(pc.yellow('\nAborted. Existing project context unchanged.'));
253
487
  process.exit(0);
254
- }
255
- }
256
-
257
- const { project_name, description, project_type } = await prompts([
258
- { type: 'text', name: 'project_name', message: 'Project name:', initial: path.basename(targetDir) },
259
- { type: 'text', name: 'description', message: 'Short description:', initial: 'A software project' },
260
- {
261
- type: 'select', name: 'project_type', message: 'Project type:',
262
- choices: [
263
- { title: 'Web App', value: 'web-app' },
264
- { title: 'API', value: 'api' },
265
- { title: 'Mobile', value: 'mobile' },
266
- { title: 'Desktop', value: 'desktop' },
267
- { title: 'CLI', value: 'cli' },
268
- { title: 'Fullstack', value: 'fullstack' },
269
- ]
270
- }
271
- ]);
272
-
273
- const { primary_language } = await prompts({
274
- type: 'select',
275
- name: 'primary_language',
276
- message: 'Primary language:',
277
- choices: ['typescript', 'javascript', 'python', 'go', 'php', 'rust', 'java', 'swift', 'kotlin', 'csharp', 'cpp'].map(l => ({ title: l, value: l }))
278
- });
279
-
280
- const { frameworks } = await prompts({
281
- type: 'multiselect',
282
- name: 'frameworks',
283
- message: 'Frameworks (Space to select, Enter to confirm):',
284
- choices: [
285
- 'vue', 'react', 'next', 'nuxt', 'svelte', 'angular',
286
- 'laravel', 'express', 'fastapi', 'django', 'gin', 'fiber', 'nestjs',
287
- 'tauri', 'flutter', 'react-native'
288
- ].map(f => ({ title: f, value: f })),
289
- min: 0
290
- });
291
-
292
- const dbAndInfra = await prompts([
293
- {
294
- type: 'select', name: 'database', message: 'Database / ORM:',
295
- choices: ['postgres', 'mysql', 'sqlite', 'mongodb', 'redis', 'dynamodb', 'none'].map(d => ({ title: d, value: d }))
296
- },
297
- { type: 'confirm', name: 'uses_docker', message: 'Uses Docker / Docker Compose?', initial: true },
298
- {
299
- type: 'select', name: 'cloud', message: 'Cloud Provider:',
300
- choices: ['aws', 'gcp', 'azure', 'vercel', 'fly', 'railway', 'none'].map(c => ({ title: c, value: c }))
301
- }
302
- ]);
303
-
304
- const { package_manager, test_framework, max_file_lines } = await prompts([
305
- {
306
- type: 'select', name: 'package_manager', message: 'Package Manager:',
307
- choices: ['npm', 'pnpm', 'yarn', 'bun', 'pip', 'uv', 'go mod', 'cargo', 'composer', 'auto-detect'].map(p => ({ title: p, value: p }))
308
- },
309
- { type: 'text', name: 'test_framework', message: 'Test Framework (jest, phpunit, etc):', initial: 'auto-detect' },
310
- { type: 'text', name: 'max_file_lines', message: 'Max lines per file:', initial: '300' }
311
- ]);
312
-
313
- // Combine data
314
- const data = {
315
- project_name, description, project_type, primary_language, frameworks,
316
- ...dbAndInfra, package_manager, test_framework, max_file_lines
317
- };
318
-
319
- data.infrastructure = [];
320
- if (data.uses_docker) data.infrastructure.push('docker');
321
- if (data.cloud !== 'none') data.infrastructure.push(data.cloud);
488
+ }
489
+ }
490
+
491
+ const scanned = useScan ? scanProject() : null;
492
+
493
+ if (useScan) {
494
+ console.log(`${pc.bold(' Scan result:')} ${scanned.primary_language} | ${scanned.frameworks.join(', ') || 'no framework'}`);
495
+ console.log(` ${pc.bold('Detected infra:')} ${scanned.infrastructure.join(', ') || 'none'}\n`);
496
+ }
497
+
498
+ const { project_name, description, project_type } = await prompts([
499
+ { type: assumeYes ? null : 'text', name: 'project_name', message: 'Project name:', initial: scanned?.project_name || path.basename(targetDir) },
500
+ { type: assumeYes ? null : 'text', name: 'description', message: 'Short description:', initial: scanned?.description || 'A software project' },
501
+ {
502
+ type: assumeYes ? null : 'select', name: 'project_type', message: 'Project type:',
503
+ choices: PROJECT_TYPE_CHOICES.map(value => ({ title: value, value })),
504
+ initial: Math.max(PROJECT_TYPE_CHOICES.indexOf(scanned?.project_type || 'web-app'), 0)
505
+ }
506
+ ]);
507
+
508
+ const { primary_language } = await prompts({
509
+ type: assumeYes ? null : 'select',
510
+ name: 'primary_language',
511
+ message: 'Primary language:',
512
+ choices: LANGUAGE_CHOICES.map(l => ({ title: l, value: l })),
513
+ initial: Math.max(LANGUAGE_CHOICES.indexOf(scanned?.primary_language || 'typescript'), 0)
514
+ });
515
+
516
+ const { frameworks } = await prompts({
517
+ type: assumeYes ? null : 'multiselect',
518
+ name: 'frameworks',
519
+ message: 'Frameworks (Space to select, Enter to confirm):',
520
+ choices: FRAMEWORK_CHOICES.map(f => ({ title: f, value: f, selected: (scanned?.frameworks || []).includes(f) })),
521
+ min: 0
522
+ });
523
+
524
+ const dbAndInfra = await prompts([
525
+ {
526
+ type: assumeYes ? null : 'select', name: 'database', message: 'Database / ORM:',
527
+ choices: DATABASE_CHOICES.map(d => ({ title: d, value: d })),
528
+ initial: Math.max(DATABASE_CHOICES.indexOf(scanned?.database || 'none'), 0)
529
+ },
530
+ { type: assumeYes ? null : 'confirm', name: 'uses_docker', message: 'Uses Docker / Docker Compose?', initial: scanned?.uses_docker ?? true },
531
+ {
532
+ type: assumeYes ? null : 'select', name: 'cloud', message: 'Cloud Provider:',
533
+ choices: CLOUD_CHOICES.map(c => ({ title: c, value: c })),
534
+ initial: Math.max(CLOUD_CHOICES.indexOf(scanned?.cloud || 'none'), 0)
535
+ }
536
+ ]);
537
+
538
+ const { package_manager, test_framework, max_file_lines } = await prompts([
539
+ {
540
+ type: assumeYes ? null : 'select', name: 'package_manager', message: 'Package Manager:',
541
+ choices: PACKAGE_MANAGER_CHOICES.map(p => ({ title: p, value: p })),
542
+ initial: Math.max(PACKAGE_MANAGER_CHOICES.indexOf(scanned?.package_manager || 'auto-detect'), 0)
543
+ },
544
+ { type: assumeYes ? null : 'text', name: 'test_framework', message: 'Test Framework (jest, phpunit, etc):', initial: scanned?.test_framework || 'auto-detect' },
545
+ { type: assumeYes ? null : 'text', name: 'max_file_lines', message: 'Max lines per file:', initial: scanned?.max_file_lines || '300' }
546
+ ]);
547
+
548
+ // Combine data
549
+ const data = {
550
+ project_name: project_name || scanned?.project_name || path.basename(targetDir),
551
+ description: description || scanned?.description || 'A software project',
552
+ project_type: project_type || scanned?.project_type || 'web-app',
553
+ primary_language: primary_language || scanned?.primary_language || 'typescript',
554
+ frameworks: frameworks || scanned?.frameworks || [],
555
+ ...dbAndInfra, package_manager, test_framework, max_file_lines
556
+ };
557
+
558
+ data.database = data.database || scanned?.database || 'none';
559
+ data.uses_docker = typeof data.uses_docker === 'boolean' ? data.uses_docker : (scanned?.uses_docker ?? true);
560
+ data.cloud = data.cloud || scanned?.cloud || 'none';
561
+ data.package_manager = data.package_manager || scanned?.package_manager || 'auto-detect';
562
+ data.test_framework = data.test_framework || scanned?.test_framework || 'auto-detect';
563
+ data.max_file_lines = data.max_file_lines || scanned?.max_file_lines || '300';
564
+ data.infrastructure = [];
565
+ if (data.uses_docker) data.infrastructure.push('docker');
566
+ if (data.cloud !== 'none') data.infrastructure.push(data.cloud);
322
567
 
323
568
  // --- Copy .agent folder ---
324
569
  const agentDir = path.join(targetDir, '.agent');
@@ -343,11 +588,13 @@ async function main() {
343
588
  }
344
589
 
345
590
  // Write contexts
346
- fs.writeFileSync(path.join(targetDir, 'PROJECT_CONTEXT.md'), generateProjectContext(data), 'utf8');
347
- console.log(` ${pc.green('✓')} Generated ${pc.bold('PROJECT_CONTEXT.md')}`);
348
-
349
- fs.writeFileSync(path.join(targetDir, 'CLAUDE.md'), generateClaudeMd(data), 'utf8');
350
- console.log(` ${pc.green('✓')} Generated ${pc.bold('CLAUDE.md')}`);
591
+ const projectContextPath = path.join(targetDir, 'PROJECT_CONTEXT.md');
592
+ fs.writeFileSync(projectContextPath, generateProjectContext(data), 'utf8');
593
+ console.log(` ${pc.green('✓')} Generated ${pc.bold('PROJECT_CONTEXT.md')}`);
594
+
595
+ const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
596
+ fs.writeFileSync(claudeMdPath, generateClaudeMd(data), 'utf8');
597
+ console.log(` ${pc.green('✓')} Generated ${pc.bold('CLAUDE.md')}`);
351
598
 
352
599
  // Summary
353
600
  const autoSkills = resolveSkills(data);
@@ -357,15 +604,20 @@ async function main() {
357
604
  ${pc.bold('Stack:')} ${data.primary_language} | ${(data.frameworks || []).join(', ') || 'no framework'}
358
605
  ${pc.bold('Infra:')} ${(data.infrastructure || []).join(', ') || 'none'}
359
606
 
360
- ${pc.bold('Auto-resolved skills:')}
361
- ${autoSkills.map(s => ' · ' + s).join('\n')}
362
-
363
- ${pc.bold('Next steps:')}
364
- 1. Your kit is installed in ${pc.bold('./.agent')}
365
- 2. Review ${pc.bold('PROJECT_CONTEXT.md')} and add architecture notes
366
- 3. Open project in Antigravity or Claude Code — the agent will read context on start!
367
- `);
368
- }
607
+ ${pc.bold('Auto-resolved skills:')}
608
+ ${autoSkills.map(s => ' · ' + s).join('\n')}
609
+
610
+ ${pc.bold('Generated files:')}
611
+ · ${pc.bold(projectContextPath)}
612
+ · ${pc.bold(claudeMdPath)}
613
+
614
+ ${pc.bold('Next steps:')}
615
+ 1. Your kit is installed in ${pc.bold('./.agent')}
616
+ 2. Review ${pc.bold('PROJECT_CONTEXT.md')} first and add architecture notes
617
+ 3. Instruct the agent to read/update ${pc.bold('PROJECT_CONTEXT.md')} before major tasks
618
+ 4. Open project in Antigravity or Claude Code — the agent will read context on start
619
+ `);
620
+ }
369
621
 
370
622
  main().catch(err => {
371
623
  console.error(pc.red('\nError during init:'), err);
package/package.json CHANGED
@@ -1,25 +1,48 @@
1
- {
2
- "name": "thachvd-kit",
3
- "version": "1.0.0",
4
- "description": "Universal Agent Kit for Antigravity, Claude Code, and OpenCode",
5
- "main": "index.js",
6
- "bin": {
7
- "thachvd-kit": "./bin/cli.js"
8
- },
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
11
- },
12
- "keywords": [
13
- "ai",
14
- "agent",
15
- "claude",
16
- "antigravity",
17
- "codex"
18
- ],
19
- "author": "thachvd",
20
- "license": "MIT",
21
- "dependencies": {
22
- "picocolors": "^1.0.0",
23
- "prompts": "^2.4.2"
24
- }
25
- }
1
+ {
2
+ "name": "thachvd-kit",
3
+ "version": "1.0.2",
4
+ "description": "Universal Agent Kit for Antigravity, Claude Code, and OpenCode",
5
+ "bin": {
6
+ "thachvd-kit": "./bin/cli.js"
7
+ },
8
+ "files": [
9
+ "agents",
10
+ "bin",
11
+ "kit",
12
+ "rules",
13
+ "scripts",
14
+ "skills",
15
+ "workflows",
16
+ "README.md",
17
+ "LICENSE",
18
+ "PROJECT_CONTEXT.template.md"
19
+ ],
20
+ "preferGlobal": true,
21
+ "engines": {
22
+ "node": ">=16.7"
23
+ },
24
+ "scripts": {
25
+ "test": "echo \"Error: no test specified\" && exit 1"
26
+ },
27
+ "keywords": [
28
+ "ai",
29
+ "agent",
30
+ "claude",
31
+ "antigravity",
32
+ "codex"
33
+ ],
34
+ "author": "thachvd",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git@github_home:holdon1996/thachvd-kit.git"
39
+ },
40
+ "homepage": "https://github.com/holdon1996/thachvd-kit",
41
+ "bugs": {
42
+ "url": "https://github.com/holdon1996/thachvd-kit/issues"
43
+ },
44
+ "dependencies": {
45
+ "picocolors": "^1.0.0",
46
+ "prompts": "^2.4.2"
47
+ }
48
+ }