@quicktvui/ai-cli 0.1.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,6 +41,8 @@ quicktvui-aicreate-project quick-tv-app
41
41
  - `--skip-install`: skip dependency install for `create-project`
42
42
  - `--strict`: fail on doctor check issues
43
43
  - `--lang <code>`: prompt language for `prompt` command (`zh` or `en`)
44
+ - `--gemini-dir <path>`: custom Gemini config directory (default `~/.gemini`, or `$GEMINI_CLI_HOME/.gemini`)
45
+ - `--skip-gemini-config`: skip updating Gemini bridge config during `init`/`update`
44
46
  - `--node-major <n>`: target Node.js LTS major for `setup-vue-env` (default `20`)
45
47
  - `--skip-node-install`: skip Node.js install stage in `setup-vue-env`
46
48
  - `--force-node-install`: force Node.js install stage in `setup-vue-env`
@@ -76,6 +78,12 @@ quicktvui-ai doctor
76
78
 
77
79
  Then reload your AI agent so it rescans local skills.
78
80
 
81
+ `init`/`update` also auto-maintains Gemini global context files:
82
+
83
+ - updates `~/.gemini/GEMINI.md` with QuickTVUI `@.../SKILL.md` bridge block
84
+ - ensures `~/.gemini/settings.json` contains `context.fileName` entries:
85
+ `GEMINI.md`, `AGENTS.md`, `SKILL.md`, `CONTEXT.md`
86
+
79
87
  ## Create project with network fallback
80
88
 
81
89
  ```bash
package/lib/index.js CHANGED
@@ -7,7 +7,7 @@ const https = require("https");
7
7
  const net = require("net");
8
8
  const { spawnSync, spawn } = require("child_process");
9
9
 
10
- const PACKAGE_VERSION = "0.1.2";
10
+ const PACKAGE_VERSION = "0.1.4";
11
11
  const DEFAULT_INSTALL_DIR = path.join(
12
12
  os.homedir(),
13
13
  ".agents",
@@ -36,6 +36,14 @@ const REQUIRED_SKILL_FILES = [
36
36
  path.join("references", "lookup-checklist.md"),
37
37
  path.join("references", "bug-report-template.md"),
38
38
  ];
39
+ const QUICKTVUI_GEMINI_BRIDGE_START = "<!-- QUICKTVUI_SKILL_BRIDGE_START -->";
40
+ const QUICKTVUI_GEMINI_BRIDGE_END = "<!-- QUICKTVUI_SKILL_BRIDGE_END -->";
41
+ const DEFAULT_GEMINI_CONTEXT_FILENAMES = [
42
+ "GEMINI.md",
43
+ "AGENTS.md",
44
+ "SKILL.md",
45
+ "CONTEXT.md",
46
+ ];
39
47
 
40
48
  function exists(filePath) {
41
49
  return fs.existsSync(filePath);
@@ -65,6 +73,21 @@ function removeDirectoryIfExists(dirPath) {
65
73
  }
66
74
  }
67
75
 
76
+ function backupFile(filePath) {
77
+ if (!exists(filePath)) return null;
78
+ const backupPath = `${filePath}.bak`;
79
+ try {
80
+ fs.copyFileSync(filePath, backupPath);
81
+ return backupPath;
82
+ } catch (error) {
83
+ return null;
84
+ }
85
+ }
86
+
87
+ function escapeRegExp(value) {
88
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
89
+ }
90
+
68
91
  function needsWindowsShell(command) {
69
92
  return process.platform === "win32" && /\.(bat|cmd)$/i.test(String(command));
70
93
  }
@@ -2288,6 +2311,8 @@ Options:
2288
2311
  --skip-install Skip dependency install in create-project
2289
2312
  --strict Non-zero exit in doctor when checks fail
2290
2313
  --lang <code> Prompt language for 'prompt' command: zh | en
2314
+ --gemini-dir <path> Gemini config directory (default: ~/.gemini or $GEMINI_CLI_HOME/.gemini)
2315
+ --skip-gemini-config Skip updating Gemini bridge files during init/update
2291
2316
  --yes Non-interactive mode; accept default prompts
2292
2317
  --no-interactive Disable interactive prompts
2293
2318
  --node-major <n> Target Node.js LTS major for setup-vue-env (default: 20)
@@ -2401,6 +2426,191 @@ function installSkills(installDir) {
2401
2426
  return { installDir, sourceDir };
2402
2427
  }
2403
2428
 
2429
+ function resolveGeminiConfigDir(args) {
2430
+ if (typeof args["gemini-dir"] === "string" && args["gemini-dir"].trim()) {
2431
+ return path.resolve(args["gemini-dir"].trim());
2432
+ }
2433
+
2434
+ const customGeminiHome = process.env.GEMINI_CLI_HOME
2435
+ ? String(process.env.GEMINI_CLI_HOME).trim()
2436
+ : "";
2437
+ if (customGeminiHome) {
2438
+ const resolved = path.resolve(customGeminiHome);
2439
+ if (path.basename(resolved).toLowerCase() === ".gemini") {
2440
+ return resolved;
2441
+ }
2442
+ return path.join(resolved, ".gemini");
2443
+ }
2444
+
2445
+ return path.join(os.homedir(), ".gemini");
2446
+ }
2447
+
2448
+ function buildGeminiBridgeImportPaths(installDir) {
2449
+ const preferredFiles = [
2450
+ "SKILL.md",
2451
+ path.join("references", "create-project-checklist.md"),
2452
+ path.join("references", "dev-env-checklist.md"),
2453
+ path.join("references", "esapp-protocol-cheatsheet.md"),
2454
+ ];
2455
+
2456
+ const importPaths = [];
2457
+ for (const relPath of preferredFiles) {
2458
+ const absolutePath = path.join(installDir, relPath);
2459
+ if (exists(absolutePath)) {
2460
+ importPaths.push(absolutePath);
2461
+ }
2462
+ }
2463
+
2464
+ if (importPaths.length === 0) {
2465
+ importPaths.push(path.join(installDir, "SKILL.md"));
2466
+ }
2467
+
2468
+ return importPaths;
2469
+ }
2470
+
2471
+ function updateGeminiBridgeConfig(args, installDir) {
2472
+ if (toBooleanFlag(args["skip-gemini-config"], false)) {
2473
+ return {
2474
+ skipped: true,
2475
+ reason: "flag",
2476
+ };
2477
+ }
2478
+
2479
+ const geminiDir = resolveGeminiConfigDir(args);
2480
+ ensureDir(geminiDir);
2481
+
2482
+ const geminiMdPath = path.join(geminiDir, "GEMINI.md");
2483
+ const settingsPath = path.join(geminiDir, "settings.json");
2484
+ const importPaths = buildGeminiBridgeImportPaths(installDir);
2485
+
2486
+ const bridgeBlock = [
2487
+ QUICKTVUI_GEMINI_BRIDGE_START,
2488
+ "QuickTVUI global skill bridge (managed by quicktvui-ai assistant).",
2489
+ ...importPaths.map((importPath) => `@${importPath}`),
2490
+ QUICKTVUI_GEMINI_BRIDGE_END,
2491
+ ].join("\n");
2492
+
2493
+ const beforeGeminiMd = exists(geminiMdPath)
2494
+ ? fs.readFileSync(geminiMdPath, "utf8")
2495
+ : "";
2496
+ const bridgePattern = new RegExp(
2497
+ `${escapeRegExp(QUICKTVUI_GEMINI_BRIDGE_START)}[\\s\\S]*?${escapeRegExp(QUICKTVUI_GEMINI_BRIDGE_END)}`,
2498
+ "m",
2499
+ );
2500
+ let nextGeminiMd;
2501
+ if (bridgePattern.test(beforeGeminiMd)) {
2502
+ nextGeminiMd = beforeGeminiMd.replace(bridgePattern, bridgeBlock);
2503
+ } else {
2504
+ const trimmed = beforeGeminiMd.replace(/\s*$/, "");
2505
+ nextGeminiMd = trimmed
2506
+ ? `${trimmed}\n\n${bridgeBlock}\n`
2507
+ : `${bridgeBlock}\n`;
2508
+ }
2509
+
2510
+ let geminiMdUpdated = false;
2511
+ let geminiMdBackup = null;
2512
+ if (nextGeminiMd !== beforeGeminiMd) {
2513
+ geminiMdBackup = backupFile(geminiMdPath);
2514
+ fs.writeFileSync(geminiMdPath, nextGeminiMd, "utf8");
2515
+ geminiMdUpdated = true;
2516
+ }
2517
+
2518
+ const result = {
2519
+ skipped: false,
2520
+ geminiDir,
2521
+ geminiMdPath,
2522
+ settingsPath,
2523
+ importPaths,
2524
+ geminiMdUpdated,
2525
+ geminiMdBackup,
2526
+ settingsUpdated: false,
2527
+ settingsBackup: null,
2528
+ settingsParseError: null,
2529
+ };
2530
+
2531
+ let settings = {};
2532
+ const beforeSettings = exists(settingsPath)
2533
+ ? fs.readFileSync(settingsPath, "utf8")
2534
+ : "";
2535
+ if (beforeSettings.trim()) {
2536
+ try {
2537
+ settings = JSON.parse(beforeSettings);
2538
+ } catch (error) {
2539
+ result.settingsParseError = error.message;
2540
+ return result;
2541
+ }
2542
+ }
2543
+
2544
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
2545
+ settings = {};
2546
+ }
2547
+ if (!settings.context || typeof settings.context !== "object") {
2548
+ settings.context = {};
2549
+ }
2550
+
2551
+ const current = settings.context.fileName;
2552
+ const normalizedList = Array.isArray(current)
2553
+ ? current
2554
+ .map((item) => String(item).trim())
2555
+ .filter((item) => item.length > 0)
2556
+ : typeof current === "string" && current.trim()
2557
+ ? [current.trim()]
2558
+ : [];
2559
+
2560
+ const lowerSet = new Set(normalizedList.map((item) => item.toLowerCase()));
2561
+ for (const fileName of DEFAULT_GEMINI_CONTEXT_FILENAMES) {
2562
+ if (!lowerSet.has(fileName.toLowerCase())) {
2563
+ normalizedList.push(fileName);
2564
+ lowerSet.add(fileName.toLowerCase());
2565
+ }
2566
+ }
2567
+ settings.context.fileName = normalizedList;
2568
+
2569
+ const nextSettings = `${JSON.stringify(settings, null, 2)}\n`;
2570
+ if (nextSettings !== beforeSettings) {
2571
+ result.settingsBackup = backupFile(settingsPath);
2572
+ fs.writeFileSync(settingsPath, nextSettings, "utf8");
2573
+ result.settingsUpdated = true;
2574
+ }
2575
+
2576
+ return result;
2577
+ }
2578
+
2579
+ function printGeminiBridgeResult(state) {
2580
+ if (!state) return;
2581
+ if (state.error) {
2582
+ console.log(`- Gemini config: failed (${state.error})`);
2583
+ console.log(
2584
+ ` Hint: rerun with --skip-gemini-config or fix filesystem permission.`,
2585
+ );
2586
+ return;
2587
+ }
2588
+ if (state.skipped) {
2589
+ console.log(`- Gemini config: skipped (--skip-gemini-config)`);
2590
+ return;
2591
+ }
2592
+
2593
+ console.log(`- Gemini config dir: ${state.geminiDir}`);
2594
+ console.log(
2595
+ `- Gemini bridge block: ${state.geminiMdUpdated ? "updated" : "already up to date"}`,
2596
+ );
2597
+ console.log(
2598
+ `- Gemini context.fileName: ${
2599
+ state.settingsParseError
2600
+ ? "skipped (settings.json parse failed)"
2601
+ : state.settingsUpdated
2602
+ ? "updated"
2603
+ : "already up to date"
2604
+ }`,
2605
+ );
2606
+ if (state.settingsParseError) {
2607
+ console.log(` Warning: ${state.settingsParseError}`);
2608
+ console.log(
2609
+ ` Hint: fix ${state.settingsPath} JSON and rerun 'quicktvui-ai init'.`,
2610
+ );
2611
+ }
2612
+ }
2613
+
2404
2614
  function printDoctorReport(skillState, projectState, projectRoot) {
2405
2615
  console.log(`Skill dir: ${skillState.installDir}`);
2406
2616
  console.log(`- Directory exists: ${skillState.exists ? "yes" : "no"}`);
@@ -2428,9 +2638,16 @@ function printDoctorReport(skillState, projectState, projectRoot) {
2428
2638
  async function runInit(args) {
2429
2639
  const installDir = getInstallDir(args);
2430
2640
  const result = installSkills(installDir);
2641
+ let geminiState;
2642
+ try {
2643
+ geminiState = updateGeminiBridgeConfig(args, result.installDir);
2644
+ } catch (error) {
2645
+ geminiState = { error: error.message };
2646
+ }
2431
2647
  console.log(`Installed QuickTVUI skill assets.`);
2432
2648
  console.log(`- source: ${result.sourceDir}`);
2433
2649
  console.log(`- target: ${result.installDir}`);
2650
+ printGeminiBridgeResult(geminiState);
2434
2651
  console.log(`Next: restart/reload your AI agent so it can rescan skills.`);
2435
2652
  }
2436
2653
 
@@ -2484,9 +2701,16 @@ async function runValidate(args) {
2484
2701
  async function runUpdate(args) {
2485
2702
  const installDir = getInstallDir(args);
2486
2703
  const result = installSkills(installDir);
2704
+ let geminiState;
2705
+ try {
2706
+ geminiState = updateGeminiBridgeConfig(args, result.installDir);
2707
+ } catch (error) {
2708
+ geminiState = { error: error.message };
2709
+ }
2487
2710
  console.log(`Updated QuickTVUI skill assets.`);
2488
2711
  console.log(`- source: ${result.sourceDir}`);
2489
2712
  console.log(`- target: ${result.installDir}`);
2713
+ printGeminiBridgeResult(geminiState);
2490
2714
  }
2491
2715
 
2492
2716
  function cloneTemplateProject(targetDir) {
@@ -2571,6 +2795,7 @@ npm install @quicktvui/ai --save-dev
2571
2795
  \`\`\`
2572
2796
 
2573
2797
  安装完成后请提醒用户重启或 reload AI Agent,以便重新扫描 ~/.agents/skills/quicktvui。
2798
+ 并提示:quicktvui-ai init 会自动更新 ~/.gemini/GEMINI.md 与 ~/.gemini/settings.json 的 QuickTVUI bridge 配置。
2574
2799
 
2575
2800
  创建项目时优先使用:
2576
2801
  \`\`\`bash
@@ -2606,6 +2831,7 @@ npm install @quicktvui/ai --save-dev
2606
2831
  \`\`\`
2607
2832
 
2608
2833
  After installation, ask the user to restart or reload the AI Agent so it rescans ~/.agents/skills/quicktvui.
2834
+ Also note that quicktvui-ai init auto-updates QuickTVUI bridge config in ~/.gemini/GEMINI.md and ~/.gemini/settings.json.
2609
2835
 
2610
2836
  When creating a project, prefer:
2611
2837
  \`\`\`bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quicktvui/ai-cli",
3
- "version": "0.1.3",
3
+ "version": "1.1.1",
4
4
  "description": "CLI for installing and validating QuickTVUI AI skills",
5
5
  "bin": {
6
6
  "quicktvui-ai": "bin/quicktvui-ai.js",
package/scripts/install CHANGED
@@ -41,4 +41,5 @@ else
41
41
  quicktvui-ai doctor --strict --dir "$INSTALL_DIR"
42
42
  fi
43
43
 
44
- echo "Done. Reload your AI agent to rescan local skills."
44
+ echo "Done. quicktvui-ai init also updated Gemini bridge config under ~/.gemini."
45
+ echo "Reload your AI agent to rescan local skills."
@@ -25,4 +25,5 @@ if ($Project -and $Project.Length -gt 0) {
25
25
  quicktvui-ai doctor --strict --dir $Dir
26
26
  }
27
27
 
28
- Write-Host "Done. Reload your AI agent to rescan local skills." -ForegroundColor Green
28
+ Write-Host "Done. quicktvui-ai init also updated Gemini bridge config under ~/.gemini." -ForegroundColor Green
29
+ Write-Host "Reload your AI agent to rescan local skills." -ForegroundColor Green