@leejungkiin/awkit 1.7.4 → 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.
package/bin/awk.js CHANGED
@@ -1945,6 +1945,22 @@ function cmdStatus() {
1945
1945
  }
1946
1946
 
1947
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
+
1948
1964
  log(`${C.gray}Tip: 'awkit sync' = harvest (pull live→repo) + install (push repo→live)${C.reset}`);
1949
1965
  log('');
1950
1966
  }
@@ -2026,6 +2042,63 @@ function cmdBuild() {
2026
2042
  }
2027
2043
  }
2028
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
+
2029
2102
  async function cmdAdmin() {
2030
2103
  info('Mở Symphony Dashboard...');
2031
2104
  try {
@@ -2115,6 +2188,59 @@ async function cmdRestart() {
2115
2188
  }
2116
2189
  }
2117
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
+
2118
2244
  function cmdHelp() {
2119
2245
  const line = `${C.gray}${'─'.repeat(56)}${C.reset}`;
2120
2246
  log('');
@@ -2138,9 +2264,13 @@ function cmdHelp() {
2138
2264
  if (identity.automation && identity.automation.build && identity.automation.build.enabled) {
2139
2265
  bCmd = identity.automation.build.command || 'Enabled';
2140
2266
  }
2267
+ let rCmd = 'Disabled';
2268
+ if (identity.automation && identity.automation.run && identity.automation.run.enabled) {
2269
+ rCmd = identity.automation.run.command || 'Enabled';
2270
+ }
2141
2271
 
2142
2272
  log(`${C.cyan}👉 Active Project: ${icon} ${C.bold}${name}${C.reset}`);
2143
- log(` [${pType} | Build Control: ${bCmd === 'Disabled' ? C.yellow + 'Manual' : C.green + bCmd}${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}]`);
2144
2274
  log(` Run ${C.green}awkit identity${C.reset} for full configuration details.`);
2145
2275
  log('');
2146
2276
  } catch (_) {}
@@ -2158,6 +2288,10 @@ function cmdHelp() {
2158
2288
  log(` ${C.green}credentials list${C.reset} List stored API keys`);
2159
2289
  log(` ${C.green}credentials set${C.reset} <k> <v> Set API key (e.g., gemini_api_key, openrouter_api_key)`);
2160
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`);
2161
2295
  log('');
2162
2296
 
2163
2297
  // Project Init
@@ -2169,6 +2303,7 @@ function cmdHelp() {
2169
2303
  log(` ${C.gray} CODEBASE.md, thiết lập router (AGENTS.md, CLAUDE.md)${C.reset}`);
2170
2304
  log(` ${C.green}identity${C.reset} Show project identity & active configurations (alias: config)`);
2171
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`);
2172
2307
  log('');
2173
2308
 
2174
2309
  // Maintenance
@@ -2195,10 +2330,21 @@ function cmdHelp() {
2195
2330
  log(` ${C.green}sync${C.reset} Full sync: harvest + install (one shot)`);
2196
2331
  log('');
2197
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
+
2198
2342
  // Symphony
2199
2343
  log(`${C.bold}🎶 Symphony${C.reset}`);
2200
2344
  log(line);
2201
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`);
2202
2348
  log('');
2203
2349
 
2204
2350
  // Packs
@@ -2222,11 +2368,23 @@ function cmdHelp() {
2222
2368
  log('');
2223
2369
 
2224
2370
  // Pipeline
2225
- log(`${C.bold}🔗 Pipeline${C.reset}`);
2371
+ log(`${C.bold}🔗 Pipeline & Sub-Agents${C.reset}`);
2226
2372
  log(line);
2227
- log(` ${C.green}pipeline plan${C.reset} <Feature> Lập kế hoạch kiến trúc (Opus 4.6 via agy)`);
2228
- log(` ${C.green}pipeline ui${C.reset} <Feature> Thiết kế shell & GUI assets (Codex)`);
2229
- log(` ${C.green}pipeline code${C.reset} <Feature> Thực thi viết code & review (Gemini + Critic)`);
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`);
2230
2388
  log('');
2231
2389
 
2232
2390
  // Trello
@@ -2252,15 +2410,16 @@ function cmdHelp() {
2252
2410
  log('');
2253
2411
 
2254
2412
  // GitNexus
2255
- log(`${C.bold}🔍 GitNexus${C.reset}`);
2413
+ log(`${C.bold}🔍 GitNexus (Shortcuts: index, clean, list)${C.reset}`);
2256
2414
  log(line);
2257
- log(` ${C.green}gitnexus list${C.reset} List all indexed repositories`);
2258
- log(` ${C.green}gitnexus status${C.reset} Show current project index info`);
2259
- log(` ${C.green}gitnexus clean${C.reset} Interactive cleanup of old indexes`);
2260
- 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`);
2261
2420
  log(` ${C.gray} --stale${C.reset} Auto-remove repos with missing paths`);
2262
2421
  log(` ${C.gray} --all${C.reset} Remove all except current project`);
2263
- log(` ${C.gray} Alias: gn${C.reset}`);
2422
+ log(` ${C.gray} Alias: gn / gitnexus${C.reset}`);
2264
2423
  log('');
2265
2424
 
2266
2425
  // Available packs
@@ -4172,9 +4331,34 @@ function cmdGitnexus(args) {
4172
4331
  break;
4173
4332
  }
4174
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
+
4175
4359
  default:
4176
4360
  err(`Unknown gitnexus sub-command: ${sub}`);
4177
- log(` Available: list, status, clean`);
4361
+ log(` Available: index, list, status, clean`);
4178
4362
  break;
4179
4363
  }
4180
4364
  }
@@ -4352,6 +4536,14 @@ function cmdIdentity(args = []) {
4352
4536
  } else {
4353
4537
  log(` ${C.bold}Build Control:${C.reset} ${C.yellow}Not Configured (Manual Mode)${C.reset}`);
4354
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
+ }
4355
4547
  log('');
4356
4548
 
4357
4549
  // Automation Gates
@@ -4488,6 +4680,335 @@ function cmdServe(args) {
4488
4680
  });
4489
4681
  }
4490
4682
 
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
+ }
4709
+
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
+
4491
5012
  // ─── Main ────────────────────────────────────────────────────────────────────
4492
5013
 
4493
5014
  const [, , command, ...args] = process.argv;
@@ -4498,6 +5019,15 @@ const [, , command, ...args] = process.argv;
4498
5019
  case 'build':
4499
5020
  cmdBuild();
4500
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;
4501
5031
  case 'init':
4502
5032
  await cmdInit(args.includes('--force'));
4503
5033
  break;
@@ -4514,7 +5044,11 @@ const [, , command, ...args] = process.argv;
4514
5044
  cmdSync();
4515
5045
  break;
4516
5046
  case 'status':
4517
- cmdStatus();
5047
+ if (args[0] === 'gn' || args[0] === 'gitnexus') {
5048
+ cmdGitnexus(['status']);
5049
+ } else {
5050
+ cmdStatus();
5051
+ }
4518
5052
  break;
4519
5053
  case 'harvest':
4520
5054
  cmdHarvest(args.includes('--dry-run'));
@@ -4562,6 +5096,16 @@ const [, , command, ...args] = process.argv;
4562
5096
  case 'creds':
4563
5097
  cmdCredentials(args);
4564
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;
4565
5109
  case 'set-openrouter': {
4566
5110
  const key = args[0];
4567
5111
  if (!key) {
@@ -4603,11 +5147,31 @@ const [, , command, ...args] = process.argv;
4603
5147
  case 'restart':
4604
5148
  await cmdRestart();
4605
5149
  break;
5150
+ case 'greet':
5151
+ cmdGreet(args);
5152
+ break;
5153
+ case 'config-global':
5154
+ cmdGlobalConfig(args);
5155
+ break;
4606
5156
  case 'identity':
4607
5157
  case 'config':
4608
- case 'project':
4609
- cmdIdentity(args);
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
+ }
4610
5173
  break;
5174
+ }
4611
5175
  case 'help':
4612
5176
  case '--help':
4613
5177
  case '-h':