@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 +8 -0
- package/lib/index.js +227 -1
- package/package.json +1 -1
- package/scripts/install +2 -1
- package/scripts/install.ps1 +2 -1
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.
|
|
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
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.
|
|
44
|
+
echo "Done. quicktvui-ai init also updated Gemini bridge config under ~/.gemini."
|
|
45
|
+
echo "Reload your AI agent to rescan local skills."
|
package/scripts/install.ps1
CHANGED
|
@@ -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.
|
|
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
|