thachvd-kit 1.0.6 → 1.0.8

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/README.md CHANGED
@@ -18,6 +18,7 @@ npx github:thachvd/thachvd-kit init
18
18
  The interactive wizard will first ask whether this is a new project or an existing one.
19
19
  If it is an existing project, it can scan the current codebase to prefill `PROJECT_CONTEXT.md`, or let you enter everything manually.
20
20
  The scan mode uses evidence-based detectors and shows why a framework was inferred, so you can quickly confirm or correct it.
21
+ After init, you can also generate an AI refinement prompt to let your editor/agent deepen the project context.
21
22
 
22
23
  ```bash
23
24
  thachvd-kit init --scan
package/bin/cli.js CHANGED
@@ -126,6 +126,20 @@ ${pc.bold('Options:')}
126
126
  `);
127
127
  }
128
128
 
129
+ function formatScanPreviewLines(scanned) {
130
+ return [
131
+ ['Project type', scanned.project_type || 'unknown'],
132
+ ['Primary language', scanned.primary_language || 'unknown'],
133
+ ['Frameworks', (scanned.frameworks || []).join(', ') || 'no framework'],
134
+ ['Database', scanned.database || 'none'],
135
+ ['Package manager', scanned.package_manager || 'auto-detect'],
136
+ ['Test framework', scanned.test_framework || 'auto-detect'],
137
+ ['Cloud', scanned.cloud || 'none'],
138
+ ['Infrastructure', (scanned.infrastructure || []).join(', ') || 'none'],
139
+ ['CI/CD', scanned.ci_cd || 'none']
140
+ ];
141
+ }
142
+
129
143
  async function askQuestions(questions) {
130
144
  return prompts(questions, {
131
145
  onCancel: () => {
@@ -365,10 +379,11 @@ const FRAMEWORK_DETECTORS = [
365
379
  id: 'django',
366
380
  kind: 'backend',
367
381
  language: 'python',
368
- projectType: 'api',
382
+ projectType: 'web-app',
369
383
  detect: ctx => buildDetection('django', [
370
384
  evidenceIf(textMatches(ctx.requirementsText, /\bdjango\b/i) || textMatches(ctx.pyprojectText, /\bdjango\b/i), 'Python deps -> django', 4),
371
- evidenceIf(ctx.fileExists('manage.py'), 'found manage.py', 2)
385
+ evidenceIf(ctx.fileExists('manage.py'), 'found manage.py', 2),
386
+ evidenceIf(ctx.fileExists('templates'), 'found templates/', 1)
372
387
  ])
373
388
  },
374
389
  {
@@ -384,17 +399,18 @@ const FRAMEWORK_DETECTORS = [
384
399
  id: 'laravel',
385
400
  kind: 'backend',
386
401
  language: 'php',
387
- projectType: 'api',
402
+ projectType: 'web-app',
388
403
  detect: ctx => buildDetection('laravel', [
389
404
  evidenceIf(hasComposerDependency(ctx.composerDepNames, 'laravel/framework'), 'composer.json -> laravel/framework', 4),
390
- evidenceIf(ctx.fileExists('artisan'), 'found artisan', 3)
405
+ evidenceIf(ctx.fileExists('artisan'), 'found artisan', 3),
406
+ evidenceIf(ctx.fileExists('resources', 'views'), 'found resources/views', 1)
391
407
  ])
392
408
  },
393
409
  {
394
410
  id: 'cakephp',
395
411
  kind: 'backend',
396
412
  language: 'php',
397
- projectType: 'api',
413
+ projectType: 'web-app',
398
414
  detect: ctx => buildDetection('cakephp', [
399
415
  evidenceIf(hasComposerDependency(ctx.composerDepNames, 'cakephp/cakephp'), 'composer.json -> cakephp/cakephp', 4),
400
416
  evidenceIf(ctx.fileExists('bin', 'cake'), 'found bin/cake', 3),
@@ -403,18 +419,21 @@ const FRAMEWORK_DETECTORS = [
403
419
  evidenceIf(ctx.fileExists('app', 'Controller', 'AppController.php'), 'found app/Controller/AppController.php', 2),
404
420
  evidenceIf(ctx.fileExists('app', 'Model', 'AppModel.php'), 'found app/Model/AppModel.php', 2),
405
421
  evidenceIf(ctx.fileExists('lib', 'Cake'), 'found lib/Cake', 3),
406
- evidenceIf(ctx.fileExists('cake', 'console', 'cake'), 'found cake/console/cake', 3)
422
+ evidenceIf(ctx.fileExists('cake', 'console', 'cake'), 'found cake/console/cake', 3),
423
+ evidenceIf(ctx.fileExists('app', 'View'), 'found app/View', 2),
424
+ evidenceIf(textMatches(ctx.viewFilesText, /\.ctp$/im), 'found .ctp view templates', 2)
407
425
  ])
408
426
  },
409
427
  {
410
428
  id: 'symfony',
411
429
  kind: 'backend',
412
430
  language: 'php',
413
- projectType: 'api',
431
+ projectType: 'web-app',
414
432
  detect: ctx => buildDetection('symfony', [
415
433
  evidenceIf(hasComposerDependency(ctx.composerDepNames, 'symfony/framework-bundle'), 'composer.json -> symfony/framework-bundle', 4),
416
434
  evidenceIf(ctx.fileExists('bin', 'console'), 'found bin/console', 2),
417
- evidenceIf(ctx.fileExists('config', 'bundles.php'), 'found config/bundles.php', 1)
435
+ evidenceIf(ctx.fileExists('config', 'bundles.php'), 'found config/bundles.php', 1),
436
+ evidenceIf(ctx.fileExists('templates'), 'found templates/', 1)
418
437
  ])
419
438
  },
420
439
  {
@@ -459,10 +478,11 @@ const FRAMEWORK_DETECTORS = [
459
478
  id: 'rails',
460
479
  kind: 'backend',
461
480
  language: 'ruby',
462
- projectType: 'api',
481
+ projectType: 'web-app',
463
482
  detect: ctx => buildDetection('rails', [
464
483
  evidenceIf(textMatches(ctx.gemfileText, /^\s*gem\s+["']rails["']/im), 'Gemfile -> rails', 4),
465
- evidenceIf(ctx.fileExists('bin', 'rails'), 'found bin/rails', 2)
484
+ evidenceIf(ctx.fileExists('bin', 'rails'), 'found bin/rails', 2),
485
+ evidenceIf(ctx.fileExists('app', 'views'), 'found app/views', 1)
466
486
  ])
467
487
  },
468
488
  {
@@ -563,7 +583,10 @@ function scanProject() {
563
583
  gradleText: `${readTextIfExists('build.gradle')}\n${readTextIfExists('build.gradle.kts')}`,
564
584
  cargoText: `${readTextIfExists('Cargo.toml')}\n${readTextIfExists('src-tauri', 'Cargo.toml')}`,
565
585
  csprojText: rootCsproj ? readTextIfExists(rootCsproj) : '',
566
- programCsText: readTextIfExists('Program.cs')
586
+ programCsText: readTextIfExists('Program.cs'),
587
+ viewFilesText: fs.existsSync(path.join(targetDir, 'app', 'View'))
588
+ ? fs.readdirSync(path.join(targetDir, 'app', 'View'), { recursive: true }).join('\n')
589
+ : ''
567
590
  };
568
591
 
569
592
  const languageDetections = LANGUAGE_DETECTORS
@@ -605,6 +628,7 @@ function scanProject() {
605
628
  if (frameworkDetails.some(item => item.projectType === 'desktop')) projectType = 'desktop';
606
629
  else if (frameworkDetails.some(item => item.projectType === 'mobile')) projectType = 'mobile';
607
630
  else if (frameworkDetails.some(item => item.kind === 'frontend') && frameworkDetails.some(item => item.kind === 'backend')) projectType = 'fullstack';
631
+ else if (frameworkDetails.some(item => item.projectType === 'web-app')) projectType = 'web-app';
608
632
  else if (frameworkDetails.some(item => item.kind === 'backend')) projectType = 'api';
609
633
  else if (frameworkDetails.some(item => item.kind === 'frontend')) projectType = 'web-app';
610
634
  else if (fileExists('package.json') && pkg.bin) projectType = 'cli';
@@ -745,13 +769,24 @@ ${(data.scan_evidence || []).map(line => '- ' + line).join('\n')}
745
769
  # Generated by thachvd-kit init wizard on ${new Date().toISOString().split('T')[0]}
746
770
  # Edit freely — this is your project's brain.
747
771
 
748
- ## Project
749
-
750
- - **Name:** ${data.project_name}
751
- - **Description:** ${data.description}
752
- - **Type:** ${data.project_type}
753
-
754
- ---
772
+ ## Project
773
+
774
+ - **Name:** ${data.project_name}
775
+ - **Description:** ${data.description}
776
+ - **Type:** ${data.project_type}
777
+
778
+ ---
779
+
780
+ ## Context Status
781
+
782
+ \`\`\`yaml
783
+ context:
784
+ status: ${data.context_status || 'baseline'}
785
+ needs_ai_refinement: ${data.needs_ai_refinement ? 'true' : 'false'}
786
+ last_refined_at: ${data.last_refined_at || 'not-yet-refined'}
787
+ \`\`\`
788
+
789
+ ---
755
790
 
756
791
  ## Stack
757
792
 
@@ -842,9 +877,73 @@ Stack: ${data.primary_language} | ${(data.frameworks || []).join(', ')}
842
877
  - \`/test\` — Run test suite
843
878
  - \`/deploy\` — Deploy checklist
844
879
 
845
- See PROJECT_CONTEXT.md for full agent routing table.
846
- `;
847
- }
880
+ See PROJECT_CONTEXT.md for full agent routing table.
881
+ `;
882
+ }
883
+
884
+ function generateAiContextRefinementPrompt(data) {
885
+ const evidence = (data.scan_evidence || []).length > 0
886
+ ? (data.scan_evidence || []).map(line => `- ${line}`).join('\n')
887
+ : '- No scan evidence was captured during init.';
888
+
889
+ return `# AI Context Refinement
890
+
891
+ Use this prompt in your AI editor after \`thachvd-kit init\`.
892
+
893
+ ## Goal
894
+
895
+ Refine \`PROJECT_CONTEXT.md\` so it reflects the real project more accurately than the baseline init scan.
896
+
897
+ ## Current Baseline
898
+
899
+ - Project: ${data.project_name}
900
+ - Type: ${data.project_type}
901
+ - Stack: ${data.primary_language} | ${(data.frameworks || []).join(', ') || 'no framework'}
902
+ - Database: ${data.database || 'none'}
903
+ - Infra: ${(data.infrastructure || []).join(', ') || 'none'}
904
+
905
+ ## Scan Evidence
906
+
907
+ ${evidence}
908
+
909
+ ## Instructions For The AI Agent
910
+
911
+ 1. Read \`PROJECT_CONTEXT.md\` first.
912
+ 2. Inspect the project selectively to improve factual accuracy.
913
+ 3. Prioritize:
914
+ - real database/config detection
915
+ - domain/capability inference
916
+ - architecture notes
917
+ - more accurate auto-resolved skills
918
+ - important project-specific constraints
919
+ 4. Update \`PROJECT_CONTEXT.md\` directly.
920
+ 5. Distinguish facts from inferences when uncertain.
921
+ 6. Do not implement product code in this pass. Only refine project context.
922
+
923
+ ## Suggested Files To Inspect
924
+
925
+ - Root manifests: \`package.json\`, \`composer.json\`, \`Cargo.toml\`, \`pyproject.toml\`
926
+ - Infrastructure/config: Docker, CI, env, framework config files
927
+ - Database config and models
928
+ - Main app entry points, routing, controllers/services
929
+ - README and existing documentation
930
+
931
+ ## Desired Improvements
932
+
933
+ - Correct the \`database\` field if config files reveal the real DB.
934
+ - Add architecture notes that explain the actual structure.
935
+ - Infer domain capabilities such as scraping, browser automation, external API integrations, queues, workers, etc.
936
+ - Expand auto-resolved skills when there is clear evidence in the codebase.
937
+ - Add caveats or assumptions if parts of the stack are still uncertain.
938
+
939
+ ## Completion Standard
940
+
941
+ When done, summarize:
942
+ - what was corrected
943
+ - what is still inferred
944
+ - which files provided the strongest evidence
945
+ `;
946
+ }
848
947
 
849
948
  // --- Main CLI ---
850
949
 
@@ -925,10 +1024,14 @@ async function main() {
925
1024
 
926
1025
  const scanned = useScan ? scanProject() : null;
927
1026
  let reviewScannedFields = !useScan;
1027
+ let requestAiRefinement = false;
928
1028
 
929
1029
  if (useScan) {
930
- console.log(`${pc.bold(' Scan result:')} ${scanned.primary_language} | ${scanned.frameworks.join(', ') || 'no framework'}`);
931
- console.log(` ${pc.bold('Detected infra:')} ${scanned.infrastructure.join(', ') || 'none'}\n`);
1030
+ console.log(` ${pc.bold('Detected context:')}`);
1031
+ formatScanPreviewLines(scanned).forEach(([label, value]) => {
1032
+ console.log(` · ${label}: ${value}`);
1033
+ });
1034
+ console.log('');
932
1035
  if ((scanned.scan_evidence || []).length > 0) {
933
1036
  console.log(` ${pc.bold('Evidence:')}`);
934
1037
  scanned.scan_evidence.forEach(line => console.log(` · ${line}`));
@@ -1018,6 +1121,20 @@ async function main() {
1018
1121
  data.max_file_lines = data.max_file_lines || scanned?.max_file_lines || '300';
1019
1122
  data.framework_details = scanned?.framework_details || [];
1020
1123
  data.scan_evidence = scanned?.scan_evidence || [];
1124
+
1125
+ if (!assumeYes) {
1126
+ const aiRefineAnswer = await askQuestions({
1127
+ type: 'confirm',
1128
+ name: 'ai_refine',
1129
+ message: 'Should the agent refine PROJECT_CONTEXT.md on first session start?',
1130
+ initial: useScan
1131
+ });
1132
+ requestAiRefinement = Boolean(aiRefineAnswer.ai_refine);
1133
+ }
1134
+
1135
+ data.context_status = 'baseline';
1136
+ data.needs_ai_refinement = requestAiRefinement;
1137
+ data.last_refined_at = 'not-yet-refined';
1021
1138
  data.infrastructure = [];
1022
1139
  if (data.uses_docker) data.infrastructure.push('docker');
1023
1140
  if (data.cloud !== 'none') data.infrastructure.push(data.cloud);
@@ -1052,9 +1169,16 @@ async function main() {
1052
1169
  const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
1053
1170
  fs.writeFileSync(claudeMdPath, generateClaudeMd(data), 'utf8');
1054
1171
  console.log(` ${pc.green('✓')} Generated ${pc.bold('CLAUDE.md')}`);
1055
-
1056
- // Summary
1057
- const autoSkills = resolveSkills(data);
1172
+
1173
+ let aiRefinementPath = null;
1174
+ if (requestAiRefinement) {
1175
+ aiRefinementPath = path.join(targetDir, 'AI_CONTEXT_REFINEMENT.md');
1176
+ fs.writeFileSync(aiRefinementPath, generateAiContextRefinementPrompt(data), 'utf8');
1177
+ console.log(` ${pc.green('✓')} Generated ${pc.bold('AI_CONTEXT_REFINEMENT.md')}`);
1178
+ }
1179
+
1180
+ // Summary
1181
+ const autoSkills = resolveSkills(data);
1058
1182
  console.log(`\n${pc.bold(pc.green('━━ Done! ━━'))}
1059
1183
 
1060
1184
  ${pc.bold('Project:')} ${data.project_name}
@@ -1067,11 +1191,12 @@ ${autoSkills.map(s => ' · ' + s).join('\n')}
1067
1191
  ${pc.bold('Generated files:')}
1068
1192
  · ${pc.bold(projectContextPath)}
1069
1193
  · ${pc.bold(claudeMdPath)}
1194
+ ${aiRefinementPath ? ` · ${pc.bold(aiRefinementPath)}` : ''}
1070
1195
 
1071
1196
  ${pc.bold('Next steps:')}
1072
1197
  1. Your kit is installed in ${pc.bold('./.agent')}
1073
1198
  2. Review ${pc.bold('PROJECT_CONTEXT.md')} first and add architecture notes
1074
- 3. Instruct the agent to read/update ${pc.bold('PROJECT_CONTEXT.md')} before major tasks
1199
+ 3. ${aiRefinementPath ? `Open ${pc.bold('AI_CONTEXT_REFINEMENT.md')} in your AI editor to refine project context` : `Instruct the agent to read/update ${pc.bold('PROJECT_CONTEXT.md')} before major tasks`}
1075
1200
  4. Open project in Antigravity or Claude Code — the agent will read context on start
1076
1201
  `);
1077
1202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thachvd-kit",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Universal Agent Kit for Antigravity, Claude Code, and OpenCode",
5
5
  "bin": {
6
6
  "thachvd-kit": "./bin/cli.js"
package/rules/GEMINI.md CHANGED
@@ -10,16 +10,19 @@ trigger: always_on
10
10
 
11
11
  ## MANDATORY FIRST ACTION
12
12
 
13
- **At session start — before anything else:**
14
-
15
- ```
16
- 1. Read PROJECT_CONTEXT.md (if exists) → load stack, agents, constraints
17
- 2. If missing → run @[skills/project-onboarding] to create it
18
- 3. Confirm: "Context loaded: [Project Name] | Stack: [Stack] | Ready."
19
- ```
20
-
21
- > All routing decisions below depend on PROJECT_CONTEXT.md.
22
- > Without it, ask user to run: `python scripts/init.py`
13
+ **At session start — before anything else:**
14
+
15
+ ```
16
+ 1. Read PROJECT_CONTEXT.md (if exists) → load stack, agents, constraints
17
+ 2. If missing → run @[skills/project-onboarding] to create it
18
+ 3. Read PROJECT_CONTEXT context.status and context.needs_ai_refinement
19
+ 4. If needs_ai_refinement = true → scan project with AI, update PROJECT_CONTEXT.md, set status=refined and needs_ai_refinement=false
20
+ 5. Confirm: "Context loaded: [Project Name] | Stack: [Stack] | Ready."
21
+ ```
22
+
23
+ > All routing decisions below depend on PROJECT_CONTEXT.md.
24
+ > Without it, ask user to run: `python scripts/init.py`
25
+ > Baseline context must be refined once when `context.needs_ai_refinement = true`.
23
26
 
24
27
  ---
25
28
 
package/scripts/init.py CHANGED
@@ -316,14 +316,16 @@ def scan_project():
316
316
  {**build_detection("django", [
317
317
  evidence_if(text_matches(requirements, r"\bdjango\b") or text_matches(pyproject, r"\bdjango\b"), "Python deps -> django", 4),
318
318
  evidence_if(Path("manage.py").exists(), "found manage.py", 2),
319
- ]), "kind": "backend", "project_type": "api", "language": "python"},
319
+ evidence_if(Path("templates").exists(), "found templates/", 1),
320
+ ]), "kind": "backend", "project_type": "web-app", "language": "python"},
320
321
  {**build_detection("flask", [
321
322
  evidence_if(text_matches(requirements, r"\bflask\b") or text_matches(pyproject, r"\bflask\b"), "Python deps -> flask", 4),
322
323
  ]), "kind": "backend", "project_type": "api", "language": "python"},
323
324
  {**build_detection("laravel", [
324
325
  evidence_if(has_composer_dependency(composer_dep_names, "laravel/framework"), "composer.json -> laravel/framework", 4),
325
326
  evidence_if(Path("artisan").exists(), "found artisan", 3),
326
- ]), "kind": "backend", "project_type": "api", "language": "php"},
327
+ evidence_if(Path("resources/views").exists(), "found resources/views", 1),
328
+ ]), "kind": "backend", "project_type": "web-app", "language": "php"},
327
329
  {**build_detection("cakephp", [
328
330
  evidence_if(has_composer_dependency(composer_dep_names, "cakephp/cakephp"), "composer.json -> cakephp/cakephp", 4),
329
331
  evidence_if(Path("bin/cake").exists(), "found bin/cake", 3),
@@ -333,12 +335,15 @@ def scan_project():
333
335
  evidence_if(Path("app/Model/AppModel.php").exists(), "found app/Model/AppModel.php", 2),
334
336
  evidence_if(Path("lib/Cake").exists(), "found lib/Cake", 3),
335
337
  evidence_if(Path("cake/console/cake").exists(), "found cake/console/cake", 3),
336
- ]), "kind": "backend", "project_type": "api", "language": "php"},
338
+ evidence_if(Path("app/View").exists(), "found app/View", 2),
339
+ evidence_if(any(p.suffix.lower() == ".ctp" for p in Path("app/View").rglob("*.ctp")) if Path("app/View").exists() else False, "found .ctp view templates", 2),
340
+ ]), "kind": "backend", "project_type": "web-app", "language": "php"},
337
341
  {**build_detection("symfony", [
338
342
  evidence_if(has_composer_dependency(composer_dep_names, "symfony/framework-bundle"), "composer.json -> symfony/framework-bundle", 4),
339
343
  evidence_if(Path("bin/console").exists(), "found bin/console", 2),
340
344
  evidence_if(Path("config/bundles.php").exists(), "found config/bundles.php", 1),
341
- ]), "kind": "backend", "project_type": "api", "language": "php"},
345
+ evidence_if(Path("templates").exists(), "found templates/", 1),
346
+ ]), "kind": "backend", "project_type": "web-app", "language": "php"},
342
347
  {**build_detection("codeigniter", [
343
348
  evidence_if(has_composer_dependency(composer_dep_names, "codeigniter4/framework"), "composer.json -> codeigniter4/framework", 4),
344
349
  evidence_if(Path("spark").exists(), "found spark", 2),
@@ -356,7 +361,8 @@ def scan_project():
356
361
  {**build_detection("rails", [
357
362
  evidence_if(text_matches(gemfile, r'^\s*gem\s+["\']rails["\']'), "Gemfile -> rails", 4),
358
363
  evidence_if(Path("bin/rails").exists(), "found bin/rails", 2),
359
- ]), "kind": "backend", "project_type": "api", "language": "ruby"},
364
+ evidence_if(Path("app/views").exists(), "found app/views", 1),
365
+ ]), "kind": "backend", "project_type": "web-app", "language": "ruby"},
360
366
  {**build_detection("spring", [
361
367
  evidence_if(text_matches(pom, r"spring-boot") or text_matches(gradle, r"spring-boot"), "Java build -> spring-boot", 4),
362
368
  ]), "kind": "backend", "project_type": "api", "language": "java"},
@@ -404,6 +410,7 @@ def scan_project():
404
410
  if any(item["project_type"] == "desktop" for item in framework_details): project_type = "desktop"
405
411
  elif any(item["project_type"] == "mobile" for item in framework_details): project_type = "mobile"
406
412
  elif any(item["kind"] == "frontend" for item in framework_details) and any(item["kind"] == "backend" for item in framework_details): project_type = "fullstack"
413
+ elif any(item["project_type"] == "web-app" for item in framework_details): project_type = "web-app"
407
414
  elif any(item["kind"] == "backend" for item in framework_details): project_type = "api"
408
415
  elif any(item["kind"] == "frontend" for item in framework_details): project_type = "web-app"
409
416
  elif Path("package.json").exists() and pkg.get("bin"): project_type = "cli"
@@ -539,13 +546,24 @@ These detections were inferred from the current codebase during `init --scan`:
539
546
  # Generated by thachvd-kit init wizard on {datetime.now().strftime('%Y-%m-%d')}
540
547
  # Edit freely — this is your project's brain.
541
548
 
542
- ## Project
543
-
544
- - **Name:** {data['project_name']}
545
- - **Description:** {data['description']}
546
- - **Type:** {data['project_type']}
547
-
548
- ---
549
+ ## Project
550
+
551
+ - **Name:** {data['project_name']}
552
+ - **Description:** {data['description']}
553
+ - **Type:** {data['project_type']}
554
+
555
+ ---
556
+
557
+ ## Context Status
558
+
559
+ ```yaml
560
+ context:
561
+ status: {data.get('context_status', 'baseline')}
562
+ needs_ai_refinement: {'true' if data.get('needs_ai_refinement') else 'false'}
563
+ last_refined_at: {data.get('last_refined_at', 'not-yet-refined')}
564
+ ```
565
+
566
+ ---
549
567
 
550
568
  ## Stack
551
569
 
@@ -607,7 +625,7 @@ Based on your stack, these skills will be loaded automatically:
607
625
  {data.get('known_issues', '<!-- Add known issues or pending tasks -->')}
608
626
  """
609
627
 
610
- def generate_claude_md(data):
628
+ def generate_claude_md(data):
611
629
  """Generate CLAUDE.md for Claude Code / Codex compatibility."""
612
630
  return f"""# CLAUDE.md — {data['project_name']}
613
631
  # Generated by thachvd-kit. See PROJECT_CONTEXT.md for full stack info.
@@ -636,8 +654,82 @@ Stack: {data['primary_language']} | {', '.join(data.get('frameworks', []))}
636
654
  - `/test` — Run test suite
637
655
  - `/deploy` — Deploy checklist
638
656
 
639
- See PROJECT_CONTEXT.md for full agent routing table.
640
- """
657
+ See PROJECT_CONTEXT.md for full agent routing table.
658
+ """
659
+
660
+ def generate_ai_context_refinement_prompt(data):
661
+ scan_evidence = data.get("scan_evidence", [])
662
+ evidence_block = "\n".join("- " + line for line in scan_evidence) if scan_evidence else "- No scan evidence was captured during init."
663
+ return f"""# AI Context Refinement
664
+
665
+ Use this prompt in your AI editor after `thachvd-kit init`.
666
+
667
+ ## Goal
668
+
669
+ Refine `PROJECT_CONTEXT.md` so it reflects the real project more accurately than the baseline init scan.
670
+
671
+ ## Current Baseline
672
+
673
+ - Project: {data['project_name']}
674
+ - Type: {data['project_type']}
675
+ - Stack: {data['primary_language']} | {', '.join(data.get('frameworks', [])) or 'no framework'}
676
+ - Database: {data.get('database', 'none')}
677
+ - Infra: {', '.join(data.get('infrastructure', [])) or 'none'}
678
+
679
+ ## Scan Evidence
680
+
681
+ {evidence_block}
682
+
683
+ ## Instructions For The AI Agent
684
+
685
+ 1. Read `PROJECT_CONTEXT.md` first.
686
+ 2. Inspect the project selectively to improve factual accuracy.
687
+ 3. Prioritize:
688
+ - real database/config detection
689
+ - domain/capability inference
690
+ - architecture notes
691
+ - more accurate auto-resolved skills
692
+ - important project-specific constraints
693
+ 4. Update `PROJECT_CONTEXT.md` directly.
694
+ 5. Distinguish facts from inferences when uncertain.
695
+ 6. Do not implement product code in this pass. Only refine project context.
696
+
697
+ ## Suggested Files To Inspect
698
+
699
+ - Root manifests: `package.json`, `composer.json`, `Cargo.toml`, `pyproject.toml`
700
+ - Infrastructure/config: Docker, CI, env, framework config files
701
+ - Database config and models
702
+ - Main app entry points, routing, controllers/services
703
+ - README and existing documentation
704
+
705
+ ## Desired Improvements
706
+
707
+ - Correct the `database` field if config files reveal the real DB.
708
+ - Add architecture notes that explain the actual structure.
709
+ - Infer domain capabilities such as scraping, browser automation, external API integrations, queues, workers, etc.
710
+ - Expand auto-resolved skills when there is clear evidence in the codebase.
711
+ - Add caveats or assumptions if parts of the stack are still uncertain.
712
+
713
+ ## Completion Standard
714
+
715
+ When done, summarize:
716
+ - what was corrected
717
+ - what is still inferred
718
+ - which files provided the strongest evidence
719
+ """
720
+
721
+ def format_scan_preview_lines(scanned):
722
+ return [
723
+ ("Project type", scanned.get("project_type", "unknown")),
724
+ ("Primary language", scanned.get("primary_language", "unknown")),
725
+ ("Frameworks", ", ".join(scanned.get("frameworks", [])) or "no framework"),
726
+ ("Database", scanned.get("database", "none")),
727
+ ("Package manager", scanned.get("package_manager", "auto-detect")),
728
+ ("Test framework", scanned.get("test_framework", "auto-detect")),
729
+ ("Cloud", scanned.get("cloud", "none")),
730
+ ("Infrastructure", ", ".join(scanned.get("infrastructure", [])) or "none"),
731
+ ("CI/CD", scanned.get("ci_cd", "none")),
732
+ ]
641
733
 
642
734
  # ─── Main wizard ─────────────────────────────────────────────────────────────
643
735
 
@@ -678,8 +770,10 @@ def main():
678
770
  review_scanned_fields = not use_scan
679
771
  if use_scan:
680
772
  print(f" {dim('Scan mode: detecting stack from current project files...')}")
681
- print(f" {bold('Scan result:')} {scanned['primary_language']} | {', '.join(scanned['frameworks']) or 'no framework'}")
682
- print(f" {bold('Detected infra:')} {', '.join(scanned['infrastructure']) or 'none'}\n")
773
+ print(f" {bold('Detected context:')}")
774
+ for label, value in format_scan_preview_lines(scanned):
775
+ print(f" · {label}: {value}")
776
+ print()
683
777
  if scanned.get("scan_evidence"):
684
778
  print(f" {bold('Evidence:')}")
685
779
  for line in scanned["scan_evidence"]:
@@ -769,6 +863,9 @@ def main():
769
863
  data["code_style"] = data.get("code_style", (scanned or {}).get("code_style", "project-standard"))
770
864
  data["framework_details"] = data.get("framework_details", (scanned or {}).get("framework_details", []))
771
865
  data["scan_evidence"] = data.get("scan_evidence", (scanned or {}).get("scan_evidence", []))
866
+ data["context_status"] = data.get("context_status", "baseline")
867
+ data["needs_ai_refinement"] = data.get("needs_ai_refinement", False)
868
+ data["last_refined_at"] = data.get("last_refined_at", "not-yet-refined")
772
869
  data["architecture_notes"] = data.get("architecture_notes", (scanned or {}).get("architecture_notes", "<!-- Add notes -->"))
773
870
  data["current_phase"] = data.get("current_phase", (scanned or {}).get("current_phase", "Initial setup"))
774
871
  data["next_phase"] = data.get("next_phase", (scanned or {}).get("next_phase", "—"))
@@ -777,6 +874,9 @@ def main():
777
874
  if data["uses_docker"]: data["infrastructure"].append("docker")
778
875
  if data["cloud"] != "none": data["infrastructure"].append(data["cloud"])
779
876
 
877
+ if not args.yes:
878
+ data["needs_ai_refinement"] = ask_bool("Should the agent refine PROJECT_CONTEXT.md on first session start?", default=use_scan)
879
+
780
880
  # ── Generate files ────────────────────────────────────────────────────
781
881
  section("Generating Files")
782
882
 
@@ -788,11 +888,17 @@ def main():
788
888
  # CLAUDE.md (for Claude Code / Codex)
789
889
  if args.platform in ("claude", "all"):
790
890
  claude_path = Path("CLAUDE.md")
791
- claude_path.write_text(generate_claude_md(data), encoding="utf-8")
792
- print(f" {green('✓')} {bold('CLAUDE.md')} generated (Claude Code / Codex)")
793
-
794
- # Summary
795
- auto_skills = resolve_skills(data)
891
+ claude_path.write_text(generate_claude_md(data), encoding="utf-8")
892
+ print(f" {green('✓')} {bold('CLAUDE.md')} generated (Claude Code / Codex)")
893
+
894
+ ai_refine_path = None
895
+ if data.get("needs_ai_refinement"):
896
+ ai_refine_path = Path("AI_CONTEXT_REFINEMENT.md")
897
+ ai_refine_path.write_text(generate_ai_context_refinement_prompt(data), encoding="utf-8")
898
+ print(f" {green('✓')} {bold('AI_CONTEXT_REFINEMENT.md')} generated")
899
+
900
+ # Summary
901
+ auto_skills = resolve_skills(data)
796
902
  print(f"""
797
903
  {bold(green('━━ Done! ━━'))}
798
904
 
@@ -806,10 +912,11 @@ def main():
806
912
  {bold('Generated files:')}
807
913
  · {bold(str(output_path.resolve()))}
808
914
  · {bold(str(Path("CLAUDE.md").resolve())) if args.platform in ("claude", "all") else 'CLAUDE.md not generated for this platform'}
915
+ · {bold(str(ai_refine_path.resolve())) if ai_refine_path else 'AI_CONTEXT_REFINEMENT.md not generated'}
809
916
 
810
917
  {bold('Next steps:')}
811
918
  1. Review {bold('PROJECT_CONTEXT.md')} first and add architecture notes
812
- 2. Instruct the agent to read/update {bold('PROJECT_CONTEXT.md')} before major tasks
919
+ 2. {"Open " + bold("AI_CONTEXT_REFINEMENT.md") + " in your AI editor to refine project context" if ai_refine_path else "Instruct the agent to read/update " + bold("PROJECT_CONTEXT.md") + " before major tasks"}
813
920
  3. Open project in your AI editor (Antigravity / Claude Code / Codex)
814
921
  4. The agent will read PROJECT_CONTEXT.md on session start
815
922