@leejungkiin/awkit 1.4.0 → 1.4.3

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 (119) hide show
  1. package/bin/awk.js +458 -7
  2. package/bin/claude-generators.js +122 -0
  3. package/core/AGENTS.md +16 -0
  4. package/core/CLAUDE.md +155 -0
  5. package/core/GEMINI.md +44 -9
  6. package/package.json +1 -1
  7. package/skills/ai-sprite-maker/SKILL.md +81 -0
  8. package/skills/ai-sprite-maker/scripts/animate_sprite.py +102 -0
  9. package/skills/ai-sprite-maker/scripts/process_sprites.py +140 -0
  10. package/skills/code-review/SKILL.md +21 -33
  11. package/skills/lucylab-tts/SKILL.md +64 -0
  12. package/skills/lucylab-tts/resources/voices_library.json +908 -0
  13. package/skills/lucylab-tts/scripts/.env +1 -0
  14. package/skills/lucylab-tts/scripts/lucylab_tts.py +506 -0
  15. package/skills/orchestrator/SKILL.md +5 -0
  16. package/skills/short-maker/SKILL.md +150 -0
  17. package/skills/short-maker/_backup/storyboard.html +106 -0
  18. package/skills/short-maker/_backup/video_mixer.py +296 -0
  19. package/skills/short-maker/outputs/fitbite-promo/background.jpg +0 -0
  20. package/skills/short-maker/outputs/fitbite-promo/final/promo-final.mp4 +0 -0
  21. package/skills/short-maker/outputs/fitbite-promo/script.md +19 -0
  22. package/skills/short-maker/outputs/fitbite-promo/segments/scene-01.mp4 +0 -0
  23. package/skills/short-maker/outputs/fitbite-promo/segments/scene-02.mp4 +0 -0
  24. package/skills/short-maker/outputs/fitbite-promo/segments/scene-03.mp4 +0 -0
  25. package/skills/short-maker/outputs/fitbite-promo/segments/scene-04.mp4 +0 -0
  26. package/skills/short-maker/outputs/fitbite-promo/storyboard/scene-01.png +0 -0
  27. package/skills/short-maker/outputs/fitbite-promo/storyboard/scene-02.png +0 -0
  28. package/skills/short-maker/outputs/fitbite-promo/storyboard/scene-03.png +0 -0
  29. package/skills/short-maker/outputs/fitbite-promo/storyboard/scene-04.png +0 -0
  30. package/skills/short-maker/outputs/fitbite-promo/storyboard.html +133 -0
  31. package/skills/short-maker/outputs/fitbite-promo/storyboard.json +38 -0
  32. package/skills/short-maker/outputs/fitbite-promo/temp/merged_chroma.mp4 +0 -0
  33. package/skills/short-maker/outputs/fitbite-promo/temp/merged_crossfaded.mp4 +0 -0
  34. package/skills/short-maker/outputs/fitbite-promo/temp/ready_00.mp4 +0 -0
  35. package/skills/short-maker/outputs/fitbite-promo/temp/ready_01.mp4 +0 -0
  36. package/skills/short-maker/outputs/fitbite-promo/temp/ready_02.mp4 +0 -0
  37. package/skills/short-maker/outputs/fitbite-promo/temp/ready_03.mp4 +0 -0
  38. package/skills/short-maker/outputs/fitbite-promo/tts/manifest.json +31 -0
  39. package/skills/short-maker/outputs/fitbite-promo/tts/scene-01.wav +0 -0
  40. package/skills/short-maker/outputs/fitbite-promo/tts/scene-02.wav +0 -0
  41. package/skills/short-maker/outputs/fitbite-promo/tts/scene-03.wav +0 -0
  42. package/skills/short-maker/outputs/fitbite-promo/tts/scene-04.wav +0 -0
  43. package/skills/short-maker/outputs/fitbite-promo/tts_script.txt +11 -0
  44. package/skills/short-maker/scripts/google-flow-cli/.project-identity +41 -0
  45. package/skills/short-maker/scripts/google-flow-cli/.trae/rules/project_rules.md +52 -0
  46. package/skills/short-maker/scripts/google-flow-cli/CODEBASE.md +67 -0
  47. package/skills/short-maker/scripts/google-flow-cli/GoogleFlowCli.code-workspace +29 -0
  48. package/skills/short-maker/scripts/google-flow-cli/README.md +168 -0
  49. package/skills/short-maker/scripts/google-flow-cli/docs/specs/PROJECT.md +12 -0
  50. package/skills/short-maker/scripts/google-flow-cli/docs/specs/REQUIREMENTS.md +22 -0
  51. package/skills/short-maker/scripts/google-flow-cli/docs/specs/ROADMAP.md +16 -0
  52. package/skills/short-maker/scripts/google-flow-cli/docs/specs/TECH-SPEC.md +13 -0
  53. package/skills/short-maker/scripts/google-flow-cli/gflow/__init__.py +3 -0
  54. package/skills/short-maker/scripts/google-flow-cli/gflow/api/__init__.py +19 -0
  55. package/skills/short-maker/scripts/google-flow-cli/gflow/api/client.py +1921 -0
  56. package/skills/short-maker/scripts/google-flow-cli/gflow/api/models.py +64 -0
  57. package/skills/short-maker/scripts/google-flow-cli/gflow/api/rpc_ids.py +98 -0
  58. package/skills/short-maker/scripts/google-flow-cli/gflow/auth/__init__.py +15 -0
  59. package/skills/short-maker/scripts/google-flow-cli/gflow/auth/browser_auth.py +692 -0
  60. package/skills/short-maker/scripts/google-flow-cli/gflow/auth/humanizer.py +417 -0
  61. package/skills/short-maker/scripts/google-flow-cli/gflow/auth/proxy_ext.py +120 -0
  62. package/skills/short-maker/scripts/google-flow-cli/gflow/auth/recaptcha.py +482 -0
  63. package/skills/short-maker/scripts/google-flow-cli/gflow/batchexecute/__init__.py +5 -0
  64. package/skills/short-maker/scripts/google-flow-cli/gflow/batchexecute/client.py +414 -0
  65. package/skills/short-maker/scripts/google-flow-cli/gflow/cli/__init__.py +1 -0
  66. package/skills/short-maker/scripts/google-flow-cli/gflow/cli/main.py +1075 -0
  67. package/skills/short-maker/scripts/google-flow-cli/pyproject.toml +36 -0
  68. package/skills/short-maker/scripts/google-flow-cli/script.txt +22 -0
  69. package/skills/short-maker/scripts/google-flow-cli/tests/__init__.py +0 -0
  70. package/skills/short-maker/scripts/google-flow-cli/tests/test_batchexecute.py +113 -0
  71. package/skills/short-maker/scripts/google-flow-cli/tests/test_client.py +190 -0
  72. package/skills/short-maker/templates/aida_script.md +40 -0
  73. package/skills/short-maker/templates/mimic_analyzer.md +29 -0
  74. package/skills/single-flow-task-execution/SKILL.md +9 -6
  75. package/skills/skill-creator/SKILL.md +44 -0
  76. package/skills/spm-build-analysis/SKILL.md +92 -0
  77. package/skills/spm-build-analysis/references/build-optimization-sources.md +155 -0
  78. package/skills/spm-build-analysis/references/recommendation-format.md +85 -0
  79. package/skills/spm-build-analysis/references/spm-analysis-checks.md +105 -0
  80. package/skills/spm-build-analysis/scripts/check_spm_pins.py +118 -0
  81. package/skills/symphony-enforcer/SKILL.md +51 -83
  82. package/skills/symphony-orchestrator/SKILL.md +1 -1
  83. package/skills/trello-sync/SKILL.md +27 -28
  84. package/skills/verification-gate/SKILL.md +13 -2
  85. package/skills/xcode-build-benchmark/SKILL.md +88 -0
  86. package/skills/xcode-build-benchmark/references/benchmark-artifacts.md +94 -0
  87. package/skills/xcode-build-benchmark/references/benchmarking-workflow.md +67 -0
  88. package/skills/xcode-build-benchmark/schemas/build-benchmark.schema.json +230 -0
  89. package/skills/xcode-build-benchmark/scripts/benchmark_builds.py +308 -0
  90. package/skills/xcode-build-fixer/SKILL.md +218 -0
  91. package/skills/xcode-build-fixer/references/build-settings-best-practices.md +216 -0
  92. package/skills/xcode-build-fixer/references/fix-patterns.md +290 -0
  93. package/skills/xcode-build-fixer/references/recommendation-format.md +85 -0
  94. package/skills/xcode-build-fixer/scripts/benchmark_builds.py +308 -0
  95. package/skills/xcode-build-orchestrator/SKILL.md +156 -0
  96. package/skills/xcode-build-orchestrator/references/benchmark-artifacts.md +94 -0
  97. package/skills/xcode-build-orchestrator/references/build-settings-best-practices.md +216 -0
  98. package/skills/xcode-build-orchestrator/references/orchestration-report-template.md +143 -0
  99. package/skills/xcode-build-orchestrator/references/recommendation-format.md +85 -0
  100. package/skills/xcode-build-orchestrator/scripts/benchmark_builds.py +308 -0
  101. package/skills/xcode-build-orchestrator/scripts/diagnose_compilation.py +273 -0
  102. package/skills/xcode-build-orchestrator/scripts/generate_optimization_report.py +533 -0
  103. package/skills/xcode-compilation-analyzer/SKILL.md +89 -0
  104. package/skills/xcode-compilation-analyzer/references/build-optimization-sources.md +155 -0
  105. package/skills/xcode-compilation-analyzer/references/code-compilation-checks.md +106 -0
  106. package/skills/xcode-compilation-analyzer/references/recommendation-format.md +85 -0
  107. package/skills/xcode-compilation-analyzer/scripts/diagnose_compilation.py +273 -0
  108. package/skills/xcode-project-analyzer/SKILL.md +76 -0
  109. package/skills/xcode-project-analyzer/references/build-optimization-sources.md +155 -0
  110. package/skills/xcode-project-analyzer/references/build-settings-best-practices.md +216 -0
  111. package/skills/xcode-project-analyzer/references/project-audit-checks.md +101 -0
  112. package/skills/xcode-project-analyzer/references/recommendation-format.md +85 -0
  113. package/templates/project-identity/android.json +0 -10
  114. package/templates/project-identity/backend-nestjs.json +0 -10
  115. package/templates/project-identity/expo.json +0 -10
  116. package/templates/project-identity/ios.json +0 -10
  117. package/templates/project-identity/web-nextjs.json +0 -10
  118. package/workflows/_uncategorized/ship-to-code.md +85 -0
  119. package/workflows/context/codebase-sync.md +10 -87
package/bin/awk.js CHANGED
@@ -36,6 +36,7 @@ const HOME = process.env.HOME || process.env.USERPROFILE;
36
36
 
37
37
  const { generateClineRules, generateClineWorkflows, generateClineSkills } = require('./cline-generators');
38
38
  const { generateCodexAgentsMd, generateCodexSkills, generateCodexAgents } = require('./codex-generators');
39
+ const { generateClaudeRules, generateClaudeSkills } = require('./claude-generators');
39
40
 
40
41
  // ─── Platform Definitions ──────────────────────────────────────────────────
41
42
 
@@ -73,6 +74,15 @@ const PLATFORMS = {
73
74
  agents: 'agents',
74
75
  skills: '../.agents/skills',
75
76
  },
77
+ },
78
+ claude: {
79
+ name: 'Claude Code',
80
+ globalRoot: process.cwd(), // Local to project
81
+ rulesFile: 'CLAUDE.md',
82
+ versionFile: '.claude/awk_version',
83
+ dirs: {
84
+ skills: '.claude/skills',
85
+ },
76
86
  }
77
87
  };
78
88
 
@@ -304,8 +314,9 @@ function cmdInstall(args = []) {
304
314
  selectedPlatforms = Object.keys(PLATFORMS);
305
315
  } else {
306
316
  if (args.includes('--gemini') || args.includes('-g') || args.includes('antigravity')) selectedPlatforms.push('antigravity');
307
- if (args.includes('--claude') || args.includes('-c') || args.includes('cline')) selectedPlatforms.push('cline');
317
+ if (args.includes('--cline') || args.includes('cline')) selectedPlatforms.push('cline');
308
318
  if (args.includes('--codex') || args.includes('-x')) selectedPlatforms.push('codex');
319
+ if (args.includes('--claude-code') || args.includes('--claude') || args.includes('-c') || args.includes('claude')) selectedPlatforms.push('claude');
309
320
 
310
321
  const pIdx = args.indexOf('--platform');
311
322
  let legacyArg = null;
@@ -324,19 +335,21 @@ function cmdInstall(args = []) {
324
335
  if (isUpdate) {
325
336
  selectedPlatforms = [getActivePlatform()];
326
337
  } else {
327
- log(`${C.cyan}Select platforms to install (e.g., type "1,2", "all", or "1,2,3"):${C.reset}`);
338
+ log(`${C.cyan}Select platforms to install (e.g., type "1,2", "all", or "1,2,3,4"):${C.reset}`);
328
339
  log(` 1. Gemini Code Assist (antigravity)`);
329
- log(` 2. Claude Code (cline - local project)`);
340
+ log(` 2. Cline (VS Code)`);
330
341
  log(` 3. Codex CLI (codex)`);
331
- log(` 4. All of the above`);
332
- const choice = promptChoice('Choice', '4').trim().toLowerCase();
342
+ log(` 4. Claude Code (.claude/)`);
343
+ log(` 5. All of the above`);
344
+ const choice = promptChoice('Choice', '5').trim().toLowerCase();
333
345
 
334
- if (choice === '4' || choice === 'all' || choice === '') {
346
+ if (choice === '5' || choice === 'all' || choice === '') {
335
347
  selectedPlatforms = Object.keys(PLATFORMS);
336
348
  } else {
337
349
  if (choice.includes('1')) selectedPlatforms.push('antigravity');
338
350
  if (choice.includes('2')) selectedPlatforms.push('cline');
339
351
  if (choice.includes('3')) selectedPlatforms.push('codex');
352
+ if (choice.includes('4')) selectedPlatforms.push('claude');
340
353
  }
341
354
  }
342
355
  }
@@ -391,6 +404,11 @@ function cmdInstall(args = []) {
391
404
  } else if (platform === 'codex') {
392
405
  info('Generating Codex AGENTS.md...');
393
406
  generateCodexAgentsMd(path.join(AWK_ROOT, 'core', 'GEMINI.md'), plat.rulesFile);
407
+ } else if (platform === 'claude') {
408
+ info('Generating Claude Code CLAUDE.md...');
409
+ const claudeTemplateSrc = path.join(AWK_ROOT, 'core', 'CLAUDE.md');
410
+ const claudeRulesDest = path.join(target, plat.rulesFile);
411
+ generateClaudeRules(claudeTemplateSrc, claudeRulesDest);
394
412
  }
395
413
 
396
414
  // 3. Backup and install workflows
@@ -444,6 +462,8 @@ function cmdInstall(args = []) {
444
462
  generateCodexSkills(skillsSrc, skillsDest);
445
463
  const agentsDest = path.join(target, plat.dirs.agents);
446
464
  generateCodexAgents(skillsSrc, agentsDest);
465
+ } else if (platform === 'claude') {
466
+ generateClaudeSkills(skillsSrc, skillsDest);
447
467
  } else {
448
468
  const skillCount = copyDirRecursive(skillsSrc, skillsDest);
449
469
  ok(`${skillCount} skill files installed`);
@@ -716,6 +736,77 @@ function cmdDoctor() {
716
736
  log('');
717
737
  }
718
738
 
739
+ /**
740
+ * Handle browser-related tasks (e.g., cleaning up recordings).
741
+ */
742
+ function cmdBrowser(args) {
743
+ if (args[0] !== 'clean') {
744
+ err('Unknown browser command. Use "awkit browser clean".');
745
+ return;
746
+ }
747
+
748
+ const recordingsDir = path.join(TARGETS.antigravity, 'browser_recordings');
749
+
750
+ log('');
751
+ log(`${C.cyan}${C.bold}🧹 AWK Browser Cleanup${C.reset}`);
752
+ log('');
753
+
754
+ if (!fs.existsSync(recordingsDir)) {
755
+ ok(`No browser_recordings directory found at ${recordingsDir}. Nothing to clean.`);
756
+ return;
757
+ }
758
+
759
+ const files = fs.readdirSync(recordingsDir).filter(f => f.endsWith('.webm') || f.endsWith('.webp') || f.endsWith('.mp4'));
760
+ if (files.length === 0) {
761
+ ok('No browser recordings found. Nothing to clean.');
762
+ return;
763
+ }
764
+
765
+ let keepDays = 7; // default 7 days
766
+ const daysArgIdx = args.indexOf('--days');
767
+ if (daysArgIdx !== -1 && args[daysArgIdx + 1]) {
768
+ keepDays = parseInt(args[daysArgIdx + 1], 10);
769
+ } else if (args.includes('--all')) {
770
+ keepDays = 0;
771
+ }
772
+
773
+ if (keepDays === 0) {
774
+ log(`Cleaning ${C.yellow}ALL${C.reset} browser recordings...`);
775
+ } else {
776
+ log(`Cleaning browser recordings older than ${C.yellow}${keepDays} days${C.reset}...`);
777
+ }
778
+
779
+ const now = Date.now();
780
+ const cutoff = now - (keepDays * 24 * 60 * 60 * 1000);
781
+ let deletedCount = 0;
782
+ let totalSizeFreed = 0;
783
+
784
+ for (const file of files) {
785
+ const filePath = path.join(recordingsDir, file);
786
+ try {
787
+ const stats = fs.statSync(filePath);
788
+ if (stats.mtimeMs < cutoff) {
789
+ totalSizeFreed += stats.size;
790
+ fs.unlinkSync(filePath);
791
+ deletedCount++;
792
+ dim(`Deleted: ${file}`);
793
+ }
794
+ } catch (e) {
795
+ warn(`Failed to process ${file}: ${e.message}`);
796
+ }
797
+ }
798
+
799
+ log('');
800
+ const sizeMb = (totalSizeFreed / (1024 * 1024)).toFixed(2);
801
+ if (deletedCount > 0) {
802
+ ok(`Cleaned ${C.green}${C.bold}${deletedCount}${C.reset} recording(s).`);
803
+ ok(`Freed ${C.green}${C.bold}${sizeMb} MB${C.reset} of disk space.`);
804
+ } else {
805
+ ok(`No recordings older than ${keepDays} days found. Disk space is already optimized.`);
806
+ }
807
+ log('');
808
+ }
809
+
719
810
  /**
720
811
  * Find a compatible Python interpreter meeting the minimum version requirement.
721
812
  * Tries python3.13, python3.12, python3.11, python3, python in order.
@@ -1420,6 +1511,55 @@ async function cmdAdmin() {
1420
1511
  }
1421
1512
  }
1422
1513
 
1514
+ async function cmdRestart() {
1515
+ info('Đang restart service awkit (Symphony)...');
1516
+ try {
1517
+ const { execSync, spawn } = require('child_process');
1518
+ try {
1519
+ // Find and kill process on port 3100
1520
+ const pids = execSync('lsof -t -i:3100').toString().trim().split('\n');
1521
+ for (const pid of pids) {
1522
+ if (pid) {
1523
+ process.kill(parseInt(pid, 10), 'SIGTERM');
1524
+ }
1525
+ }
1526
+ info('Đã dừng service hiện tại.');
1527
+ } catch (e) {
1528
+ // Probably no process running on port 3100
1529
+ dim('Không tìm thấy service đang chạy.');
1530
+ }
1531
+
1532
+ await new Promise(r => setTimeout(r, 1000));
1533
+
1534
+ // Auto-build production bundle so code changes take effect
1535
+ const symphonyDir = path.join(AWK_ROOT, '..', 'symphony');
1536
+ if (fs.existsSync(path.join(symphonyDir, 'package.json'))) {
1537
+ info('Đang build production bundle...');
1538
+ try {
1539
+ execSync('npm run build', { cwd: symphonyDir, stdio: 'pipe' });
1540
+ log(`${C.green}✅ Build thành công!${C.reset}`);
1541
+ } catch (buildErr) {
1542
+ warn('Build thất bại, sử dụng bundle cũ.');
1543
+ dim(buildErr.message?.slice(0, 200));
1544
+ }
1545
+ }
1546
+
1547
+ info('Đang khởi động lại service ngầm...');
1548
+ const child = spawn('symphony', ['start'], {
1549
+ detached: true,
1550
+ stdio: 'ignore'
1551
+ });
1552
+ child.unref();
1553
+
1554
+ info('Vui lòng đợi 3 giây để service khởi động...');
1555
+ await new Promise(r => setTimeout(r, 3000));
1556
+
1557
+ log(`${C.green}✅ Restart thành công!${C.reset}`);
1558
+ } catch (e) {
1559
+ err('Lỗi khi restart service: ' + e.message);
1560
+ }
1561
+ }
1562
+
1423
1563
  function cmdHelp() {
1424
1564
  const line = `${C.gray}${'─'.repeat(56)}${C.reset}`;
1425
1565
  log('');
@@ -1447,6 +1587,15 @@ function cmdHelp() {
1447
1587
  log(` ${C.gray} CODEBASE.md, .symphony/ (Symphony task DB)${C.reset}`);
1448
1588
  log('');
1449
1589
 
1590
+ // Maintenance
1591
+ log(`${C.bold}🧹 Maintenance${C.reset}`);
1592
+ log(line);
1593
+ log(` ${C.green}serve${C.reset} [dir] [-p <port>] Start local HTTP server for assets in CWD`);
1594
+ log(` ${C.green}browser clean${C.reset} Clean browser recordings`);
1595
+ log(` ${C.gray} --days <N>${C.reset} Keep recordings from last N days (default: 7)`);
1596
+ log(` ${C.gray} --all${C.reset} Delete all recordings`);
1597
+ log('');
1598
+
1450
1599
  // Sync
1451
1600
  log(`${C.bold}🔄 Sync${C.reset}`);
1452
1601
  log(line);
@@ -2009,6 +2158,29 @@ async function cmdInit(forceFlag = false) {
2009
2158
  }
2010
2159
  }
2011
2160
 
2161
+ // ── 5.5. GitNexus: Code Intelligence Index ──────────────────────────────
2162
+ info('Indexing codebase with GitNexus...');
2163
+ try {
2164
+ // Only index if there are actual source files
2165
+ const hasSource = execSync(
2166
+ `find . -maxdepth 4 \\( -name "*.swift" -o -name "*.kt" -o -name "*.ts" -o -name "*.js" -o -name "*.dart" -o -name "*.py" \\) -not -path "*/node_modules/*" -not -path "*/.build/*" -not -path "*/Pods/*" | head -1`,
2167
+ { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
2168
+ ).trim();
2169
+
2170
+ if (hasSource) {
2171
+ execSync('npx -y gitnexus analyze', {
2172
+ cwd,
2173
+ stdio: 'ignore',
2174
+ });
2175
+ ok('GitNexus index created (.gitnexus/)');
2176
+ } else {
2177
+ dim('No source files found — skipping GitNexus index (run later: npx gitnexus analyze)');
2178
+ }
2179
+ } catch (e) {
2180
+ warn(`GitNexus indexing failed: ${e.message}`);
2181
+ dim('Run manually later: npx gitnexus analyze');
2182
+ }
2183
+
2012
2184
  // ── 6. Summary ─────────────────────────────────────────────────────────────
2013
2185
  log('');
2014
2186
  log(`${C.gray}${'─'.repeat(56)}${C.reset}`);
@@ -2017,11 +2189,13 @@ async function cmdInit(forceFlag = false) {
2017
2189
  dim(`Type: ${projectType}`);
2018
2190
  dim(`Firebase: analytics, crashlytics, remote-config, auth`);
2019
2191
  dim(`Files: .project-identity, ${workspaceName}, CODEBASE.md`);
2020
- dim(`Symphony: task tracking ready)`);
2192
+ dim(`Symphony: task tracking ready`);
2193
+ dim(`GitNexus: code intelligence index`);
2021
2194
  log('');
2022
2195
  log(`${C.cyan}👉 Open ${workspaceName} in VS Code to get started.${C.reset}`);
2023
2196
  log(`${C.cyan}👉 Run '/codebase-sync' in AI chat to keep CODEBASE.md updated.${C.reset}`);
2024
2197
  log(`${C.cyan}👉 Run 'symphony task list' to manage tasks.${C.reset}`);
2198
+ log(`${C.cyan}👉 Run 'npx gitnexus analyze' after major changes to refresh index.${C.reset}`);
2025
2199
  log('');
2026
2200
  }
2027
2201
 
@@ -2219,6 +2393,178 @@ function cmdTelegram(args) {
2219
2393
  }
2220
2394
  }
2221
2395
 
2396
+ // ─── Credentials Management ───────────────────────────────────────────────────
2397
+
2398
+ const CREDENTIALS_CONFIG_PATH = path.join(TARGETS.antigravity, '.credentials.json');
2399
+
2400
+ function credentialsLoad() {
2401
+ if (!fs.existsSync(CREDENTIALS_CONFIG_PATH)) return {};
2402
+ try {
2403
+ return JSON.parse(fs.readFileSync(CREDENTIALS_CONFIG_PATH, 'utf8'));
2404
+ } catch (_) {
2405
+ return {};
2406
+ }
2407
+ }
2408
+
2409
+ function credentialsSave(config) {
2410
+ const dir = path.dirname(CREDENTIALS_CONFIG_PATH);
2411
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
2412
+ fs.writeFileSync(CREDENTIALS_CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
2413
+ }
2414
+
2415
+ function credentialsHelp() {
2416
+ log('');
2417
+ log(`${C.cyan}${C.bold}🔑 Credentials Commands${C.reset}`);
2418
+ log('');
2419
+ log(` ${C.green}awkit credentials list${C.reset} List all stored credentials`);
2420
+ log(` ${C.green}awkit credentials set${C.reset} <key> <value> Set a credential`);
2421
+ log(` ${C.green}awkit credentials get${C.reset} <key> Get a credential value`);
2422
+ log(` ${C.green}awkit credentials remove${C.reset} <key> Remove a credential`);
2423
+ log(` ${C.green}awkit credentials setup${C.reset} Interactive setup wizard`);
2424
+ log('');
2425
+ log(` ${C.gray}Known keys: gemini_api_key, lucylab_bearer${C.reset}`);
2426
+ log(` ${C.gray}Config: ${CREDENTIALS_CONFIG_PATH}${C.reset}`);
2427
+ log('');
2428
+ }
2429
+
2430
+ async function credentialsSetup() {
2431
+ log('');
2432
+ log(`${C.cyan}${C.bold}🔑 API Credentials Setup${C.reset}`);
2433
+ log('');
2434
+ log(`${C.gray} Credentials are stored in: ${CREDENTIALS_CONFIG_PATH}${C.reset}`);
2435
+ log(`${C.gray} Used by Short Maker, Symphony Admin, and other services.${C.reset}`);
2436
+ log('');
2437
+
2438
+ const readline = require('readline');
2439
+ const rl = readline.createInterface({
2440
+ input: process.stdin,
2441
+ output: process.stdout
2442
+ });
2443
+
2444
+ const question = (query) => new Promise(resolve => rl.question(query, resolve));
2445
+ const sanitize = (s) => s.trim().replace(/^bearer\s+/i, '').replace(/\s+/g, '');
2446
+
2447
+ const config = credentialsLoad();
2448
+
2449
+ try {
2450
+ // Gemini API Key
2451
+ log(`${C.gray} Get your key at: https://aistudio.google.com/apikey${C.reset}`);
2452
+ const geminiKey = sanitize(await question(` ${C.yellow}Gemini API Key${config.gemini_api_key ? ` [${config.gemini_api_key.slice(0, 8)}...]` : ''}: ${C.reset}`));
2453
+ if (geminiKey) {
2454
+ config.gemini_api_key = geminiKey;
2455
+ ok('Gemini API Key saved');
2456
+ } else if (config.gemini_api_key) {
2457
+ dim('Kept existing Gemini API Key');
2458
+ }
2459
+
2460
+ log('');
2461
+
2462
+ // LucyLab Bearer
2463
+ log(`${C.gray} LucyLab TTS bearer token for voice generation${C.reset}`);
2464
+ const lucylabToken = sanitize(await question(` ${C.yellow}LucyLab Bearer${config.lucylab_bearer ? ` [${config.lucylab_bearer.slice(0, 8)}...]` : ''}: ${C.reset}`));
2465
+ if (lucylabToken) {
2466
+ config.lucylab_bearer = lucylabToken;
2467
+ ok('LucyLab Bearer saved');
2468
+ } else if (config.lucylab_bearer) {
2469
+ dim('Kept existing LucyLab Bearer');
2470
+ }
2471
+
2472
+ credentialsSave(config);
2473
+ log('');
2474
+ ok(`Credentials saved to ${CREDENTIALS_CONFIG_PATH}`);
2475
+ log('');
2476
+ } catch (e) {
2477
+ warn(`Failed to setup credentials: ${e.message}`);
2478
+ } finally {
2479
+ rl.close();
2480
+ }
2481
+ }
2482
+
2483
+ function cmdCredentials(args) {
2484
+ const subCmd = args[0];
2485
+ const key = args[1];
2486
+ const value = args.slice(2).join(' ');
2487
+
2488
+ switch (subCmd) {
2489
+ case 'list': {
2490
+ const config = credentialsLoad();
2491
+ const keys = Object.keys(config);
2492
+ if (keys.length === 0) {
2493
+ warn('No credentials stored. Run "awkit credentials setup" to configure.');
2494
+ return;
2495
+ }
2496
+ log('');
2497
+ log(`${C.cyan}${C.bold}🔑 Stored Credentials${C.reset}`);
2498
+ log('');
2499
+ for (const k of keys) {
2500
+ const val = config[k];
2501
+ const masked = val ? `${val.slice(0, 8)}${'•'.repeat(Math.max(0, val.length - 8))}` : '(empty)';
2502
+ log(` ${C.green}${k}${C.reset} = ${C.gray}${masked}${C.reset}`);
2503
+ }
2504
+ log('');
2505
+ dim(`Config: ${CREDENTIALS_CONFIG_PATH}`);
2506
+ log('');
2507
+ break;
2508
+ }
2509
+
2510
+ case 'set': {
2511
+ let valueToSet = value;
2512
+ if (valueToSet) {
2513
+ valueToSet = valueToSet.trim().replace(/^bearer\s+/i, '').replace(/\s+/g, '');
2514
+ }
2515
+ if (!key || !valueToSet) {
2516
+ err('Usage: awkit credentials set <key> <value>');
2517
+ dim('Example: awkit credentials set gemini_api_key AIzaSy...');
2518
+ return;
2519
+ }
2520
+ const config = credentialsLoad();
2521
+ config[key] = valueToSet;
2522
+ credentialsSave(config);
2523
+ ok(`${key} saved ✅`);
2524
+ break;
2525
+ }
2526
+
2527
+ case 'get': {
2528
+ if (!key) {
2529
+ err('Usage: awkit credentials get <key>');
2530
+ return;
2531
+ }
2532
+ const config = credentialsLoad();
2533
+ if (config[key]) {
2534
+ log(config[key]);
2535
+ } else {
2536
+ warn(`Key "${key}" not found.`);
2537
+ }
2538
+ break;
2539
+ }
2540
+
2541
+ case 'remove':
2542
+ case 'delete': {
2543
+ if (!key) {
2544
+ err('Usage: awkit credentials remove <key>');
2545
+ return;
2546
+ }
2547
+ const config = credentialsLoad();
2548
+ if (config[key]) {
2549
+ delete config[key];
2550
+ credentialsSave(config);
2551
+ ok(`${key} removed`);
2552
+ } else {
2553
+ warn(`Key "${key}" not found.`);
2554
+ }
2555
+ break;
2556
+ }
2557
+
2558
+ case 'setup':
2559
+ credentialsSetup();
2560
+ break;
2561
+
2562
+ default:
2563
+ credentialsHelp();
2564
+ break;
2565
+ }
2566
+ }
2567
+
2222
2568
  // ─── Trello Integration ───────────────────────────────────────────────────────
2223
2569
 
2224
2570
  /**
@@ -2490,6 +2836,98 @@ function checkAutoUpdate() {
2490
2836
  }
2491
2837
  }
2492
2838
 
2839
+ // ─── Native HTTP Server ───────────────────────────────────────────────────────
2840
+
2841
+ function cmdServe(args) {
2842
+ const http = require('http');
2843
+
2844
+ let port = 8080;
2845
+ let serveDir = process.cwd();
2846
+
2847
+ for (let i = 0; i < args.length; i++) {
2848
+ if (args[i] === '--port' || args[i] === '-p') {
2849
+ port = parseInt(args[++i], 10) || 8080;
2850
+ } else if (!args[i].startsWith('-')) {
2851
+ serveDir = path.resolve(process.cwd(), args[i]);
2852
+ }
2853
+ }
2854
+
2855
+ if (!fs.existsSync(serveDir)) {
2856
+ err(`Directory not found: ${serveDir}`);
2857
+ return;
2858
+ }
2859
+
2860
+ const mimeTypes = {
2861
+ '.html': 'text/html',
2862
+ '.js': 'text/javascript',
2863
+ '.css': 'text/css',
2864
+ '.json': 'application/json',
2865
+ '.png': 'image/png',
2866
+ '.jpg': 'image/jpeg',
2867
+ '.jpeg': 'image/jpeg',
2868
+ '.gif': 'image/gif',
2869
+ '.svg': 'image/svg+xml',
2870
+ '.wav': 'audio/wav',
2871
+ '.mp4': 'video/mp4',
2872
+ '.woff': 'application/font-woff',
2873
+ '.ttf': 'application/font-ttf',
2874
+ '.eot': 'application/vnd.ms-fontobject',
2875
+ '.otf': 'application/font-otf',
2876
+ '.wasm': 'application/wasm'
2877
+ };
2878
+
2879
+ const server = http.createServer((request, response) => {
2880
+ response.setHeader('Access-Control-Allow-Origin', '*');
2881
+ response.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
2882
+ response.setHeader('Access-Control-Allow-Headers', 'Content-Type');
2883
+
2884
+ if (request.method === 'OPTIONS') {
2885
+ response.writeHead(200);
2886
+ response.end();
2887
+ return;
2888
+ }
2889
+
2890
+ let filePath = '.' + request.url.split('?')[0];
2891
+ if (filePath === './') {
2892
+ filePath = './index.html';
2893
+ }
2894
+
2895
+ const absPath = path.join(serveDir, filePath);
2896
+ const extname = String(path.extname(absPath)).toLowerCase();
2897
+ const contentType = mimeTypes[extname] || 'application/octet-stream';
2898
+
2899
+ fs.readFile(absPath, (error, content) => {
2900
+ if (error) {
2901
+ if (error.code === 'ENOENT') {
2902
+ response.writeHead(404, { 'Content-Type': 'text/plain' });
2903
+ response.end('404 Not Found', 'utf-8');
2904
+ } else {
2905
+ response.writeHead(500, { 'Content-Type': 'text/plain' });
2906
+ response.end('500 Internal Server Error: ' + error.code, 'utf-8');
2907
+ }
2908
+ } else {
2909
+ response.writeHead(200, { 'Content-Type': contentType });
2910
+ response.end(content, 'utf-8');
2911
+ }
2912
+ });
2913
+ });
2914
+
2915
+ server.listen(port, '0.0.0.0', () => {
2916
+ log('');
2917
+ log(`${C.cyan}${C.bold}🚀 awkit serve running at:${C.reset}`);
2918
+ log(`${C.green} http://localhost:${port}${C.reset}`);
2919
+ dim(`Serving directory: ${serveDir}`);
2920
+ dim(`Press Ctrl+C to stop`);
2921
+ log('');
2922
+ }).on('error', (e) => {
2923
+ if (e.code === 'EADDRINUSE') {
2924
+ err(`Port ${port} is already in use. Try a different port with --port <number>`);
2925
+ } else {
2926
+ err(`Server error: ${e.message}`);
2927
+ }
2928
+ });
2929
+ }
2930
+
2493
2931
  // ─── Main ────────────────────────────────────────────────────────────────────
2494
2932
 
2495
2933
  // Check for updates (max once per day) before continuing
@@ -2523,6 +2961,9 @@ const [, , command, ...args] = process.argv;
2523
2961
  case 'doctor':
2524
2962
  cmdDoctor();
2525
2963
  break;
2964
+ case 'browser':
2965
+ cmdBrowser(args);
2966
+ break;
2526
2967
  case 'enable-pack':
2527
2968
  cmdEnablePack(args[0]);
2528
2969
  break;
@@ -2547,9 +2988,19 @@ const [, , command, ...args] = process.argv;
2547
2988
  case 'telegram':
2548
2989
  cmdTelegram(args);
2549
2990
  break;
2991
+ case 'credentials':
2992
+ case 'creds':
2993
+ cmdCredentials(args);
2994
+ break;
2995
+ case 'serve':
2996
+ cmdServe(args);
2997
+ break;
2550
2998
  case 'admin':
2551
2999
  cmdAdmin();
2552
3000
  break;
3001
+ case 'restart':
3002
+ await cmdRestart();
3003
+ break;
2553
3004
  case 'help':
2554
3005
  case '--help':
2555
3006
  case '-h':