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/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: ["
|
|
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", "
|
|
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
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
{ type: 'text', name: '
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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('
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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.
|
|
4
|
-
"description": "Universal Agent Kit for Antigravity, Claude Code, and OpenCode",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
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
|
+
}
|