@arvoretech/hub 0.7.7 → 0.8.0
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);
|
|
@@ -3126,7 +3158,7 @@ var scanCommand = new Command18("scan").description("Detect git repositories not
|
|
|
3126
3158
|
hasChanges = true;
|
|
3127
3159
|
}
|
|
3128
3160
|
}
|
|
3129
|
-
console.log(chalk18.blue("\nScanning for unsynced skills, agents, and
|
|
3161
|
+
console.log(chalk18.blue("\nScanning for unsynced skills, agents, steering, and MCPs...\n"));
|
|
3130
3162
|
const unsyncedAssets = await findUnsyncedAssets(hubDir);
|
|
3131
3163
|
if (unsyncedAssets.length === 0) {
|
|
3132
3164
|
console.log(chalk18.green("All assets are synced."));
|