@leejungkiin/awkit 1.7.1 → 1.7.5

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 (262) hide show
  1. package/README.md +42 -5
  2. package/bin/awk.js +1148 -92
  3. package/core/CLAUDE.md +1 -1
  4. package/core/GEMINI.md +152 -167
  5. package/core/GEMINI.md.bak +153 -116
  6. package/core/GreetingEngine/index.js +225 -0
  7. package/core/GreetingEngine/locales/en.json +6 -0
  8. package/core/GreetingEngine/locales/ja.json +6 -0
  9. package/core/GreetingEngine/locales/vi.json +6 -0
  10. package/core/GreetingEngine/test.js +37 -0
  11. package/core/skill-runtime-manifest.json +3 -0
  12. package/docs/Claude Fable 5.md +3826 -0
  13. package/docs/PRD.md +57 -0
  14. package/docs/android_kotlin_system_instruction.md +210 -0
  15. package/docs/brainstorm_multi_agent_cli.md +211 -0
  16. package/docs/brainstorm_ponytail_integration.md +146 -0
  17. package/docs/brainstorm_smart_setup.md +113 -0
  18. package/docs/deep-research-report (1).md +293 -0
  19. package/docs/history/GEMINI.v1.md +135 -0
  20. package/docs/history/PRD.v1.md +55 -0
  21. package/docs/history/PRD.v2.md +57 -0
  22. package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
  23. package/docs/history/implementation_plan.v1.md +58 -0
  24. package/docs/history/memoizedfibonacci_spec.v1.md +127 -0
  25. package/docs/specs/greetingapp_spec.md +97 -0
  26. package/docs/specs/memoizedfibonacci_spec.md +211 -0
  27. package/package.json +4 -1
  28. package/scripts/artifact-storage.js +130 -0
  29. package/scripts/automation-gate.js +35 -2
  30. package/scripts/claude-plan.js +76 -0
  31. package/scripts/dependency-manager.js +210 -0
  32. package/scripts/exec-rtk.js +11 -5
  33. package/scripts/i18n-helper.js +381 -0
  34. package/scripts/multi-model-pipeline.js +386 -0
  35. package/scripts/qwen-exec.js +147 -0
  36. package/skill-packs/mobile-ios/pack.json +4 -2
  37. package/skill-packs/reverse-engineering/pack.json +1 -0
  38. package/skills/CATALOG.md +20 -0
  39. package/skills/GEMINI.md +9 -1
  40. package/skills/TRIGGER_INDEX.md +10 -0
  41. package/skills/ai-music/SKILL.md +275 -0
  42. package/skills/android-re-analyzer/SKILL.md +238 -0
  43. package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
  44. package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
  45. package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
  46. package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
  47. package/skills/android-re-analyzer/references/setup-guide.md +221 -0
  48. package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
  49. package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
  50. package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
  51. package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
  52. package/skills/animal-island-ui-style/SKILL.md +1450 -0
  53. package/skills/app-store-review-agent/SKILL.md +164 -0
  54. package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
  55. package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
  56. package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
  57. package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
  58. package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
  59. package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
  60. package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
  61. package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
  62. package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
  63. package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
  64. package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
  65. package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
  66. package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
  67. package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
  68. package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
  69. package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
  70. package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
  71. package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
  72. package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
  73. package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
  74. package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
  75. package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
  76. package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
  77. package/skills/awf-ponytail/SKILL.md +91 -0
  78. package/skills/awf-ponytail-review/SKILL.md +67 -0
  79. package/skills/awf-session-restore/SKILL.md +3 -3
  80. package/skills/brainstorm-agent/SKILL.md +11 -2
  81. package/skills/brainstorm-agent/templates/brief-template.md +8 -0
  82. package/skills/claude-planner/SKILL.md +71 -0
  83. package/skills/code-review/SKILL.md +87 -0
  84. package/skills/codex-conductor/SKILL.md +25 -1
  85. package/skills/expo-game-development/SKILL.md +163 -0
  86. package/skills/flutter/LICENSE.txt +202 -0
  87. package/skills/flutter/SKILL.md +127 -0
  88. package/skills/flutter-project-creater/LICENSE.txt +202 -0
  89. package/skills/flutter-project-creater/SKILL.md +106 -0
  90. package/skills/game-developer/SKILL.md +163 -0
  91. package/skills/game-developer/references/ecs-patterns.md +501 -0
  92. package/skills/game-developer/references/multiplayer-networking.md +475 -0
  93. package/skills/game-developer/references/performance-optimization.md +422 -0
  94. package/skills/game-developer/references/unity-patterns.md +271 -0
  95. package/skills/game-developer/references/unreal-cpp.md +352 -0
  96. package/skills/generate-gui-assets/SKILL.md +305 -0
  97. package/skills/generate-gui-assets/agents/openai.yaml +4 -0
  98. package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
  99. package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
  100. package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
  101. package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  102. package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
  103. package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
  104. package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
  105. package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
  106. package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
  107. package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
  108. package/skills/godot-game-development/SKILL.md +142 -0
  109. package/skills/hatch-pet/LICENSE.txt +201 -0
  110. package/skills/hatch-pet/SKILL.md +420 -0
  111. package/skills/hatch-pet/agents/openai.yaml +4 -0
  112. package/skills/hatch-pet/references/animation-rows.md +29 -0
  113. package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
  114. package/skills/hatch-pet/references/qa-rubric.md +60 -0
  115. package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  116. package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
  117. package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
  118. package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
  119. package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
  120. package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
  121. package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
  122. package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
  123. package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
  124. package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
  125. package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
  126. package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
  127. package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
  128. package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
  129. package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
  130. package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
  131. package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
  132. package/skills/i18n-orchestrator/SKILL.md +37 -0
  133. package/skills/ios-simulator-skill/SKILL.md +390 -0
  134. package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
  135. package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
  136. package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
  137. package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
  138. package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
  139. package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
  140. package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
  141. package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
  142. package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
  143. package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
  144. package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
  145. package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
  146. package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
  147. package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
  148. package/skills/ios-simulator-skill/scripts/container.py +668 -0
  149. package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
  150. package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
  151. package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
  152. package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
  153. package/skills/ios-simulator-skill/scripts/location.py +467 -0
  154. package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
  155. package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
  156. package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
  157. package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
  158. package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
  159. package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
  160. package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
  161. package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
  162. package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
  163. package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
  164. package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
  165. package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
  166. package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
  167. package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
  168. package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
  169. package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
  170. package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
  171. package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
  172. package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
  173. package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
  174. package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
  175. package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
  176. package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
  177. package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
  178. package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
  179. package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
  180. package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
  181. package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
  182. package/skills/orchestrator/SKILL.md +0 -20
  183. package/skills/persistent-storage/SKILL.md +55 -0
  184. package/skills/qwen-conductor/SKILL.md +100 -0
  185. package/skills/short-maker/SKILL.md +23 -0
  186. package/skills/short-maker/scripts/effects.js +56 -0
  187. package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
  188. package/skills/short-maker/scripts/videomix.js +601 -0
  189. package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
  190. package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
  191. package/skills/smali-to-kotlin/SKILL.md +128 -0
  192. package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
  193. package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
  194. package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
  195. package/skills/smali-to-kotlin/library-patterns.md +189 -0
  196. package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
  197. package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
  198. package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
  199. package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
  200. package/skills/smali-to-kotlin/phase-3-build.md +248 -0
  201. package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
  202. package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
  203. package/skills/smali-to-kotlin/templates/app-map.md +101 -0
  204. package/skills/smali-to-kotlin/templates/architecture.md +142 -0
  205. package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
  206. package/skills/spec-gate/SKILL.md +6 -2
  207. package/skills/symphony-enforcer/SKILL.md +8 -0
  208. package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
  209. package/skills/symphony-enforcer/examples/three-phase.md +16 -0
  210. package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
  211. package/skills/unity-game-development/SKILL.md +231 -0
  212. package/skills/video-edit/SKILL.md +36 -0
  213. package/skills/video-edit/scripts/video_edit.py +324 -0
  214. package/templates/project-identity/android.json +2 -2
  215. package/templates/project-identity/backend-nestjs.json +2 -2
  216. package/templates/project-identity/expo.json +2 -2
  217. package/templates/project-identity/ios.json +2 -2
  218. package/templates/project-identity/web-nextjs.json +2 -2
  219. package/templates/setup-mapping.json +48 -0
  220. package/templates/specs/design-template.md +161 -71
  221. package/templates/specs/requirements-template.md +65 -133
  222. package/templates/specs/task-spec-template.xml +3 -0
  223. package/workflows/_uncategorized/critic.md +40 -0
  224. package/workflows/_uncategorized/git-rebase-flow.md +81 -0
  225. package/workflows/_uncategorized/image-gen.md +118 -0
  226. package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
  227. package/workflows/_uncategorized/pixel-gen.md +86 -0
  228. package/workflows/_uncategorized/pixel-setup.md +90 -0
  229. package/workflows/_uncategorized/ponytail-review.md +59 -0
  230. package/workflows/_uncategorized/reverse-android-build.md +222 -0
  231. package/workflows/_uncategorized/reverse-android-design.md +139 -0
  232. package/workflows/_uncategorized/reverse-android-discover.md +150 -0
  233. package/workflows/_uncategorized/reverse-android-scan.md +158 -0
  234. package/workflows/_uncategorized/reverse-android.md +143 -0
  235. package/workflows/_uncategorized/reverse-ios-build.md +240 -0
  236. package/workflows/_uncategorized/reverse-ios-design.md +112 -0
  237. package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
  238. package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
  239. package/workflows/_uncategorized/reverse-ios.md +152 -0
  240. package/workflows/_uncategorized/safety-router.md +34 -0
  241. package/workflows/_uncategorized/teach.md +89 -0
  242. package/workflows/_uncategorized/verify-ui.md +53 -0
  243. package/workflows/_uncategorized/visualize-screenshots.md +34 -0
  244. package/workflows/ads/ads-analyst.md +201 -0
  245. package/workflows/ads/ads-audit.md +106 -0
  246. package/workflows/ads/ads-optimize.md +97 -0
  247. package/workflows/ads/ads-targeting.md +241 -0
  248. package/workflows/ads/adsExpert.md +160 -0
  249. package/workflows/ads/smali-ads-config.md +400 -0
  250. package/workflows/ads/smali-ads-flow.md +331 -0
  251. package/workflows/ads/smali-ads-interstitial.md +377 -0
  252. package/workflows/ads/smali-ads-native.md +382 -0
  253. package/workflows/context/teach.md +89 -0
  254. package/workflows/gitnexus.md +8 -8
  255. package/workflows/lifecycle/brainstorm.md +43 -0
  256. package/workflows/lifecycle/code.md +5 -0
  257. package/workflows/lifecycle/init.md +23 -5
  258. package/workflows/lifecycle/multi-model-pipeline.md +60 -0
  259. package/workflows/multi-agent-pipeline.md +52 -0
  260. package/workflows/quality/ponytail-review.md +59 -0
  261. package/workflows/roles/critic.md +40 -0
  262. package/workflows/roles/safety-router.md +34 -0
package/bin/awk.js CHANGED
@@ -19,6 +19,7 @@
19
19
  * awkit list-packs List available skill packs
20
20
  * awkit tg setup Setup Telegram Bot API credentials
21
21
  * awkit tg send Send a message via Telegram Bot API
22
+ * awkit build Run project build command securely based on configuration
22
23
  * awkit version Show current version
23
24
  *
24
25
  * Created by Kien AI
@@ -43,6 +44,11 @@ const { cmdGate } = require('../scripts/automation-gate');
43
44
  const { cmdObsidian } = require('../scripts/obsidian-sync');
44
45
  const { cmdQuota } = require('../scripts/cockpit-quota');
45
46
  const { cmdVideo } = require('../scripts/video-analyzer');
47
+ const { cmdI18n } = require('../scripts/i18n-helper');
48
+ const { cmdStorage } = require('../scripts/artifact-storage');
49
+ const { cmdPipeline } = require('../scripts/multi-model-pipeline');
50
+ const depManager = require('../scripts/dependency-manager');
51
+
46
52
 
47
53
  // ─── Platform Definitions ──────────────────────────────────────────────────
48
54
 
@@ -368,7 +374,7 @@ function copyDirRecursive(src, dest, options = {}) {
368
374
  const entries = fs.readdirSync(src, { withFileTypes: true });
369
375
 
370
376
  for (const entry of entries) {
371
- if (entry.name === '.DS_Store' || entry.name === '.git') continue;
377
+ if (entry.name === '.DS_Store' || entry.name === '.git' || entry.name === '.venv' || entry.name === 'node_modules') continue;
372
378
 
373
379
  const srcPath = path.join(src, entry.name);
374
380
 
@@ -898,14 +904,19 @@ function cmdUpdate() {
898
904
  log(`${C.gray} local repo: v${AWK_VERSION}${C.reset}`);
899
905
  log('');
900
906
 
901
- // 3. Already up-to-date?
902
- if (npmLatest && npmLatest === installedVersion) {
903
- ok(`Already on the latest version (v${installedVersion}) 🎉`);
904
- return;
907
+ // 3. Already up-to-date or newer?
908
+ if (npmLatest) {
909
+ if (npmLatest === installedVersion) {
910
+ ok(`Already on the latest version (v${installedVersion}) 🎉`);
911
+ return;
912
+ } else if (isNewerVersion(installedVersion, npmLatest)) {
913
+ ok(`Your installed version (v${installedVersion}) is newer than the npm registry version (v${npmLatest}) 🎉`);
914
+ return;
915
+ }
905
916
  }
906
917
 
907
918
  // 4. New version available on npm → install from registry
908
- if (npmLatest && npmLatest !== installedVersion) {
919
+ if (npmLatest && isNewerVersion(npmLatest, installedVersion)) {
909
920
  info(`Upgrading: v${installedVersion} → v${npmLatest}`);
910
921
  info('Running: npm install -g @leejungkiin/awkit');
911
922
  try {
@@ -924,13 +935,15 @@ function cmdUpdate() {
924
935
  // 5. Offline fallback: compare AWK_VERSION (local repo) vs installed
925
936
  if (installedVersion === AWK_VERSION) {
926
937
  ok(`Already on latest version (v${AWK_VERSION}) — could not verify with npm`);
927
- } else {
938
+ } else if (isNewerVersion(AWK_VERSION, installedVersion)) {
928
939
  info(`Upgrading from v${installedVersion} → v${AWK_VERSION} (local only, npm unreachable)`);
929
940
  cmdInstall(['--update']);
941
+ } else {
942
+ ok(`Your installed version (v${installedVersion}) is newer than or equal to local repo version (v${AWK_VERSION})`);
930
943
  }
931
944
  }
932
945
 
933
- function cmdDoctor() {
946
+ async function cmdDoctor() {
934
947
  log('');
935
948
  log(`${C.cyan}${C.bold}🏥 AWK Health Check${C.reset}`);
936
949
  log('');
@@ -998,16 +1011,27 @@ function cmdDoctor() {
998
1011
  warn('schemas/ directory missing'); issues++;
999
1012
  }
1000
1013
 
1001
- // 4.5. Check RTK Integration
1002
- try {
1003
- const { checkRtk } = require('../scripts/exec-rtk');
1004
- if (checkRtk()) {
1005
- ok('RTK (Rust Token Killer) is installed and active');
1014
+ // 4.5. Check RTK & GitNexus Integration
1015
+ if (depManager.checkRtk()) {
1016
+ ok(`RTK (Rust Token Killer) is installed (v${depManager.getRtkVersion() || 'unknown'})`);
1017
+ } else {
1018
+ warn('RTK (Rust Token Killer) not found.');
1019
+ if (promptYN('Would you like to install RTK automatically now?')) {
1020
+ depManager.installRtk();
1006
1021
  } else {
1007
- warn('RTK (Rust Token Killer) not found. Run "awkit rtk setup" to optimize token usage.');
1022
+ issues++;
1023
+ }
1024
+ }
1025
+
1026
+ if (depManager.checkGitnexus()) {
1027
+ ok('GitNexus (Code Intelligence Indexer) is installed and active');
1028
+ } else {
1029
+ warn('GitNexus (Code Intelligence Indexer) not found.');
1030
+ if (promptYN('Would you like to install GitNexus globally now?')) {
1031
+ depManager.installGitnexus();
1032
+ } else {
1033
+ issues++;
1008
1034
  }
1009
- } catch (e) {
1010
- warn(`RTK check failed: ${e.message}`);
1011
1035
  }
1012
1036
 
1013
1037
  // 5. Check version
@@ -1021,6 +1045,25 @@ function cmdDoctor() {
1021
1045
  warn('Version file missing. Run "awkit install" first.'); issues++;
1022
1046
  }
1023
1047
 
1048
+ // 5.5. Check Updates
1049
+ try {
1050
+ const updates = await depManager.checkUpdates(AWK_VERSION);
1051
+ if (updates.latestAwkit && isNewerVersion(updates.latestAwkit, AWK_VERSION)) {
1052
+ warn(`AWKit update available: v${updates.latestAwkit} (current: v${AWK_VERSION})`);
1053
+ } else {
1054
+ ok('AWKit is up-to-date');
1055
+ }
1056
+
1057
+ const currentRtk = depManager.getRtkVersion();
1058
+ if (updates.latestRtk && currentRtk && isNewerVersion(updates.latestRtk, currentRtk)) {
1059
+ warn(`RTK update available: v${updates.latestRtk} (current: v${currentRtk})`);
1060
+ } else if (currentRtk) {
1061
+ ok('RTK is up-to-date');
1062
+ }
1063
+ } catch (e) {
1064
+ warn(`Update check failed: ${e.message}`);
1065
+ }
1066
+
1024
1067
  log('');
1025
1068
  log(`${C.bold}Runtime Profile:${C.reset}`);
1026
1069
  log(` Profile: ${C.cyan}${installState.profile}${C.reset}`);
@@ -1710,15 +1753,19 @@ function cmdListPacks() {
1710
1753
  }
1711
1754
 
1712
1755
  for (const pack of packs) {
1713
- const readmePath = path.join(packsDir, pack.name, 'README.md');
1714
1756
  const configPath = path.join(packsDir, pack.name, 'pack.json');
1757
+ const config = fs.existsSync(configPath) ? readJsonFile(configPath, {}) : {};
1758
+ if (config.hidden === true) {
1759
+ continue;
1760
+ }
1761
+
1762
+ const readmePath = path.join(packsDir, pack.name, 'README.md');
1715
1763
  let desc = '';
1716
1764
  if (fs.existsSync(readmePath)) {
1717
1765
  const content = fs.readFileSync(readmePath, 'utf8');
1718
1766
  desc = content.split('\n').find(l => l.trim() && !l.startsWith('#')) || '';
1719
1767
  }
1720
- if (!desc && fs.existsSync(configPath)) {
1721
- const config = readJsonFile(configPath, {});
1768
+ if (!desc) {
1722
1769
  desc = config.description || '';
1723
1770
  }
1724
1771
  log(` ${C.green}${pack.name}${C.reset} ${C.gray}${desc}${C.reset}`);
@@ -1898,6 +1945,22 @@ function cmdStatus() {
1898
1945
  }
1899
1946
 
1900
1947
  log('');
1948
+
1949
+ // ── GitNexus Info ──────────────────────────────────────────────────────
1950
+ const cwd = process.cwd();
1951
+ const gitnexusDir = path.join(cwd, '.gitnexus');
1952
+ if (fs.existsSync(gitnexusDir)) {
1953
+ const registry = readGitnexusRegistry();
1954
+ const match = registry.find(r => r.path === cwd);
1955
+ if (match) {
1956
+ const stats = match.stats || {};
1957
+ log(`${C.bold}GitNexus Info:${C.reset}`);
1958
+ log(` Active repo: ${C.green}${match.name}${C.reset} (${stats.nodes || 0} nodes, ${stats.edges || 0} relations)`);
1959
+ log(` Tip: Run 'awkit index' to refresh index or 'awkit status gn' for details.`);
1960
+ log('');
1961
+ }
1962
+ }
1963
+
1901
1964
  log(`${C.gray}Tip: 'awkit sync' = harvest (pull live→repo) + install (push repo→live)${C.reset}`);
1902
1965
  log('');
1903
1966
  }
@@ -1938,6 +2001,104 @@ function cmdSync() {
1938
2001
  log('');
1939
2002
  }
1940
2003
 
2004
+ // ─── Build: Safe project-specific compilation ────────────────────────────────
2005
+
2006
+ function cmdBuild() {
2007
+ log('');
2008
+ log(`${C.cyan}${C.bold}🔨 AWK Build Control${C.reset}`);
2009
+ log('');
2010
+
2011
+ const pjPath = path.join(process.cwd(), '.project-identity');
2012
+ const pj = readJsonFile(pjPath, {});
2013
+
2014
+ if (!pj.automation || !pj.automation.build) {
2015
+ warn('No build configuration found in .project-identity under "automation.build".');
2016
+ log('Skipping build...');
2017
+ return;
2018
+ }
2019
+
2020
+ const { enabled, command } = pj.automation.build;
2021
+
2022
+ if (!enabled) {
2023
+ warn('Build is disabled for this project (automation.build.enabled is false).');
2024
+ log('Skipping build...');
2025
+ return;
2026
+ }
2027
+
2028
+ if (!command) {
2029
+ err('Build command is not configured in .project-identity (automation.build.command is empty).');
2030
+ return;
2031
+ }
2032
+
2033
+ info(`Running build command: ${C.bold}${command}${C.reset}`);
2034
+ try {
2035
+ execSync(command, { stdio: 'inherit' });
2036
+ log('');
2037
+ ok('Build successful!');
2038
+ } catch (e) {
2039
+ log('');
2040
+ err(`Build failed: ${e.message}`);
2041
+ process.exit(1);
2042
+ }
2043
+ }
2044
+
2045
+ // ─── Run: Safe project-specific run/start script execution ──────────────────
2046
+
2047
+ function cmdRun(runArgs = []) {
2048
+ log('');
2049
+ log(`${C.cyan}${C.bold}🚀 AWK Run Control${C.reset}`);
2050
+ log('');
2051
+
2052
+ const pjPath = path.join(process.cwd(), '.project-identity');
2053
+ const pj = readJsonFile(pjPath, {});
2054
+
2055
+ if (!pj.automation || !pj.automation.run) {
2056
+ warn('No run configuration found in .project-identity under "automation.run".');
2057
+ log('Skipping run...');
2058
+ return;
2059
+ }
2060
+
2061
+ const { enabled, command } = pj.automation.run;
2062
+
2063
+ if (!enabled) {
2064
+ warn('Run is disabled for this project (automation.run.enabled is false).');
2065
+ log('Skipping run...');
2066
+ return;
2067
+ }
2068
+
2069
+ if (!command) {
2070
+ err('Run command is not configured in .project-identity (automation.run.command is empty).');
2071
+ return;
2072
+ }
2073
+
2074
+ const fullCommand = runArgs.length > 0 ? `${command} ${runArgs.join(' ')}` : command;
2075
+
2076
+ info(`Running command: ${C.bold}${fullCommand}${C.reset}`);
2077
+ try {
2078
+ execSync(fullCommand, { stdio: 'inherit' });
2079
+ } catch (e) {
2080
+ log('');
2081
+ err(`Run failed: ${e.message}`);
2082
+ process.exit(e.status || 1);
2083
+ }
2084
+ }
2085
+
2086
+ function cmdTask(taskArgs = []) {
2087
+ try {
2088
+ execSync(`symphony task ${taskArgs.join(' ')}`, { stdio: 'inherit' });
2089
+ } catch (e) {
2090
+ process.exit(e.status || 1);
2091
+ }
2092
+ }
2093
+
2094
+ function cmdProject(projectArgs = []) {
2095
+ try {
2096
+ execSync(`symphony project ${projectArgs.join(' ')}`, { stdio: 'inherit' });
2097
+ } catch (e) {
2098
+ process.exit(e.status || 1);
2099
+ }
2100
+ }
2101
+
1941
2102
  async function cmdAdmin() {
1942
2103
  info('Mở Symphony Dashboard...');
1943
2104
  try {
@@ -2027,6 +2188,59 @@ async function cmdRestart() {
2027
2188
  }
2028
2189
  }
2029
2190
 
2191
+ function cmdGreet(args) {
2192
+ let name = 'Guest';
2193
+ let lang = 'en';
2194
+ let timezone = undefined;
2195
+
2196
+ for (let i = 0; i < args.length; i++) {
2197
+ const arg = args[i];
2198
+ if (arg === '--help' || arg === '-h') {
2199
+ log('');
2200
+ log(`${C.cyan}${C.bold}👋 awkit greet — Personalized greetings${C.reset}`);
2201
+ log('');
2202
+ log('Usage:');
2203
+ log(' awkit greet [options]');
2204
+ log('');
2205
+ log('Options:');
2206
+ log(' -n, --name <name> Name of the person to greet (default: "Guest")');
2207
+ log(' -l, --lang <lang> Language code (en, vi, ja) (default: "en")');
2208
+ log(' -t, --timezone <tz> Timezone string (default: system timezone)');
2209
+ log(' -h, --help Show this help message');
2210
+ log('');
2211
+ return;
2212
+ }
2213
+ if ((arg === '--name' || arg === '-n') && args[i + 1] && !args[i + 1].startsWith('-')) {
2214
+ name = args[i + 1];
2215
+ i++;
2216
+ } else if (arg.startsWith('--name=')) {
2217
+ name = arg.split('=')[1];
2218
+ } else if ((arg === '--lang' || arg === '-l') && args[i + 1] && !args[i + 1].startsWith('-')) {
2219
+ lang = args[i + 1];
2220
+ i++;
2221
+ } else if (arg.startsWith('--lang=')) {
2222
+ lang = arg.split('=')[1];
2223
+ } else if ((arg === '--timezone' || arg === '-t') && args[i + 1] && !args[i + 1].startsWith('-')) {
2224
+ timezone = args[i + 1];
2225
+ i++;
2226
+ } else if (arg.startsWith('--timezone=')) {
2227
+ timezone = arg.split('=')[1];
2228
+ }
2229
+ }
2230
+
2231
+ try {
2232
+ const GreetingEngine = require('../core/GreetingEngine');
2233
+ const result = GreetingEngine.generateGreeting({ name, lang, timezone });
2234
+ if (result.status === 'success') {
2235
+ log(result.data.message);
2236
+ } else {
2237
+ err('Failed to generate greeting');
2238
+ }
2239
+ } catch (e) {
2240
+ err('Failed to execute Greeting Engine: ' + e.message);
2241
+ }
2242
+ }
2243
+
2030
2244
  function cmdHelp() {
2031
2245
  const line = `${C.gray}${'─'.repeat(56)}${C.reset}`;
2032
2246
  log('');
@@ -2035,6 +2249,33 @@ function cmdHelp() {
2035
2249
  log(`${C.cyan}${C.bold}╚═══════════════════════════════════════════════════════╝${C.reset}`);
2036
2250
  log('');
2037
2251
 
2252
+ // Project Context Detector
2253
+ const cwd = process.cwd();
2254
+ const identityPath = path.join(cwd, '.project-identity');
2255
+ if (fs.existsSync(identityPath)) {
2256
+ try {
2257
+ const raw = fs.readFileSync(identityPath, 'utf8');
2258
+ const identity = JSON.parse(raw);
2259
+ const icon = identity.icon || '⚡';
2260
+ const name = identity.projectName || 'Unnamed Project';
2261
+ const pType = identity.projectType ? `Type: ${identity.projectType}` : 'Type: Generic';
2262
+
2263
+ let bCmd = 'Disabled';
2264
+ if (identity.automation && identity.automation.build && identity.automation.build.enabled) {
2265
+ bCmd = identity.automation.build.command || 'Enabled';
2266
+ }
2267
+ let rCmd = 'Disabled';
2268
+ if (identity.automation && identity.automation.run && identity.automation.run.enabled) {
2269
+ rCmd = identity.automation.run.command || 'Enabled';
2270
+ }
2271
+
2272
+ log(`${C.cyan}👉 Active Project: ${icon} ${C.bold}${name}${C.reset}`);
2273
+ log(` [${pType} | Build Control: ${bCmd === 'Disabled' ? C.yellow + 'Manual' : C.green + bCmd} | Run Control: ${rCmd === 'Disabled' ? C.yellow + 'Manual' : C.green + rCmd}${C.reset}]`);
2274
+ log(` Run ${C.green}awkit identity${C.reset} for full configuration details.`);
2275
+ log('');
2276
+ } catch (_) {}
2277
+ }
2278
+
2038
2279
  // Install
2039
2280
  log(`${C.bold}⚙️ Setup${C.reset}`);
2040
2281
  log(line);
@@ -2047,6 +2288,10 @@ function cmdHelp() {
2047
2288
  log(` ${C.green}credentials list${C.reset} List stored API keys`);
2048
2289
  log(` ${C.green}credentials set${C.reset} <k> <v> Set API key (e.g., gemini_api_key, openrouter_api_key)`);
2049
2290
  log(` ${C.green}set-openrouter${C.reset} <key> Shorthand for setting OpenRouter API Key`);
2291
+ log(` ${C.green}config-global list${C.reset} List all global configurations`);
2292
+ log(` ${C.green}config-global set${C.reset} <k> <v> Set global configuration`);
2293
+ log(` ${C.green}config-global audio${C.reset} <on|off> Turn audio alerts on/off globally`);
2294
+ log(` ${C.green}config audio${C.reset} <on|off> Shorthand to turn audio alerts on/off globally`);
2050
2295
  log('');
2051
2296
 
2052
2297
  // Project Init
@@ -2056,6 +2301,9 @@ function cmdHelp() {
2056
2301
  log(` ${C.gray} --force${C.reset} Overwrite existing files`);
2057
2302
  log(` ${C.gray} Generates: .project-identity, <Name>.code-workspace,${C.reset}`);
2058
2303
  log(` ${C.gray} CODEBASE.md, thiết lập router (AGENTS.md, CLAUDE.md)${C.reset}`);
2304
+ log(` ${C.green}identity${C.reset} Show project identity & active configurations (alias: config)`);
2305
+ log(` ${C.green}build${C.reset} Run project build based on .project-identity config`);
2306
+ log(` ${C.green}run${C.reset} [args] Run project-specific start/dev commands based on config`);
2059
2307
  log('');
2060
2308
 
2061
2309
  // Maintenance
@@ -2082,10 +2330,21 @@ function cmdHelp() {
2082
2330
  log(` ${C.green}sync${C.reset} Full sync: harvest + install (one shot)`);
2083
2331
  log('');
2084
2332
 
2333
+ // GreetingApp
2334
+ log(`${C.bold}👋 GreetingApp${C.reset}`);
2335
+ log(line);
2336
+ log(` ${C.green}greet${C.reset} [options] Generate personalized dynamic greeting`);
2337
+ log(` ${C.gray} -n, --name <name>${C.reset} User name to greet (default: Guest)`);
2338
+ log(` ${C.gray} -l, --lang <lang>${C.reset} Language code: en, vi, ja (default: en)`);
2339
+ log(` ${C.gray} -t, --timezone <tz>${C.reset} Timezone parameter`);
2340
+ log('');
2341
+
2085
2342
  // Symphony
2086
2343
  log(`${C.bold}🎶 Symphony${C.reset}`);
2087
2344
  log(line);
2088
2345
  log(` ${C.green}admin${C.reset} Khởi động Symphony Dashboard`);
2346
+ log(` ${C.green}task${C.reset} [args] Manage Symphony tasks directly`);
2347
+ log(` ${C.green}project${C.reset} [args] Manage Symphony projects directly`);
2089
2348
  log('');
2090
2349
 
2091
2350
  // Packs
@@ -2098,6 +2357,36 @@ function cmdHelp() {
2098
2357
  log(` ${C.green}disable-pack${C.reset} <name> Uninstall a skill pack (backed up)`);
2099
2358
  log('');
2100
2359
 
2360
+ // Storage
2361
+ log(`${C.bold}🗄️ Storage${C.reset}`);
2362
+ log(line);
2363
+ log(` ${C.green}storage set${C.reset} <key> <val> Save key-value pair`);
2364
+ log(` ${C.green}storage get${C.reset} <key> Retrieve value by key`);
2365
+ log(` ${C.green}storage delete${C.reset} <key> Delete key`);
2366
+ log(` ${C.green}storage list${C.reset} [prefix] List keys with optional prefix`);
2367
+ log(` ${C.gray} --shared Access shared storage space${C.reset}`);
2368
+ log('');
2369
+
2370
+ // Pipeline
2371
+ log(`${C.bold}🔗 Pipeline & Sub-Agents${C.reset}`);
2372
+ log(line);
2373
+ log(` ${C.green}pipeline plan${C.reset} <Feature> Lập kế hoạch kiến trúc (Claude / Gemini fallback)`);
2374
+ log(` ${C.green}pipeline ui${C.reset} <Feature> Thiết kế shell & GUI assets (Codex / Gemini fallback)`);
2375
+ log(` ${C.green}pipeline code${C.reset} <Feature> Thực thi viết code & review (Qwen / Gemini fallback)`);
2376
+ log('');
2377
+ log(` ${C.bold}🤖 Multi-Agent Collaborative System${C.reset}`);
2378
+ log(` AWKit integrates third-party CLI agents to optimize reasoning quality & costs:`);
2379
+ log(` • ${C.cyan}Claude Code${C.reset} ➜ Architect planning & complex reasoning (Gate 2/4)`);
2380
+ log(` • ${C.cyan}Codex CLI${C.reset} ➜ UI shell design, asset generation & reviews (Gate 2.5/5)`);
2381
+ log(` • ${C.cyan}Qwen Coder${C.reset} ➜ Fast local boilerplate code execution (Gate 4)`);
2382
+ log(` • ${C.cyan}Gemini Flash${C.reset} ➜ Central coordination, active context & fallback router`);
2383
+ log('');
2384
+ log(` To query or configure active runners and alerts:`);
2385
+ log(` - Run ${C.green}awkit config list${C.reset} to show current settings`);
2386
+ log(` - Run ${C.green}awkit config runners${C.reset} to check paths & binary availability`);
2387
+ log(` - Run ${C.green}awkit config <runner> <on|off>${C.reset} to toggle specific runner`);
2388
+ log('');
2389
+
2101
2390
  // Trello
2102
2391
  log(`${C.bold}📋 Trello${C.reset}`);
2103
2392
  log(line);
@@ -2121,15 +2410,16 @@ function cmdHelp() {
2121
2410
  log('');
2122
2411
 
2123
2412
  // GitNexus
2124
- log(`${C.bold}🔍 GitNexus${C.reset}`);
2413
+ log(`${C.bold}🔍 GitNexus (Shortcuts: index, clean, list)${C.reset}`);
2125
2414
  log(line);
2126
- log(` ${C.green}gitnexus list${C.reset} List all indexed repositories`);
2127
- log(` ${C.green}gitnexus status${C.reset} Show current project index info`);
2128
- log(` ${C.green}gitnexus clean${C.reset} Interactive cleanup of old indexes`);
2129
- log(` ${C.green}gitnexus clean${C.reset} ${C.gray}<name...>${C.reset} Remove specific repo(s) by name`);
2415
+ log(` ${C.green}index${C.reset} Index/refresh current codebase (alias: analyze)`);
2416
+ log(` ${C.green}list${C.reset} List all indexed repositories`);
2417
+ log(` ${C.green}status gn${C.reset} Show current project index info (or 'gn status')`);
2418
+ log(` ${C.green}clean${C.reset} Interactive cleanup of old indexes`);
2419
+ log(` ${C.green}clean${C.reset} ${C.gray}<name...>${C.reset} Remove specific repo(s) by name`);
2130
2420
  log(` ${C.gray} --stale${C.reset} Auto-remove repos with missing paths`);
2131
2421
  log(` ${C.gray} --all${C.reset} Remove all except current project`);
2132
- log(` ${C.gray} Alias: gn${C.reset}`);
2422
+ log(` ${C.gray} Alias: gn / gitnexus${C.reset}`);
2133
2423
  log('');
2134
2424
 
2135
2425
  // Available packs
@@ -2139,6 +2429,11 @@ function cmdHelp() {
2139
2429
  if (packs.length) {
2140
2430
  log(` Available packs:`);
2141
2431
  for (const p of packs) {
2432
+ const configPath = path.join(packsDir, p.name, 'pack.json');
2433
+ const config = fs.existsSync(configPath) ? readJsonFile(configPath, {}) : {};
2434
+ if (config.hidden === true) {
2435
+ continue;
2436
+ }
2142
2437
  const readmePath = path.join(packsDir, p.name, 'README.md');
2143
2438
  let tagline = '';
2144
2439
  if (fs.existsSync(readmePath)) {
@@ -2212,10 +2507,55 @@ function detectProjectType(cwd) {
2212
2507
  if (entries.includes('pubspec.yaml')) {
2213
2508
  return 'flutter';
2214
2509
  }
2510
+
2511
+ // JS/Node project check
2512
+ if (entries.includes('package.json')) {
2513
+ try {
2514
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
2515
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
2516
+ if (pkg.bin) {
2517
+ return 'cli';
2518
+ }
2519
+ if (deps['next']) {
2520
+ return 'web-nextjs';
2521
+ }
2522
+ if (deps['@nestjs/core']) {
2523
+ return 'backend-nestjs';
2524
+ }
2525
+ return 'node';
2526
+ } catch (_) { /* continue */ }
2527
+ }
2528
+
2215
2529
  // Default: generic mobile-firebase
2216
2530
  return 'mobile-firebase';
2217
2531
  }
2218
2532
 
2533
+ /**
2534
+ * Get the default safe build command based on projectType.
2535
+ */
2536
+ function getDefaultBuildCommand(projectType) {
2537
+ switch (projectType) {
2538
+ case 'ios':
2539
+ return 'xcodebuild -scheme YourScheme -sdk iphonesimulator -configuration Debug';
2540
+ case 'android':
2541
+ return './gradlew assembleDebug';
2542
+ case 'expo':
2543
+ return 'npx expo prebuild';
2544
+ case 'flutter':
2545
+ return 'flutter build apk --debug';
2546
+ case 'web-nextjs':
2547
+ return 'npm run build';
2548
+ case 'backend-nestjs':
2549
+ return 'npm run build';
2550
+ case 'cli':
2551
+ case 'node':
2552
+ return 'npm test';
2553
+ case 'mobile-firebase':
2554
+ default:
2555
+ return 'npm run build';
2556
+ }
2557
+ }
2558
+
2219
2559
  /**
2220
2560
  * Build .project-identity object from detected type + project name.
2221
2561
  */
@@ -2326,6 +2666,70 @@ function buildProjectIdentity(projectName, projectType, cwd, date) {
2326
2666
  servicesDir: 'lib/shared/services',
2327
2667
  modelsDir: 'lib/shared/models',
2328
2668
  },
2669
+ cli: {
2670
+ projectType: 'cli',
2671
+ techStack: {
2672
+ platform: 'CLI',
2673
+ language: 'JavaScript / TypeScript',
2674
+ runtime: 'Node.js',
2675
+ framework: 'Vanilla NodeJS',
2676
+ testing: 'Jest / doctor',
2677
+ },
2678
+ codingStandards: { language: 'en', namingConvention: 'camelCase', indentation: 'spaces-4', lineLength: 120 },
2679
+ architecture: 'Modular',
2680
+ featuresDir: 'src/commands',
2681
+ sharedUIDir: 'src/ui',
2682
+ servicesDir: 'src/services',
2683
+ modelsDir: 'src/models',
2684
+ },
2685
+ node: {
2686
+ projectType: 'node',
2687
+ techStack: {
2688
+ platform: 'NodeJS Backend/App',
2689
+ language: 'JavaScript / TypeScript',
2690
+ runtime: 'Node.js',
2691
+ testing: 'Jest',
2692
+ },
2693
+ codingStandards: { language: 'en', namingConvention: 'camelCase', indentation: 'spaces-4', lineLength: 120 },
2694
+ architecture: 'Modular',
2695
+ featuresDir: 'src/features',
2696
+ sharedUIDir: 'src/views',
2697
+ servicesDir: 'src/services',
2698
+ modelsDir: 'src/models',
2699
+ },
2700
+ 'web-nextjs': {
2701
+ projectType: 'web-nextjs',
2702
+ techStack: {
2703
+ platform: 'Web',
2704
+ language: 'TypeScript / JavaScript',
2705
+ framework: 'Next.js',
2706
+ styling: 'Tailwind CSS / CSS Modules',
2707
+ stateManagement: 'Zustand / Redux Toolkit / React Context',
2708
+ networking: 'Axios / Fetch',
2709
+ },
2710
+ codingStandards: { language: 'en', namingConvention: 'camelCase', indentation: 'spaces-2', lineLength: 100 },
2711
+ architecture: 'App Router / Pages Router',
2712
+ featuresDir: 'src/app',
2713
+ sharedUIDir: 'src/components',
2714
+ servicesDir: 'src/services',
2715
+ modelsDir: 'src/models',
2716
+ },
2717
+ 'backend-nestjs': {
2718
+ projectType: 'backend-nestjs',
2719
+ techStack: {
2720
+ platform: 'Backend',
2721
+ language: 'TypeScript',
2722
+ framework: 'NestJS',
2723
+ database: 'PostgreSQL / MySQL / MongoDB',
2724
+ orm: 'Prisma / TypeORM / Mongoose',
2725
+ },
2726
+ codingStandards: { language: 'en', namingConvention: 'camelCase', indentation: 'spaces-2', lineLength: 100 },
2727
+ architecture: 'Controller-Service-Repository',
2728
+ featuresDir: 'src/modules',
2729
+ sharedUIDir: 'src/common',
2730
+ servicesDir: 'src/services',
2731
+ modelsDir: 'src/models',
2732
+ },
2329
2733
  };
2330
2734
 
2331
2735
  // Fallback: generic mobile-firebase
@@ -2394,6 +2798,10 @@ function buildProjectIdentity(projectName, projectType, cwd, date) {
2394
2798
  "pixel-mcp": {
2395
2799
  enabled: false
2396
2800
  }
2801
+ },
2802
+ build: {
2803
+ enabled: false,
2804
+ command: getDefaultBuildCommand(projectType)
2397
2805
  }
2398
2806
  },
2399
2807
  modelPolicy: {
@@ -2637,6 +3045,10 @@ async function cmdInit(forceFlag = false) {
2637
3045
  "pixel-mcp": {
2638
3046
  enabled: false
2639
3047
  }
3048
+ },
3049
+ build: {
3050
+ enabled: false,
3051
+ command: getDefaultBuildCommand(projectType)
2640
3052
  }
2641
3053
  };
2642
3054
  fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
@@ -2657,6 +3069,18 @@ async function cmdInit(forceFlag = false) {
2657
3069
  currentIdentity.automation.mcp = { "pixel-mcp": { enabled: false } };
2658
3070
  changed = true;
2659
3071
  }
3072
+ if (!currentIdentity.projectType) {
3073
+ currentIdentity.projectType = projectType;
3074
+ changed = true;
3075
+ }
3076
+ if (!currentIdentity.automation.build) {
3077
+ const detectedType = currentIdentity.projectType || projectType || 'mobile-firebase';
3078
+ currentIdentity.automation.build = {
3079
+ enabled: false,
3080
+ command: getDefaultBuildCommand(detectedType)
3081
+ };
3082
+ changed = true;
3083
+ }
2660
3084
  if (!currentIdentity.modelPolicy) {
2661
3085
  currentIdentity.modelPolicy = {
2662
3086
  mode: "auto",
@@ -2841,17 +3265,20 @@ Additional project context can be found in:
2841
3265
  ).trim();
2842
3266
 
2843
3267
  if (hasSource) {
2844
- execSync('npx -y gitnexus analyze', {
3268
+ const usePnpm = fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'));
3269
+ const indexCmd = usePnpm ? 'pnpm dlx gitnexus analyze' : 'npx -y gitnexus analyze';
3270
+ info(`Running index with: ${indexCmd}...`);
3271
+ execSync(indexCmd, {
2845
3272
  cwd,
2846
3273
  stdio: 'ignore',
2847
3274
  });
2848
3275
  ok('GitNexus index created (.gitnexus/)');
2849
3276
  } else {
2850
- dim('No source files found — skipping GitNexus index (run later: npx gitnexus analyze)');
3277
+ dim('No source files found — skipping GitNexus index (run later: pnpm dlx gitnexus analyze or npx gitnexus analyze)');
2851
3278
  }
2852
3279
  } catch (e) {
2853
3280
  warn(`GitNexus indexing failed: ${e.message}`);
2854
- dim('Run manually later: npx gitnexus analyze');
3281
+ dim('Run manually later: pnpm dlx gitnexus analyze or npx gitnexus analyze');
2855
3282
  }
2856
3283
 
2857
3284
  // ── 6. Summary ─────────────────────────────────────────────────────────────
@@ -2866,9 +3293,10 @@ Additional project context can be found in:
2866
3293
  dim(`GitNexus: code intelligence index`);
2867
3294
  log('');
2868
3295
  log(`${C.cyan}👉 Open ${workspaceName} in VS Code to get started.${C.reset}`);
3296
+ log(`${C.cyan}👉 Run '/init' in AI chat to activate Smart Ecosystem Setup (SAEE) & recommendations.${C.reset}`);
2869
3297
  log(`${C.cyan}👉 Run '/codebase-sync' in AI chat to keep CODEBASE.md updated.${C.reset}`);
2870
3298
  log(`${C.cyan}👉 Run 'symphony task list' to manage tasks.${C.reset}`);
2871
- log(`${C.cyan}👉 Run 'npx gitnexus analyze' after major changes to refresh index.${C.reset}`);
3299
+ log(`${C.cyan}👉 Run 'pnpm dlx gitnexus analyze' (or 'npx gitnexus analyze') after major changes to refresh index.${C.reset}`);
2872
3300
  log('');
2873
3301
 
2874
3302
  // ── 7. Open Documentation ───────────────────────────────────────────────────
@@ -3520,41 +3948,90 @@ function cmdTrello(args) {
3520
3948
  }
3521
3949
  }
3522
3950
 
3523
- // ─── Auto-Update Checker ──────────────────────────────────────────────────────
3524
-
3525
- function checkAutoUpdate() {
3526
- const checkFile = path.join(TARGETS.antigravity, '.awk_update_check');
3527
- const now = Date.now();
3528
- const ONEDAY = 24 * 60 * 60 * 1000;
3951
+ /**
3952
+ * Helper to check if semantic version `latest` is newer than `current`.
3953
+ */
3954
+ function isNewerVersion(latest, current) {
3955
+ if (!latest || !current) return false;
3956
+ const clean = v => v.replace(/^v/, '');
3957
+ const lClean = clean(latest);
3958
+ const cClean = clean(current);
3959
+ if (lClean === cClean) return false;
3960
+
3961
+ const parse = v => {
3962
+ const parts = v.split('-');
3963
+ const numbers = parts[0].split('.').map(Number);
3964
+ const prerelease = parts[1] ? parts[1].split('.') : [];
3965
+ return { numbers, prerelease };
3966
+ };
3529
3967
 
3530
- if (fs.existsSync(checkFile)) {
3531
- try {
3532
- const lastCheck = parseInt(fs.readFileSync(checkFile, 'utf8'), 10);
3533
- if (!isNaN(lastCheck) && (now - lastCheck < ONEDAY)) {
3534
- return; // already checked recently
3535
- }
3536
- } catch (_) { }
3968
+ const l = parse(lClean);
3969
+ const c = parse(cClean);
3970
+
3971
+ // Compare main version numbers
3972
+ for (let i = 0; i < 3; i++) {
3973
+ const ln = l.numbers[i] || 0;
3974
+ const cn = c.numbers[i] || 0;
3975
+ if (ln > cn) return true;
3976
+ if (ln < cn) return false;
3977
+ }
3978
+
3979
+ const lIsPrerelease = l.prerelease.length > 0;
3980
+ const cIsPrerelease = c.prerelease.length > 0;
3981
+
3982
+ if (lIsPrerelease && !cIsPrerelease) return false;
3983
+ if (!lIsPrerelease && cIsPrerelease) return true;
3984
+
3985
+ const maxLength = Math.max(l.prerelease.length, c.prerelease.length);
3986
+ for (let i = 0; i < maxLength; i++) {
3987
+ const lp = l.prerelease[i];
3988
+ const cp = c.prerelease[i];
3989
+ if (lp === undefined) return false;
3990
+ if (cp === undefined) return true;
3991
+ const lpNum = parseInt(lp, 10);
3992
+ const cpNum = parseInt(cp, 10);
3993
+ const lpIsNum = !isNaN(lpNum);
3994
+ const cpIsNum = !isNaN(cpNum);
3995
+ if (lpIsNum && cpIsNum) {
3996
+ if (lpNum > cpNum) return true;
3997
+ if (lpNum < cpNum) return false;
3998
+ } else if (lpIsNum) {
3999
+ return false;
4000
+ } else if (cpIsNum) {
4001
+ return true;
4002
+ } else {
4003
+ if (lp > cp) return true;
4004
+ if (lp < cp) return false;
4005
+ }
3537
4006
  }
4007
+ return false;
4008
+ }
3538
4009
 
3539
- // Touch the file so we don't retry immediately on failure
3540
- try { fs.writeFileSync(checkFile, now.toString()); } catch (_) { }
4010
+ // ─── Auto-Update Checker ──────────────────────────────────────────────────────
3541
4011
 
3542
- // Check for update using npm registry
4012
+ async function checkAutoUpdate() {
3543
4013
  try {
3544
- const output = execSync('npm view @leejungkiin/awkit version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 });
3545
- const npmVersion = output.trim();
3546
-
3547
- // Simple string comparison for versions like "1.0.0" (Assumes SemVer)
3548
- if (npmVersion && npmVersion !== AWK_VERSION) {
4014
+ const updates = await depManager.checkUpdates(AWK_VERSION);
4015
+ if (updates.latestAwkit && isNewerVersion(updates.latestAwkit, AWK_VERSION)) {
3549
4016
  log('');
3550
- log(`${C.yellow}${C.bold}🌟 [Thông báo] Có phiên bản mới cho AWKit! (v${npmVersion})${C.reset}`);
4017
+ log(`${C.yellow}${C.bold}🌟 [Thông báo] Có phiên bản mới cho AWKit! (v${updates.latestAwkit})${C.reset}`);
3551
4018
  log(`${C.gray} Phiên bản hiện tại: v${AWK_VERSION}${C.reset}`);
3552
4019
  log(`${C.gray} Chạy lệnh sau để nâng cấp:${C.reset}`);
3553
4020
  log(`${C.cyan} npm i -g @leejungkiin/awkit && awkit install${C.reset}`);
3554
4021
  log('');
3555
4022
  }
4023
+
4024
+ const currentRtk = depManager.getRtkVersion();
4025
+ if (updates.latestRtk && currentRtk && isNewerVersion(updates.latestRtk, currentRtk)) {
4026
+ log('');
4027
+ log(`${C.yellow}${C.bold}🌟 [Thông báo] Có phiên bản mới cho RTK! (v${updates.latestRtk})${C.reset}`);
4028
+ log(`${C.gray} Phiên bản hiện tại: v${currentRtk}${C.reset}`);
4029
+ log(`${C.gray} Chạy lệnh sau để nâng cấp:${C.reset}`);
4030
+ log(`${C.cyan} awkit rtk setup${C.reset}`);
4031
+ log('');
4032
+ }
3556
4033
  } catch (_) {
3557
- // Fail silently (offline, npm not installed, package not published yet)
4034
+ // Fail silently in auto check
3558
4035
  }
3559
4036
  }
3560
4037
 
@@ -3585,7 +4062,7 @@ function cmdGitnexus(args) {
3585
4062
  const registry = readGitnexusRegistry();
3586
4063
  if (registry.length === 0) {
3587
4064
  info('No GitNexus indexed repositories found.');
3588
- dim('Run `npx gitnexus analyze` in a project to index it.');
4065
+ dim('Run `pnpm dlx gitnexus analyze` or `npx gitnexus analyze` in a project to index it.');
3589
4066
  return;
3590
4067
  }
3591
4068
 
@@ -3618,7 +4095,7 @@ function cmdGitnexus(args) {
3618
4095
 
3619
4096
  if (!match) {
3620
4097
  warn('Current directory is not indexed by GitNexus.');
3621
- dim('Run `npx gitnexus analyze` to index this project.');
4098
+ dim('Run `pnpm dlx gitnexus analyze` or `npx gitnexus analyze` to index this project.');
3622
4099
  return;
3623
4100
  }
3624
4101
 
@@ -3644,7 +4121,7 @@ function cmdGitnexus(args) {
3644
4121
  if (stale) {
3645
4122
  log('');
3646
4123
  warn(`Index is ${ageDays} days old. Consider refreshing:`);
3647
- dim(' npx gitnexus analyze');
4124
+ dim(' pnpm dlx gitnexus analyze (or npx gitnexus analyze)');
3648
4125
  }
3649
4126
 
3650
4127
  // Show projectId from .project-identity if available
@@ -3854,9 +4331,34 @@ function cmdGitnexus(args) {
3854
4331
  break;
3855
4332
  }
3856
4333
 
4334
+ case 'index':
4335
+ case 'analyze':
4336
+ case 'build': {
4337
+ const cwd = process.cwd();
4338
+ const usePnpm = fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'));
4339
+ const indexCmd = usePnpm ? 'pnpm' : 'npx';
4340
+ const indexArgs = usePnpm ? ['dlx', 'gitnexus', 'analyze'] : ['-y', 'gitnexus', 'analyze'];
4341
+
4342
+ info(`Indexing codebase with GitNexus (${usePnpm ? 'pnpm' : 'npx'})...`);
4343
+ dim(`Running: ${indexCmd} ${indexArgs.join(' ')}`);
4344
+
4345
+ const result = spawnSync(indexCmd, indexArgs, {
4346
+ cwd,
4347
+ stdio: 'inherit',
4348
+ shell: true
4349
+ });
4350
+
4351
+ if (result.status === 0) {
4352
+ ok('GitNexus index successfully refreshed! ✨');
4353
+ } else {
4354
+ err(`GitNexus indexing failed with exit code ${result.status}`);
4355
+ }
4356
+ break;
4357
+ }
4358
+
3857
4359
  default:
3858
4360
  err(`Unknown gitnexus sub-command: ${sub}`);
3859
- log(` Available: list, status, clean`);
4361
+ log(` Available: index, list, status, clean`);
3860
4362
  break;
3861
4363
  }
3862
4364
  }
@@ -3868,43 +4370,77 @@ async function cmdRtkSetup() {
3868
4370
  log(`${C.cyan}${C.bold}║ ⚙️ RTK Auto-Setup & Configuration ║${C.reset}`);
3869
4371
  log(`${C.cyan}${C.bold}╚═══════════════════════════════════════════════════════╝${C.reset}`);
3870
4372
 
3871
- log(`\nChecking dependencies...`);
3872
- try {
3873
- execSync('which cargo', { stdio: 'ignore' });
3874
- ok('Cargo is installed.');
3875
- } catch (_) {
3876
- err('Cargo/Rust not found. Please install Rust (https://rustup.rs/) and retry.');
3877
- return;
3878
- }
3879
-
3880
- log(`\nInstalling RTK (Rust Token Killer) via Cargo...`);
3881
- try {
3882
- execSync('cargo install --git https://github.com/rtk-ai/rtk', { stdio: 'inherit' });
3883
- ok('RTK successfully installed/updated via Cargo.');
3884
- } catch (e) {
3885
- err(`Failed to install RTK: ${e.message}`);
3886
- return;
3887
- }
4373
+ const installed = depManager.installRtk();
4374
+ if (!installed) return;
3888
4375
 
3889
4376
  const zshrcPath = path.join(HOME, '.zshrc');
3890
4377
  log(`\nConfiguring Shell Aliases in ~/.zshrc...`);
3891
4378
  if (fs.existsSync(zshrcPath)) {
3892
- const zshrc = fs.readFileSync(zshrcPath, 'utf8');
3893
- const aliasBlock = `
3894
- # RTK Alias for AI Agents (AWKit)
3895
- alias git="rtk git"
3896
- alias npm="rtk npm"
3897
- alias pnpm="rtk pnpm"
3898
- alias yarn="rtk yarn"
3899
- alias cargo="rtk cargo"
4379
+ let zshrc = fs.readFileSync(zshrcPath, 'utf8');
4380
+
4381
+ // Clean up old aliases to avoid collision
4382
+ const oldAliases = [
4383
+ 'alias git="rtk git"',
4384
+ 'alias npm="rtk npm"',
4385
+ 'alias pnpm="rtk pnpm"',
4386
+ 'alias yarn="rtk yarn"',
4387
+ 'alias cargo="rtk cargo"'
4388
+ ];
4389
+ for (const oldAlias of oldAliases) {
4390
+ if (zshrc.includes(oldAlias)) {
4391
+ zshrc = zshrc.replace(new RegExp(`\\n?${oldAlias}\\n?`, 'g'), '\n');
4392
+ }
4393
+ }
4394
+
4395
+ const functionBlock = `
4396
+ # RTK Integration for AI Agents (AWKit)
4397
+ unalias npm pnpm yarn cargo git 2>/dev/null
4398
+ npm() {
4399
+ if [[ "$1" == "login" || "$1" == "adduser" || "$1" == "publish" ]]; then
4400
+ command npm "$@"
4401
+ else
4402
+ rtk npm "$@"
4403
+ fi
4404
+ }
4405
+ pnpm() {
4406
+ if [[ "$1" == "login" || "$1" == "publish" ]]; then
4407
+ command pnpm "$@"
4408
+ else
4409
+ rtk pnpm "$@"
4410
+ fi
4411
+ }
4412
+ yarn() {
4413
+ if [[ "$1" == "login" || "$1" == "publish" ]]; then
4414
+ command yarn "$@"
4415
+ else
4416
+ rtk yarn "$@"
4417
+ fi
4418
+ }
4419
+ cargo() {
4420
+ if [[ "$1" == "login" || "$1" == "publish" ]]; then
4421
+ command cargo "$@"
4422
+ else
4423
+ rtk cargo "$@"
4424
+ fi
4425
+ }
4426
+ git() {
4427
+ if [[ "$1" == "rebase" ]]; then
4428
+ command git "$@"
4429
+ else
4430
+ rtk git "$@"
4431
+ fi
4432
+ }
3900
4433
  `;
3901
- if (zshrc.includes('alias git="rtk git"')) {
3902
- ok('Aliases already present in ~/.zshrc.');
3903
- } else {
3904
- fs.appendFileSync(zshrcPath, aliasBlock, 'utf8');
3905
- ok('Shell aliases successfully added to ~/.zshrc.');
3906
- log(`\n${C.cyan}Please reload your shell (source ~/.zshrc) to apply shifts.${C.reset}`);
4434
+ // Clean up old RTK integration block if exists to allow updating
4435
+ if (zshrc.includes('# RTK Integration for AI Agents (AWKit)')) {
4436
+ const startIdx = zshrc.indexOf('# RTK Integration for AI Agents (AWKit)');
4437
+ zshrc = zshrc.substring(0, startIdx);
3907
4438
  }
4439
+
4440
+ zshrc += functionBlock;
4441
+ fs.writeFileSync(zshrcPath, zshrc, 'utf8');
4442
+ ok('Shell wrappers successfully added/updated in ~/.zshrc.');
4443
+ log(`\n${C.cyan}Please reload your shell (source ~/.zshrc) to apply shifts.${C.reset}`);
3908
4444
  } else {
3909
4445
  warn('~/.zshrc not found. Skipping alias setup.');
3910
4446
  }
@@ -3920,6 +4456,138 @@ async function cmdRtk(args) {
3920
4456
  }
3921
4457
  }
3922
4458
 
4459
+ // ─── Project Identity CLI View ───────────────────────────────────────────────
4460
+
4461
+ function cmdIdentity(args = []) {
4462
+ const cwd = process.cwd();
4463
+ const identityPath = path.join(cwd, '.project-identity');
4464
+
4465
+ if (!fs.existsSync(identityPath)) {
4466
+ err('No .project-identity found in the current directory.');
4467
+ log(` Run ${C.cyan}awkit init${C.reset} to create one for this project.`);
4468
+ return;
4469
+ }
4470
+
4471
+ try {
4472
+ const raw = fs.readFileSync(identityPath, 'utf8');
4473
+ const identity = JSON.parse(raw);
4474
+
4475
+ // Handle specific field querying
4476
+ if (args.length > 0) {
4477
+ const query = args[0];
4478
+
4479
+ if (query === '--keys') {
4480
+ console.log(Object.keys(identity).join('\n'));
4481
+ return;
4482
+ }
4483
+
4484
+ if (query === '--json') {
4485
+ console.log(JSON.stringify(identity, null, 2));
4486
+ return;
4487
+ }
4488
+
4489
+ // Helper to get nested value by dot notation (e.g., automation.build.enabled)
4490
+ const getNested = (obj, path) => {
4491
+ return path.split('.').reduce((acc, part) => acc && acc[part], obj);
4492
+ };
4493
+
4494
+ const val = getNested(identity, query);
4495
+ if (val !== undefined) {
4496
+ if (typeof val === 'object') {
4497
+ console.log(JSON.stringify(val));
4498
+ } else {
4499
+ console.log(val);
4500
+ }
4501
+ }
4502
+ return; // Exit silently if undefined, ideal for bash scripts
4503
+ }
4504
+
4505
+ const border = `${C.cyan}${C.bold}╔═══════════════════════════════════════════════════════╗${C.reset}`;
4506
+ const bottom = `${C.cyan}${C.bold}╚═══════════════════════════════════════════════════════╝${C.reset}`;
4507
+ const line = `${C.gray}${'─'.repeat(56)}${C.reset}`;
4508
+
4509
+ log('');
4510
+ log(border);
4511
+ log(`${C.cyan}${C.bold}║ 🆔 PROJECT IDENTITY: ${C.green}${C.bold}${String(identity.projectName || 'Unnamed Project').padEnd(30)}${C.cyan}${C.bold} ║${C.reset}`);
4512
+ log(bottom);
4513
+ log('');
4514
+
4515
+ // Basic Info
4516
+ log(`${C.bold}ℹ️ Basic Information${C.reset}`);
4517
+ log(line);
4518
+ log(` ${C.bold}Project Stage:${C.reset} ${identity.projectStage || 'development'}`);
4519
+ log(` ${C.bold}Project Type:${C.reset} ${identity.projectType ? C.green + identity.projectType + C.reset : C.yellow + 'Not configured' + C.reset}`);
4520
+ log(` ${C.bold}Primary Lang:${C.reset} ${identity.primaryLanguage || 'en'}`);
4521
+ if (identity.techStack) {
4522
+ const rt = Array.isArray(identity.techStack.runtime) ? identity.techStack.runtime.join(', ') : 'N/A';
4523
+ const lang = Array.isArray(identity.techStack.languages) ? identity.techStack.languages.join(', ') : 'N/A';
4524
+ log(` ${C.bold}Runtime/Lang:${C.reset} ${rt} / ${lang}`);
4525
+ }
4526
+ log('');
4527
+
4528
+ // Build Configuration
4529
+ log(`${C.bold}🛠️ Build & Run Control${C.reset}`);
4530
+ log(line);
4531
+ if (identity.automation && identity.automation.build) {
4532
+ const b = identity.automation.build;
4533
+ const statusStr = b.enabled ? `${C.green}Enabled (Safe Auto-Run via awkit build)${C.reset}` : `${C.yellow}Disabled / Manual Only${C.reset}`;
4534
+ log(` ${C.bold}Build Control:${C.reset} ${statusStr}`);
4535
+ log(` ${C.bold}Build Command:${C.reset} ${C.cyan}${b.command || 'N/A'}${C.reset}`);
4536
+ } else {
4537
+ log(` ${C.bold}Build Control:${C.reset} ${C.yellow}Not Configured (Manual Mode)${C.reset}`);
4538
+ }
4539
+ if (identity.automation && identity.automation.run) {
4540
+ const r = identity.automation.run;
4541
+ const statusStr = r.enabled ? `${C.green}Enabled (Safe Auto-Run via awkit run)${C.reset}` : `${C.yellow}Disabled / Manual Only${C.reset}`;
4542
+ log(` ${C.bold}Run Control:${C.reset} ${statusStr}`);
4543
+ log(` ${C.bold}Run Command:${C.reset} ${C.cyan}${r.command || 'N/A'}${C.reset}`);
4544
+ } else {
4545
+ log(` ${C.bold}Run Control:${C.reset} ${C.yellow}Not Configured (Manual Mode)${C.reset}`);
4546
+ }
4547
+ log('');
4548
+
4549
+ // Automation Gates
4550
+ log(`${C.bold}🔒 Automation & Sync Gates${C.reset}`);
4551
+ log(line);
4552
+ if (identity.automation) {
4553
+ const git = identity.automation.git || {};
4554
+ log(` ${C.bold}Git AutoCommit:${C.reset} ${git.autoCommit ? C.green + 'true' : C.red + 'false'}${C.reset}`);
4555
+ log(` ${C.bold}Git AutoPush:${C.reset} ${git.autoPush ? C.green + 'true' : C.red + 'false'}${C.reset}`);
4556
+
4557
+ const trello = identity.automation.trello || {};
4558
+ const syncStr = trello.enabled
4559
+ ? `${C.green}Enabled${C.reset} (AutoSync: ${trello.autoSync ? C.green + 'true' : C.red + 'false'}${C.reset})`
4560
+ : `${C.red}Disabled${C.reset}`;
4561
+ log(` ${C.bold}Trello Sync:${C.reset} ${syncStr}`);
4562
+ if (identity.trello) {
4563
+ log(` ${C.bold}Trello Board:${C.reset} ${identity.trello.board || 'N/A'}`);
4564
+ log(` ${C.bold}Trello Card:${C.reset} ${identity.trello.card || 'N/A'}`);
4565
+ }
4566
+
4567
+ const tg = identity.automation.telegram || {};
4568
+ const tgStr = tg.enabled ? `${C.green}Enabled${C.reset}` : `${C.red}Disabled${C.reset}`;
4569
+ log(` ${C.bold}Telegram Alerts:${C.reset} ${tgStr}`);
4570
+ if (tg.triggers) {
4571
+ const trigs = Object.keys(tg.triggers).filter(k => tg.triggers[k]).join(', ');
4572
+ log(` ${C.bold}Alert Triggers:${C.reset} ${C.gray}${trigs || 'None'}${C.reset}`);
4573
+ }
4574
+ }
4575
+ log('');
4576
+
4577
+ // Caveman Mode
4578
+ if (identity.communication && identity.communication.cavemanMode) {
4579
+ const cm = identity.communication.cavemanMode;
4580
+ log(`${C.bold}💬 Communication Mode${C.reset}`);
4581
+ log(line);
4582
+ log(` ${C.bold}Caveman Mode:${C.reset} ${cm.enabled ? C.green + 'Enabled (' + cm.level + ')' : C.red + 'Disabled'}${C.reset}`);
4583
+ log('');
4584
+ }
4585
+
4586
+ } catch (e) {
4587
+ err(`Failed to parse .project-identity: ${e.message}`);
4588
+ }
4589
+ }
4590
+
3923
4591
  // ─── Native HTTP Server ───────────────────────────────────────────────────────
3924
4592
 
3925
4593
  function cmdServe(args) {
@@ -4012,15 +4680,354 @@ function cmdServe(args) {
4012
4680
  });
4013
4681
  }
4014
4682
 
4015
- // ─── Main ────────────────────────────────────────────────────────────────────
4683
+ const GLOBAL_CONFIG_PATH = path.join(HOME, '.awkit_config.json');
4684
+
4685
+ function readGlobalConfig() {
4686
+ const defaultConf = {
4687
+ audioAlerts: true,
4688
+ runners: {
4689
+ claude: true,
4690
+ codex: true,
4691
+ qwen: true
4692
+ }
4693
+ };
4694
+ if (!fs.existsSync(GLOBAL_CONFIG_PATH)) return defaultConf;
4695
+ try {
4696
+ const parsed = JSON.parse(fs.readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
4697
+ return {
4698
+ ...defaultConf,
4699
+ ...parsed,
4700
+ runners: {
4701
+ ...defaultConf.runners,
4702
+ ...(parsed.runners || {})
4703
+ }
4704
+ };
4705
+ } catch (_) {
4706
+ return defaultConf;
4707
+ }
4708
+ }
4016
4709
 
4017
- // Check for updates (max once per day) before continuing
4018
- checkAutoUpdate();
4710
+ function writeGlobalConfig(config) {
4711
+ try {
4712
+ fs.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
4713
+ return true;
4714
+ } catch (e) {
4715
+ err(`Failed to write global config: ${e.message}`);
4716
+ return false;
4717
+ }
4718
+ }
4719
+
4720
+ function findClaudePath() {
4721
+ let localConfig = {};
4722
+ try {
4723
+ const p = path.join(process.cwd(), '.project-identity');
4724
+ if (fs.existsSync(p)) {
4725
+ localConfig = JSON.parse(fs.readFileSync(p, 'utf8'));
4726
+ }
4727
+ } catch (_) {}
4728
+
4729
+ if (localConfig.automation?.multiAgent?.runners?.claude) {
4730
+ const p = localConfig.automation.multiAgent.runners.claude;
4731
+ if (fs.existsSync(p)) return p;
4732
+ }
4733
+ const homeClaude = path.join(os.homedir(), '.claude', 'local', 'claude');
4734
+ if (fs.existsSync(homeClaude)) return homeClaude;
4735
+
4736
+ try {
4737
+ const shell = process.env.SHELL || '/bin/zsh';
4738
+ const shellCmd = shell.endsWith('zsh') ? 'zsh -lc "which claude"' : 'bash -lc "which claude"';
4739
+ const detected = execSync(shellCmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
4740
+ if (detected && fs.existsSync(detected)) return detected;
4741
+ } catch (_) {}
4742
+
4743
+ return 'claude';
4744
+ }
4745
+
4746
+ function findCodexPath() {
4747
+ let localConfig = {};
4748
+ try {
4749
+ const p = path.join(process.cwd(), '.project-identity');
4750
+ if (fs.existsSync(p)) {
4751
+ localConfig = JSON.parse(fs.readFileSync(p, 'utf8'));
4752
+ }
4753
+ } catch (_) {}
4754
+
4755
+ if (localConfig.automation?.multiAgent?.runners?.codex) {
4756
+ const p = localConfig.automation.multiAgent.runners.codex;
4757
+ if (fs.existsSync(p)) return p;
4758
+ }
4759
+ try {
4760
+ const shell = process.env.SHELL || '/bin/zsh';
4761
+ const shellCmd = shell.endsWith('zsh') ? 'zsh -lc "which codex"' : 'bash -lc "which codex"';
4762
+ const detected = execSync(shellCmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
4763
+ if (detected && fs.existsSync(detected)) return detected;
4764
+ } catch (_) {}
4765
+ return 'codex';
4766
+ }
4767
+
4768
+ function findQwenPath() {
4769
+ let localConfig = {};
4770
+ try {
4771
+ const p = path.join(process.cwd(), '.project-identity');
4772
+ if (fs.existsSync(p)) {
4773
+ localConfig = JSON.parse(fs.readFileSync(p, 'utf8'));
4774
+ }
4775
+ } catch (_) {}
4776
+
4777
+ if (localConfig.automation?.multiAgent?.runners?.qwen) {
4778
+ const p = localConfig.automation.multiAgent.runners.qwen;
4779
+ if (fs.existsSync(p)) return p;
4780
+ }
4781
+ try {
4782
+ const shell = process.env.SHELL || '/bin/zsh';
4783
+ const shellCmd = shell.endsWith('zsh') ? 'zsh -lc "which qwen"' : 'bash -lc "which qwen"';
4784
+ const detected = execSync(shellCmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
4785
+ if (detected && fs.existsSync(detected)) return detected;
4786
+ } catch (_) {}
4787
+ return 'qwen';
4788
+ }
4789
+
4790
+ function isRunnerEnabled(runnerName) {
4791
+ let localConfig = {};
4792
+ try {
4793
+ const p = path.join(process.cwd(), '.project-identity');
4794
+ if (fs.existsSync(p)) {
4795
+ localConfig = JSON.parse(fs.readFileSync(p, 'utf8'));
4796
+ }
4797
+ } catch (_) {}
4798
+
4799
+ const localVal = localConfig.automation?.multiAgent?.runnersEnabled?.[runnerName];
4800
+ if (localVal !== undefined) return localVal;
4801
+
4802
+ const globalConfig = readGlobalConfig();
4803
+ const globalVal = globalConfig.runners?.[runnerName];
4804
+ if (globalVal !== undefined) return globalVal;
4805
+
4806
+ return true;
4807
+ }
4808
+
4809
+ function isRunnerAvailable(runnerName) {
4810
+ if (!isRunnerEnabled(runnerName)) return false;
4811
+ let p;
4812
+ if (runnerName === 'claude') p = findClaudePath();
4813
+ else if (runnerName === 'codex') p = findCodexPath();
4814
+ else if (runnerName === 'qwen') p = findQwenPath();
4815
+ else return false;
4816
+
4817
+ if (fs.existsSync(p)) return true;
4818
+ try {
4819
+ execSync(`which ${p} || command -v ${p}`, { stdio: 'ignore' });
4820
+ return true;
4821
+ } catch (_) {
4822
+ return false;
4823
+ }
4824
+ }
4825
+
4826
+ function cmdGlobalConfig(args = []) {
4827
+ const sub = args[0];
4828
+ if (sub === 'audio') {
4829
+ const config = readGlobalConfig();
4830
+ const action = args[1];
4831
+ if (!action) {
4832
+ info(`Audio Alerts: ${config.audioAlerts ? 'ON' : 'OFF'}`);
4833
+ return;
4834
+ }
4835
+ if (action === 'on' || action === 'true' || action === '1') {
4836
+ config.audioAlerts = true;
4837
+ if (writeGlobalConfig(config)) {
4838
+ ok('Audio alerts turned ON globally.');
4839
+ }
4840
+ } else if (action === 'off' || action === 'false' || action === '0') {
4841
+ config.audioAlerts = false;
4842
+ if (writeGlobalConfig(config)) {
4843
+ ok('Audio alerts turned OFF globally.');
4844
+ }
4845
+ } else {
4846
+ err(`Invalid audio option: ${action}. Use 'on' or 'off'.`);
4847
+ }
4848
+ return;
4849
+ }
4850
+
4851
+ if (sub === 'claude' || sub === 'codex' || sub === 'qwen') {
4852
+ const config = readGlobalConfig();
4853
+ const action = args[1];
4854
+ if (!action) {
4855
+ info(`${sub.toUpperCase()} Runner: ${config.runners?.[sub] !== false ? 'ON' : 'OFF'}`);
4856
+ return;
4857
+ }
4858
+ if (action === 'on' || action === 'true' || action === '1') {
4859
+ config.runners = config.runners || {};
4860
+ config.runners[sub] = true;
4861
+ if (writeGlobalConfig(config)) {
4862
+ ok(`${sub.toUpperCase()} runner turned ON globally.`);
4863
+ }
4864
+ } else if (action === 'off' || action === 'false' || action === '0') {
4865
+ config.runners = config.runners || {};
4866
+ config.runners[sub] = false;
4867
+ if (writeGlobalConfig(config)) {
4868
+ ok(`${sub.toUpperCase()} runner turned OFF globally.`);
4869
+ }
4870
+ } else {
4871
+ err(`Invalid runner option: ${action}. Use 'on' or 'off'.`);
4872
+ }
4873
+ return;
4874
+ }
4875
+
4876
+ if (sub === 'runners' || sub === 'runner') {
4877
+ const line = `${C.gray}${'─'.repeat(60)}${C.reset}`;
4878
+ log('');
4879
+ log(`${C.cyan}${C.bold}🏃 RUNNER AGENTS STATUS${C.reset}`);
4880
+ log(line);
4881
+
4882
+ const runners = ['claude', 'codex', 'qwen'];
4883
+ for (const r of runners) {
4884
+ const enabled = isRunnerEnabled(r);
4885
+ const path = r === 'claude' ? findClaudePath() : (r === 'codex' ? findCodexPath() : findQwenPath());
4886
+ const available = isRunnerAvailable(r);
4887
+
4888
+ const statusStr = enabled ? `${C.green}ON${C.reset}` : `${C.red}OFF${C.reset}`;
4889
+ const availStr = available ? `${C.green}✅ Available${C.reset}` : `${C.red}❌ Unavailable${C.reset}`;
4890
+
4891
+ log(` ● ${C.bold}${r.padEnd(8)}${C.reset} Status: [${statusStr}] Path: ${path.padEnd(30)} [${availStr}]`);
4892
+ }
4893
+ log('');
4894
+ return;
4895
+ }
4896
+
4897
+ if (sub === 'models' || sub === 'model') {
4898
+ const line = `${C.gray}${'─'.repeat(60)}${C.reset}`;
4899
+ log('');
4900
+ log(`${C.cyan}${C.bold}🤖 MODEL ROUTING POLICY${C.reset}`);
4901
+ log(line);
4902
+
4903
+ let localConfig = {};
4904
+ let projectLoaded = false;
4905
+ try {
4906
+ const p = path.join(process.cwd(), '.project-identity');
4907
+ if (fs.existsSync(p)) {
4908
+ localConfig = JSON.parse(fs.readFileSync(p, 'utf8'));
4909
+ projectLoaded = true;
4910
+ }
4911
+ } catch (_) {}
4912
+
4913
+ if (projectLoaded) {
4914
+ log(` Project: ${C.green}${C.bold}${localConfig.projectName || 'Active Project'}${C.reset}`);
4915
+ const policy = localConfig.modelPolicy || {};
4916
+ log(` Routing Mode: ${C.cyan}${policy.mode || 'auto'}${C.reset}`);
4917
+ log(` Default Tier: ${C.cyan}${policy.defaultTier || 'STANDARD'}${C.reset}`);
4918
+
4919
+ if (policy.tierOverrides && Object.keys(policy.tierOverrides).length > 0) {
4920
+ log(` Tier Overrides:`);
4921
+ for (const [pattern, tier] of Object.entries(policy.tierOverrides)) {
4922
+ log(` * ${pattern.padEnd(20)} ➜ ${C.yellow}${tier}${C.reset}`);
4923
+ }
4924
+ }
4925
+ } else {
4926
+ log(` No project loaded in current directory.`);
4927
+ }
4928
+
4929
+ log(line);
4930
+ log(` ${C.bold}Default Models:${C.reset}`);
4931
+ log(` * Standard execution: ${C.green}gemini-3.5-flash${C.reset} (Fast & cost-efficient)`);
4932
+ log(` * Heavy / Spec tasks: ${C.green}gemini-3.1-pro${C.reset} (High reasoning quality)`);
4933
+ log(` * Light tasks: ${C.green}gemini-3.1-flash-lite${C.reset} (Ultra-low latency)`);
4934
+ log('');
4935
+ return;
4936
+ }
4937
+
4938
+ if (!sub || sub === 'list') {
4939
+ const config = readGlobalConfig();
4940
+ const line = `${C.gray}${'─'.repeat(60)}${C.reset}`;
4941
+ log('');
4942
+ log(`${C.cyan}${C.bold}⚙️ AWK CLI CONFIGURATION${C.reset}`);
4943
+ log(line);
4944
+
4945
+ const formatBool = (val) => val !== false ? `${C.green}ON${C.reset}` : `${C.red}OFF${C.reset}`;
4946
+
4947
+ log(` ${C.bold}audioAlerts${C.reset.padEnd(20)} ${formatBool(config.audioAlerts).padEnd(15)} Play speech and chimes on complete`);
4948
+ log(` ${C.bold}runners.claude${C.reset.padEnd(20)} ${formatBool(config.runners?.claude).padEnd(15)} Use Claude Code CLI for planning`);
4949
+ log(` ${C.bold}runners.codex${C.reset.padEnd(20)} ${formatBool(config.runners?.codex).padEnd(15)} Use Codex CLI for UI and assets`);
4950
+ log(` ${C.bold}runners.qwen${C.reset.padEnd(20)} ${formatBool(config.runners?.qwen).padEnd(15)} Use Qwen Coder CLI for execution`);
4951
+ log(line);
4952
+ log(`💡 To toggle, run: ${C.cyan}awkit config <param> <on|off>${C.reset}`);
4953
+ log(` Example: ${C.cyan}awkit config claude off${C.reset}`);
4954
+ log('');
4955
+ return;
4956
+ }
4957
+
4958
+ if (sub === 'get' && args[1]) {
4959
+ const config = readGlobalConfig();
4960
+ const key = args[1];
4961
+ let val;
4962
+ if (key.includes('.')) {
4963
+ val = key.split('.').reduce((acc, part) => acc && acc[part], config);
4964
+ } else {
4965
+ val = config[key];
4966
+ }
4967
+ if (val !== undefined) {
4968
+ console.log(val);
4969
+ } else {
4970
+ err(`Key "${key}" not found in global config.`);
4971
+ }
4972
+ return;
4973
+ }
4974
+
4975
+ if (sub === 'set' && args[1] && args[2] !== undefined) {
4976
+ const config = readGlobalConfig();
4977
+ const key = args[1];
4978
+ let val = args[2];
4979
+ if (val === 'true') val = true;
4980
+ if (val === 'false') val = false;
4981
+
4982
+ if (key.includes('.')) {
4983
+ const parts = key.split('.');
4984
+ let current = config;
4985
+ for (let i = 0; i < parts.length - 1; i++) {
4986
+ current[parts[i]] = current[parts[i]] || {};
4987
+ current = current[parts[i]];
4988
+ }
4989
+ current[parts[parts.length - 1]] = val;
4990
+ } else {
4991
+ config[key] = val;
4992
+ }
4993
+
4994
+ if (writeGlobalConfig(config)) {
4995
+ ok(`Global config updated: "${key}" = ${val}`);
4996
+ }
4997
+ return;
4998
+ }
4999
+
5000
+ err('Usage:');
5001
+ log(' awkit config list');
5002
+ log(' awkit config get <key>');
5003
+ log(' awkit config set <key> <value>');
5004
+ log(' awkit config audio <on|off>');
5005
+ log(' awkit config claude <on|off>');
5006
+ log(' awkit config codex <on|off>');
5007
+ log(' awkit config qwen <on|off>');
5008
+ log(' awkit config runners');
5009
+ log(' awkit config models');
5010
+ }
5011
+
5012
+ // ─── Main ────────────────────────────────────────────────────────────────────
4019
5013
 
4020
5014
  const [, , command, ...args] = process.argv;
4021
5015
 
4022
5016
  (async () => {
5017
+ await checkAutoUpdate();
4023
5018
  switch (command) {
5019
+ case 'build':
5020
+ cmdBuild();
5021
+ break;
5022
+ case 'run':
5023
+ cmdRun(args);
5024
+ break;
5025
+ case 'task':
5026
+ cmdTask(args);
5027
+ break;
5028
+ case 'project':
5029
+ cmdProject(args);
5030
+ break;
4024
5031
  case 'init':
4025
5032
  await cmdInit(args.includes('--force'));
4026
5033
  break;
@@ -4037,13 +5044,23 @@ const [, , command, ...args] = process.argv;
4037
5044
  cmdSync();
4038
5045
  break;
4039
5046
  case 'status':
4040
- cmdStatus();
5047
+ if (args[0] === 'gn' || args[0] === 'gitnexus') {
5048
+ cmdGitnexus(['status']);
5049
+ } else {
5050
+ cmdStatus();
5051
+ }
4041
5052
  break;
4042
5053
  case 'harvest':
4043
5054
  cmdHarvest(args.includes('--dry-run'));
4044
5055
  break;
4045
5056
  case 'doctor':
4046
- cmdDoctor();
5057
+ await cmdDoctor();
5058
+ break;
5059
+ case 'storage':
5060
+ cmdStorage(args);
5061
+ break;
5062
+ case 'pipeline':
5063
+ cmdPipeline(args);
4047
5064
  break;
4048
5065
  case 'browser':
4049
5066
  cmdBrowser(args);
@@ -4079,6 +5096,16 @@ const [, , command, ...args] = process.argv;
4079
5096
  case 'creds':
4080
5097
  cmdCredentials(args);
4081
5098
  break;
5099
+ case 'index':
5100
+ case 'analyze':
5101
+ cmdGitnexus(['index', ...args]);
5102
+ break;
5103
+ case 'clean':
5104
+ cmdGitnexus(['clean', ...args]);
5105
+ break;
5106
+ case 'list':
5107
+ cmdGitnexus(['list', ...args]);
5108
+ break;
4082
5109
  case 'set-openrouter': {
4083
5110
  const key = args[0];
4084
5111
  if (!key) {
@@ -4094,6 +5121,10 @@ const [, , command, ...args] = process.argv;
4094
5121
  case 'rtk':
4095
5122
  await cmdRtk(args);
4096
5123
  break;
5124
+ case 'i18n':
5125
+ await cmdI18n(args);
5126
+ break;
5127
+
4097
5128
  case 'gitnexus':
4098
5129
  case 'gn':
4099
5130
  cmdGitnexus(args);
@@ -4116,6 +5147,31 @@ const [, , command, ...args] = process.argv;
4116
5147
  case 'restart':
4117
5148
  await cmdRestart();
4118
5149
  break;
5150
+ case 'greet':
5151
+ cmdGreet(args);
5152
+ break;
5153
+ case 'config-global':
5154
+ cmdGlobalConfig(args);
5155
+ break;
5156
+ case 'identity':
5157
+ case 'config':
5158
+ case 'project': {
5159
+ const isGlobalSub = command === 'config' && (
5160
+ args.length === 0 ||
5161
+ ['audio', 'claude', 'codex', 'qwen', 'runners', 'runner', 'models', 'model', 'list', 'get', 'set'].includes(args[0])
5162
+ );
5163
+ if (isGlobalSub) {
5164
+ // If there are no arguments but a project identity exists, show project identity as default
5165
+ if (args.length === 0 && fs.existsSync(path.join(process.cwd(), '.project-identity'))) {
5166
+ cmdIdentity(args);
5167
+ } else {
5168
+ cmdGlobalConfig(args);
5169
+ }
5170
+ } else {
5171
+ cmdIdentity(args);
5172
+ }
5173
+ break;
5174
+ }
4119
5175
  case 'help':
4120
5176
  case '--help':
4121
5177
  case '-h':