@arvoretech/hub 0.7.7 → 0.8.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.
|
@@ -121,7 +121,7 @@ async function checkAndAutoRegenerate(hubDir) {
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
console.log(chalk.yellow("\n Detected outdated configs, auto-regenerating..."));
|
|
124
|
-
const { generators: generators2 } = await import("./generate-
|
|
124
|
+
const { generators: generators2 } = await import("./generate-BYB47MCP.js");
|
|
125
125
|
const generator = generators2[result.editor];
|
|
126
126
|
if (!generator) {
|
|
127
127
|
console.log(chalk.red(` Unknown editor '${result.editor}' in cache. Run 'hub generate' manually.`));
|
|
@@ -144,6 +144,41 @@ function stripFrontMatter(content) {
|
|
|
144
144
|
if (match) return content.slice(match[0].length);
|
|
145
145
|
return content;
|
|
146
146
|
}
|
|
147
|
+
function parseFrontMatter(content) {
|
|
148
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
149
|
+
if (!match) return null;
|
|
150
|
+
const result = {};
|
|
151
|
+
for (const line of match[1].split("\n")) {
|
|
152
|
+
const colonIdx = line.indexOf(":");
|
|
153
|
+
if (colonIdx === -1) continue;
|
|
154
|
+
const key = line.slice(0, colonIdx).trim();
|
|
155
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
156
|
+
if (key) result[key] = value;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
async function readExistingMcpDisabledState(mcpJsonPath) {
|
|
161
|
+
const disabledState = {};
|
|
162
|
+
if (!existsSync2(mcpJsonPath)) return disabledState;
|
|
163
|
+
try {
|
|
164
|
+
const content = JSON.parse(await readFile3(mcpJsonPath, "utf-8"));
|
|
165
|
+
const servers = content.mcpServers || content.mcp || {};
|
|
166
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
167
|
+
if (typeof config.disabled === "boolean") {
|
|
168
|
+
disabledState[name] = config.disabled;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
return disabledState;
|
|
174
|
+
}
|
|
175
|
+
function applyDisabledState(mcpConfig, disabledState) {
|
|
176
|
+
for (const [name, entry] of Object.entries(mcpConfig)) {
|
|
177
|
+
if (name in disabledState) {
|
|
178
|
+
entry.disabled = disabledState[name];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
147
182
|
async function fetchHubDocsSkill(skillsDir) {
|
|
148
183
|
try {
|
|
149
184
|
const res = await fetch(HUB_DOCS_URL);
|
|
@@ -1559,7 +1594,7 @@ async function generateKiro(config, hubDir) {
|
|
|
1559
1594
|
await writeManagedFile(join3(hubDir, ".gitignore"), gitignoreLines);
|
|
1560
1595
|
console.log(chalk2.green(" Generated .gitignore"));
|
|
1561
1596
|
const kiroRule = buildKiroOrchestratorRule(config);
|
|
1562
|
-
const kiroOrchestrator = buildKiroSteeringContent(kiroRule);
|
|
1597
|
+
const kiroOrchestrator = buildKiroSteeringContent(kiroRule, "always", { name: "orchestrator" });
|
|
1563
1598
|
await writeFile2(join3(steeringDir, "orchestrator.md"), kiroOrchestrator, "utf-8");
|
|
1564
1599
|
console.log(chalk2.green(" Generated .kiro/steering/orchestrator.md"));
|
|
1565
1600
|
await writeFile2(join3(hubDir, "AGENTS.md"), kiroRule + "\n", "utf-8");
|
|
@@ -1571,8 +1606,36 @@ async function generateKiro(config, hubDir) {
|
|
|
1571
1606
|
for (const file of mdFiles) {
|
|
1572
1607
|
const raw = await readFile3(join3(hubSteeringDir, file), "utf-8");
|
|
1573
1608
|
const content = stripFrontMatter(raw);
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1609
|
+
const destPath = join3(steeringDir, file);
|
|
1610
|
+
let inclusion = "always";
|
|
1611
|
+
let meta;
|
|
1612
|
+
if (existsSync2(destPath)) {
|
|
1613
|
+
const existingContent = await readFile3(destPath, "utf-8");
|
|
1614
|
+
const existingFm = parseFrontMatter(existingContent);
|
|
1615
|
+
if (existingFm) {
|
|
1616
|
+
if (existingFm.inclusion === "auto" || existingFm.inclusion === "manual" || existingFm.inclusion === "fileMatch") {
|
|
1617
|
+
inclusion = "auto";
|
|
1618
|
+
}
|
|
1619
|
+
if (existingFm.name || existingFm.description) {
|
|
1620
|
+
meta = {};
|
|
1621
|
+
if (existingFm.name) meta.name = existingFm.name;
|
|
1622
|
+
if (existingFm.description) meta.description = existingFm.description;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
const sourceFm = parseFrontMatter(raw);
|
|
1627
|
+
if (sourceFm) {
|
|
1628
|
+
if (sourceFm.inclusion === "auto" || sourceFm.inclusion === "manual" || sourceFm.inclusion === "fileMatch") {
|
|
1629
|
+
inclusion = "auto";
|
|
1630
|
+
}
|
|
1631
|
+
if (sourceFm.name || sourceFm.description) {
|
|
1632
|
+
meta = meta || {};
|
|
1633
|
+
if (sourceFm.name) meta.name = sourceFm.name;
|
|
1634
|
+
if (sourceFm.description) meta.description = sourceFm.description;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
const kiroSteering = buildKiroSteeringContent(content, inclusion, meta);
|
|
1638
|
+
await writeFile2(destPath, kiroSteering, "utf-8");
|
|
1576
1639
|
}
|
|
1577
1640
|
if (mdFiles.length > 0) {
|
|
1578
1641
|
console.log(chalk2.green(` Copied ${mdFiles.length} steering files to .kiro/steering/`));
|
|
@@ -1631,8 +1694,11 @@ async function generateKiro(config, hubDir) {
|
|
|
1631
1694
|
mcpConfig[mcp.name] = buildKiroMcpEntry(mcp, mode);
|
|
1632
1695
|
}
|
|
1633
1696
|
}
|
|
1697
|
+
const mcpJsonPath = join3(settingsDir, "mcp.json");
|
|
1698
|
+
const disabledState = await readExistingMcpDisabledState(mcpJsonPath);
|
|
1699
|
+
applyDisabledState(mcpConfig, disabledState);
|
|
1634
1700
|
await writeFile2(
|
|
1635
|
-
|
|
1701
|
+
mcpJsonPath,
|
|
1636
1702
|
JSON.stringify({ mcpServers: mcpConfig }, null, 2) + "\n",
|
|
1637
1703
|
"utf-8"
|
|
1638
1704
|
);
|
|
@@ -1812,8 +1878,21 @@ async function resolveEditor(opts) {
|
|
|
1812
1878
|
]);
|
|
1813
1879
|
return editor;
|
|
1814
1880
|
}
|
|
1815
|
-
var generateCommand = new Command("generate").description("Generate editor-specific configuration files from hub.yaml").option("-e, --editor <editor>", "Target editor (cursor, claude-code, kiro, opencode)").option("--reset-editor", "Reset saved editor preference and choose again").action(async (opts) => {
|
|
1881
|
+
var generateCommand = new Command("generate").description("Generate editor-specific configuration files from hub.yaml").option("-e, --editor <editor>", "Target editor (cursor, claude-code, kiro, opencode)").option("--reset-editor", "Reset saved editor preference and choose again").option("--check", "Check if generated configs are outdated (exit code 1 if outdated)").action(async (opts) => {
|
|
1816
1882
|
const hubDir = process.cwd();
|
|
1883
|
+
if (opts.check) {
|
|
1884
|
+
const result = await checkOutdated(hubDir);
|
|
1885
|
+
if (result.reason === "no-previous-generate") {
|
|
1886
|
+
console.log(chalk2.yellow("No previous generate found. Run 'hub generate' first."));
|
|
1887
|
+
process.exit(1);
|
|
1888
|
+
}
|
|
1889
|
+
if (result.outdated) {
|
|
1890
|
+
console.log(chalk2.yellow("Generated configs are outdated. Run 'hub generate' to update."));
|
|
1891
|
+
process.exit(1);
|
|
1892
|
+
}
|
|
1893
|
+
console.log(chalk2.green("Generated configs are up to date."));
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1817
1896
|
const config = await loadHubConfig(hubDir);
|
|
1818
1897
|
if (config.memory) {
|
|
1819
1898
|
const hasMemoryMcp = config.mcps?.some(
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
checkAndAutoRegenerate,
|
|
4
4
|
generateCommand,
|
|
5
5
|
loadHubConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VMSQC56H.js";
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { Command as Command19 } from "commander";
|
|
@@ -3021,6 +3021,34 @@ async function findUnsyncedAssets(hubDir) {
|
|
|
3021
3021
|
} catch {
|
|
3022
3022
|
}
|
|
3023
3023
|
}
|
|
3024
|
+
const hubYamlPath = join17(hubDir, "hub.yaml");
|
|
3025
|
+
if (existsSync14(hubYamlPath)) {
|
|
3026
|
+
const hubContent = await readFile8(hubYamlPath, "utf-8");
|
|
3027
|
+
const hubConfig = parse2(hubContent);
|
|
3028
|
+
const hubMcpNames = new Set((hubConfig.mcps || []).map((m) => m.name));
|
|
3029
|
+
const mcpConfigPaths = [
|
|
3030
|
+
{ path: join17(hubDir, ".cursor", "mcp.json"), source: ".cursor", key: "mcpServers" },
|
|
3031
|
+
{ path: join17(hubDir, ".kiro", "settings", "mcp.json"), source: ".kiro", key: "mcpServers" },
|
|
3032
|
+
{ path: join17(hubDir, ".mcp.json"), source: ".mcp.json", key: "mcpServers" },
|
|
3033
|
+
{ path: join17(hubDir, "opencode.json"), source: "opencode.json", key: "mcp" }
|
|
3034
|
+
];
|
|
3035
|
+
for (const { path: mcpPath, source, key } of mcpConfigPaths) {
|
|
3036
|
+
if (!existsSync14(mcpPath)) continue;
|
|
3037
|
+
try {
|
|
3038
|
+
const content = JSON.parse(await readFile8(mcpPath, "utf-8"));
|
|
3039
|
+
const servers = content[key];
|
|
3040
|
+
if (!servers) continue;
|
|
3041
|
+
for (const serverName of Object.keys(servers)) {
|
|
3042
|
+
if (hubMcpNames.has(serverName)) continue;
|
|
3043
|
+
const mcpKey = `mcp:${serverName}`;
|
|
3044
|
+
if (seen.has(mcpKey)) continue;
|
|
3045
|
+
seen.add(mcpKey);
|
|
3046
|
+
unsynced.push({ type: "mcp", name: serverName, source });
|
|
3047
|
+
}
|
|
3048
|
+
} catch {
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3024
3052
|
return unsynced;
|
|
3025
3053
|
}
|
|
3026
3054
|
function stripFrontMatter(content) {
|
|
@@ -3030,6 +3058,10 @@ function stripFrontMatter(content) {
|
|
|
3030
3058
|
}
|
|
3031
3059
|
async function syncAssets(hubDir, assets) {
|
|
3032
3060
|
for (const asset of assets) {
|
|
3061
|
+
if (asset.type === "mcp") {
|
|
3062
|
+
console.log(chalk18.yellow(` MCP '${asset.name}' found in ${asset.source} but not in hub.yaml \u2014 add it manually.`));
|
|
3063
|
+
continue;
|
|
3064
|
+
}
|
|
3033
3065
|
if (asset.type === "skill") {
|
|
3034
3066
|
const src = join17(hubDir, asset.source, "skills", asset.name);
|
|
3035
3067
|
const dest = join17(hubDir, "skills", asset.name);
|
|
@@ -3070,7 +3102,7 @@ async function syncAssets(hubDir, assets) {
|
|
|
3070
3102
|
}
|
|
3071
3103
|
}
|
|
3072
3104
|
}
|
|
3073
|
-
var scanCommand = new Command18("scan").description("Detect git repositories not registered in hub.yaml").option("-y, --yes", "Auto-add all found repos without prompting").action(async (opts) => {
|
|
3105
|
+
var scanCommand = new Command18("scan").description("Detect git repositories not registered in hub.yaml").option("-y, --yes", "Auto-add all found repos without prompting").option("--check", "Check for unsynced assets without prompting (exit code 1 if found)").action(async (opts) => {
|
|
3074
3106
|
const hubDir = process.cwd();
|
|
3075
3107
|
const configPath = join17(hubDir, "hub.yaml");
|
|
3076
3108
|
if (!existsSync14(configPath)) {
|
|
@@ -3079,6 +3111,23 @@ var scanCommand = new Command18("scan").description("Detect git repositories not
|
|
|
3079
3111
|
}
|
|
3080
3112
|
const content = await readFile8(configPath, "utf-8");
|
|
3081
3113
|
const config = parse2(content);
|
|
3114
|
+
if (opts.check) {
|
|
3115
|
+
const unregistered2 = await findUnregisteredRepos(hubDir, config);
|
|
3116
|
+
const unsyncedAssets2 = await findUnsyncedAssets(hubDir);
|
|
3117
|
+
const total = unregistered2.length + unsyncedAssets2.length;
|
|
3118
|
+
if (total > 0) {
|
|
3119
|
+
if (unregistered2.length > 0) {
|
|
3120
|
+
console.log(chalk18.yellow(`Found ${unregistered2.length} unregistered repo(s): ${unregistered2.join(", ")}`));
|
|
3121
|
+
}
|
|
3122
|
+
if (unsyncedAssets2.length > 0) {
|
|
3123
|
+
console.log(chalk18.yellow(`Found ${unsyncedAssets2.length} unsynced asset(s): ${unsyncedAssets2.map((a) => a.name).join(", ")}`));
|
|
3124
|
+
}
|
|
3125
|
+
console.log(chalk18.yellow("Run 'hub scan' to sync."));
|
|
3126
|
+
process.exit(1);
|
|
3127
|
+
}
|
|
3128
|
+
console.log(chalk18.green("All repos and assets are synced."));
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3082
3131
|
let hasChanges = false;
|
|
3083
3132
|
console.log(chalk18.blue("\nScanning for unregistered repositories...\n"));
|
|
3084
3133
|
const unregistered = await findUnregisteredRepos(hubDir, config);
|
|
@@ -3126,7 +3175,7 @@ var scanCommand = new Command18("scan").description("Detect git repositories not
|
|
|
3126
3175
|
hasChanges = true;
|
|
3127
3176
|
}
|
|
3128
3177
|
}
|
|
3129
|
-
console.log(chalk18.blue("\nScanning for unsynced skills, agents, and
|
|
3178
|
+
console.log(chalk18.blue("\nScanning for unsynced skills, agents, steering, and MCPs...\n"));
|
|
3130
3179
|
const unsyncedAssets = await findUnsyncedAssets(hubDir);
|
|
3131
3180
|
if (unsyncedAssets.length === 0) {
|
|
3132
3181
|
console.log(chalk18.green("All assets are synced."));
|