Train 1D: merge via --admin on .113 validation

Squash merge from local merge-train (Hard Rule owner-approved). Tip 029cdf4215cf465f0e1716ac9f84a84692b1e881 validated on 192.168.0.113: 26631/26653 pass.
This commit is contained in:
Austin Liu
2026-07-28 00:00:18 +09:30
committed by GitHub
parent 0f874825cf
commit 419f8b4845
3 changed files with 119 additions and 75 deletions

View File

@@ -2,6 +2,7 @@
name: cli-backup-sync
description: Backup and restore OmniRoute data from the CLI. Trigger incremental snapshots, sync to cloud storage, manage backup schedules, and restore from archive files.
---
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
## Overview

View File

@@ -42,55 +42,55 @@ export interface ParsedCliRegistry {
* Files that don't map to a known family are ignored.
*/
const FILE_FAMILY_MAP: Record<string, SkillArea> = {
"serve": "cli-serve",
"dashboard": "cli-serve",
"stop": "cli-serve",
"restart": "cli-serve",
"health": "cli-health",
"status": "cli-health",
"doctor": "cli-health",
"providers": "cli-providers",
serve: "cli-serve",
dashboard: "cli-serve",
stop: "cli-serve",
restart: "cli-serve",
health: "cli-health",
status: "cli-health",
doctor: "cli-health",
providers: "cli-providers",
"provider-cmd": "cli-providers",
"test-provider": "cli-providers",
"keys": "cli-keys",
"oauth": "cli-keys",
"models": "cli-models",
"chat": "cli-chat",
"stream": "cli-chat",
"repl": "cli-chat",
"combo": "cli-routing",
"routing": "cli-routing",
"resilience": "cli-resilience",
"quota": "cli-resilience",
"compression": "cli-compression",
keys: "cli-keys",
oauth: "cli-keys",
models: "cli-models",
chat: "cli-chat",
stream: "cli-chat",
repl: "cli-chat",
combo: "cli-routing",
routing: "cli-routing",
resilience: "cli-resilience",
quota: "cli-resilience",
compression: "cli-compression",
"context-eng": "cli-contexts",
"contexts": "cli-contexts",
"sessions": "cli-contexts",
"cost": "cli-cost-usage",
"usage": "cli-cost-usage",
"pricing": "cli-cost-usage",
"mcp": "cli-mcp",
"a2a": "cli-a2a",
"tunnel": "cli-tunnel",
"backup": "cli-backup-sync",
"sync": "cli-backup-sync",
"cloud": "cli-backup-sync",
"audit": "cli-policy-audit",
"policy": "cli-policy-audit",
"logs": "cli-policy-audit",
"telemetry": "cli-policy-audit",
"batches": "cli-batches",
"files": "cli-batches",
"eval": "cli-eval",
"simulate": "cli-eval",
"skills": "cli-plugins-skills",
"plugin": "cli-plugins-skills",
"memory": "cli-plugins-skills",
"setup": "cli-setup",
"config": "cli-setup",
"env": "cli-setup",
"update": "cli-setup",
"autostart": "cli-setup",
contexts: "cli-contexts",
sessions: "cli-contexts",
cost: "cli-cost-usage",
usage: "cli-cost-usage",
pricing: "cli-cost-usage",
mcp: "cli-mcp",
a2a: "cli-a2a",
tunnel: "cli-tunnel",
backup: "cli-backup-sync",
sync: "cli-backup-sync",
cloud: "cli-backup-sync",
audit: "cli-policy-audit",
policy: "cli-policy-audit",
logs: "cli-policy-audit",
telemetry: "cli-policy-audit",
batches: "cli-batches",
files: "cli-batches",
eval: "cli-eval",
simulate: "cli-eval",
skills: "cli-plugins-skills",
plugin: "cli-plugins-skills",
memory: "cli-plugins-skills",
setup: "cli-setup",
config: "cli-setup",
env: "cli-setup",
update: "cli-setup",
autostart: "cli-setup",
};
// ── Regex patterns ───────────────────────────────────────────────────────────
@@ -139,16 +139,21 @@ function extractCommandsFromContent(content: string, topLevelName: string): RawC
// Slice between this command call and the next to scope description/options
const slice = content.slice(cmdIndex, nextIndex);
// If the slice itself contains a nested subcommand definition (e.g., `const auto = backup.command("auto")`),
// restrict slice to end before the child `.command()` call so child options aren't attributed to parent.
const subCmdMatch = /[\s\S]+?(?=\b[a-zA-Z0-9_$]+\.command\()/g.exec(slice);
const effectiveSlice = subCmdMatch ? subCmdMatch[0] : slice;
// Extract description (first match in slice)
DESCRIPTION_RE.lastIndex = 0;
const descMatch = DESCRIPTION_RE.exec(slice);
const descMatch = DESCRIPTION_RE.exec(effectiveSlice);
const description = descMatch ? descMatch[1] : "";
// Extract flags in slice
const flags: string[] = [];
OPTION_RE.lastIndex = 0;
let optMatch: RegExpExecArray | null;
while ((optMatch = OPTION_RE.exec(slice)) !== null) {
while ((optMatch = OPTION_RE.exec(effectiveSlice)) !== null) {
flags.push(optMatch[1]);
}
@@ -187,7 +192,7 @@ export function parseCliRegistry(): ParsedCliRegistry {
} catch (err) {
throw new Error(
`cliRegistryParser: could not read ${commandsDir}. ` +
`Run from project root. Underlying error: ${err instanceof Error ? err.message : String(err)}`,
`Run from project root. Underlying error: ${err instanceof Error ? err.message : String(err)}`
);
}

View File

@@ -5,9 +5,8 @@ import path from "node:path";
import os from "node:os";
// Dynamic import to pick up ESM module
const { parseCliRegistry, getCommandsForFamily } = await import(
"../../src/lib/agentSkills/cliRegistryParser.ts"
);
const { parseCliRegistry, getCommandsForFamily } =
await import("../../src/lib/agentSkills/cliRegistryParser.ts");
// ─── Fixture helpers ──────────────────────────────────────────────────────────
@@ -15,9 +14,7 @@ const { parseCliRegistry, getCommandsForFamily } = await import(
* Creates a temporary directory mirroring bin/cli/commands/,
* writes fixture .mjs files, changes CWD, returns cleanup fn.
*/
function withFixtureCli(
files: Record<string, string>,
): { cleanup: () => void } {
function withFixtureCli(files: Record<string, string>): { cleanup: () => void } {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-cli-test-"));
const commandsDir = path.join(tmpDir, "bin", "cli", "commands");
fs.mkdirSync(commandsDir, { recursive: true });
@@ -159,7 +156,7 @@ test("parseCliRegistry() recognises providers family with ≥5 subcommands", ()
assert.ok(providerCmds, "Expected 'cli-providers' family to exist");
assert.ok(
providerCmds!.length >= 5,
`Expected ≥5 provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`,
`Expected ≥5 provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`
);
} finally {
cleanup();
@@ -174,10 +171,7 @@ test("parseCliRegistry() recognises health family commands", () => {
const { families } = parseCliRegistry();
const healthCmds = families.get("cli-health");
assert.ok(healthCmds, "Expected 'cli-health' family to exist");
assert.ok(
healthCmds!.length >= 2,
`Expected ≥2 health commands, got ${healthCmds!.length}`,
);
assert.ok(healthCmds!.length >= 2, `Expected ≥2 health commands, got ${healthCmds!.length}`);
} finally {
cleanup();
}
@@ -195,7 +189,7 @@ test("parseCliRegistry() extracts description for each command", () => {
assert.ok(providers, "Expected providers command");
assert.ok(
providers!.description.length > 0,
`Expected non-empty description for providers, got: "${providers!.description}"`,
`Expected non-empty description for providers, got: "${providers!.description}"`
);
} finally {
cleanup();
@@ -213,7 +207,7 @@ test("parseCliRegistry() marks subcommands with isSubcommand=true (after first)"
const subCmds = providerCmds.filter((c) => c.isSubcommand);
assert.ok(
subCmds.length >= 4,
`Expected ≥4 subcommands (list, available, test, etc.), got ${subCmds.length}`,
`Expected ≥4 subcommands (list, available, test, etc.), got ${subCmds.length}`
);
} finally {
cleanup();
@@ -232,10 +226,7 @@ test("parseCliRegistry() extracts flags from .option() calls", () => {
if (rotate) {
// If rotate exists and has flags, verify format
for (const flag of rotate.flags) {
assert.ok(
typeof flag === "string" && flag.length > 0,
`Invalid flag: "${flag}"`,
);
assert.ok(typeof flag === "string" && flag.length > 0, `Invalid flag: "${flag}"`);
}
}
} finally {
@@ -243,6 +234,58 @@ test("parseCliRegistry() extracts flags from .option() calls", () => {
}
});
test("parseCliRegistry() does not mis-attribute child options to parent command when subcommand variable is created inline", () => {
const fixture = `
export function registerBackup(program) {
const backup = program.command("backup").description("Manage backups");
backup
.command("create")
.description("Create a backup")
.option("--name <name>", "Name")
.option("--cloud", "Cloud backup");
const auto = backup.command("auto").description("Auto backup");
auto
.command("enable")
.description("Enable auto backup")
.option("--cron <expr>", "Cron schedule");
backup
.command("status")
.description("Show status");
}
`;
const { cleanup } = withFixtureCli({
"backup.mjs": fixture,
});
try {
const { commands } = parseCliRegistry();
const backupCmd = commands.get("backup");
assert.ok(backupCmd, "backup command should exist");
assert.deepEqual(
backupCmd.flags,
[],
"parent backup command should not collect flags from nested subcommands"
);
const statusCmd = commands.get("backup status");
assert.ok(statusCmd, "backup status command should exist");
assert.deepEqual(
statusCmd.flags,
[],
"backup status should not collect flags from other subcommands"
);
const createCmd = commands.get("backup create");
assert.ok(createCmd, "backup create command should exist");
assert.deepEqual(createCmd.flags, ["--name <name>", "--cloud"]);
} finally {
cleanup();
}
});
test("parseCliRegistry() skips unrecognised .mjs files", () => {
const { cleanup } = withFixtureCli({
"unknown-custom.mjs": `export function register(p) {}`,
@@ -251,9 +294,7 @@ test("parseCliRegistry() skips unrecognised .mjs files", () => {
try {
const { families } = parseCliRegistry();
// No family should be mapped from unknown-custom
const hasUnknown = [...families.keys()].some((k) =>
String(k).includes("unknown-custom"),
);
const hasUnknown = [...families.keys()].some((k) => String(k).includes("unknown-custom"));
assert.equal(hasUnknown, false, "unknown-custom.mjs should not create a family");
// providers.mjs should still be parsed
assert.ok(families.has("cli-providers"), "Expected cli-providers family from providers.mjs");
@@ -270,7 +311,7 @@ test("parseCliRegistry() throws if commands directory is missing", () => {
assert.throws(
() => parseCliRegistry(),
/cliRegistryParser: could not read/,
"Expected error when commands dir is missing",
"Expected error when commands dir is missing"
);
} finally {
process.chdir(originalCwd);
@@ -288,16 +329,13 @@ test("parseCliRegistry() with real providers.mjs: providers family has ≥5 comm
assert.ok(providerCmds, "Expected cli-providers family from real providers.mjs");
assert.ok(
providerCmds!.length >= 5,
`Expected ≥5 real provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`,
`Expected ≥5 real provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`
);
});
test("getCommandsForFamily('cli-providers') with real files: returns ≥5 strings", () => {
const commands = getCommandsForFamily("cli-providers");
assert.ok(
commands.length >= 5,
`Expected ≥5 cli-providers commands, got ${commands.length}`,
);
assert.ok(commands.length >= 5, `Expected ≥5 cli-providers commands, got ${commands.length}`);
for (const cmd of commands) {
assert.ok(typeof cmd === "string" && cmd.length > 0, `Invalid command name: "${cmd}"`);
}