allagents 1.11.4-next.1 → 1.11.6-next.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/dist/index.js +1978 -1623
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15693,13 +15693,14 @@ var init_esm = __esm(() => {
|
|
|
15693
15693
|
});
|
|
15694
15694
|
|
|
15695
15695
|
// src/core/git-errors.ts
|
|
15696
|
-
function classifyError(error, url) {
|
|
15696
|
+
function classifyError(error, url, timeoutMs = 60000) {
|
|
15697
15697
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15698
15698
|
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
15699
15699
|
const isAuthError = errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("Permission denied") || errorMessage.includes("Repository not found");
|
|
15700
15700
|
const isServerError = /returned error: 5\d\d/.test(errorMessage) || errorMessage.includes("Internal Server Error");
|
|
15701
15701
|
if (isTimeout) {
|
|
15702
|
-
|
|
15702
|
+
const timeoutSeconds = Math.round(timeoutMs / 1000);
|
|
15703
|
+
return new GitCloneError(`Clone timed out after ${timeoutSeconds}s for ${url}.
|
|
15703
15704
|
Check your network connection and repository access.
|
|
15704
15705
|
For SSH: ssh-add -l (to check loaded keys)
|
|
15705
15706
|
For HTTPS: Check your git credentials`, url, true, false);
|
|
@@ -15732,38 +15733,50 @@ var init_git_errors = __esm(() => {
|
|
|
15732
15733
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
15733
15734
|
import { tmpdir } from "node:os";
|
|
15734
15735
|
import { join, normalize as normalize2, resolve, sep } from "node:path";
|
|
15736
|
+
function createGit(baseDir) {
|
|
15737
|
+
return esm_default(baseDir, {
|
|
15738
|
+
timeout: { block: CLONE_TIMEOUT_MS },
|
|
15739
|
+
config: [
|
|
15740
|
+
"filter.lfs.required=false",
|
|
15741
|
+
"filter.lfs.smudge=",
|
|
15742
|
+
"filter.lfs.clean=",
|
|
15743
|
+
"filter.lfs.process="
|
|
15744
|
+
]
|
|
15745
|
+
}).env({
|
|
15746
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
15747
|
+
GIT_LFS_SKIP_SMUDGE: "1"
|
|
15748
|
+
});
|
|
15749
|
+
}
|
|
15735
15750
|
function gitHubUrl(owner, repo) {
|
|
15736
15751
|
return `https://github.com/${owner}/${repo}.git`;
|
|
15737
15752
|
}
|
|
15738
15753
|
async function cloneToTemp(url, ref) {
|
|
15739
15754
|
const tempDir = await mkdtemp(join(tmpdir(), "allagents-"));
|
|
15740
|
-
const git =
|
|
15755
|
+
const git = createGit();
|
|
15741
15756
|
const cloneOptions = ref ? ["--depth", "1", "--branch", ref] : ["--depth", "1"];
|
|
15742
15757
|
try {
|
|
15743
15758
|
await git.clone(url, tempDir, cloneOptions);
|
|
15744
15759
|
return tempDir;
|
|
15745
15760
|
} catch (error) {
|
|
15746
15761
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
15747
|
-
throw classifyError(error, url);
|
|
15762
|
+
throw classifyError(error, url, CLONE_TIMEOUT_MS);
|
|
15748
15763
|
}
|
|
15749
15764
|
}
|
|
15750
15765
|
async function cloneTo(url, dest, ref) {
|
|
15751
|
-
const git =
|
|
15766
|
+
const git = createGit();
|
|
15752
15767
|
const cloneOptions = ref ? ["--depth", "1", "--branch", ref] : ["--depth", "1"];
|
|
15753
15768
|
try {
|
|
15754
15769
|
await git.clone(url, dest, cloneOptions);
|
|
15755
15770
|
} catch (error) {
|
|
15756
|
-
throw classifyError(error, url);
|
|
15771
|
+
throw classifyError(error, url, CLONE_TIMEOUT_MS);
|
|
15757
15772
|
}
|
|
15758
15773
|
}
|
|
15759
15774
|
async function pull(repoPath) {
|
|
15760
|
-
const git =
|
|
15761
|
-
timeout: { block: CLONE_TIMEOUT_MS }
|
|
15762
|
-
});
|
|
15775
|
+
const git = createGit(repoPath);
|
|
15763
15776
|
await git.pull();
|
|
15764
15777
|
}
|
|
15765
15778
|
async function repoExists(url) {
|
|
15766
|
-
const git =
|
|
15779
|
+
const git = createGit();
|
|
15767
15780
|
try {
|
|
15768
15781
|
await git.listRemote([url]);
|
|
15769
15782
|
return true;
|
|
@@ -15772,7 +15785,7 @@ async function repoExists(url) {
|
|
|
15772
15785
|
}
|
|
15773
15786
|
}
|
|
15774
15787
|
async function refExists(url, ref) {
|
|
15775
|
-
const git =
|
|
15788
|
+
const git = createGit();
|
|
15776
15789
|
try {
|
|
15777
15790
|
const result = await git.listRemote([
|
|
15778
15791
|
"--refs",
|
|
@@ -15792,10 +15805,17 @@ async function cleanupTempDir(dir) {
|
|
|
15792
15805
|
}
|
|
15793
15806
|
await rm(dir, { recursive: true, force: true });
|
|
15794
15807
|
}
|
|
15795
|
-
var
|
|
15808
|
+
var DEFAULT_CLONE_TIMEOUT_MS = 300000, CLONE_TIMEOUT_MS;
|
|
15796
15809
|
var init_git = __esm(() => {
|
|
15797
15810
|
init_esm();
|
|
15798
15811
|
init_git_errors();
|
|
15812
|
+
CLONE_TIMEOUT_MS = (() => {
|
|
15813
|
+
const raw = process.env.ALLAGENTS_CLONE_TIMEOUT_MS;
|
|
15814
|
+
if (!raw)
|
|
15815
|
+
return DEFAULT_CLONE_TIMEOUT_MS;
|
|
15816
|
+
const parsed = Number.parseInt(raw, 10);
|
|
15817
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
15818
|
+
})();
|
|
15799
15819
|
});
|
|
15800
15820
|
|
|
15801
15821
|
// src/utils/plugin-path.ts
|
|
@@ -25580,794 +25600,939 @@ var init_skill = __esm(() => {
|
|
|
25580
25600
|
import_gray_matter = __toESM(require_gray_matter(), 1);
|
|
25581
25601
|
});
|
|
25582
25602
|
|
|
25583
|
-
// src/
|
|
25584
|
-
|
|
25603
|
+
// src/models/marketplace-manifest.ts
|
|
25604
|
+
var UrlSourceSchema, GitHubSourceSchema, PluginSourceRefSchema, ContactSchema, LspServerSchema, MarketplacePluginEntrySchema, MarketplaceManifestSchema, MarketplaceManifestLenientSchema;
|
|
25605
|
+
var init_marketplace_manifest = __esm(() => {
|
|
25606
|
+
init_zod();
|
|
25607
|
+
UrlSourceSchema = exports_external.object({
|
|
25608
|
+
source: exports_external.literal("url"),
|
|
25609
|
+
url: exports_external.string().url()
|
|
25610
|
+
});
|
|
25611
|
+
GitHubSourceSchema = exports_external.object({
|
|
25612
|
+
source: exports_external.literal("github"),
|
|
25613
|
+
repo: exports_external.string().min(1)
|
|
25614
|
+
}).transform((val) => ({
|
|
25615
|
+
source: "url",
|
|
25616
|
+
url: `https://github.com/${val.repo}`
|
|
25617
|
+
}));
|
|
25618
|
+
PluginSourceRefSchema = exports_external.union([exports_external.string(), UrlSourceSchema, GitHubSourceSchema]);
|
|
25619
|
+
ContactSchema = exports_external.object({
|
|
25620
|
+
name: exports_external.string(),
|
|
25621
|
+
email: exports_external.string().optional()
|
|
25622
|
+
});
|
|
25623
|
+
LspServerSchema = exports_external.object({
|
|
25624
|
+
command: exports_external.string(),
|
|
25625
|
+
args: exports_external.array(exports_external.string()).optional(),
|
|
25626
|
+
extensionToLanguage: exports_external.record(exports_external.string()).optional(),
|
|
25627
|
+
startupTimeout: exports_external.number().optional()
|
|
25628
|
+
});
|
|
25629
|
+
MarketplacePluginEntrySchema = exports_external.object({
|
|
25630
|
+
name: exports_external.string().min(1),
|
|
25631
|
+
description: exports_external.string().min(1),
|
|
25632
|
+
source: PluginSourceRefSchema,
|
|
25633
|
+
version: exports_external.string().optional(),
|
|
25634
|
+
author: ContactSchema.optional(),
|
|
25635
|
+
category: exports_external.string().optional(),
|
|
25636
|
+
homepage: exports_external.string().optional(),
|
|
25637
|
+
strict: exports_external.boolean().optional(),
|
|
25638
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
25639
|
+
skills: exports_external.array(exports_external.string()).optional(),
|
|
25640
|
+
lspServers: exports_external.record(LspServerSchema).optional()
|
|
25641
|
+
});
|
|
25642
|
+
MarketplaceManifestSchema = exports_external.object({
|
|
25643
|
+
$schema: exports_external.string().optional(),
|
|
25644
|
+
name: exports_external.string().min(1),
|
|
25645
|
+
version: exports_external.string().optional(),
|
|
25646
|
+
description: exports_external.string().min(1),
|
|
25647
|
+
owner: ContactSchema.optional(),
|
|
25648
|
+
plugins: exports_external.array(MarketplacePluginEntrySchema)
|
|
25649
|
+
});
|
|
25650
|
+
MarketplaceManifestLenientSchema = exports_external.object({
|
|
25651
|
+
name: exports_external.string().optional(),
|
|
25652
|
+
plugins: exports_external.array(exports_external.unknown())
|
|
25653
|
+
}).passthrough();
|
|
25654
|
+
});
|
|
25655
|
+
|
|
25656
|
+
// src/utils/marketplace-manifest-parser.ts
|
|
25657
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
25585
25658
|
import { existsSync as existsSync4 } from "node:fs";
|
|
25586
|
-
import { join as join7,
|
|
25587
|
-
async function
|
|
25588
|
-
const
|
|
25589
|
-
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
|
|
25594
|
-
return;
|
|
25659
|
+
import { join as join7, resolve as resolve5 } from "node:path";
|
|
25660
|
+
async function parseMarketplaceManifest(marketplacePath) {
|
|
25661
|
+
const manifestPath = join7(marketplacePath, MANIFEST_PATH);
|
|
25662
|
+
if (!existsSync4(manifestPath)) {
|
|
25663
|
+
return {
|
|
25664
|
+
success: false,
|
|
25665
|
+
error: `Marketplace manifest not found: ${manifestPath}`
|
|
25666
|
+
};
|
|
25595
25667
|
}
|
|
25596
|
-
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
|
|
25601
|
-
|
|
25602
|
-
|
|
25603
|
-
|
|
25604
|
-
await writeFile(filePath, content + rulesContent, "utf-8");
|
|
25668
|
+
let raw;
|
|
25669
|
+
try {
|
|
25670
|
+
raw = await readFile3(manifestPath, "utf-8");
|
|
25671
|
+
} catch (err) {
|
|
25672
|
+
return {
|
|
25673
|
+
success: false,
|
|
25674
|
+
error: `Failed to read marketplace manifest: ${err instanceof Error ? err.message : String(err)}`
|
|
25675
|
+
};
|
|
25605
25676
|
}
|
|
25677
|
+
let json2;
|
|
25678
|
+
try {
|
|
25679
|
+
json2 = JSON.parse(raw);
|
|
25680
|
+
} catch {
|
|
25681
|
+
return {
|
|
25682
|
+
success: false,
|
|
25683
|
+
error: "Failed to parse marketplace.json as JSON: invalid syntax"
|
|
25684
|
+
};
|
|
25685
|
+
}
|
|
25686
|
+
const strictResult = MarketplaceManifestSchema.safeParse(json2);
|
|
25687
|
+
if (strictResult.success) {
|
|
25688
|
+
return { success: true, data: strictResult.data, warnings: [] };
|
|
25689
|
+
}
|
|
25690
|
+
return parseLeniently(json2);
|
|
25606
25691
|
}
|
|
25607
|
-
function
|
|
25608
|
-
|
|
25609
|
-
|
|
25610
|
-
|
|
25611
|
-
|
|
25612
|
-
|
|
25613
|
-
|
|
25614
|
-
|
|
25615
|
-
const
|
|
25616
|
-
|
|
25617
|
-
|
|
25618
|
-
|
|
25619
|
-
|
|
25692
|
+
function parseLeniently(json2) {
|
|
25693
|
+
const lenientResult = MarketplaceManifestLenientSchema.safeParse(json2);
|
|
25694
|
+
if (!lenientResult.success) {
|
|
25695
|
+
return {
|
|
25696
|
+
success: false,
|
|
25697
|
+
error: 'Marketplace manifest must contain a "plugins" array'
|
|
25698
|
+
};
|
|
25699
|
+
}
|
|
25700
|
+
const raw = lenientResult.data;
|
|
25701
|
+
const warnings = [];
|
|
25702
|
+
const obj = json2;
|
|
25703
|
+
const validPlugins = [];
|
|
25704
|
+
for (let i2 = 0;i2 < raw.plugins.length; i2++) {
|
|
25705
|
+
const entry = raw.plugins[i2];
|
|
25706
|
+
const entryResult = MarketplacePluginEntrySchema.safeParse(entry);
|
|
25707
|
+
if (entryResult.success) {
|
|
25708
|
+
validPlugins.push(entryResult.data);
|
|
25620
25709
|
continue;
|
|
25621
25710
|
}
|
|
25622
|
-
|
|
25623
|
-
|
|
25624
|
-
|
|
25625
|
-
await cp(sourcePath, destPath);
|
|
25711
|
+
const extracted = extractPluginEntry(entry, i2, warnings);
|
|
25712
|
+
if (extracted) {
|
|
25713
|
+
validPlugins.push(extracted);
|
|
25626
25714
|
}
|
|
25627
25715
|
}
|
|
25716
|
+
const data = {
|
|
25717
|
+
name: typeof raw.name === "string" ? raw.name : "unknown",
|
|
25718
|
+
description: typeof obj.description === "string" ? obj.description : "",
|
|
25719
|
+
plugins: validPlugins
|
|
25720
|
+
};
|
|
25721
|
+
return { success: true, data, warnings };
|
|
25628
25722
|
}
|
|
25629
|
-
function
|
|
25630
|
-
|
|
25631
|
-
}
|
|
25632
|
-
|
|
25633
|
-
const { dryRun = false } = options2;
|
|
25634
|
-
const mapping = getMapping(client, options2);
|
|
25635
|
-
const results = [];
|
|
25636
|
-
if (!mapping.commandsPath) {
|
|
25637
|
-
return results;
|
|
25638
|
-
}
|
|
25639
|
-
const sourceDir = join7(pluginPath, "commands");
|
|
25640
|
-
if (!existsSync4(sourceDir)) {
|
|
25641
|
-
return results;
|
|
25642
|
-
}
|
|
25643
|
-
const destDir = join7(workspacePath, mapping.commandsPath);
|
|
25644
|
-
if (!dryRun) {
|
|
25645
|
-
await mkdir3(destDir, { recursive: true });
|
|
25723
|
+
function extractPluginEntry(entry, index, warnings) {
|
|
25724
|
+
if (!entry || typeof entry !== "object") {
|
|
25725
|
+
warnings.push(`plugins[${index}]: not an object, skipped`);
|
|
25726
|
+
return null;
|
|
25646
25727
|
}
|
|
25647
|
-
const
|
|
25648
|
-
const
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
if (dryRun) {
|
|
25653
|
-
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
25654
|
-
}
|
|
25655
|
-
try {
|
|
25656
|
-
const content = await readFile3(sourcePath, "utf-8");
|
|
25657
|
-
await writeFile(destPath, content, "utf-8");
|
|
25658
|
-
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
25659
|
-
} catch (error) {
|
|
25660
|
-
return {
|
|
25661
|
-
source: sourcePath,
|
|
25662
|
-
destination: destPath,
|
|
25663
|
-
action: "failed",
|
|
25664
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
25665
|
-
};
|
|
25666
|
-
}
|
|
25667
|
-
});
|
|
25668
|
-
return Promise.all(copyPromises);
|
|
25669
|
-
}
|
|
25670
|
-
async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
|
|
25671
|
-
const { dryRun = false, skillNameMap, syncMode = "copy", canonicalSkillsPath } = options2;
|
|
25672
|
-
const mapping = getMapping(client, options2);
|
|
25673
|
-
const results = [];
|
|
25674
|
-
if (!mapping.skillsPath) {
|
|
25675
|
-
return results;
|
|
25728
|
+
const obj = entry;
|
|
25729
|
+
const name = typeof obj.name === "string" && obj.name ? obj.name : undefined;
|
|
25730
|
+
if (!name) {
|
|
25731
|
+
warnings.push(`plugins[${index}]: missing "name" field, skipped`);
|
|
25732
|
+
return null;
|
|
25676
25733
|
}
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25734
|
+
let description = "";
|
|
25735
|
+
if (typeof obj.description === "string" && obj.description) {
|
|
25736
|
+
description = obj.description;
|
|
25737
|
+
} else if (obj.metadata && typeof obj.metadata === "object" && typeof obj.metadata.description === "string") {
|
|
25738
|
+
description = obj.metadata.description;
|
|
25739
|
+
warnings.push(`plugins[${index}] ("${name}"): "description" found in metadata instead of top level`);
|
|
25682
25740
|
} else {
|
|
25683
|
-
|
|
25684
|
-
const flatSkills = [];
|
|
25685
|
-
for (const entry of entries) {
|
|
25686
|
-
if (!entry.isDirectory())
|
|
25687
|
-
continue;
|
|
25688
|
-
const skillMdPath = join7(pluginPath, entry.name, "SKILL.md");
|
|
25689
|
-
if (existsSync4(skillMdPath)) {
|
|
25690
|
-
flatSkills.push({ name: entry.name, sourcePath: join7(pluginPath, entry.name), isRootLevel: false });
|
|
25691
|
-
}
|
|
25692
|
-
}
|
|
25693
|
-
if (flatSkills.length > 0) {
|
|
25694
|
-
skillSources = flatSkills;
|
|
25695
|
-
} else {
|
|
25696
|
-
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
25697
|
-
if (existsSync4(rootSkillMd)) {
|
|
25698
|
-
const content = await readFile3(rootSkillMd, "utf-8");
|
|
25699
|
-
const metadata = parseSkillMetadata(content);
|
|
25700
|
-
const skillName = metadata?.name ?? basename2(pluginPath);
|
|
25701
|
-
skillSources = [{ name: skillName, sourcePath: pluginPath, isRootLevel: true }];
|
|
25702
|
-
} else {
|
|
25703
|
-
return results;
|
|
25704
|
-
}
|
|
25705
|
-
}
|
|
25741
|
+
warnings.push(`plugins[${index}] ("${name}"): missing "description" field`);
|
|
25706
25742
|
}
|
|
25707
|
-
|
|
25708
|
-
|
|
25743
|
+
let source = "";
|
|
25744
|
+
const sourceResult = PluginSourceRefSchema.safeParse(obj.source);
|
|
25745
|
+
if (sourceResult.success) {
|
|
25746
|
+
source = sourceResult.data;
|
|
25747
|
+
} else {
|
|
25748
|
+
warnings.push(`plugins[${index}] ("${name}"): missing or invalid "source" field`);
|
|
25709
25749
|
}
|
|
25710
|
-
|
|
25711
|
-
|
|
25750
|
+
return {
|
|
25751
|
+
name,
|
|
25752
|
+
description,
|
|
25753
|
+
source,
|
|
25754
|
+
...typeof obj.version === "string" && { version: obj.version },
|
|
25755
|
+
...typeof obj.category === "string" && { category: obj.category },
|
|
25756
|
+
...typeof obj.homepage === "string" && { homepage: obj.homepage },
|
|
25757
|
+
...Array.isArray(obj.skills) && obj.skills.every((s) => typeof s === "string") && { skills: obj.skills }
|
|
25758
|
+
};
|
|
25759
|
+
}
|
|
25760
|
+
function resolvePluginSourcePath(source, marketplacePath) {
|
|
25761
|
+
if (typeof source === "object") {
|
|
25762
|
+
return source.url;
|
|
25712
25763
|
}
|
|
25713
|
-
|
|
25714
|
-
|
|
25715
|
-
|
|
25764
|
+
return resolve5(marketplacePath, source);
|
|
25765
|
+
}
|
|
25766
|
+
var MANIFEST_PATH = ".claude-plugin/marketplace.json";
|
|
25767
|
+
var init_marketplace_manifest_parser = __esm(() => {
|
|
25768
|
+
init_marketplace_manifest();
|
|
25769
|
+
});
|
|
25770
|
+
|
|
25771
|
+
// src/core/workspace-modify.ts
|
|
25772
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
25773
|
+
import { mkdir as mkdir3, readFile as readFile4, writeFile } from "node:fs/promises";
|
|
25774
|
+
import { join as join8 } from "node:path";
|
|
25775
|
+
async function setClients(clients, workspacePath = process.cwd()) {
|
|
25776
|
+
try {
|
|
25777
|
+
await ensureWorkspace(workspacePath);
|
|
25778
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
25779
|
+
const content = await readFile4(configPath, "utf-8");
|
|
25780
|
+
const config = load(content);
|
|
25781
|
+
config.clients = clients;
|
|
25782
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
25783
|
+
return { success: true };
|
|
25784
|
+
} catch (error) {
|
|
25785
|
+
return {
|
|
25786
|
+
success: false,
|
|
25787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25788
|
+
};
|
|
25716
25789
|
}
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25790
|
+
}
|
|
25791
|
+
async function ensureWorkspace(workspacePath, clients) {
|
|
25792
|
+
const configDir = join8(workspacePath, CONFIG_DIR);
|
|
25793
|
+
const configPath = join8(configDir, WORKSPACE_CONFIG_FILE);
|
|
25794
|
+
if (existsSync5(configPath))
|
|
25795
|
+
return;
|
|
25796
|
+
const defaultConfig = {
|
|
25797
|
+
repositories: [],
|
|
25798
|
+
plugins: [],
|
|
25799
|
+
clients: clients ?? [...DEFAULT_PROJECT_CLIENTS]
|
|
25800
|
+
};
|
|
25801
|
+
await mkdir3(configDir, { recursive: true });
|
|
25802
|
+
await writeFile(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
|
|
25803
|
+
}
|
|
25804
|
+
async function addPlugin(plugin, workspacePath = process.cwd(), force) {
|
|
25805
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
25806
|
+
await ensureWorkspace(workspacePath);
|
|
25807
|
+
if (isPluginSpec(plugin)) {
|
|
25808
|
+
const resolved = await resolvePluginSpecWithAutoRegister(plugin, { workspacePath });
|
|
25809
|
+
if (!resolved.success) {
|
|
25722
25810
|
return {
|
|
25723
|
-
|
|
25724
|
-
|
|
25725
|
-
action: "copied"
|
|
25811
|
+
success: false,
|
|
25812
|
+
error: resolved.error || "Unknown error"
|
|
25726
25813
|
};
|
|
25727
25814
|
}
|
|
25728
|
-
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
}
|
|
25815
|
+
return await addPluginToConfig(resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin, configPath, resolved.registeredAs, force);
|
|
25816
|
+
}
|
|
25817
|
+
if (isGitHubUrl(plugin)) {
|
|
25818
|
+
const validation = validatePluginSource(plugin);
|
|
25819
|
+
if (!validation.valid) {
|
|
25820
|
+
return {
|
|
25821
|
+
success: false,
|
|
25822
|
+
error: validation.error || "Invalid GitHub URL"
|
|
25823
|
+
};
|
|
25738
25824
|
}
|
|
25739
|
-
|
|
25740
|
-
|
|
25741
|
-
await mkdir3(skillDestPath, { recursive: true });
|
|
25742
|
-
await cp(join7(skill.sourcePath, "SKILL.md"), join7(skillDestPath, "SKILL.md"));
|
|
25743
|
-
} else if (options2.exclude && options2.exclude.length > 0) {
|
|
25744
|
-
await copyDirectoryWithExclusions(skill.sourcePath, skillDestPath, pluginPath, options2.exclude);
|
|
25745
|
-
} else {
|
|
25746
|
-
await cp(skill.sourcePath, skillDestPath, { recursive: true });
|
|
25747
|
-
}
|
|
25825
|
+
const verifyResult = await verifyGitHubUrlExists(plugin);
|
|
25826
|
+
if (!verifyResult.exists) {
|
|
25748
25827
|
return {
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
action: "copied"
|
|
25828
|
+
success: false,
|
|
25829
|
+
error: verifyResult.error || `GitHub URL not found: ${plugin}`
|
|
25752
25830
|
};
|
|
25753
|
-
}
|
|
25831
|
+
}
|
|
25832
|
+
} else {
|
|
25833
|
+
const fullPath = join8(workspacePath, plugin);
|
|
25834
|
+
if (!existsSync5(fullPath) && !existsSync5(plugin)) {
|
|
25754
25835
|
return {
|
|
25755
|
-
|
|
25756
|
-
|
|
25757
|
-
action: "failed",
|
|
25758
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
25836
|
+
success: false,
|
|
25837
|
+
error: `Plugin not found at ${plugin}`
|
|
25759
25838
|
};
|
|
25760
25839
|
}
|
|
25761
|
-
}
|
|
25762
|
-
return
|
|
25840
|
+
}
|
|
25841
|
+
return await addPluginToConfig(plugin, configPath, undefined, force);
|
|
25763
25842
|
}
|
|
25764
|
-
async function
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
|
|
25768
|
-
|
|
25769
|
-
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
if (!entry.isDirectory())
|
|
25776
|
-
continue;
|
|
25777
|
-
const skillMdPath = join7(pluginPath, entry.name, "SKILL.md");
|
|
25778
|
-
if (existsSync4(skillMdPath)) {
|
|
25779
|
-
flatDirs.push({ name: entry.name, path: join7(pluginPath, entry.name) });
|
|
25843
|
+
async function addPluginToConfig(plugin, configPath, autoRegistered, force) {
|
|
25844
|
+
try {
|
|
25845
|
+
const content = await readFile4(configPath, "utf-8");
|
|
25846
|
+
const config = load(content);
|
|
25847
|
+
const existingExactIndex = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
|
|
25848
|
+
if (existingExactIndex !== -1) {
|
|
25849
|
+
if (!force) {
|
|
25850
|
+
return {
|
|
25851
|
+
success: false,
|
|
25852
|
+
error: `Plugin already exists in .allagents/workspace.yaml: ${plugin}`
|
|
25853
|
+
};
|
|
25780
25854
|
}
|
|
25781
25855
|
}
|
|
25782
|
-
if (
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25856
|
+
if (!force) {
|
|
25857
|
+
const newIdentity = await resolveGitHubIdentity(plugin);
|
|
25858
|
+
if (newIdentity) {
|
|
25859
|
+
for (const existing of config.plugins) {
|
|
25860
|
+
const existingSource = getPluginSource(existing);
|
|
25861
|
+
const existingIdentity = await resolveGitHubIdentity(existingSource);
|
|
25862
|
+
if (existingIdentity === newIdentity) {
|
|
25863
|
+
return {
|
|
25864
|
+
success: false,
|
|
25865
|
+
error: `Plugin duplicates existing entry '${existingSource}': both resolve to ${newIdentity}`
|
|
25866
|
+
};
|
|
25867
|
+
}
|
|
25868
|
+
}
|
|
25793
25869
|
}
|
|
25794
25870
|
}
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
if (Array.isArray(pluginSkillsConfig)) {
|
|
25799
|
-
filteredDirs = candidateDirs.filter((e) => pluginSkillsConfig.includes(e.name));
|
|
25800
|
-
} else {
|
|
25801
|
-
filteredDirs = candidateDirs.filter((e) => !pluginSkillsConfig.exclude.includes(e.name));
|
|
25871
|
+
const wasReplaced = force && existingExactIndex !== -1;
|
|
25872
|
+
if (wasReplaced) {
|
|
25873
|
+
config.plugins.splice(existingExactIndex, 1);
|
|
25802
25874
|
}
|
|
25803
|
-
|
|
25804
|
-
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25875
|
+
config.plugins.push(plugin);
|
|
25876
|
+
const newContent = dump(config, { lineWidth: -1 });
|
|
25877
|
+
await writeFile(configPath, newContent, "utf-8");
|
|
25878
|
+
const result = {
|
|
25879
|
+
success: true,
|
|
25880
|
+
normalizedPlugin: plugin
|
|
25881
|
+
};
|
|
25882
|
+
if (autoRegistered) {
|
|
25883
|
+
result.autoRegistered = autoRegistered;
|
|
25810
25884
|
}
|
|
25811
|
-
|
|
25812
|
-
|
|
25885
|
+
if (wasReplaced) {
|
|
25886
|
+
result.replaced = true;
|
|
25887
|
+
}
|
|
25888
|
+
return result;
|
|
25889
|
+
} catch (error) {
|
|
25890
|
+
return {
|
|
25891
|
+
success: false,
|
|
25892
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25893
|
+
};
|
|
25813
25894
|
}
|
|
25814
|
-
return filteredDirs.map((entry) => ({
|
|
25815
|
-
folderName: entry.name,
|
|
25816
|
-
skillPath: entry.path,
|
|
25817
|
-
pluginPath,
|
|
25818
|
-
pluginSource
|
|
25819
|
-
}));
|
|
25820
25895
|
}
|
|
25821
|
-
async function
|
|
25822
|
-
const
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
if (!mapping.hooksPath) {
|
|
25826
|
-
return results;
|
|
25827
|
-
}
|
|
25828
|
-
const sourceDir = join7(pluginPath, "hooks");
|
|
25829
|
-
if (!existsSync4(sourceDir)) {
|
|
25830
|
-
return results;
|
|
25831
|
-
}
|
|
25832
|
-
const destDir = join7(workspacePath, mapping.hooksPath);
|
|
25833
|
-
if (dryRun) {
|
|
25834
|
-
results.push({ source: sourceDir, destination: destDir, action: "copied" });
|
|
25835
|
-
return results;
|
|
25836
|
-
}
|
|
25837
|
-
await mkdir3(destDir, { recursive: true });
|
|
25896
|
+
async function hasPlugin(plugin, workspacePath = process.cwd()) {
|
|
25897
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
25898
|
+
if (!existsSync5(configPath))
|
|
25899
|
+
return false;
|
|
25838
25900
|
try {
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25901
|
+
const content = await readFile4(configPath, "utf-8");
|
|
25902
|
+
const config = load(content);
|
|
25903
|
+
if (config.plugins.some((entry) => getPluginSource(entry) === plugin))
|
|
25904
|
+
return true;
|
|
25905
|
+
if (!isPluginSpec(plugin)) {
|
|
25906
|
+
return config.plugins.some((entry) => {
|
|
25907
|
+
const source = getPluginSource(entry);
|
|
25908
|
+
return source.startsWith(`${plugin}@`) || source === plugin;
|
|
25909
|
+
});
|
|
25843
25910
|
}
|
|
25844
|
-
|
|
25845
|
-
} catch
|
|
25846
|
-
|
|
25847
|
-
source: sourceDir,
|
|
25848
|
-
destination: destDir,
|
|
25849
|
-
action: "failed",
|
|
25850
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
25851
|
-
});
|
|
25911
|
+
return false;
|
|
25912
|
+
} catch {
|
|
25913
|
+
return false;
|
|
25852
25914
|
}
|
|
25853
|
-
return results;
|
|
25854
25915
|
}
|
|
25855
|
-
async function
|
|
25856
|
-
const
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
if (!existsSync4(sourceDir)) {
|
|
25864
|
-
return results;
|
|
25865
|
-
}
|
|
25866
|
-
const destDir = join7(workspacePath, mapping.agentsPath);
|
|
25867
|
-
if (!dryRun) {
|
|
25868
|
-
await mkdir3(destDir, { recursive: true });
|
|
25916
|
+
async function removePlugin(plugin, workspacePath = process.cwd()) {
|
|
25917
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
25918
|
+
if (!existsSync5(configPath)) {
|
|
25919
|
+
return {
|
|
25920
|
+
success: false,
|
|
25921
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
|
|
25922
|
+
Run 'allagents workspace init <path>' to create a new workspace`
|
|
25923
|
+
};
|
|
25869
25924
|
}
|
|
25870
|
-
|
|
25871
|
-
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
25876
|
-
|
|
25925
|
+
try {
|
|
25926
|
+
const content = await readFile4(configPath, "utf-8");
|
|
25927
|
+
const config = load(content);
|
|
25928
|
+
let index = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
|
|
25929
|
+
if (index === -1 && isPluginSpec(plugin) === false) {
|
|
25930
|
+
index = config.plugins.findIndex((entry) => {
|
|
25931
|
+
const source = getPluginSource(entry);
|
|
25932
|
+
return source.startsWith(`${plugin}@`) || source === plugin;
|
|
25933
|
+
});
|
|
25877
25934
|
}
|
|
25878
|
-
|
|
25879
|
-
const
|
|
25880
|
-
|
|
25881
|
-
|
|
25882
|
-
|
|
25935
|
+
if (index === -1) {
|
|
25936
|
+
const identity = await resolveGitHubIdentity(plugin);
|
|
25937
|
+
if (identity) {
|
|
25938
|
+
for (let i2 = 0;i2 < config.plugins.length; i2++) {
|
|
25939
|
+
const p = config.plugins[i2];
|
|
25940
|
+
if (!p)
|
|
25941
|
+
continue;
|
|
25942
|
+
const existing = await resolveGitHubIdentity(getPluginSource(p));
|
|
25943
|
+
if (existing === identity) {
|
|
25944
|
+
index = i2;
|
|
25945
|
+
break;
|
|
25946
|
+
}
|
|
25947
|
+
}
|
|
25948
|
+
}
|
|
25949
|
+
}
|
|
25950
|
+
if (index === -1) {
|
|
25883
25951
|
return {
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
action: "failed",
|
|
25887
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
25952
|
+
success: false,
|
|
25953
|
+
error: `Plugin not found in .allagents/workspace.yaml: ${plugin}`
|
|
25888
25954
|
};
|
|
25889
25955
|
}
|
|
25890
|
-
|
|
25891
|
-
|
|
25956
|
+
const removedEntry = getPluginSource(config.plugins[index]);
|
|
25957
|
+
config.plugins.splice(index, 1);
|
|
25958
|
+
pruneDisabledSkillsForPlugin(config, removedEntry);
|
|
25959
|
+
pruneEnabledSkillsForPlugin(config, removedEntry);
|
|
25960
|
+
const newContent = dump(config, { lineWidth: -1 });
|
|
25961
|
+
await writeFile(configPath, newContent, "utf-8");
|
|
25962
|
+
return { success: true };
|
|
25963
|
+
} catch (error) {
|
|
25964
|
+
return {
|
|
25965
|
+
success: false,
|
|
25966
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25967
|
+
};
|
|
25968
|
+
}
|
|
25892
25969
|
}
|
|
25893
|
-
|
|
25894
|
-
|
|
25895
|
-
|
|
25896
|
-
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25902
|
-
|
|
25903
|
-
|
|
25904
|
-
|
|
25905
|
-
|
|
25906
|
-
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
await writeFile(destPath, content, "utf-8");
|
|
25914
|
-
} else {
|
|
25915
|
-
await cp(sourcePath, destPath);
|
|
25916
|
-
}
|
|
25970
|
+
function pruneDisabledSkillsForPlugin(config, pluginEntry) {
|
|
25971
|
+
if (!config.disabledSkills?.length)
|
|
25972
|
+
return;
|
|
25973
|
+
const names = extractPluginNames(pluginEntry);
|
|
25974
|
+
if (names.length === 0)
|
|
25975
|
+
return;
|
|
25976
|
+
const prefixes = names.map((n) => `${n}:`);
|
|
25977
|
+
config.disabledSkills = config.disabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
|
|
25978
|
+
if (config.disabledSkills.length === 0) {
|
|
25979
|
+
config.disabledSkills = undefined;
|
|
25980
|
+
}
|
|
25981
|
+
}
|
|
25982
|
+
function extractPluginNames(pluginSource) {
|
|
25983
|
+
if (isPluginSpec(pluginSource)) {
|
|
25984
|
+
const parsed = parsePluginSpec(pluginSource);
|
|
25985
|
+
if (!parsed)
|
|
25986
|
+
return [];
|
|
25987
|
+
const names = [parsed.plugin];
|
|
25988
|
+
if (parsed.marketplaceName && parsed.marketplaceName !== parsed.plugin) {
|
|
25989
|
+
names.push(parsed.marketplaceName);
|
|
25917
25990
|
}
|
|
25991
|
+
return names;
|
|
25918
25992
|
}
|
|
25993
|
+
if (isGitHubUrl(pluginSource)) {
|
|
25994
|
+
const parsed = parseGitHubUrl(pluginSource);
|
|
25995
|
+
if (parsed) {
|
|
25996
|
+
const names = [];
|
|
25997
|
+
const ownerRepo = `${parsed.owner}-${parsed.repo}`;
|
|
25998
|
+
if (ownerRepo !== parsed.repo)
|
|
25999
|
+
names.push(ownerRepo);
|
|
26000
|
+
if (parsed.subpath) {
|
|
26001
|
+
const subpathName = parsed.subpath.split("/").filter(Boolean).pop();
|
|
26002
|
+
if (subpathName && !names.includes(subpathName))
|
|
26003
|
+
names.push(subpathName);
|
|
26004
|
+
}
|
|
26005
|
+
if (!names.includes(parsed.repo))
|
|
26006
|
+
names.push(parsed.repo);
|
|
26007
|
+
if (!names.includes(ownerRepo))
|
|
26008
|
+
names.push(ownerRepo);
|
|
26009
|
+
return names;
|
|
26010
|
+
}
|
|
26011
|
+
}
|
|
26012
|
+
const parts = pluginSource.split(/[/\\]/).filter(Boolean);
|
|
26013
|
+
const last2 = parts[parts.length - 1];
|
|
26014
|
+
if (!last2)
|
|
26015
|
+
return [];
|
|
26016
|
+
return [last2.replace(/\.git$/, "")];
|
|
25919
26017
|
}
|
|
25920
|
-
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
|
|
26018
|
+
function findPluginEntryByName(config, pluginName) {
|
|
26019
|
+
return config.plugins.findIndex((entry) => extractPluginNames(getPluginSource(entry)).includes(pluginName));
|
|
26020
|
+
}
|
|
26021
|
+
function ensureObjectPluginEntry(config, index) {
|
|
26022
|
+
const entry = config.plugins[index];
|
|
26023
|
+
if (entry === undefined)
|
|
26024
|
+
throw new Error(`Plugin entry at index ${index} not found`);
|
|
26025
|
+
if (typeof entry === "string") {
|
|
26026
|
+
const objectEntry = { source: entry };
|
|
26027
|
+
config.plugins[index] = objectEntry;
|
|
26028
|
+
return objectEntry;
|
|
25926
26029
|
}
|
|
25927
|
-
|
|
25928
|
-
|
|
25929
|
-
|
|
26030
|
+
return entry;
|
|
26031
|
+
}
|
|
26032
|
+
function uniqueSkillNames(skillNames) {
|
|
26033
|
+
const unique = [];
|
|
26034
|
+
const seen = new Set;
|
|
26035
|
+
for (const skillName of skillNames) {
|
|
26036
|
+
if (seen.has(skillName))
|
|
26037
|
+
continue;
|
|
26038
|
+
seen.add(skillName);
|
|
26039
|
+
unique.push(skillName);
|
|
26040
|
+
}
|
|
26041
|
+
return unique;
|
|
26042
|
+
}
|
|
26043
|
+
function formatGitHubSource(parsed, styleSource) {
|
|
26044
|
+
const basePath = `${parsed.owner}/${parsed.repo}`;
|
|
26045
|
+
if (styleSource.startsWith("http://") || styleSource.startsWith("https://") || styleSource.startsWith("github.com/")) {
|
|
26046
|
+
const baseUrl = `https://github.com/${basePath}`;
|
|
26047
|
+
if (!parsed.branch)
|
|
26048
|
+
return baseUrl;
|
|
26049
|
+
return parsed.subpath ? `${baseUrl}/tree/${parsed.branch}/${parsed.subpath}` : `${baseUrl}/tree/${parsed.branch}`;
|
|
26050
|
+
}
|
|
26051
|
+
if (!parsed.branch) {
|
|
26052
|
+
return parsed.subpath ? `${basePath}/${parsed.subpath}` : basePath;
|
|
26053
|
+
}
|
|
26054
|
+
return parsed.subpath ? `${basePath}@${parsed.branch}/${parsed.subpath}` : `${basePath}@${parsed.branch}`;
|
|
26055
|
+
}
|
|
26056
|
+
function canonicalizeGitHubPluginSource(currentSource, nextSource) {
|
|
26057
|
+
const current = parseGitHubUrl(currentSource);
|
|
26058
|
+
const next = parseGitHubUrl(nextSource);
|
|
26059
|
+
if (!current || !next)
|
|
26060
|
+
return nextSource;
|
|
26061
|
+
if (current.owner.toLowerCase() !== next.owner.toLowerCase() || current.repo.toLowerCase() !== next.repo.toLowerCase()) {
|
|
26062
|
+
return nextSource;
|
|
26063
|
+
}
|
|
26064
|
+
if (current.branch && next.branch && current.branch !== next.branch) {
|
|
26065
|
+
return currentSource;
|
|
26066
|
+
}
|
|
26067
|
+
const currentParts = current.subpath?.split("/").filter(Boolean) ?? [];
|
|
26068
|
+
const nextParts = next.subpath?.split("/").filter(Boolean) ?? [];
|
|
26069
|
+
const sharedParts = [];
|
|
26070
|
+
const sharedLength = Math.min(currentParts.length, nextParts.length);
|
|
26071
|
+
for (let i2 = 0;i2 < sharedLength; i2++) {
|
|
26072
|
+
if (currentParts[i2] !== nextParts[i2])
|
|
26073
|
+
break;
|
|
26074
|
+
sharedParts.push(currentParts[i2]);
|
|
26075
|
+
}
|
|
26076
|
+
return formatGitHubSource({
|
|
26077
|
+
owner: current.owner,
|
|
26078
|
+
repo: current.repo,
|
|
26079
|
+
...current.branch || next.branch ? { branch: current.branch ?? next.branch } : {},
|
|
26080
|
+
...sharedParts.length > 0 ? { subpath: sharedParts.join("/") } : {}
|
|
26081
|
+
}, currentSource);
|
|
26082
|
+
}
|
|
26083
|
+
async function findPluginEntryByGitHubIdentity(config, source) {
|
|
26084
|
+
const identity = await resolveGitHubIdentity(source);
|
|
26085
|
+
if (!identity)
|
|
26086
|
+
return -1;
|
|
26087
|
+
for (let i2 = 0;i2 < config.plugins.length; i2++) {
|
|
26088
|
+
const entry = config.plugins[i2];
|
|
26089
|
+
if (!entry)
|
|
26090
|
+
continue;
|
|
26091
|
+
const existingIdentity = await resolveGitHubIdentity(getPluginSource(entry));
|
|
26092
|
+
if (existingIdentity === identity)
|
|
26093
|
+
return i2;
|
|
25930
26094
|
}
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
26095
|
+
return -1;
|
|
26096
|
+
}
|
|
26097
|
+
async function upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames) {
|
|
26098
|
+
const normalizedSkills = uniqueSkillNames(skillNames);
|
|
26099
|
+
const exactIndex = config.plugins.findIndex((entry2) => getPluginSource(entry2) === source);
|
|
26100
|
+
if (exactIndex !== -1) {
|
|
26101
|
+
const entry2 = ensureObjectPluginEntry(config, exactIndex);
|
|
26102
|
+
entry2.source = source;
|
|
26103
|
+
entry2.skills = normalizedSkills;
|
|
26104
|
+
return { success: true, normalizedPlugin: source };
|
|
26105
|
+
}
|
|
26106
|
+
const semanticIndex = await findPluginEntryByGitHubIdentity(config, source);
|
|
26107
|
+
if (semanticIndex === -1) {
|
|
26108
|
+
config.plugins.push({ source, skills: normalizedSkills });
|
|
26109
|
+
return { success: true, normalizedPlugin: source };
|
|
26110
|
+
}
|
|
26111
|
+
const entry = ensureObjectPluginEntry(config, semanticIndex);
|
|
26112
|
+
const normalizedSource = canonicalizeGitHubPluginSource(entry.source, source);
|
|
26113
|
+
entry.source = normalizedSource;
|
|
26114
|
+
entry.skills = normalizedSkills;
|
|
26115
|
+
return { success: true, normalizedPlugin: normalizedSource };
|
|
26116
|
+
}
|
|
26117
|
+
function parseSkillKey(skillKey) {
|
|
26118
|
+
const colonIdx = skillKey.indexOf(":");
|
|
26119
|
+
if (colonIdx === -1)
|
|
26120
|
+
return null;
|
|
26121
|
+
return {
|
|
26122
|
+
pluginName: skillKey.slice(0, colonIdx),
|
|
26123
|
+
skillName: skillKey.slice(colonIdx + 1)
|
|
26124
|
+
};
|
|
26125
|
+
}
|
|
26126
|
+
async function addDisabledSkill(skillKey, workspacePath = process.cwd()) {
|
|
26127
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26128
|
+
if (!existsSync5(configPath)) {
|
|
26129
|
+
return {
|
|
26130
|
+
success: false,
|
|
26131
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26132
|
+
};
|
|
25935
26133
|
}
|
|
26134
|
+
const parsed = parseSkillKey(skillKey);
|
|
26135
|
+
if (!parsed) {
|
|
26136
|
+
return {
|
|
26137
|
+
success: false,
|
|
26138
|
+
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
26139
|
+
};
|
|
26140
|
+
}
|
|
26141
|
+
const { pluginName, skillName } = parsed;
|
|
25936
26142
|
try {
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
|
|
26143
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26144
|
+
const config = load(content);
|
|
26145
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26146
|
+
if (index === -1) {
|
|
26147
|
+
return {
|
|
26148
|
+
success: false,
|
|
26149
|
+
error: `Plugin '${pluginName}' not found in workspace config`
|
|
26150
|
+
};
|
|
25941
26151
|
}
|
|
25942
|
-
|
|
26152
|
+
const entry = ensureObjectPluginEntry(config, index);
|
|
26153
|
+
if (Array.isArray(entry.skills)) {
|
|
26154
|
+
return {
|
|
26155
|
+
success: false,
|
|
26156
|
+
error: `Plugin '${pluginName}' is in allowlist mode; use removeEnabledSkill to disable a skill`
|
|
26157
|
+
};
|
|
26158
|
+
}
|
|
26159
|
+
const existing = entry.skills?.exclude ?? [];
|
|
26160
|
+
if (existing.includes(skillName)) {
|
|
26161
|
+
return {
|
|
26162
|
+
success: false,
|
|
26163
|
+
error: `Skill '${skillKey}' is already disabled`
|
|
26164
|
+
};
|
|
26165
|
+
}
|
|
26166
|
+
entry.skills = { exclude: [...existing, skillName] };
|
|
26167
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26168
|
+
return { success: true };
|
|
25943
26169
|
} catch (error) {
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
25949
|
-
});
|
|
26170
|
+
return {
|
|
26171
|
+
success: false,
|
|
26172
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26173
|
+
};
|
|
25950
26174
|
}
|
|
25951
|
-
return results;
|
|
25952
26175
|
}
|
|
25953
|
-
async function
|
|
25954
|
-
const
|
|
25955
|
-
|
|
25956
|
-
|
|
25957
|
-
|
|
25958
|
-
|
|
25959
|
-
|
|
25960
|
-
...syncMode && { syncMode },
|
|
25961
|
-
...canonicalSkillsPath && { canonicalSkillsPath }
|
|
25962
|
-
}),
|
|
25963
|
-
copyHooks(pluginPath, workspacePath, client, baseOptions),
|
|
25964
|
-
copyAgents(pluginPath, workspacePath, client, baseOptions)
|
|
25965
|
-
]);
|
|
25966
|
-
const githubResults = await copyGitHubContent(pluginPath, workspacePath, client, {
|
|
25967
|
-
...baseOptions,
|
|
25968
|
-
...skillNameMap && { skillNameMap }
|
|
25969
|
-
});
|
|
25970
|
-
return [...commandResults, ...skillResults, ...hookResults, ...agentResults, ...githubResults];
|
|
25971
|
-
}
|
|
25972
|
-
function isExplicitGitHubSource(source) {
|
|
25973
|
-
if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/") || source.startsWith("github.com/") || source.startsWith("gh:")) {
|
|
25974
|
-
return true;
|
|
26176
|
+
async function removeDisabledSkill(skillKey, workspacePath = process.cwd()) {
|
|
26177
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26178
|
+
if (!existsSync5(configPath)) {
|
|
26179
|
+
return {
|
|
26180
|
+
success: false,
|
|
26181
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26182
|
+
};
|
|
25975
26183
|
}
|
|
25976
|
-
|
|
25977
|
-
|
|
25978
|
-
|
|
25979
|
-
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
}
|
|
25983
|
-
}
|
|
26184
|
+
const parsed = parseSkillKey(skillKey);
|
|
26185
|
+
if (!parsed) {
|
|
26186
|
+
return {
|
|
26187
|
+
success: false,
|
|
26188
|
+
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
26189
|
+
};
|
|
25984
26190
|
}
|
|
25985
|
-
|
|
25986
|
-
|
|
25987
|
-
|
|
25988
|
-
|
|
25989
|
-
|
|
25990
|
-
|
|
26191
|
+
const { pluginName, skillName } = parsed;
|
|
26192
|
+
try {
|
|
26193
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26194
|
+
const config = load(content);
|
|
26195
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26196
|
+
if (index === -1) {
|
|
26197
|
+
return {
|
|
26198
|
+
success: false,
|
|
26199
|
+
error: `Plugin '${pluginName}' not found in workspace config`
|
|
26200
|
+
};
|
|
25991
26201
|
}
|
|
25992
|
-
|
|
25993
|
-
|
|
26202
|
+
const entry = config.plugins[index];
|
|
26203
|
+
if (!entry) {
|
|
26204
|
+
return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
|
|
25994
26205
|
}
|
|
25995
|
-
if (
|
|
25996
|
-
return {
|
|
26206
|
+
if (typeof entry === "string" || !entry.skills || Array.isArray(entry.skills)) {
|
|
26207
|
+
return {
|
|
26208
|
+
success: false,
|
|
26209
|
+
error: `Skill '${skillKey}' is already enabled`
|
|
26210
|
+
};
|
|
25997
26211
|
}
|
|
25998
|
-
|
|
25999
|
-
}
|
|
26000
|
-
const parsed = parseFileSource(source);
|
|
26001
|
-
if (parsed.type === "github" && parsed.owner && parsed.repo && parsed.filePath) {
|
|
26002
|
-
const cacheKey = `${parsed.owner}/${parsed.repo}`;
|
|
26003
|
-
const cachePath = githubCache?.get(cacheKey);
|
|
26004
|
-
if (!cachePath) {
|
|
26212
|
+
if (!entry.skills.exclude.includes(skillName)) {
|
|
26005
26213
|
return {
|
|
26006
|
-
|
|
26007
|
-
error: `
|
|
26214
|
+
success: false,
|
|
26215
|
+
error: `Skill '${skillKey}' is already enabled`
|
|
26008
26216
|
};
|
|
26009
26217
|
}
|
|
26010
|
-
|
|
26011
|
-
|
|
26012
|
-
|
|
26218
|
+
const newExclude = entry.skills.exclude.filter((s) => s !== skillName);
|
|
26219
|
+
entry.skills = newExclude.length > 0 ? { exclude: newExclude } : undefined;
|
|
26220
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26221
|
+
return { success: true };
|
|
26222
|
+
} catch (error) {
|
|
26013
26223
|
return {
|
|
26014
|
-
|
|
26015
|
-
error:
|
|
26224
|
+
success: false,
|
|
26225
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26016
26226
|
};
|
|
26017
26227
|
}
|
|
26018
|
-
return null;
|
|
26019
26228
|
}
|
|
26020
|
-
async function
|
|
26021
|
-
const
|
|
26022
|
-
|
|
26023
|
-
|
|
26024
|
-
|
|
26025
|
-
|
|
26026
|
-
|
|
26027
|
-
if (typeof file === "string") {
|
|
26028
|
-
stringPatterns.push(file);
|
|
26029
|
-
} else {
|
|
26030
|
-
let dest = file.dest;
|
|
26031
|
-
if (!dest && file.source) {
|
|
26032
|
-
const parts = file.source.split("/");
|
|
26033
|
-
dest = parts[parts.length - 1] || file.source;
|
|
26034
|
-
}
|
|
26035
|
-
if (!dest) {
|
|
26036
|
-
results.push({
|
|
26037
|
-
source: "unknown",
|
|
26038
|
-
destination: join7(workspacePath, "unknown"),
|
|
26039
|
-
action: "failed",
|
|
26040
|
-
error: "File entry must have at least source or dest specified"
|
|
26041
|
-
});
|
|
26042
|
-
continue;
|
|
26043
|
-
}
|
|
26044
|
-
objectEntries.push(file.source ? { source: file.source, dest } : { dest });
|
|
26045
|
-
}
|
|
26229
|
+
async function addEnabledSkill(skillKey, workspacePath = process.cwd()) {
|
|
26230
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26231
|
+
if (!existsSync5(configPath)) {
|
|
26232
|
+
return {
|
|
26233
|
+
success: false,
|
|
26234
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26235
|
+
};
|
|
26046
26236
|
}
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
26050
|
-
|
|
26051
|
-
|
|
26052
|
-
|
|
26053
|
-
destination: join7(workspacePath, pattern),
|
|
26054
|
-
action: "failed",
|
|
26055
|
-
error: `Cannot resolve file '${pattern}' - no workspace.source configured and no explicit source provided`
|
|
26056
|
-
});
|
|
26057
|
-
}
|
|
26058
|
-
}
|
|
26059
|
-
} else {
|
|
26060
|
-
const resolvedFiles = await resolveGlobPatterns(sourcePath, stringPatterns);
|
|
26061
|
-
for (const resolved of resolvedFiles) {
|
|
26062
|
-
const destPath = join7(workspacePath, resolved.relativePath);
|
|
26063
|
-
if (!existsSync4(resolved.sourcePath)) {
|
|
26064
|
-
const wasLiteral = stringPatterns.some((p) => !isGlobPattern(p) && !p.startsWith("!") && p === resolved.relativePath);
|
|
26065
|
-
if (wasLiteral) {
|
|
26066
|
-
results.push({
|
|
26067
|
-
source: resolved.sourcePath,
|
|
26068
|
-
destination: destPath,
|
|
26069
|
-
action: "failed",
|
|
26070
|
-
error: `Source file not found: ${resolved.sourcePath}`
|
|
26071
|
-
});
|
|
26072
|
-
}
|
|
26073
|
-
continue;
|
|
26074
|
-
}
|
|
26075
|
-
if (dryRun) {
|
|
26076
|
-
results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
|
|
26077
|
-
if (AGENT_FILES2.includes(resolved.relativePath)) {
|
|
26078
|
-
copiedAgentFiles.push(resolved.relativePath);
|
|
26079
|
-
}
|
|
26080
|
-
continue;
|
|
26081
|
-
}
|
|
26082
|
-
try {
|
|
26083
|
-
await mkdir3(dirname4(destPath), { recursive: true });
|
|
26084
|
-
const content = await readFile3(resolved.sourcePath, "utf-8");
|
|
26085
|
-
await writeFile(destPath, content, "utf-8");
|
|
26086
|
-
results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
|
|
26087
|
-
if (AGENT_FILES2.includes(resolved.relativePath)) {
|
|
26088
|
-
copiedAgentFiles.push(resolved.relativePath);
|
|
26089
|
-
}
|
|
26090
|
-
} catch (error) {
|
|
26091
|
-
results.push({
|
|
26092
|
-
source: resolved.sourcePath,
|
|
26093
|
-
destination: destPath,
|
|
26094
|
-
action: "failed",
|
|
26095
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
26096
|
-
});
|
|
26097
|
-
}
|
|
26098
|
-
}
|
|
26099
|
-
}
|
|
26237
|
+
const parsed = parseSkillKey(skillKey);
|
|
26238
|
+
if (!parsed) {
|
|
26239
|
+
return {
|
|
26240
|
+
success: false,
|
|
26241
|
+
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
26242
|
+
};
|
|
26100
26243
|
}
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
error: `Failed to resolve source: ${entry.source}`
|
|
26112
|
-
});
|
|
26113
|
-
continue;
|
|
26114
|
-
}
|
|
26115
|
-
if (resolved.error) {
|
|
26116
|
-
results.push({
|
|
26117
|
-
source: entry.source,
|
|
26118
|
-
destination: destPath,
|
|
26119
|
-
action: "failed",
|
|
26120
|
-
error: resolved.error
|
|
26121
|
-
});
|
|
26122
|
-
continue;
|
|
26123
|
-
}
|
|
26124
|
-
srcPath = resolved.path;
|
|
26125
|
-
} else {
|
|
26126
|
-
if (!sourcePath) {
|
|
26127
|
-
results.push({
|
|
26128
|
-
source: entry.dest,
|
|
26129
|
-
destination: destPath,
|
|
26130
|
-
action: "failed",
|
|
26131
|
-
error: `Cannot resolve file '${entry.dest}' - no workspace.source configured and no explicit source provided`
|
|
26132
|
-
});
|
|
26133
|
-
continue;
|
|
26134
|
-
}
|
|
26135
|
-
srcPath = join7(sourcePath, entry.dest);
|
|
26136
|
-
}
|
|
26137
|
-
if (!existsSync4(srcPath)) {
|
|
26138
|
-
results.push({
|
|
26139
|
-
source: srcPath,
|
|
26140
|
-
destination: destPath,
|
|
26141
|
-
action: "failed",
|
|
26142
|
-
error: `Source file not found: ${srcPath}`
|
|
26143
|
-
});
|
|
26144
|
-
continue;
|
|
26145
|
-
}
|
|
26146
|
-
if (dryRun) {
|
|
26147
|
-
results.push({ source: srcPath, destination: destPath, action: "copied" });
|
|
26148
|
-
if (AGENT_FILES2.includes(entry.dest)) {
|
|
26149
|
-
copiedAgentFiles.push(entry.dest);
|
|
26150
|
-
}
|
|
26151
|
-
continue;
|
|
26244
|
+
const { pluginName, skillName } = parsed;
|
|
26245
|
+
try {
|
|
26246
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26247
|
+
const config = load(content);
|
|
26248
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26249
|
+
if (index === -1) {
|
|
26250
|
+
return {
|
|
26251
|
+
success: false,
|
|
26252
|
+
error: `Plugin '${pluginName}' not found in workspace config`
|
|
26253
|
+
};
|
|
26152
26254
|
}
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
copiedAgentFiles.push(entry.dest);
|
|
26160
|
-
}
|
|
26161
|
-
} catch (error) {
|
|
26162
|
-
results.push({
|
|
26163
|
-
source: srcPath,
|
|
26164
|
-
destination: destPath,
|
|
26165
|
-
action: "failed",
|
|
26166
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
26167
|
-
});
|
|
26255
|
+
const entry = ensureObjectPluginEntry(config, index);
|
|
26256
|
+
if (entry.skills && !Array.isArray(entry.skills)) {
|
|
26257
|
+
return {
|
|
26258
|
+
success: false,
|
|
26259
|
+
error: `Plugin '${pluginName}' is in blocklist mode; use removeDisabledSkill to enable a skill`
|
|
26260
|
+
};
|
|
26168
26261
|
}
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
} catch (error) {
|
|
26176
|
-
results.push({
|
|
26177
|
-
source: "WORKSPACE-RULES",
|
|
26178
|
-
destination: targetPath,
|
|
26179
|
-
action: "failed",
|
|
26180
|
-
error: error instanceof Error ? error.message : "Failed to inject WORKSPACE-RULES"
|
|
26181
|
-
});
|
|
26182
|
-
}
|
|
26262
|
+
const existing = entry.skills ?? [];
|
|
26263
|
+
if (existing.includes(skillName)) {
|
|
26264
|
+
return {
|
|
26265
|
+
success: false,
|
|
26266
|
+
error: `Skill '${skillKey}' is already enabled`
|
|
26267
|
+
};
|
|
26183
26268
|
}
|
|
26269
|
+
entry.skills = [...existing, skillName];
|
|
26270
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26271
|
+
return { success: true };
|
|
26272
|
+
} catch (error) {
|
|
26273
|
+
return {
|
|
26274
|
+
success: false,
|
|
26275
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26276
|
+
};
|
|
26184
26277
|
}
|
|
26185
|
-
return results;
|
|
26186
26278
|
}
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
init_client_mapping();
|
|
26191
|
-
init_constants();
|
|
26192
|
-
init_plugin_path();
|
|
26193
|
-
init_symlink();
|
|
26194
|
-
init_link_adjuster();
|
|
26195
|
-
init_skill();
|
|
26196
|
-
import_micromatch = __toESM(require_micromatch(), 1);
|
|
26197
|
-
AGENT_FILES2 = ["AGENTS.md", "CLAUDE.md"];
|
|
26198
|
-
});
|
|
26199
|
-
|
|
26200
|
-
// src/models/marketplace-manifest.ts
|
|
26201
|
-
var UrlSourceSchema, GitHubSourceSchema, PluginSourceRefSchema, ContactSchema, LspServerSchema, MarketplacePluginEntrySchema, MarketplaceManifestSchema, MarketplaceManifestLenientSchema;
|
|
26202
|
-
var init_marketplace_manifest = __esm(() => {
|
|
26203
|
-
init_zod();
|
|
26204
|
-
UrlSourceSchema = exports_external.object({
|
|
26205
|
-
source: exports_external.literal("url"),
|
|
26206
|
-
url: exports_external.string().url()
|
|
26207
|
-
});
|
|
26208
|
-
GitHubSourceSchema = exports_external.object({
|
|
26209
|
-
source: exports_external.literal("github"),
|
|
26210
|
-
repo: exports_external.string().min(1)
|
|
26211
|
-
}).transform((val) => ({
|
|
26212
|
-
source: "url",
|
|
26213
|
-
url: `https://github.com/${val.repo}`
|
|
26214
|
-
}));
|
|
26215
|
-
PluginSourceRefSchema = exports_external.union([exports_external.string(), UrlSourceSchema, GitHubSourceSchema]);
|
|
26216
|
-
ContactSchema = exports_external.object({
|
|
26217
|
-
name: exports_external.string(),
|
|
26218
|
-
email: exports_external.string().optional()
|
|
26219
|
-
});
|
|
26220
|
-
LspServerSchema = exports_external.object({
|
|
26221
|
-
command: exports_external.string(),
|
|
26222
|
-
args: exports_external.array(exports_external.string()).optional(),
|
|
26223
|
-
extensionToLanguage: exports_external.record(exports_external.string()).optional(),
|
|
26224
|
-
startupTimeout: exports_external.number().optional()
|
|
26225
|
-
});
|
|
26226
|
-
MarketplacePluginEntrySchema = exports_external.object({
|
|
26227
|
-
name: exports_external.string().min(1),
|
|
26228
|
-
description: exports_external.string().min(1),
|
|
26229
|
-
source: PluginSourceRefSchema,
|
|
26230
|
-
version: exports_external.string().optional(),
|
|
26231
|
-
author: ContactSchema.optional(),
|
|
26232
|
-
category: exports_external.string().optional(),
|
|
26233
|
-
homepage: exports_external.string().optional(),
|
|
26234
|
-
strict: exports_external.boolean().optional(),
|
|
26235
|
-
tags: exports_external.array(exports_external.string()).optional(),
|
|
26236
|
-
skills: exports_external.array(exports_external.string()).optional(),
|
|
26237
|
-
lspServers: exports_external.record(LspServerSchema).optional()
|
|
26238
|
-
});
|
|
26239
|
-
MarketplaceManifestSchema = exports_external.object({
|
|
26240
|
-
$schema: exports_external.string().optional(),
|
|
26241
|
-
name: exports_external.string().min(1),
|
|
26242
|
-
version: exports_external.string().optional(),
|
|
26243
|
-
description: exports_external.string().min(1),
|
|
26244
|
-
owner: ContactSchema.optional(),
|
|
26245
|
-
plugins: exports_external.array(MarketplacePluginEntrySchema)
|
|
26246
|
-
});
|
|
26247
|
-
MarketplaceManifestLenientSchema = exports_external.object({
|
|
26248
|
-
name: exports_external.string().optional(),
|
|
26249
|
-
plugins: exports_external.array(exports_external.unknown())
|
|
26250
|
-
}).passthrough();
|
|
26251
|
-
});
|
|
26252
|
-
|
|
26253
|
-
// src/utils/marketplace-manifest-parser.ts
|
|
26254
|
-
import { readFile as readFile4 } from "node:fs/promises";
|
|
26255
|
-
import { existsSync as existsSync5 } from "node:fs";
|
|
26256
|
-
import { join as join8, resolve as resolve5 } from "node:path";
|
|
26257
|
-
async function parseMarketplaceManifest(marketplacePath) {
|
|
26258
|
-
const manifestPath = join8(marketplacePath, MANIFEST_PATH);
|
|
26259
|
-
if (!existsSync5(manifestPath)) {
|
|
26279
|
+
async function removeEnabledSkill(skillKey, workspacePath = process.cwd()) {
|
|
26280
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26281
|
+
if (!existsSync5(configPath)) {
|
|
26260
26282
|
return {
|
|
26261
26283
|
success: false,
|
|
26262
|
-
error:
|
|
26284
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26263
26285
|
};
|
|
26264
26286
|
}
|
|
26265
|
-
|
|
26266
|
-
|
|
26267
|
-
raw = await readFile4(manifestPath, "utf-8");
|
|
26268
|
-
} catch (err) {
|
|
26287
|
+
const parsed = parseSkillKey(skillKey);
|
|
26288
|
+
if (!parsed) {
|
|
26269
26289
|
return {
|
|
26270
26290
|
success: false,
|
|
26271
|
-
error: `
|
|
26291
|
+
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
26272
26292
|
};
|
|
26273
26293
|
}
|
|
26274
|
-
|
|
26294
|
+
const { pluginName, skillName } = parsed;
|
|
26275
26295
|
try {
|
|
26276
|
-
|
|
26277
|
-
|
|
26296
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26297
|
+
const config = load(content);
|
|
26298
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26299
|
+
if (index === -1) {
|
|
26300
|
+
return {
|
|
26301
|
+
success: false,
|
|
26302
|
+
error: `Plugin '${pluginName}' not found in workspace config`
|
|
26303
|
+
};
|
|
26304
|
+
}
|
|
26305
|
+
const entry = config.plugins[index];
|
|
26306
|
+
if (!entry) {
|
|
26307
|
+
return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
|
|
26308
|
+
}
|
|
26309
|
+
if (typeof entry === "string" || !entry.skills || !Array.isArray(entry.skills)) {
|
|
26310
|
+
return {
|
|
26311
|
+
success: false,
|
|
26312
|
+
error: `Skill '${skillKey}' is already disabled`
|
|
26313
|
+
};
|
|
26314
|
+
}
|
|
26315
|
+
if (!entry.skills.includes(skillName)) {
|
|
26316
|
+
return {
|
|
26317
|
+
success: false,
|
|
26318
|
+
error: `Skill '${skillKey}' is already disabled`
|
|
26319
|
+
};
|
|
26320
|
+
}
|
|
26321
|
+
const newSkills = entry.skills.filter((s) => s !== skillName);
|
|
26322
|
+
entry.skills = newSkills;
|
|
26323
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26324
|
+
return { success: true };
|
|
26325
|
+
} catch (error) {
|
|
26278
26326
|
return {
|
|
26279
26327
|
success: false,
|
|
26280
|
-
error:
|
|
26328
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26281
26329
|
};
|
|
26282
26330
|
}
|
|
26283
|
-
const strictResult = MarketplaceManifestSchema.safeParse(json2);
|
|
26284
|
-
if (strictResult.success) {
|
|
26285
|
-
return { success: true, data: strictResult.data, warnings: [] };
|
|
26286
|
-
}
|
|
26287
|
-
return parseLeniently(json2);
|
|
26288
26331
|
}
|
|
26289
|
-
function
|
|
26290
|
-
const
|
|
26291
|
-
if (!
|
|
26332
|
+
async function setPluginSkillsMode(pluginName, mode, skillNames, workspacePath = process.cwd()) {
|
|
26333
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26334
|
+
if (!existsSync5(configPath)) {
|
|
26292
26335
|
return {
|
|
26293
26336
|
success: false,
|
|
26294
|
-
error:
|
|
26337
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26295
26338
|
};
|
|
26296
26339
|
}
|
|
26297
|
-
|
|
26298
|
-
|
|
26299
|
-
|
|
26300
|
-
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
continue;
|
|
26340
|
+
try {
|
|
26341
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26342
|
+
const config = load(content);
|
|
26343
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26344
|
+
if (index === -1) {
|
|
26345
|
+
return {
|
|
26346
|
+
success: false,
|
|
26347
|
+
error: `Plugin '${pluginName}' not found in workspace config`
|
|
26348
|
+
};
|
|
26307
26349
|
}
|
|
26308
|
-
const
|
|
26309
|
-
if (
|
|
26310
|
-
|
|
26350
|
+
const entry = ensureObjectPluginEntry(config, index);
|
|
26351
|
+
if (mode === "allowlist") {
|
|
26352
|
+
entry.skills = [...skillNames];
|
|
26353
|
+
} else {
|
|
26354
|
+
entry.skills = skillNames.length > 0 ? { exclude: [...skillNames] } : undefined;
|
|
26311
26355
|
}
|
|
26356
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26357
|
+
return { success: true };
|
|
26358
|
+
} catch (error) {
|
|
26359
|
+
return {
|
|
26360
|
+
success: false,
|
|
26361
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26362
|
+
};
|
|
26312
26363
|
}
|
|
26313
|
-
const data = {
|
|
26314
|
-
name: typeof raw.name === "string" ? raw.name : "unknown",
|
|
26315
|
-
description: typeof obj.description === "string" ? obj.description : "",
|
|
26316
|
-
plugins: validPlugins
|
|
26317
|
-
};
|
|
26318
|
-
return { success: true, data, warnings };
|
|
26319
26364
|
}
|
|
26320
|
-
function
|
|
26321
|
-
|
|
26322
|
-
|
|
26323
|
-
|
|
26365
|
+
async function upsertGitHubPluginSourceAllowlist(source, skillNames, workspacePath = process.cwd()) {
|
|
26366
|
+
await ensureWorkspace(workspacePath);
|
|
26367
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26368
|
+
if (!existsSync5(configPath)) {
|
|
26369
|
+
return {
|
|
26370
|
+
success: false,
|
|
26371
|
+
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
26372
|
+
};
|
|
26324
26373
|
}
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
|
|
26374
|
+
try {
|
|
26375
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26376
|
+
const config = load(content);
|
|
26377
|
+
const result = await upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames);
|
|
26378
|
+
if (!result.success)
|
|
26379
|
+
return result;
|
|
26380
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26381
|
+
return result;
|
|
26382
|
+
} catch (error) {
|
|
26383
|
+
return {
|
|
26384
|
+
success: false,
|
|
26385
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26386
|
+
};
|
|
26330
26387
|
}
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26388
|
+
}
|
|
26389
|
+
function pruneEnabledSkillsForPlugin(config, pluginEntry) {
|
|
26390
|
+
if (!config.enabledSkills?.length)
|
|
26391
|
+
return;
|
|
26392
|
+
const names = extractPluginNames(pluginEntry);
|
|
26393
|
+
if (names.length === 0)
|
|
26394
|
+
return;
|
|
26395
|
+
const prefixes = names.map((n) => `${n}:`);
|
|
26396
|
+
config.enabledSkills = config.enabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
|
|
26397
|
+
if (config.enabledSkills.length === 0) {
|
|
26398
|
+
config.enabledSkills = undefined;
|
|
26339
26399
|
}
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
if (
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
warnings.push(`plugins[${index}] ("${name}"): missing or invalid "source" field`);
|
|
26400
|
+
}
|
|
26401
|
+
async function resolveGitHubIdentity(pluginSource) {
|
|
26402
|
+
if (isGitHubUrl(pluginSource)) {
|
|
26403
|
+
const parsed = parseGitHubUrl(pluginSource);
|
|
26404
|
+
return parsed ? `${parsed.owner}/${parsed.repo}`.toLowerCase() : null;
|
|
26346
26405
|
}
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26406
|
+
if (isPluginSpec(pluginSource)) {
|
|
26407
|
+
const parsed = parsePluginSpec(pluginSource);
|
|
26408
|
+
if (!parsed)
|
|
26409
|
+
return null;
|
|
26410
|
+
const marketplace = await getMarketplace(parsed.marketplaceName);
|
|
26411
|
+
if (!marketplace)
|
|
26412
|
+
return null;
|
|
26413
|
+
const manifestResult = await parseMarketplaceManifest(marketplace.path);
|
|
26414
|
+
if (!manifestResult.success)
|
|
26415
|
+
return null;
|
|
26416
|
+
const entry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
|
|
26417
|
+
if (!entry || typeof entry.source === "string")
|
|
26418
|
+
return null;
|
|
26419
|
+
const parsedUrl = parseGitHubUrl(entry.source.url);
|
|
26420
|
+
return parsedUrl ? `${parsedUrl.owner}/${parsedUrl.repo}`.toLowerCase() : null;
|
|
26421
|
+
}
|
|
26422
|
+
return null;
|
|
26356
26423
|
}
|
|
26357
|
-
function
|
|
26358
|
-
|
|
26359
|
-
|
|
26424
|
+
async function migrateWorkspaceSkillsV1toV2(workspacePath) {
|
|
26425
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26426
|
+
if (!existsSync5(configPath))
|
|
26427
|
+
return;
|
|
26428
|
+
let config;
|
|
26429
|
+
try {
|
|
26430
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26431
|
+
config = load(content);
|
|
26432
|
+
} catch {
|
|
26433
|
+
return;
|
|
26434
|
+
}
|
|
26435
|
+
if (!config || config.version !== undefined && config.version >= 2)
|
|
26436
|
+
return;
|
|
26437
|
+
const enabledSkills = config.enabledSkills ?? [];
|
|
26438
|
+
const disabledSkills = config.disabledSkills ?? [];
|
|
26439
|
+
const enabledByPlugin = new Map;
|
|
26440
|
+
for (const skillKey of enabledSkills) {
|
|
26441
|
+
const parsed = parseSkillKey(skillKey);
|
|
26442
|
+
if (!parsed)
|
|
26443
|
+
continue;
|
|
26444
|
+
const list = enabledByPlugin.get(parsed.pluginName) ?? [];
|
|
26445
|
+
list.push(parsed.skillName);
|
|
26446
|
+
enabledByPlugin.set(parsed.pluginName, list);
|
|
26447
|
+
}
|
|
26448
|
+
const disabledByPlugin = new Map;
|
|
26449
|
+
for (const skillKey of disabledSkills) {
|
|
26450
|
+
const parsed = parseSkillKey(skillKey);
|
|
26451
|
+
if (!parsed)
|
|
26452
|
+
continue;
|
|
26453
|
+
const list = disabledByPlugin.get(parsed.pluginName) ?? [];
|
|
26454
|
+
list.push(parsed.skillName);
|
|
26455
|
+
disabledByPlugin.set(parsed.pluginName, list);
|
|
26456
|
+
}
|
|
26457
|
+
for (const [pluginName, skillNames] of enabledByPlugin) {
|
|
26458
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26459
|
+
if (index === -1) {
|
|
26460
|
+
console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
|
|
26461
|
+
continue;
|
|
26462
|
+
}
|
|
26463
|
+
const entry = ensureObjectPluginEntry(config, index);
|
|
26464
|
+
entry.skills = skillNames;
|
|
26465
|
+
}
|
|
26466
|
+
for (const [pluginName, skillNames] of disabledByPlugin) {
|
|
26467
|
+
const index = findPluginEntryByName(config, pluginName);
|
|
26468
|
+
if (index === -1) {
|
|
26469
|
+
console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
|
|
26470
|
+
continue;
|
|
26471
|
+
}
|
|
26472
|
+
const entry = ensureObjectPluginEntry(config, index);
|
|
26473
|
+
if (!Array.isArray(entry.skills)) {
|
|
26474
|
+
entry.skills = { exclude: skillNames };
|
|
26475
|
+
}
|
|
26476
|
+
}
|
|
26477
|
+
config.enabledSkills = undefined;
|
|
26478
|
+
config.disabledSkills = undefined;
|
|
26479
|
+
config.version = 2;
|
|
26480
|
+
try {
|
|
26481
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26482
|
+
} catch (error) {
|
|
26483
|
+
console.warn(`[migrate v1→v2] Failed to write migrated config: ${error instanceof Error ? error.message : String(error)}`);
|
|
26360
26484
|
}
|
|
26361
|
-
return resolve5(marketplacePath, source);
|
|
26362
26485
|
}
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26486
|
+
async function updateRepositories(changes, workspacePath = process.cwd()) {
|
|
26487
|
+
if (changes.remove.length === 0 && changes.add.length === 0) {
|
|
26488
|
+
return { success: true };
|
|
26489
|
+
}
|
|
26490
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26491
|
+
try {
|
|
26492
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26493
|
+
const config = load(content);
|
|
26494
|
+
const removeSet = new Set(changes.remove);
|
|
26495
|
+
config.repositories = config.repositories.filter((repo) => !removeSet.has(repo.path));
|
|
26496
|
+
config.repositories.push(...changes.add);
|
|
26497
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26498
|
+
return { success: true };
|
|
26499
|
+
} catch (error) {
|
|
26500
|
+
return {
|
|
26501
|
+
success: false,
|
|
26502
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26503
|
+
};
|
|
26504
|
+
}
|
|
26505
|
+
}
|
|
26506
|
+
async function setRepositories(repositories, workspacePath = process.cwd()) {
|
|
26507
|
+
const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
26508
|
+
try {
|
|
26509
|
+
const content = await readFile4(configPath, "utf-8");
|
|
26510
|
+
const config = load(content);
|
|
26511
|
+
config.repositories = repositories;
|
|
26512
|
+
await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
26513
|
+
return { success: true };
|
|
26514
|
+
} catch (error) {
|
|
26515
|
+
return {
|
|
26516
|
+
success: false,
|
|
26517
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26518
|
+
};
|
|
26519
|
+
}
|
|
26520
|
+
}
|
|
26521
|
+
var DEFAULT_PROJECT_CLIENTS;
|
|
26522
|
+
var init_workspace_modify = __esm(() => {
|
|
26523
|
+
init_js_yaml();
|
|
26524
|
+
init_constants();
|
|
26525
|
+
init_workspace_config();
|
|
26526
|
+
init_marketplace_manifest_parser();
|
|
26527
|
+
init_plugin_path();
|
|
26528
|
+
init_marketplace();
|
|
26529
|
+
DEFAULT_PROJECT_CLIENTS = ["universal"];
|
|
26366
26530
|
});
|
|
26367
26531
|
|
|
26368
26532
|
// src/core/user-workspace.ts
|
|
26369
26533
|
var exports_user_workspace = {};
|
|
26370
26534
|
__export(exports_user_workspace, {
|
|
26535
|
+
upsertUserGitHubPluginSourceAllowlist: () => upsertUserGitHubPluginSourceAllowlist,
|
|
26371
26536
|
setUserPluginSkillsMode: () => setUserPluginSkillsMode,
|
|
26372
26537
|
setUserClients: () => setUserClients,
|
|
26373
26538
|
removeUserPluginsForMarketplace: () => removeUserPluginsForMarketplace,
|
|
@@ -26391,7 +26556,7 @@ __export(exports_user_workspace, {
|
|
|
26391
26556
|
});
|
|
26392
26557
|
import { existsSync as existsSync6 } from "node:fs";
|
|
26393
26558
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
|
|
26394
|
-
import { basename as
|
|
26559
|
+
import { basename as basename2, join as join9, resolve as resolve6 } from "node:path";
|
|
26395
26560
|
function getUserWorkspaceConfigPath() {
|
|
26396
26561
|
return join9(getAllagentsDir(), WORKSPACE_CONFIG_FILE);
|
|
26397
26562
|
}
|
|
@@ -26611,7 +26776,7 @@ async function setUserClients(clients) {
|
|
|
26611
26776
|
};
|
|
26612
26777
|
}
|
|
26613
26778
|
}
|
|
26614
|
-
function
|
|
26779
|
+
function parseSkillKey2(skillKey) {
|
|
26615
26780
|
const colonIdx = skillKey.indexOf(":");
|
|
26616
26781
|
if (colonIdx === -1)
|
|
26617
26782
|
return null;
|
|
@@ -26644,7 +26809,7 @@ async function getUserDisabledSkills() {
|
|
|
26644
26809
|
async function addUserDisabledSkill(skillKey) {
|
|
26645
26810
|
await ensureUserWorkspace();
|
|
26646
26811
|
const configPath = getUserWorkspaceConfigPath();
|
|
26647
|
-
const parsed =
|
|
26812
|
+
const parsed = parseSkillKey2(skillKey);
|
|
26648
26813
|
if (!parsed) {
|
|
26649
26814
|
return {
|
|
26650
26815
|
success: false,
|
|
@@ -26689,7 +26854,7 @@ async function addUserDisabledSkill(skillKey) {
|
|
|
26689
26854
|
async function removeUserDisabledSkill(skillKey) {
|
|
26690
26855
|
await ensureUserWorkspace();
|
|
26691
26856
|
const configPath = getUserWorkspaceConfigPath();
|
|
26692
|
-
const parsed =
|
|
26857
|
+
const parsed = parseSkillKey2(skillKey);
|
|
26693
26858
|
if (!parsed) {
|
|
26694
26859
|
return {
|
|
26695
26860
|
success: false,
|
|
@@ -26758,7 +26923,7 @@ async function getUserEnabledSkills() {
|
|
|
26758
26923
|
async function addUserEnabledSkill(skillKey) {
|
|
26759
26924
|
await ensureUserWorkspace();
|
|
26760
26925
|
const configPath = getUserWorkspaceConfigPath();
|
|
26761
|
-
const parsed =
|
|
26926
|
+
const parsed = parseSkillKey2(skillKey);
|
|
26762
26927
|
if (!parsed) {
|
|
26763
26928
|
return {
|
|
26764
26929
|
success: false,
|
|
@@ -26803,7 +26968,7 @@ async function addUserEnabledSkill(skillKey) {
|
|
|
26803
26968
|
async function removeUserEnabledSkill(skillKey) {
|
|
26804
26969
|
await ensureUserWorkspace();
|
|
26805
26970
|
const configPath = getUserWorkspaceConfigPath();
|
|
26806
|
-
const parsed =
|
|
26971
|
+
const parsed = parseSkillKey2(skillKey);
|
|
26807
26972
|
if (!parsed) {
|
|
26808
26973
|
return {
|
|
26809
26974
|
success: false,
|
|
@@ -26876,6 +27041,24 @@ async function setUserPluginSkillsMode(pluginName, mode, skillNames) {
|
|
|
26876
27041
|
};
|
|
26877
27042
|
}
|
|
26878
27043
|
}
|
|
27044
|
+
async function upsertUserGitHubPluginSourceAllowlist(source, skillNames) {
|
|
27045
|
+
await ensureUserWorkspace();
|
|
27046
|
+
const configPath = getUserWorkspaceConfigPath();
|
|
27047
|
+
try {
|
|
27048
|
+
const content = await readFile5(configPath, "utf-8");
|
|
27049
|
+
const config = load(content);
|
|
27050
|
+
const result = await upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames);
|
|
27051
|
+
if (!result.success)
|
|
27052
|
+
return result;
|
|
27053
|
+
await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
27054
|
+
return result;
|
|
27055
|
+
} catch (error) {
|
|
27056
|
+
return {
|
|
27057
|
+
success: false,
|
|
27058
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27059
|
+
};
|
|
27060
|
+
}
|
|
27061
|
+
}
|
|
26879
27062
|
function pluginSourceToInfo(plugin, scope) {
|
|
26880
27063
|
const parsed = parsePluginSpec(plugin);
|
|
26881
27064
|
if (parsed) {
|
|
@@ -26890,14 +27073,14 @@ function pluginSourceToInfo(plugin, scope) {
|
|
|
26890
27073
|
const ghParsed = parseGitHubUrl(plugin);
|
|
26891
27074
|
return {
|
|
26892
27075
|
spec: plugin,
|
|
26893
|
-
name: ghParsed?.repo ??
|
|
27076
|
+
name: ghParsed?.repo ?? basename2(plugin),
|
|
26894
27077
|
marketplace: "",
|
|
26895
27078
|
scope
|
|
26896
27079
|
};
|
|
26897
27080
|
}
|
|
26898
27081
|
return {
|
|
26899
27082
|
spec: plugin,
|
|
26900
|
-
name:
|
|
27083
|
+
name: basename2(plugin),
|
|
26901
27084
|
marketplace: "",
|
|
26902
27085
|
scope
|
|
26903
27086
|
};
|
|
@@ -27021,8 +27204,8 @@ var init_user_workspace = __esm(() => {
|
|
|
27021
27204
|
|
|
27022
27205
|
// src/core/marketplace.ts
|
|
27023
27206
|
import { existsSync as existsSync7 } from "node:fs";
|
|
27024
|
-
import { mkdir as mkdir5, readFile as readFile6, readdir as
|
|
27025
|
-
import { basename as
|
|
27207
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir2, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
27208
|
+
import { basename as basename3, dirname as dirname4, join as join10, resolve as resolve7 } from "node:path";
|
|
27026
27209
|
function parseLocation(location) {
|
|
27027
27210
|
const [owner = "", repo = "", ...rest] = location.split("/");
|
|
27028
27211
|
const branch = rest.length > 0 ? rest.join("/") : undefined;
|
|
@@ -27052,7 +27235,7 @@ async function loadRegistryFromPath(registryPath) {
|
|
|
27052
27235
|
}
|
|
27053
27236
|
}
|
|
27054
27237
|
async function saveRegistryToPath(registry, registryPath) {
|
|
27055
|
-
const dir =
|
|
27238
|
+
const dir = dirname4(registryPath);
|
|
27056
27239
|
if (!existsSync7(dir)) {
|
|
27057
27240
|
await mkdir5(dir, { recursive: true });
|
|
27058
27241
|
}
|
|
@@ -27108,7 +27291,7 @@ function parseMarketplaceSource(source) {
|
|
|
27108
27291
|
};
|
|
27109
27292
|
}
|
|
27110
27293
|
const absPath = resolve7(source);
|
|
27111
|
-
const name = source.split(/[/\\]/).filter(Boolean).pop() ||
|
|
27294
|
+
const name = source.split(/[/\\]/).filter(Boolean).pop() || basename3(absPath) || "local";
|
|
27112
27295
|
return {
|
|
27113
27296
|
type: "local",
|
|
27114
27297
|
location: absPath,
|
|
@@ -27458,7 +27641,7 @@ async function listMarketplacePlugins(name, workspacePath) {
|
|
|
27458
27641
|
return { plugins: [], warnings: manifestResult.warnings };
|
|
27459
27642
|
}
|
|
27460
27643
|
try {
|
|
27461
|
-
const entries = await
|
|
27644
|
+
const entries = await readdir2(pluginsDir, { withFileTypes: true });
|
|
27462
27645
|
const plugins = entries.filter((e) => e.isDirectory()).map((e) => ({
|
|
27463
27646
|
name: e.name,
|
|
27464
27647
|
path: join10(pluginsDir, e.name)
|
|
@@ -27810,653 +27993,762 @@ var init_marketplace = __esm(() => {
|
|
|
27810
27993
|
updatedMarketplaceCache = new Set;
|
|
27811
27994
|
});
|
|
27812
27995
|
|
|
27813
|
-
// src/core/
|
|
27996
|
+
// src/core/skills.ts
|
|
27814
27997
|
import { existsSync as existsSync8 } from "node:fs";
|
|
27815
|
-
import {
|
|
27816
|
-
import { join as join11 } from "node:path";
|
|
27817
|
-
async function
|
|
27818
|
-
|
|
27819
|
-
await
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
-
return { success: true };
|
|
27826
|
-
} catch (error) {
|
|
27998
|
+
import { readFile as readFile7, readdir as readdir3 } from "node:fs/promises";
|
|
27999
|
+
import { join as join11, basename as basename4, resolve as resolve8 } from "node:path";
|
|
28000
|
+
async function resolvePluginPath(pluginSource, workspacePath) {
|
|
28001
|
+
if (isPluginSpec(pluginSource)) {
|
|
28002
|
+
const resolved2 = await resolvePluginSpecWithAutoRegister(pluginSource, {
|
|
28003
|
+
offline: true,
|
|
28004
|
+
workspacePath
|
|
28005
|
+
});
|
|
28006
|
+
if (!resolved2.success || !resolved2.path)
|
|
28007
|
+
return null;
|
|
27827
28008
|
return {
|
|
27828
|
-
|
|
27829
|
-
|
|
28009
|
+
path: resolved2.path,
|
|
28010
|
+
pluginName: resolved2.pluginName
|
|
27830
28011
|
};
|
|
27831
28012
|
}
|
|
27832
|
-
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
27839
|
-
|
|
27840
|
-
|
|
27841
|
-
|
|
27842
|
-
};
|
|
27843
|
-
await mkdir6(configDir, { recursive: true });
|
|
27844
|
-
await writeFile4(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
|
|
27845
|
-
}
|
|
27846
|
-
async function addPlugin(plugin, workspacePath = process.cwd(), force) {
|
|
27847
|
-
const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
27848
|
-
await ensureWorkspace(workspacePath);
|
|
27849
|
-
if (isPluginSpec(plugin)) {
|
|
27850
|
-
const resolved = await resolvePluginSpecWithAutoRegister(plugin, { workspacePath });
|
|
27851
|
-
if (!resolved.success) {
|
|
27852
|
-
return {
|
|
27853
|
-
success: false,
|
|
27854
|
-
error: resolved.error || "Unknown error"
|
|
27855
|
-
};
|
|
27856
|
-
}
|
|
27857
|
-
return await addPluginToConfig(resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin, configPath, resolved.registeredAs, force);
|
|
27858
|
-
}
|
|
27859
|
-
if (isGitHubUrl(plugin)) {
|
|
27860
|
-
const validation = validatePluginSource(plugin);
|
|
27861
|
-
if (!validation.valid) {
|
|
27862
|
-
return {
|
|
27863
|
-
success: false,
|
|
27864
|
-
error: validation.error || "Invalid GitHub URL"
|
|
27865
|
-
};
|
|
27866
|
-
}
|
|
27867
|
-
const verifyResult = await verifyGitHubUrlExists(plugin);
|
|
27868
|
-
if (!verifyResult.exists) {
|
|
27869
|
-
return {
|
|
27870
|
-
success: false,
|
|
27871
|
-
error: verifyResult.error || `GitHub URL not found: ${plugin}`
|
|
27872
|
-
};
|
|
27873
|
-
}
|
|
27874
|
-
} else {
|
|
27875
|
-
const fullPath = join11(workspacePath, plugin);
|
|
27876
|
-
if (!existsSync8(fullPath) && !existsSync8(plugin)) {
|
|
27877
|
-
return {
|
|
27878
|
-
success: false,
|
|
27879
|
-
error: `Plugin not found at ${plugin}`
|
|
27880
|
-
};
|
|
27881
|
-
}
|
|
27882
|
-
}
|
|
27883
|
-
return await addPluginToConfig(plugin, configPath, undefined, force);
|
|
27884
|
-
}
|
|
27885
|
-
async function addPluginToConfig(plugin, configPath, autoRegistered, force) {
|
|
27886
|
-
try {
|
|
27887
|
-
const content = await readFile7(configPath, "utf-8");
|
|
27888
|
-
const config = load(content);
|
|
27889
|
-
const existingExactIndex = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
|
|
27890
|
-
if (existingExactIndex !== -1) {
|
|
27891
|
-
if (!force) {
|
|
27892
|
-
return {
|
|
27893
|
-
success: false,
|
|
27894
|
-
error: `Plugin already exists in .allagents/workspace.yaml: ${plugin}`
|
|
27895
|
-
};
|
|
27896
|
-
}
|
|
27897
|
-
}
|
|
27898
|
-
if (!force) {
|
|
27899
|
-
const newIdentity = await resolveGitHubIdentity(plugin);
|
|
27900
|
-
if (newIdentity) {
|
|
27901
|
-
for (const existing of config.plugins) {
|
|
27902
|
-
const existingSource = getPluginSource(existing);
|
|
27903
|
-
const existingIdentity = await resolveGitHubIdentity(existingSource);
|
|
27904
|
-
if (existingIdentity === newIdentity) {
|
|
27905
|
-
return {
|
|
27906
|
-
success: false,
|
|
27907
|
-
error: `Plugin duplicates existing entry '${existingSource}': both resolve to ${newIdentity}`
|
|
27908
|
-
};
|
|
27909
|
-
}
|
|
27910
|
-
}
|
|
27911
|
-
}
|
|
27912
|
-
}
|
|
27913
|
-
const wasReplaced = force && existingExactIndex !== -1;
|
|
27914
|
-
if (wasReplaced) {
|
|
27915
|
-
config.plugins.splice(existingExactIndex, 1);
|
|
27916
|
-
}
|
|
27917
|
-
config.plugins.push(plugin);
|
|
27918
|
-
const newContent = dump(config, { lineWidth: -1 });
|
|
27919
|
-
await writeFile4(configPath, newContent, "utf-8");
|
|
27920
|
-
const result = {
|
|
27921
|
-
success: true,
|
|
27922
|
-
normalizedPlugin: plugin
|
|
27923
|
-
};
|
|
27924
|
-
if (autoRegistered) {
|
|
27925
|
-
result.autoRegistered = autoRegistered;
|
|
27926
|
-
}
|
|
27927
|
-
if (wasReplaced) {
|
|
27928
|
-
result.replaced = true;
|
|
27929
|
-
}
|
|
27930
|
-
return result;
|
|
27931
|
-
} catch (error) {
|
|
27932
|
-
return {
|
|
27933
|
-
success: false,
|
|
27934
|
-
error: error instanceof Error ? error.message : String(error)
|
|
27935
|
-
};
|
|
28013
|
+
if (isGitHubUrl(pluginSource)) {
|
|
28014
|
+
const parsed = parseGitHubUrl(pluginSource);
|
|
28015
|
+
const result = await fetchPlugin(pluginSource, {
|
|
28016
|
+
offline: true,
|
|
28017
|
+
...parsed?.branch && { branch: parsed.branch }
|
|
28018
|
+
});
|
|
28019
|
+
if (!result.success)
|
|
28020
|
+
return null;
|
|
28021
|
+
const path = parsed?.subpath ? join11(result.cachePath, parsed.subpath) : result.cachePath;
|
|
28022
|
+
return existsSync8(path) ? { path } : null;
|
|
27936
28023
|
}
|
|
28024
|
+
const resolved = resolve8(workspacePath, pluginSource);
|
|
28025
|
+
return existsSync8(resolved) ? { path: resolved } : null;
|
|
27937
28026
|
}
|
|
27938
|
-
async function
|
|
27939
|
-
const
|
|
27940
|
-
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
const
|
|
27945
|
-
if (
|
|
27946
|
-
|
|
27947
|
-
|
|
27948
|
-
return config.plugins.some((entry) => {
|
|
27949
|
-
const source = getPluginSource(entry);
|
|
27950
|
-
return source.startsWith(`${plugin}@`) || source === plugin;
|
|
27951
|
-
});
|
|
28027
|
+
async function discoverNestedSkillEntries(pluginPath) {
|
|
28028
|
+
const entries = await readdir3(pluginPath, { withFileTypes: true });
|
|
28029
|
+
const discovered = [];
|
|
28030
|
+
for (const entry of entries) {
|
|
28031
|
+
if (!entry.isDirectory())
|
|
28032
|
+
continue;
|
|
28033
|
+
const skillPath = join11(pluginPath, entry.name);
|
|
28034
|
+
if (existsSync8(join11(skillPath, "SKILL.md"))) {
|
|
28035
|
+
discovered.push({ name: entry.name, skillPath });
|
|
28036
|
+
continue;
|
|
27952
28037
|
}
|
|
27953
|
-
|
|
27954
|
-
} catch {
|
|
27955
|
-
return false;
|
|
28038
|
+
discovered.push(...await discoverNestedSkillEntries(skillPath));
|
|
27956
28039
|
}
|
|
28040
|
+
return discovered;
|
|
27957
28041
|
}
|
|
27958
|
-
async function
|
|
28042
|
+
async function getAllSkillsFromPlugins(workspacePath = process.cwd()) {
|
|
27959
28043
|
const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
27960
28044
|
if (!existsSync8(configPath)) {
|
|
27961
|
-
return
|
|
27962
|
-
success: false,
|
|
27963
|
-
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
|
|
27964
|
-
Run 'allagents workspace init <path>' to create a new workspace`
|
|
27965
|
-
};
|
|
28045
|
+
return [];
|
|
27966
28046
|
}
|
|
27967
|
-
|
|
27968
|
-
|
|
27969
|
-
|
|
27970
|
-
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
28047
|
+
const content = await readFile7(configPath, "utf-8");
|
|
28048
|
+
const config = load(content);
|
|
28049
|
+
const isV1Fallback = config.version === undefined || config.version < 2;
|
|
28050
|
+
const disabledSkills = isV1Fallback ? new Set(config.disabledSkills ?? []) : new Set;
|
|
28051
|
+
const enabledSkills = isV1Fallback && config.enabledSkills ? new Set(config.enabledSkills) : null;
|
|
28052
|
+
const skills = [];
|
|
28053
|
+
for (const pluginEntry of config.plugins) {
|
|
28054
|
+
const pluginSource = getPluginSource(pluginEntry);
|
|
28055
|
+
const resolved = await resolvePluginPath(pluginSource, workspacePath);
|
|
28056
|
+
if (!resolved)
|
|
28057
|
+
continue;
|
|
28058
|
+
const pluginPath = resolved.path;
|
|
28059
|
+
const pluginName = resolved.pluginName ?? getPluginName(pluginPath);
|
|
28060
|
+
const skillsDir = join11(pluginPath, "skills");
|
|
28061
|
+
const pluginSkillsConfig = typeof pluginEntry === "string" ? undefined : pluginEntry.skills;
|
|
28062
|
+
const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && [...enabledSkills].some((s) => s.startsWith(`${pluginName}`));
|
|
28063
|
+
let skillEntries;
|
|
28064
|
+
if (existsSync8(skillsDir)) {
|
|
28065
|
+
const entries = await readdir3(skillsDir, { withFileTypes: true });
|
|
28066
|
+
skillEntries = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, skillPath: join11(skillsDir, e.name) }));
|
|
28067
|
+
} else {
|
|
28068
|
+
const nestedSkills = await discoverNestedSkillEntries(pluginPath);
|
|
28069
|
+
if (nestedSkills.length > 0) {
|
|
28070
|
+
skillEntries = nestedSkills;
|
|
28071
|
+
} else {
|
|
28072
|
+
const rootSkillMd = join11(pluginPath, "SKILL.md");
|
|
28073
|
+
if (existsSync8(rootSkillMd)) {
|
|
28074
|
+
const skillContent = await readFile7(rootSkillMd, "utf-8");
|
|
28075
|
+
const metadata = parseSkillMetadata(skillContent);
|
|
28076
|
+
const skillName = metadata?.name ?? basename4(pluginPath);
|
|
28077
|
+
skillEntries = [{ name: skillName, skillPath: pluginPath }];
|
|
28078
|
+
} else {
|
|
28079
|
+
skillEntries = [];
|
|
28080
|
+
}
|
|
28081
|
+
}
|
|
27976
28082
|
}
|
|
27977
|
-
|
|
27978
|
-
|
|
27979
|
-
|
|
27980
|
-
|
|
27981
|
-
|
|
27982
|
-
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
index = i2;
|
|
27987
|
-
break;
|
|
27988
|
-
}
|
|
28083
|
+
const pluginSkillsMode = pluginSkillsConfig === undefined ? "none" : Array.isArray(pluginSkillsConfig) ? "allowlist" : "blocklist";
|
|
28084
|
+
for (const { name, skillPath } of skillEntries) {
|
|
28085
|
+
const skillKey = `${pluginName}:${name}`;
|
|
28086
|
+
let isDisabled;
|
|
28087
|
+
if (pluginSkillsConfig !== undefined) {
|
|
28088
|
+
if (Array.isArray(pluginSkillsConfig)) {
|
|
28089
|
+
isDisabled = !pluginSkillsConfig.includes(name);
|
|
28090
|
+
} else {
|
|
28091
|
+
isDisabled = pluginSkillsConfig.exclude.includes(name);
|
|
27989
28092
|
}
|
|
28093
|
+
} else if (isV1Fallback) {
|
|
28094
|
+
isDisabled = hasEnabledEntries ? !enabledSkills?.has(skillKey) : disabledSkills.has(skillKey);
|
|
28095
|
+
} else {
|
|
28096
|
+
isDisabled = false;
|
|
27990
28097
|
}
|
|
28098
|
+
skills.push({
|
|
28099
|
+
name,
|
|
28100
|
+
pluginName,
|
|
28101
|
+
pluginSource,
|
|
28102
|
+
path: skillPath,
|
|
28103
|
+
disabled: isDisabled,
|
|
28104
|
+
pluginSkillsMode
|
|
28105
|
+
});
|
|
27991
28106
|
}
|
|
27992
|
-
|
|
27993
|
-
|
|
27994
|
-
|
|
27995
|
-
|
|
27996
|
-
|
|
28107
|
+
}
|
|
28108
|
+
return skills;
|
|
28109
|
+
}
|
|
28110
|
+
async function findSkillByName(skillName, workspacePath = process.cwd()) {
|
|
28111
|
+
const allSkills = await getAllSkillsFromPlugins(workspacePath);
|
|
28112
|
+
return allSkills.filter((s) => s.name === skillName);
|
|
28113
|
+
}
|
|
28114
|
+
async function discoverSkillNames(pluginPath) {
|
|
28115
|
+
if (!existsSync8(pluginPath))
|
|
28116
|
+
return [];
|
|
28117
|
+
const skillsDir = join11(pluginPath, "skills");
|
|
28118
|
+
if (existsSync8(skillsDir)) {
|
|
28119
|
+
const entries = await readdir3(skillsDir, { withFileTypes: true });
|
|
28120
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
28121
|
+
}
|
|
28122
|
+
const nestedSkills = await discoverNestedSkillEntries(pluginPath);
|
|
28123
|
+
if (nestedSkills.length > 0)
|
|
28124
|
+
return nestedSkills.map((entry) => entry.name);
|
|
28125
|
+
const rootSkillMd = join11(pluginPath, "SKILL.md");
|
|
28126
|
+
if (existsSync8(rootSkillMd)) {
|
|
28127
|
+
try {
|
|
28128
|
+
const content = await readFile7(rootSkillMd, "utf-8");
|
|
28129
|
+
const { parseSkillMetadata: parseSkillMetadata2 } = await Promise.resolve().then(() => (init_skill(), exports_skill));
|
|
28130
|
+
const metadata = parseSkillMetadata2(content);
|
|
28131
|
+
return [metadata?.name ?? basename4(pluginPath)];
|
|
28132
|
+
} catch {
|
|
28133
|
+
return [basename4(pluginPath)];
|
|
27997
28134
|
}
|
|
27998
|
-
const removedEntry = getPluginSource(config.plugins[index]);
|
|
27999
|
-
config.plugins.splice(index, 1);
|
|
28000
|
-
pruneDisabledSkillsForPlugin(config, removedEntry);
|
|
28001
|
-
pruneEnabledSkillsForPlugin(config, removedEntry);
|
|
28002
|
-
const newContent = dump(config, { lineWidth: -1 });
|
|
28003
|
-
await writeFile4(configPath, newContent, "utf-8");
|
|
28004
|
-
return { success: true };
|
|
28005
|
-
} catch (error) {
|
|
28006
|
-
return {
|
|
28007
|
-
success: false,
|
|
28008
|
-
error: error instanceof Error ? error.message : String(error)
|
|
28009
|
-
};
|
|
28010
28135
|
}
|
|
28136
|
+
return [];
|
|
28011
28137
|
}
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28016
|
-
|
|
28138
|
+
var init_skills = __esm(() => {
|
|
28139
|
+
init_js_yaml();
|
|
28140
|
+
init_constants();
|
|
28141
|
+
init_workspace_config();
|
|
28142
|
+
init_plugin();
|
|
28143
|
+
init_plugin_path();
|
|
28144
|
+
init_marketplace();
|
|
28145
|
+
init_skill();
|
|
28146
|
+
});
|
|
28147
|
+
|
|
28148
|
+
// src/core/transform.ts
|
|
28149
|
+
import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir6, cp, readdir as readdir4 } from "node:fs/promises";
|
|
28150
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
28151
|
+
import { join as join12, basename as basename5, dirname as dirname5, relative as relative2 } from "node:path";
|
|
28152
|
+
async function ensureWorkspaceRules(filePath, repositories, skillsIndexRefs = []) {
|
|
28153
|
+
const rulesContent = generateWorkspaceRules(repositories, skillsIndexRefs);
|
|
28154
|
+
const startMarker = "<!-- WORKSPACE-RULES:START -->";
|
|
28155
|
+
const endMarker = "<!-- WORKSPACE-RULES:END -->";
|
|
28156
|
+
if (!existsSync9(filePath)) {
|
|
28157
|
+
await writeFile4(filePath, `${rulesContent.trim()}
|
|
28158
|
+
`, "utf-8");
|
|
28017
28159
|
return;
|
|
28018
|
-
|
|
28019
|
-
|
|
28020
|
-
|
|
28021
|
-
|
|
28160
|
+
}
|
|
28161
|
+
const content = await readFile8(filePath, "utf-8");
|
|
28162
|
+
const startIndex = content.indexOf(startMarker);
|
|
28163
|
+
const endIndex = content.indexOf(endMarker);
|
|
28164
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
28165
|
+
const before = content.substring(0, startIndex);
|
|
28166
|
+
const after = content.substring(endIndex + endMarker.length);
|
|
28167
|
+
await writeFile4(filePath, before + rulesContent.trim() + after, "utf-8");
|
|
28168
|
+
} else {
|
|
28169
|
+
await writeFile4(filePath, content + rulesContent, "utf-8");
|
|
28022
28170
|
}
|
|
28023
28171
|
}
|
|
28024
|
-
function
|
|
28025
|
-
if (
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28172
|
+
function isExcluded(pluginPath, filePath, exclude) {
|
|
28173
|
+
if (!exclude || exclude.length === 0)
|
|
28174
|
+
return false;
|
|
28175
|
+
const relativePath = relative2(pluginPath, filePath).replaceAll("\\", "/");
|
|
28176
|
+
return import_micromatch.default.isMatch(relativePath, exclude);
|
|
28177
|
+
}
|
|
28178
|
+
async function copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, exclude) {
|
|
28179
|
+
await mkdir6(destDir, { recursive: true });
|
|
28180
|
+
const entries = await readdir4(sourceDir, { withFileTypes: true });
|
|
28181
|
+
for (const entry of entries) {
|
|
28182
|
+
const sourcePath = join12(sourceDir, entry.name);
|
|
28183
|
+
const destPath = join12(destDir, entry.name);
|
|
28184
|
+
if (isExcluded(pluginPath, sourcePath, exclude)) {
|
|
28185
|
+
continue;
|
|
28032
28186
|
}
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
if (parsed) {
|
|
28038
|
-
const names = [parsed.repo];
|
|
28039
|
-
const ownerRepo = `${parsed.owner}-${parsed.repo}`;
|
|
28040
|
-
if (ownerRepo !== parsed.repo)
|
|
28041
|
-
names.push(ownerRepo);
|
|
28042
|
-
return names;
|
|
28187
|
+
if (entry.isDirectory()) {
|
|
28188
|
+
await copyDirectoryWithExclusions(sourcePath, destPath, pluginPath, exclude);
|
|
28189
|
+
} else {
|
|
28190
|
+
await cp(sourcePath, destPath);
|
|
28043
28191
|
}
|
|
28044
28192
|
}
|
|
28045
|
-
const parts = pluginSource.split(/[/\\]/).filter(Boolean);
|
|
28046
|
-
const last2 = parts[parts.length - 1];
|
|
28047
|
-
if (!last2)
|
|
28048
|
-
return [];
|
|
28049
|
-
return [last2.replace(/\.git$/, "")];
|
|
28050
28193
|
}
|
|
28051
|
-
function
|
|
28052
|
-
return
|
|
28194
|
+
function getMapping(client, options2) {
|
|
28195
|
+
return options2?.clientMappings?.[client] ?? CLIENT_MAPPINGS[client];
|
|
28053
28196
|
}
|
|
28054
|
-
function
|
|
28055
|
-
const
|
|
28056
|
-
|
|
28057
|
-
|
|
28058
|
-
if (
|
|
28059
|
-
|
|
28060
|
-
config.plugins[index] = objectEntry;
|
|
28061
|
-
return objectEntry;
|
|
28197
|
+
async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
|
|
28198
|
+
const { dryRun = false } = options2;
|
|
28199
|
+
const mapping = getMapping(client, options2);
|
|
28200
|
+
const results = [];
|
|
28201
|
+
if (!mapping.commandsPath) {
|
|
28202
|
+
return results;
|
|
28062
28203
|
}
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
const colonIdx = skillKey.indexOf(":");
|
|
28067
|
-
if (colonIdx === -1)
|
|
28068
|
-
return null;
|
|
28069
|
-
return {
|
|
28070
|
-
pluginName: skillKey.slice(0, colonIdx),
|
|
28071
|
-
skillName: skillKey.slice(colonIdx + 1)
|
|
28072
|
-
};
|
|
28073
|
-
}
|
|
28074
|
-
async function addDisabledSkill(skillKey, workspacePath = process.cwd()) {
|
|
28075
|
-
const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
28076
|
-
if (!existsSync8(configPath)) {
|
|
28077
|
-
return {
|
|
28078
|
-
success: false,
|
|
28079
|
-
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
28080
|
-
};
|
|
28204
|
+
const sourceDir = join12(pluginPath, "commands");
|
|
28205
|
+
if (!existsSync9(sourceDir)) {
|
|
28206
|
+
return results;
|
|
28081
28207
|
}
|
|
28082
|
-
const
|
|
28083
|
-
if (!
|
|
28084
|
-
|
|
28085
|
-
success: false,
|
|
28086
|
-
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
28087
|
-
};
|
|
28208
|
+
const destDir = join12(workspacePath, mapping.commandsPath);
|
|
28209
|
+
if (!dryRun) {
|
|
28210
|
+
await mkdir6(destDir, { recursive: true });
|
|
28088
28211
|
}
|
|
28089
|
-
const
|
|
28090
|
-
|
|
28091
|
-
|
|
28092
|
-
const
|
|
28093
|
-
const
|
|
28094
|
-
if (
|
|
28095
|
-
return {
|
|
28096
|
-
success: false,
|
|
28097
|
-
error: `Plugin '${pluginName}' not found in workspace config`
|
|
28098
|
-
};
|
|
28212
|
+
const files = await readdir4(sourceDir);
|
|
28213
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
28214
|
+
const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join12(sourceDir, file), options2.exclude)).map(async (file) => {
|
|
28215
|
+
const sourcePath = join12(sourceDir, file);
|
|
28216
|
+
const destPath = join12(destDir, file);
|
|
28217
|
+
if (dryRun) {
|
|
28218
|
+
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
28099
28219
|
}
|
|
28100
|
-
|
|
28101
|
-
|
|
28220
|
+
try {
|
|
28221
|
+
const content = await readFile8(sourcePath, "utf-8");
|
|
28222
|
+
await writeFile4(destPath, content, "utf-8");
|
|
28223
|
+
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
28224
|
+
} catch (error) {
|
|
28102
28225
|
return {
|
|
28103
|
-
|
|
28104
|
-
|
|
28226
|
+
source: sourcePath,
|
|
28227
|
+
destination: destPath,
|
|
28228
|
+
action: "failed",
|
|
28229
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28105
28230
|
};
|
|
28106
28231
|
}
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28232
|
+
});
|
|
28233
|
+
return Promise.all(copyPromises);
|
|
28234
|
+
}
|
|
28235
|
+
async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
|
|
28236
|
+
const { dryRun = false, skillNameMap, syncMode = "copy", canonicalSkillsPath } = options2;
|
|
28237
|
+
const mapping = getMapping(client, options2);
|
|
28238
|
+
const results = [];
|
|
28239
|
+
if (!mapping.skillsPath) {
|
|
28240
|
+
return results;
|
|
28241
|
+
}
|
|
28242
|
+
const skillsDir = join12(pluginPath, "skills");
|
|
28243
|
+
let skillSources;
|
|
28244
|
+
if (existsSync9(skillsDir)) {
|
|
28245
|
+
const entries = await readdir4(skillsDir, { withFileTypes: true });
|
|
28246
|
+
skillSources = entries.filter((e) => e.isDirectory()).filter((e) => !isExcluded(pluginPath, join12(skillsDir, e.name), options2.exclude)).map((e) => ({ name: e.name, sourcePath: join12(skillsDir, e.name), isRootLevel: false }));
|
|
28247
|
+
} else {
|
|
28248
|
+
const nestedSkills = (await discoverNestedSkillEntries(pluginPath)).filter((entry) => !isExcluded(pluginPath, entry.skillPath, options2.exclude)).map((entry) => ({ name: entry.name, sourcePath: entry.skillPath, isRootLevel: false }));
|
|
28249
|
+
if (nestedSkills.length > 0) {
|
|
28250
|
+
skillSources = nestedSkills;
|
|
28251
|
+
} else {
|
|
28252
|
+
const rootSkillMd = join12(pluginPath, "SKILL.md");
|
|
28253
|
+
if (existsSync9(rootSkillMd)) {
|
|
28254
|
+
const content = await readFile8(rootSkillMd, "utf-8");
|
|
28255
|
+
const metadata = parseSkillMetadata(content);
|
|
28256
|
+
const skillName = metadata?.name ?? basename5(pluginPath);
|
|
28257
|
+
skillSources = [{ name: skillName, sourcePath: pluginPath, isRootLevel: true }];
|
|
28258
|
+
} else {
|
|
28259
|
+
return results;
|
|
28260
|
+
}
|
|
28113
28261
|
}
|
|
28114
|
-
entry.skills = { exclude: [...existing, skillName] };
|
|
28115
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28116
|
-
return { success: true };
|
|
28117
|
-
} catch (error) {
|
|
28118
|
-
return {
|
|
28119
|
-
success: false,
|
|
28120
|
-
error: error instanceof Error ? error.message : String(error)
|
|
28121
|
-
};
|
|
28122
28262
|
}
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
28126
|
-
if (!existsSync8(configPath)) {
|
|
28127
|
-
return {
|
|
28128
|
-
success: false,
|
|
28129
|
-
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
|
|
28130
|
-
};
|
|
28263
|
+
if (skillNameMap) {
|
|
28264
|
+
skillSources = skillSources.filter((s) => skillNameMap.has(s.name));
|
|
28131
28265
|
}
|
|
28132
|
-
|
|
28133
|
-
|
|
28134
|
-
return {
|
|
28135
|
-
success: false,
|
|
28136
|
-
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
28137
|
-
};
|
|
28266
|
+
if (skillSources.length === 0) {
|
|
28267
|
+
return results;
|
|
28138
28268
|
}
|
|
28139
|
-
const
|
|
28140
|
-
|
|
28141
|
-
|
|
28142
|
-
|
|
28143
|
-
|
|
28144
|
-
|
|
28269
|
+
const destDir = join12(workspacePath, mapping.skillsPath);
|
|
28270
|
+
if (!dryRun) {
|
|
28271
|
+
await mkdir6(destDir, { recursive: true });
|
|
28272
|
+
}
|
|
28273
|
+
const useSymlinks = syncMode === "symlink" && !isUniversalClient(client) && canonicalSkillsPath;
|
|
28274
|
+
const copyPromises = skillSources.map(async (skill) => {
|
|
28275
|
+
const resolvedName = skillNameMap?.get(skill.name) ?? skill.name;
|
|
28276
|
+
const skillDestPath = join12(destDir, resolvedName);
|
|
28277
|
+
if (dryRun) {
|
|
28145
28278
|
return {
|
|
28146
|
-
|
|
28147
|
-
|
|
28279
|
+
source: skill.sourcePath,
|
|
28280
|
+
destination: skillDestPath,
|
|
28281
|
+
action: "copied"
|
|
28148
28282
|
};
|
|
28149
28283
|
}
|
|
28150
|
-
|
|
28151
|
-
|
|
28152
|
-
|
|
28284
|
+
if (useSymlinks) {
|
|
28285
|
+
const canonicalSkillPath = join12(workspacePath, canonicalSkillsPath, resolvedName);
|
|
28286
|
+
const symlinkCreated = await createSymlink(canonicalSkillPath, skillDestPath);
|
|
28287
|
+
if (symlinkCreated) {
|
|
28288
|
+
return {
|
|
28289
|
+
source: canonicalSkillPath,
|
|
28290
|
+
destination: skillDestPath,
|
|
28291
|
+
action: "copied"
|
|
28292
|
+
};
|
|
28293
|
+
}
|
|
28153
28294
|
}
|
|
28154
|
-
|
|
28295
|
+
try {
|
|
28296
|
+
if (skill.isRootLevel) {
|
|
28297
|
+
await mkdir6(skillDestPath, { recursive: true });
|
|
28298
|
+
await cp(join12(skill.sourcePath, "SKILL.md"), join12(skillDestPath, "SKILL.md"));
|
|
28299
|
+
} else if (options2.exclude && options2.exclude.length > 0) {
|
|
28300
|
+
await copyDirectoryWithExclusions(skill.sourcePath, skillDestPath, pluginPath, options2.exclude);
|
|
28301
|
+
} else {
|
|
28302
|
+
await cp(skill.sourcePath, skillDestPath, { recursive: true });
|
|
28303
|
+
}
|
|
28155
28304
|
return {
|
|
28156
|
-
|
|
28157
|
-
|
|
28305
|
+
source: skill.sourcePath,
|
|
28306
|
+
destination: skillDestPath,
|
|
28307
|
+
action: "copied"
|
|
28158
28308
|
};
|
|
28159
|
-
}
|
|
28160
|
-
if (!entry.skills.exclude.includes(skillName)) {
|
|
28309
|
+
} catch (error) {
|
|
28161
28310
|
return {
|
|
28162
|
-
|
|
28163
|
-
|
|
28311
|
+
source: skill.sourcePath,
|
|
28312
|
+
destination: skillDestPath,
|
|
28313
|
+
action: "failed",
|
|
28314
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28164
28315
|
};
|
|
28165
28316
|
}
|
|
28166
|
-
|
|
28167
|
-
|
|
28168
|
-
|
|
28169
|
-
|
|
28170
|
-
|
|
28171
|
-
|
|
28172
|
-
|
|
28173
|
-
|
|
28174
|
-
};
|
|
28317
|
+
});
|
|
28318
|
+
return Promise.all(copyPromises);
|
|
28319
|
+
}
|
|
28320
|
+
async function collectPluginSkills(pluginPath, pluginSource, disabledSkills, pluginName, enabledSkills, pluginSkillsConfig) {
|
|
28321
|
+
const skillsDir = join12(pluginPath, "skills");
|
|
28322
|
+
const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && pluginName && [...enabledSkills].some((s) => s.startsWith(`${pluginName}:`));
|
|
28323
|
+
let candidateDirs;
|
|
28324
|
+
if (existsSync9(skillsDir)) {
|
|
28325
|
+
const entries = await readdir4(skillsDir, { withFileTypes: true });
|
|
28326
|
+
candidateDirs = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, path: join12(skillsDir, e.name) }));
|
|
28327
|
+
} else {
|
|
28328
|
+
const nestedDirs = await discoverNestedSkillEntries(pluginPath).then((entries) => entries.map((entry) => ({ name: entry.name, path: entry.skillPath })));
|
|
28329
|
+
if (nestedDirs.length > 0) {
|
|
28330
|
+
candidateDirs = nestedDirs;
|
|
28331
|
+
} else {
|
|
28332
|
+
const rootSkillMd = join12(pluginPath, "SKILL.md");
|
|
28333
|
+
if (existsSync9(rootSkillMd)) {
|
|
28334
|
+
const content = await readFile8(rootSkillMd, "utf-8");
|
|
28335
|
+
const metadata = parseSkillMetadata(content);
|
|
28336
|
+
const skillName = metadata?.name ?? basename5(pluginPath);
|
|
28337
|
+
candidateDirs = [{ name: skillName, path: pluginPath }];
|
|
28338
|
+
} else {
|
|
28339
|
+
candidateDirs = [];
|
|
28340
|
+
}
|
|
28341
|
+
}
|
|
28342
|
+
}
|
|
28343
|
+
let filteredDirs;
|
|
28344
|
+
if (pluginSkillsConfig !== undefined) {
|
|
28345
|
+
if (Array.isArray(pluginSkillsConfig)) {
|
|
28346
|
+
filteredDirs = candidateDirs.filter((e) => pluginSkillsConfig.includes(e.name));
|
|
28347
|
+
} else {
|
|
28348
|
+
filteredDirs = candidateDirs.filter((e) => !pluginSkillsConfig.exclude.includes(e.name));
|
|
28349
|
+
}
|
|
28350
|
+
} else if (pluginName) {
|
|
28351
|
+
if (hasEnabledEntries) {
|
|
28352
|
+
filteredDirs = candidateDirs.filter((e) => enabledSkills?.has(`${pluginName}:${e.name}`));
|
|
28353
|
+
} else if (disabledSkills) {
|
|
28354
|
+
filteredDirs = candidateDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`));
|
|
28355
|
+
} else {
|
|
28356
|
+
filteredDirs = candidateDirs;
|
|
28357
|
+
}
|
|
28358
|
+
} else {
|
|
28359
|
+
filteredDirs = candidateDirs;
|
|
28175
28360
|
}
|
|
28361
|
+
return filteredDirs.map((entry) => ({
|
|
28362
|
+
folderName: entry.name,
|
|
28363
|
+
skillPath: entry.path,
|
|
28364
|
+
pluginPath,
|
|
28365
|
+
pluginSource
|
|
28366
|
+
}));
|
|
28176
28367
|
}
|
|
28177
|
-
async function
|
|
28178
|
-
const
|
|
28179
|
-
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
|
|
28183
|
-
};
|
|
28368
|
+
async function copyHooks(pluginPath, workspacePath, client, options2 = {}) {
|
|
28369
|
+
const { dryRun = false } = options2;
|
|
28370
|
+
const mapping = getMapping(client, options2);
|
|
28371
|
+
const results = [];
|
|
28372
|
+
if (!mapping.hooksPath) {
|
|
28373
|
+
return results;
|
|
28184
28374
|
}
|
|
28185
|
-
const
|
|
28186
|
-
if (!
|
|
28187
|
-
return
|
|
28188
|
-
success: false,
|
|
28189
|
-
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
28190
|
-
};
|
|
28375
|
+
const sourceDir = join12(pluginPath, "hooks");
|
|
28376
|
+
if (!existsSync9(sourceDir)) {
|
|
28377
|
+
return results;
|
|
28191
28378
|
}
|
|
28192
|
-
const
|
|
28379
|
+
const destDir = join12(workspacePath, mapping.hooksPath);
|
|
28380
|
+
if (dryRun) {
|
|
28381
|
+
results.push({ source: sourceDir, destination: destDir, action: "copied" });
|
|
28382
|
+
return results;
|
|
28383
|
+
}
|
|
28384
|
+
await mkdir6(destDir, { recursive: true });
|
|
28193
28385
|
try {
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
|
|
28197
|
-
|
|
28198
|
-
return {
|
|
28199
|
-
success: false,
|
|
28200
|
-
error: `Plugin '${pluginName}' not found in workspace config`
|
|
28201
|
-
};
|
|
28202
|
-
}
|
|
28203
|
-
const entry = ensureObjectPluginEntry(config, index);
|
|
28204
|
-
if (entry.skills && !Array.isArray(entry.skills)) {
|
|
28205
|
-
return {
|
|
28206
|
-
success: false,
|
|
28207
|
-
error: `Plugin '${pluginName}' is in blocklist mode; use removeDisabledSkill to enable a skill`
|
|
28208
|
-
};
|
|
28209
|
-
}
|
|
28210
|
-
const existing = entry.skills ?? [];
|
|
28211
|
-
if (existing.includes(skillName)) {
|
|
28212
|
-
return {
|
|
28213
|
-
success: false,
|
|
28214
|
-
error: `Skill '${skillKey}' is already enabled`
|
|
28215
|
-
};
|
|
28386
|
+
if (options2.exclude && options2.exclude.length > 0) {
|
|
28387
|
+
await copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, options2.exclude);
|
|
28388
|
+
} else {
|
|
28389
|
+
await cp(sourceDir, destDir, { recursive: true });
|
|
28216
28390
|
}
|
|
28217
|
-
|
|
28218
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28219
|
-
return { success: true };
|
|
28391
|
+
results.push({ source: sourceDir, destination: destDir, action: "copied" });
|
|
28220
28392
|
} catch (error) {
|
|
28221
|
-
|
|
28222
|
-
|
|
28223
|
-
|
|
28224
|
-
|
|
28393
|
+
results.push({
|
|
28394
|
+
source: sourceDir,
|
|
28395
|
+
destination: destDir,
|
|
28396
|
+
action: "failed",
|
|
28397
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28398
|
+
});
|
|
28225
28399
|
}
|
|
28400
|
+
return results;
|
|
28226
28401
|
}
|
|
28227
|
-
async function
|
|
28228
|
-
const
|
|
28229
|
-
|
|
28230
|
-
|
|
28231
|
-
|
|
28232
|
-
|
|
28233
|
-
};
|
|
28402
|
+
async function copyAgents(pluginPath, workspacePath, client, options2 = {}) {
|
|
28403
|
+
const { dryRun = false } = options2;
|
|
28404
|
+
const mapping = getMapping(client, options2);
|
|
28405
|
+
const results = [];
|
|
28406
|
+
if (!mapping.agentsPath) {
|
|
28407
|
+
return results;
|
|
28234
28408
|
}
|
|
28235
|
-
const
|
|
28236
|
-
if (!
|
|
28237
|
-
return
|
|
28238
|
-
success: false,
|
|
28239
|
-
error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
|
|
28240
|
-
};
|
|
28409
|
+
const sourceDir = join12(pluginPath, "agents");
|
|
28410
|
+
if (!existsSync9(sourceDir)) {
|
|
28411
|
+
return results;
|
|
28241
28412
|
}
|
|
28242
|
-
const
|
|
28243
|
-
|
|
28244
|
-
|
|
28245
|
-
|
|
28246
|
-
|
|
28247
|
-
|
|
28413
|
+
const destDir = join12(workspacePath, mapping.agentsPath);
|
|
28414
|
+
if (!dryRun) {
|
|
28415
|
+
await mkdir6(destDir, { recursive: true });
|
|
28416
|
+
}
|
|
28417
|
+
const files = await readdir4(sourceDir);
|
|
28418
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
28419
|
+
const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join12(sourceDir, file), options2.exclude)).map(async (file) => {
|
|
28420
|
+
const sourcePath = join12(sourceDir, file);
|
|
28421
|
+
const destPath = join12(destDir, file);
|
|
28422
|
+
if (dryRun) {
|
|
28423
|
+
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
28424
|
+
}
|
|
28425
|
+
try {
|
|
28426
|
+
const content = await readFile8(sourcePath, "utf-8");
|
|
28427
|
+
await writeFile4(destPath, content, "utf-8");
|
|
28428
|
+
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
28429
|
+
} catch (error) {
|
|
28248
28430
|
return {
|
|
28249
|
-
|
|
28250
|
-
|
|
28431
|
+
source: sourcePath,
|
|
28432
|
+
destination: destPath,
|
|
28433
|
+
action: "failed",
|
|
28434
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28251
28435
|
};
|
|
28252
28436
|
}
|
|
28253
|
-
|
|
28254
|
-
|
|
28255
|
-
|
|
28437
|
+
});
|
|
28438
|
+
return Promise.all(copyPromises);
|
|
28439
|
+
}
|
|
28440
|
+
async function copyAndAdjustDirectory(sourceDir, destDir, sourceBase, pluginPath, skillsPath, skillNameMap, exclude) {
|
|
28441
|
+
await mkdir6(destDir, { recursive: true });
|
|
28442
|
+
const entries = await readdir4(sourceDir, { withFileTypes: true });
|
|
28443
|
+
for (const entry of entries) {
|
|
28444
|
+
const sourcePath = join12(sourceDir, entry.name);
|
|
28445
|
+
const destPath = join12(destDir, entry.name);
|
|
28446
|
+
if (isExcluded(pluginPath, sourcePath, exclude)) {
|
|
28447
|
+
continue;
|
|
28256
28448
|
}
|
|
28257
|
-
if (
|
|
28258
|
-
|
|
28259
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
28449
|
+
if (entry.isDirectory()) {
|
|
28450
|
+
await copyAndAdjustDirectory(sourcePath, destPath, sourceBase, pluginPath, skillsPath, skillNameMap, exclude);
|
|
28451
|
+
} else {
|
|
28452
|
+
const relativePath = relative2(sourceBase, sourcePath).replaceAll("\\", "/");
|
|
28453
|
+
const isMarkdown = entry.name.endsWith(".md") || entry.name.endsWith(".markdown");
|
|
28454
|
+
if (isMarkdown) {
|
|
28455
|
+
let content = await readFile8(sourcePath, "utf-8");
|
|
28456
|
+
content = adjustLinksInContent(content, relativePath, {
|
|
28457
|
+
...skillNameMap && { skillNameMap },
|
|
28458
|
+
workspaceSkillsPath: skillsPath
|
|
28459
|
+
});
|
|
28460
|
+
await writeFile4(destPath, content, "utf-8");
|
|
28461
|
+
} else {
|
|
28462
|
+
await cp(sourcePath, destPath);
|
|
28463
|
+
}
|
|
28262
28464
|
}
|
|
28263
|
-
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28465
|
+
}
|
|
28466
|
+
}
|
|
28467
|
+
async function copyGitHubContent(pluginPath, workspacePath, client, options2 = {}) {
|
|
28468
|
+
const { dryRun = false, skillNameMap } = options2;
|
|
28469
|
+
const mapping = getMapping(client, options2);
|
|
28470
|
+
const results = [];
|
|
28471
|
+
if (!mapping.githubPath) {
|
|
28472
|
+
return results;
|
|
28473
|
+
}
|
|
28474
|
+
const sourceDir = join12(pluginPath, ".github");
|
|
28475
|
+
if (!existsSync9(sourceDir)) {
|
|
28476
|
+
return results;
|
|
28477
|
+
}
|
|
28478
|
+
const destDir = join12(workspacePath, mapping.githubPath);
|
|
28479
|
+
if (dryRun) {
|
|
28480
|
+
results.push({ source: sourceDir, destination: destDir, action: "copied" });
|
|
28481
|
+
return results;
|
|
28482
|
+
}
|
|
28483
|
+
try {
|
|
28484
|
+
if (mapping.skillsPath || options2.exclude && options2.exclude.length > 0) {
|
|
28485
|
+
await copyAndAdjustDirectory(sourceDir, destDir, sourceDir, pluginPath, mapping.skillsPath ?? "", skillNameMap, options2.exclude);
|
|
28486
|
+
} else {
|
|
28487
|
+
await cp(sourceDir, destDir, { recursive: true });
|
|
28268
28488
|
}
|
|
28269
|
-
|
|
28270
|
-
entry.skills = newSkills;
|
|
28271
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28272
|
-
return { success: true };
|
|
28489
|
+
results.push({ source: sourceDir, destination: destDir, action: "copied" });
|
|
28273
28490
|
} catch (error) {
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
28491
|
+
results.push({
|
|
28492
|
+
source: sourceDir,
|
|
28493
|
+
destination: destDir,
|
|
28494
|
+
action: "failed",
|
|
28495
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28496
|
+
});
|
|
28278
28497
|
}
|
|
28498
|
+
return results;
|
|
28279
28499
|
}
|
|
28280
|
-
async function
|
|
28281
|
-
const
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28500
|
+
async function copyPluginToWorkspace(pluginPath, workspacePath, client, options2 = {}) {
|
|
28501
|
+
const { skillNameMap, syncMode, canonicalSkillsPath, ...baseOptions } = options2;
|
|
28502
|
+
const [commandResults, skillResults, hookResults, agentResults] = await Promise.all([
|
|
28503
|
+
copyCommands(pluginPath, workspacePath, client, baseOptions),
|
|
28504
|
+
copySkills(pluginPath, workspacePath, client, {
|
|
28505
|
+
...baseOptions,
|
|
28506
|
+
...skillNameMap && { skillNameMap },
|
|
28507
|
+
...syncMode && { syncMode },
|
|
28508
|
+
...canonicalSkillsPath && { canonicalSkillsPath }
|
|
28509
|
+
}),
|
|
28510
|
+
copyHooks(pluginPath, workspacePath, client, baseOptions),
|
|
28511
|
+
copyAgents(pluginPath, workspacePath, client, baseOptions)
|
|
28512
|
+
]);
|
|
28513
|
+
const githubResults = await copyGitHubContent(pluginPath, workspacePath, client, {
|
|
28514
|
+
...baseOptions,
|
|
28515
|
+
...skillNameMap && { skillNameMap }
|
|
28516
|
+
});
|
|
28517
|
+
return [...commandResults, ...skillResults, ...hookResults, ...agentResults, ...githubResults];
|
|
28518
|
+
}
|
|
28519
|
+
function isExplicitGitHubSource(source) {
|
|
28520
|
+
if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/") || source.startsWith("github.com/") || source.startsWith("gh:")) {
|
|
28521
|
+
return true;
|
|
28522
|
+
}
|
|
28523
|
+
if (!source.startsWith(".") && !source.startsWith("/") && source.includes("/")) {
|
|
28524
|
+
const parts = source.split("/");
|
|
28525
|
+
if (parts.length >= 3) {
|
|
28526
|
+
const validOwnerRepo = /^[a-zA-Z0-9_.-]+$/;
|
|
28527
|
+
if (parts[0] && parts[1] && validOwnerRepo.test(parts[0]) && validOwnerRepo.test(parts[1])) {
|
|
28528
|
+
return true;
|
|
28529
|
+
}
|
|
28530
|
+
}
|
|
28531
|
+
}
|
|
28532
|
+
return false;
|
|
28533
|
+
}
|
|
28534
|
+
function resolveFileSourcePath(source, defaultSourcePath, githubCache) {
|
|
28535
|
+
if (!isExplicitGitHubSource(source)) {
|
|
28536
|
+
if (source.startsWith("/")) {
|
|
28537
|
+
return { path: source };
|
|
28538
|
+
}
|
|
28539
|
+
if (source.startsWith("../")) {
|
|
28540
|
+
return { path: join12(process.cwd(), source) };
|
|
28541
|
+
}
|
|
28542
|
+
if (defaultSourcePath) {
|
|
28543
|
+
return { path: join12(defaultSourcePath, source) };
|
|
28544
|
+
}
|
|
28545
|
+
return { path: join12(process.cwd(), source) };
|
|
28287
28546
|
}
|
|
28288
|
-
|
|
28289
|
-
|
|
28290
|
-
const
|
|
28291
|
-
const
|
|
28292
|
-
if (
|
|
28547
|
+
const parsed = parseFileSource(source);
|
|
28548
|
+
if (parsed.type === "github" && parsed.owner && parsed.repo && parsed.filePath) {
|
|
28549
|
+
const cacheKey = `${parsed.owner}/${parsed.repo}`;
|
|
28550
|
+
const cachePath = githubCache?.get(cacheKey);
|
|
28551
|
+
if (!cachePath) {
|
|
28293
28552
|
return {
|
|
28294
|
-
|
|
28295
|
-
error: `
|
|
28553
|
+
path: "",
|
|
28554
|
+
error: `GitHub cache not found for ${cacheKey}. Ensure the repo is fetched.`
|
|
28296
28555
|
};
|
|
28297
28556
|
}
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28301
|
-
} else {
|
|
28302
|
-
entry.skills = skillNames.length > 0 ? { exclude: [...skillNames] } : undefined;
|
|
28303
|
-
}
|
|
28304
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28305
|
-
return { success: true };
|
|
28306
|
-
} catch (error) {
|
|
28557
|
+
return { path: join12(cachePath, parsed.filePath) };
|
|
28558
|
+
}
|
|
28559
|
+
if (parsed.type === "github") {
|
|
28307
28560
|
return {
|
|
28308
|
-
|
|
28309
|
-
error:
|
|
28561
|
+
path: "",
|
|
28562
|
+
error: `Invalid GitHub file source: ${source}. Must include path to file (e.g., owner/repo/path/to/file.md)`
|
|
28310
28563
|
};
|
|
28311
28564
|
}
|
|
28312
|
-
}
|
|
28313
|
-
function pruneEnabledSkillsForPlugin(config, pluginEntry) {
|
|
28314
|
-
if (!config.enabledSkills?.length)
|
|
28315
|
-
return;
|
|
28316
|
-
const names = extractPluginNames(pluginEntry);
|
|
28317
|
-
if (names.length === 0)
|
|
28318
|
-
return;
|
|
28319
|
-
const prefixes = names.map((n) => `${n}:`);
|
|
28320
|
-
config.enabledSkills = config.enabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
|
|
28321
|
-
if (config.enabledSkills.length === 0) {
|
|
28322
|
-
config.enabledSkills = undefined;
|
|
28323
|
-
}
|
|
28324
|
-
}
|
|
28325
|
-
async function resolveGitHubIdentity(pluginSource) {
|
|
28326
|
-
if (isGitHubUrl(pluginSource)) {
|
|
28327
|
-
const parsed = parseGitHubUrl(pluginSource);
|
|
28328
|
-
return parsed ? `${parsed.owner}/${parsed.repo}`.toLowerCase() : null;
|
|
28329
|
-
}
|
|
28330
|
-
if (isPluginSpec(pluginSource)) {
|
|
28331
|
-
const parsed = parsePluginSpec(pluginSource);
|
|
28332
|
-
if (!parsed)
|
|
28333
|
-
return null;
|
|
28334
|
-
const marketplace = await getMarketplace(parsed.marketplaceName);
|
|
28335
|
-
if (!marketplace)
|
|
28336
|
-
return null;
|
|
28337
|
-
const manifestResult = await parseMarketplaceManifest(marketplace.path);
|
|
28338
|
-
if (!manifestResult.success)
|
|
28339
|
-
return null;
|
|
28340
|
-
const entry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
|
|
28341
|
-
if (!entry || typeof entry.source === "string")
|
|
28342
|
-
return null;
|
|
28343
|
-
const parsedUrl = parseGitHubUrl(entry.source.url);
|
|
28344
|
-
return parsedUrl ? `${parsedUrl.owner}/${parsedUrl.repo}`.toLowerCase() : null;
|
|
28345
|
-
}
|
|
28346
28565
|
return null;
|
|
28347
28566
|
}
|
|
28348
|
-
async function
|
|
28349
|
-
const
|
|
28350
|
-
|
|
28351
|
-
|
|
28352
|
-
|
|
28353
|
-
|
|
28354
|
-
|
|
28355
|
-
|
|
28356
|
-
|
|
28357
|
-
|
|
28358
|
-
|
|
28359
|
-
|
|
28360
|
-
|
|
28361
|
-
|
|
28362
|
-
|
|
28363
|
-
|
|
28364
|
-
|
|
28365
|
-
|
|
28366
|
-
|
|
28367
|
-
|
|
28368
|
-
|
|
28369
|
-
|
|
28370
|
-
|
|
28567
|
+
async function copyWorkspaceFiles(sourcePath, workspacePath, files, options2 = {}) {
|
|
28568
|
+
const { dryRun = false, githubCache, repositories = [], skillsIndexRefs = [] } = options2;
|
|
28569
|
+
const results = [];
|
|
28570
|
+
const stringPatterns = [];
|
|
28571
|
+
const objectEntries = [];
|
|
28572
|
+
const copiedAgentFiles = [];
|
|
28573
|
+
for (const file of files) {
|
|
28574
|
+
if (typeof file === "string") {
|
|
28575
|
+
stringPatterns.push(file);
|
|
28576
|
+
} else {
|
|
28577
|
+
let dest = file.dest;
|
|
28578
|
+
if (!dest && file.source) {
|
|
28579
|
+
const parts = file.source.split("/");
|
|
28580
|
+
dest = parts[parts.length - 1] || file.source;
|
|
28581
|
+
}
|
|
28582
|
+
if (!dest) {
|
|
28583
|
+
results.push({
|
|
28584
|
+
source: "unknown",
|
|
28585
|
+
destination: join12(workspacePath, "unknown"),
|
|
28586
|
+
action: "failed",
|
|
28587
|
+
error: "File entry must have at least source or dest specified"
|
|
28588
|
+
});
|
|
28589
|
+
continue;
|
|
28590
|
+
}
|
|
28591
|
+
objectEntries.push(file.source ? { source: file.source, dest } : { dest });
|
|
28592
|
+
}
|
|
28371
28593
|
}
|
|
28372
|
-
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28594
|
+
if (stringPatterns.length > 0) {
|
|
28595
|
+
if (!sourcePath) {
|
|
28596
|
+
for (const pattern of stringPatterns) {
|
|
28597
|
+
if (!isGlobPattern(pattern) && !pattern.startsWith("!")) {
|
|
28598
|
+
results.push({
|
|
28599
|
+
source: pattern,
|
|
28600
|
+
destination: join12(workspacePath, pattern),
|
|
28601
|
+
action: "failed",
|
|
28602
|
+
error: `Cannot resolve file '${pattern}' - no workspace.source configured and no explicit source provided`
|
|
28603
|
+
});
|
|
28604
|
+
}
|
|
28605
|
+
}
|
|
28606
|
+
} else {
|
|
28607
|
+
const resolvedFiles = await resolveGlobPatterns(sourcePath, stringPatterns);
|
|
28608
|
+
for (const resolved of resolvedFiles) {
|
|
28609
|
+
const destPath = join12(workspacePath, resolved.relativePath);
|
|
28610
|
+
if (!existsSync9(resolved.sourcePath)) {
|
|
28611
|
+
const wasLiteral = stringPatterns.some((p) => !isGlobPattern(p) && !p.startsWith("!") && p === resolved.relativePath);
|
|
28612
|
+
if (wasLiteral) {
|
|
28613
|
+
results.push({
|
|
28614
|
+
source: resolved.sourcePath,
|
|
28615
|
+
destination: destPath,
|
|
28616
|
+
action: "failed",
|
|
28617
|
+
error: `Source file not found: ${resolved.sourcePath}`
|
|
28618
|
+
});
|
|
28619
|
+
}
|
|
28620
|
+
continue;
|
|
28621
|
+
}
|
|
28622
|
+
if (dryRun) {
|
|
28623
|
+
results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
|
|
28624
|
+
if (AGENT_FILES2.includes(resolved.relativePath)) {
|
|
28625
|
+
copiedAgentFiles.push(resolved.relativePath);
|
|
28626
|
+
}
|
|
28627
|
+
continue;
|
|
28628
|
+
}
|
|
28629
|
+
try {
|
|
28630
|
+
await mkdir6(dirname5(destPath), { recursive: true });
|
|
28631
|
+
const content = await readFile8(resolved.sourcePath, "utf-8");
|
|
28632
|
+
await writeFile4(destPath, content, "utf-8");
|
|
28633
|
+
results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
|
|
28634
|
+
if (AGENT_FILES2.includes(resolved.relativePath)) {
|
|
28635
|
+
copiedAgentFiles.push(resolved.relativePath);
|
|
28636
|
+
}
|
|
28637
|
+
} catch (error) {
|
|
28638
|
+
results.push({
|
|
28639
|
+
source: resolved.sourcePath,
|
|
28640
|
+
destination: destPath,
|
|
28641
|
+
action: "failed",
|
|
28642
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28643
|
+
});
|
|
28644
|
+
}
|
|
28645
|
+
}
|
|
28646
|
+
}
|
|
28380
28647
|
}
|
|
28381
|
-
for (const
|
|
28382
|
-
const
|
|
28383
|
-
|
|
28384
|
-
|
|
28648
|
+
for (const entry of objectEntries) {
|
|
28649
|
+
const destPath = join12(workspacePath, entry.dest);
|
|
28650
|
+
let srcPath;
|
|
28651
|
+
if (entry.source) {
|
|
28652
|
+
const resolved = resolveFileSourcePath(entry.source, sourcePath, githubCache);
|
|
28653
|
+
if (!resolved) {
|
|
28654
|
+
results.push({
|
|
28655
|
+
source: entry.source,
|
|
28656
|
+
destination: destPath,
|
|
28657
|
+
action: "failed",
|
|
28658
|
+
error: `Failed to resolve source: ${entry.source}`
|
|
28659
|
+
});
|
|
28660
|
+
continue;
|
|
28661
|
+
}
|
|
28662
|
+
if (resolved.error) {
|
|
28663
|
+
results.push({
|
|
28664
|
+
source: entry.source,
|
|
28665
|
+
destination: destPath,
|
|
28666
|
+
action: "failed",
|
|
28667
|
+
error: resolved.error
|
|
28668
|
+
});
|
|
28669
|
+
continue;
|
|
28670
|
+
}
|
|
28671
|
+
srcPath = resolved.path;
|
|
28672
|
+
} else {
|
|
28673
|
+
if (!sourcePath) {
|
|
28674
|
+
results.push({
|
|
28675
|
+
source: entry.dest,
|
|
28676
|
+
destination: destPath,
|
|
28677
|
+
action: "failed",
|
|
28678
|
+
error: `Cannot resolve file '${entry.dest}' - no workspace.source configured and no explicit source provided`
|
|
28679
|
+
});
|
|
28680
|
+
continue;
|
|
28681
|
+
}
|
|
28682
|
+
srcPath = join12(sourcePath, entry.dest);
|
|
28683
|
+
}
|
|
28684
|
+
if (!existsSync9(srcPath)) {
|
|
28685
|
+
results.push({
|
|
28686
|
+
source: srcPath,
|
|
28687
|
+
destination: destPath,
|
|
28688
|
+
action: "failed",
|
|
28689
|
+
error: `Source file not found: ${srcPath}`
|
|
28690
|
+
});
|
|
28385
28691
|
continue;
|
|
28386
28692
|
}
|
|
28387
|
-
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
28391
|
-
|
|
28392
|
-
if (index === -1) {
|
|
28393
|
-
console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
|
|
28693
|
+
if (dryRun) {
|
|
28694
|
+
results.push({ source: srcPath, destination: destPath, action: "copied" });
|
|
28695
|
+
if (AGENT_FILES2.includes(entry.dest)) {
|
|
28696
|
+
copiedAgentFiles.push(entry.dest);
|
|
28697
|
+
}
|
|
28394
28698
|
continue;
|
|
28395
28699
|
}
|
|
28396
|
-
|
|
28397
|
-
|
|
28398
|
-
|
|
28700
|
+
try {
|
|
28701
|
+
await mkdir6(dirname5(destPath), { recursive: true });
|
|
28702
|
+
const content = await readFile8(srcPath, "utf-8");
|
|
28703
|
+
await writeFile4(destPath, content, "utf-8");
|
|
28704
|
+
results.push({ source: srcPath, destination: destPath, action: "copied" });
|
|
28705
|
+
if (AGENT_FILES2.includes(entry.dest)) {
|
|
28706
|
+
copiedAgentFiles.push(entry.dest);
|
|
28707
|
+
}
|
|
28708
|
+
} catch (error) {
|
|
28709
|
+
results.push({
|
|
28710
|
+
source: srcPath,
|
|
28711
|
+
destination: destPath,
|
|
28712
|
+
action: "failed",
|
|
28713
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
28714
|
+
});
|
|
28399
28715
|
}
|
|
28400
28716
|
}
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
try {
|
|
28416
|
-
const content = await readFile7(configPath, "utf-8");
|
|
28417
|
-
const config = load(content);
|
|
28418
|
-
const removeSet = new Set(changes.remove);
|
|
28419
|
-
config.repositories = config.repositories.filter((repo) => !removeSet.has(repo.path));
|
|
28420
|
-
config.repositories.push(...changes.add);
|
|
28421
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28422
|
-
return { success: true };
|
|
28423
|
-
} catch (error) {
|
|
28424
|
-
return {
|
|
28425
|
-
success: false,
|
|
28426
|
-
error: error instanceof Error ? error.message : String(error)
|
|
28427
|
-
};
|
|
28428
|
-
}
|
|
28429
|
-
}
|
|
28430
|
-
async function setRepositories(repositories, workspacePath = process.cwd()) {
|
|
28431
|
-
const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
28432
|
-
try {
|
|
28433
|
-
const content = await readFile7(configPath, "utf-8");
|
|
28434
|
-
const config = load(content);
|
|
28435
|
-
config.repositories = repositories;
|
|
28436
|
-
await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
|
|
28437
|
-
return { success: true };
|
|
28438
|
-
} catch (error) {
|
|
28439
|
-
return {
|
|
28440
|
-
success: false,
|
|
28441
|
-
error: error instanceof Error ? error.message : String(error)
|
|
28442
|
-
};
|
|
28717
|
+
if (!dryRun && repositories.length > 0) {
|
|
28718
|
+
for (const agentFile of copiedAgentFiles) {
|
|
28719
|
+
const targetPath = join12(workspacePath, agentFile);
|
|
28720
|
+
try {
|
|
28721
|
+
await ensureWorkspaceRules(targetPath, repositories, skillsIndexRefs);
|
|
28722
|
+
} catch (error) {
|
|
28723
|
+
results.push({
|
|
28724
|
+
source: "WORKSPACE-RULES",
|
|
28725
|
+
destination: targetPath,
|
|
28726
|
+
action: "failed",
|
|
28727
|
+
error: error instanceof Error ? error.message : "Failed to inject WORKSPACE-RULES"
|
|
28728
|
+
});
|
|
28729
|
+
}
|
|
28730
|
+
}
|
|
28443
28731
|
}
|
|
28732
|
+
return results;
|
|
28444
28733
|
}
|
|
28445
|
-
var
|
|
28446
|
-
var
|
|
28447
|
-
|
|
28734
|
+
var import_micromatch, AGENT_FILES2;
|
|
28735
|
+
var init_transform = __esm(() => {
|
|
28736
|
+
init_glob_patterns();
|
|
28737
|
+
init_client_mapping();
|
|
28448
28738
|
init_constants();
|
|
28449
|
-
init_workspace_config();
|
|
28450
|
-
init_marketplace_manifest_parser();
|
|
28451
28739
|
init_plugin_path();
|
|
28452
|
-
|
|
28453
|
-
|
|
28740
|
+
init_symlink();
|
|
28741
|
+
init_link_adjuster();
|
|
28742
|
+
init_skill();
|
|
28743
|
+
init_skills();
|
|
28744
|
+
import_micromatch = __toESM(require_micromatch(), 1);
|
|
28745
|
+
AGENT_FILES2 = ["AGENTS.md", "CLAUDE.md"];
|
|
28454
28746
|
});
|
|
28455
28747
|
|
|
28456
28748
|
// src/core/repo-skills.ts
|
|
28457
|
-
import { existsSync as
|
|
28458
|
-
import { readdir as
|
|
28459
|
-
import { basename as
|
|
28749
|
+
import { existsSync as existsSync10, lstatSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
28750
|
+
import { readdir as readdir5, readFile as readFile9 } from "node:fs/promises";
|
|
28751
|
+
import { basename as basename6, join as join13, relative as relative3, resolve as resolve9 } from "node:path";
|
|
28460
28752
|
async function discoverRepoSkills(repoPath, options2) {
|
|
28461
28753
|
if (options2.disabled)
|
|
28462
28754
|
return [];
|
|
@@ -28476,19 +28768,19 @@ async function discoverRepoSkills(repoPath, options2) {
|
|
|
28476
28768
|
const results = [];
|
|
28477
28769
|
const seen = new Set;
|
|
28478
28770
|
for (const skillDir of skillDirs) {
|
|
28479
|
-
const absDir =
|
|
28480
|
-
if (!
|
|
28771
|
+
const absDir = join13(repoPath, skillDir);
|
|
28772
|
+
if (!existsSync10(absDir))
|
|
28481
28773
|
continue;
|
|
28482
28774
|
let entries;
|
|
28483
28775
|
try {
|
|
28484
|
-
entries = await
|
|
28776
|
+
entries = await readdir5(absDir, { withFileTypes: true });
|
|
28485
28777
|
} catch {
|
|
28486
28778
|
continue;
|
|
28487
28779
|
}
|
|
28488
28780
|
for (const entry of entries) {
|
|
28489
28781
|
if (!entry.isDirectory())
|
|
28490
28782
|
continue;
|
|
28491
|
-
const entryPath =
|
|
28783
|
+
const entryPath = join13(absDir, entry.name);
|
|
28492
28784
|
try {
|
|
28493
28785
|
const stat2 = lstatSync(entryPath);
|
|
28494
28786
|
if (stat2.isSymbolicLink())
|
|
@@ -28496,14 +28788,14 @@ async function discoverRepoSkills(repoPath, options2) {
|
|
|
28496
28788
|
} catch {
|
|
28497
28789
|
continue;
|
|
28498
28790
|
}
|
|
28499
|
-
const skillMdPath =
|
|
28500
|
-
if (!
|
|
28791
|
+
const skillMdPath = join13(entryPath, "SKILL.md");
|
|
28792
|
+
if (!existsSync10(skillMdPath))
|
|
28501
28793
|
continue;
|
|
28502
28794
|
const relPath = relative3(repoPath, skillMdPath);
|
|
28503
28795
|
if (seen.has(relPath))
|
|
28504
28796
|
continue;
|
|
28505
28797
|
try {
|
|
28506
|
-
const content = await
|
|
28798
|
+
const content = await readFile9(skillMdPath, "utf-8");
|
|
28507
28799
|
const metadata = parseSkillMetadata(content);
|
|
28508
28800
|
if (!metadata)
|
|
28509
28801
|
continue;
|
|
@@ -28524,7 +28816,7 @@ async function discoverWorkspaceSkills(workspacePath, repositories, clientNames)
|
|
|
28524
28816
|
for (const repo of repositories) {
|
|
28525
28817
|
if (repo.skills === false || repo.skills === undefined)
|
|
28526
28818
|
continue;
|
|
28527
|
-
const repoAbsPath =
|
|
28819
|
+
const repoAbsPath = resolve9(workspacePath, repo.path);
|
|
28528
28820
|
const discoverOpts = Array.isArray(repo.skills) ? { skillPaths: repo.skills } : { clients: clientNames };
|
|
28529
28821
|
const repoSkills = await discoverRepoSkills(repoAbsPath, discoverOpts);
|
|
28530
28822
|
for (const skill of repoSkills) {
|
|
@@ -28556,7 +28848,7 @@ async function discoverWorkspaceSkills(workspacePath, repositories, clientNames)
|
|
|
28556
28848
|
function writeSkillsIndex(workspacePath, skillsByRepo) {
|
|
28557
28849
|
if (skillsByRepo.size === 0)
|
|
28558
28850
|
return { writtenFiles: [], refs: [] };
|
|
28559
|
-
const indexDir =
|
|
28851
|
+
const indexDir = join13(workspacePath, ".allagents", "skills-index");
|
|
28560
28852
|
mkdirSync(indexDir, { recursive: true });
|
|
28561
28853
|
const writtenFiles = [];
|
|
28562
28854
|
const refs = [];
|
|
@@ -28574,15 +28866,15 @@ ${skillEntries}
|
|
|
28574
28866
|
</available_skills>
|
|
28575
28867
|
`;
|
|
28576
28868
|
const filePath = `skills-index/${repoName}.md`;
|
|
28577
|
-
writeFileSync(
|
|
28869
|
+
writeFileSync(join13(indexDir, `${repoName}.md`), content, "utf-8");
|
|
28578
28870
|
writtenFiles.push(filePath);
|
|
28579
28871
|
refs.push({ repoName, indexPath: `.allagents/${filePath}` });
|
|
28580
28872
|
}
|
|
28581
28873
|
return { writtenFiles, refs };
|
|
28582
28874
|
}
|
|
28583
28875
|
function cleanupSkillsIndex(workspacePath, currentFiles) {
|
|
28584
|
-
const indexDir =
|
|
28585
|
-
if (!
|
|
28876
|
+
const indexDir = join13(workspacePath, ".allagents", "skills-index");
|
|
28877
|
+
if (!existsSync10(indexDir))
|
|
28586
28878
|
return;
|
|
28587
28879
|
const currentSet = new Set(currentFiles.map((f) => f.replace("skills-index/", "")));
|
|
28588
28880
|
let entries;
|
|
@@ -28593,7 +28885,7 @@ function cleanupSkillsIndex(workspacePath, currentFiles) {
|
|
|
28593
28885
|
}
|
|
28594
28886
|
for (const entry of entries) {
|
|
28595
28887
|
if (!currentSet.has(entry)) {
|
|
28596
|
-
rmSync(
|
|
28888
|
+
rmSync(join13(indexDir, entry), { force: true });
|
|
28597
28889
|
}
|
|
28598
28890
|
}
|
|
28599
28891
|
try {
|
|
@@ -28606,11 +28898,11 @@ function cleanupSkillsIndex(workspacePath, currentFiles) {
|
|
|
28606
28898
|
function groupSkillsByRepo(skills, repositories) {
|
|
28607
28899
|
const repoNameMap = new Map;
|
|
28608
28900
|
for (const repo of repositories) {
|
|
28609
|
-
repoNameMap.set(repo.path, repo.name ??
|
|
28901
|
+
repoNameMap.set(repo.path, repo.name ?? basename6(repo.path));
|
|
28610
28902
|
}
|
|
28611
28903
|
const grouped = new Map;
|
|
28612
28904
|
for (const skill of skills) {
|
|
28613
|
-
const repoName = repoNameMap.get(skill.repoPath) ??
|
|
28905
|
+
const repoName = repoNameMap.get(skill.repoPath) ?? basename6(skill.repoPath);
|
|
28614
28906
|
const existing = grouped.get(skill.repoPath);
|
|
28615
28907
|
if (existing) {
|
|
28616
28908
|
existing.skills.push(skill);
|
|
@@ -28626,9 +28918,9 @@ var init_repo_skills = __esm(() => {
|
|
|
28626
28918
|
});
|
|
28627
28919
|
|
|
28628
28920
|
// src/core/workspace-repo.ts
|
|
28629
|
-
import { readFile as
|
|
28630
|
-
import { existsSync as
|
|
28631
|
-
import { join as
|
|
28921
|
+
import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
|
|
28922
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
28923
|
+
import { join as join14 } from "node:path";
|
|
28632
28924
|
async function detectRemote(repoPath) {
|
|
28633
28925
|
try {
|
|
28634
28926
|
const env2 = { ...process.env };
|
|
@@ -28670,10 +28962,10 @@ function normalizePath(p) {
|
|
|
28670
28962
|
}
|
|
28671
28963
|
async function addRepository(path, options2 = {}, workspacePath = process.cwd()) {
|
|
28672
28964
|
const normalizedPath = normalizePath(path);
|
|
28673
|
-
const configPath =
|
|
28965
|
+
const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
28674
28966
|
await ensureWorkspace(workspacePath);
|
|
28675
28967
|
try {
|
|
28676
|
-
const content = await
|
|
28968
|
+
const content = await readFile10(configPath, "utf-8");
|
|
28677
28969
|
const config = load(content);
|
|
28678
28970
|
if (config.repositories.some((r) => normalizePath(r.path) === normalizedPath)) {
|
|
28679
28971
|
return { success: false, error: `Repository already exists: ${normalizedPath}` };
|
|
@@ -28693,8 +28985,8 @@ async function addRepository(path, options2 = {}, workspacePath = process.cwd())
|
|
|
28693
28985
|
}
|
|
28694
28986
|
}
|
|
28695
28987
|
async function removeRepository(path, workspacePath = process.cwd()) {
|
|
28696
|
-
const configPath =
|
|
28697
|
-
if (!
|
|
28988
|
+
const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
28989
|
+
if (!existsSync11(configPath)) {
|
|
28698
28990
|
return {
|
|
28699
28991
|
success: false,
|
|
28700
28992
|
error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
|
|
@@ -28702,7 +28994,7 @@ async function removeRepository(path, workspacePath = process.cwd()) {
|
|
|
28702
28994
|
};
|
|
28703
28995
|
}
|
|
28704
28996
|
try {
|
|
28705
|
-
const content = await
|
|
28997
|
+
const content = await readFile10(configPath, "utf-8");
|
|
28706
28998
|
const config = load(content);
|
|
28707
28999
|
const normalizedPath = normalizePath(path);
|
|
28708
29000
|
const index = config.repositories.findIndex((r) => normalizePath(r.path) === normalizedPath);
|
|
@@ -28717,11 +29009,11 @@ async function removeRepository(path, workspacePath = process.cwd()) {
|
|
|
28717
29009
|
}
|
|
28718
29010
|
}
|
|
28719
29011
|
async function listRepositories(workspacePath = process.cwd()) {
|
|
28720
|
-
const configPath =
|
|
28721
|
-
if (!
|
|
29012
|
+
const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
29013
|
+
if (!existsSync11(configPath))
|
|
28722
29014
|
return [];
|
|
28723
29015
|
try {
|
|
28724
|
-
const content = await
|
|
29016
|
+
const content = await readFile10(configPath, "utf-8");
|
|
28725
29017
|
const config = load(content);
|
|
28726
29018
|
return config.repositories ?? [];
|
|
28727
29019
|
} catch {
|
|
@@ -28732,10 +29024,10 @@ function resolveClientNames(clients) {
|
|
|
28732
29024
|
return (clients ?? []).map((c) => typeof c === "string" ? c : c.name);
|
|
28733
29025
|
}
|
|
28734
29026
|
async function updateAgentFiles(workspacePath = process.cwd()) {
|
|
28735
|
-
const configPath =
|
|
28736
|
-
if (!
|
|
29027
|
+
const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
29028
|
+
if (!existsSync11(configPath))
|
|
28737
29029
|
return;
|
|
28738
|
-
const content = await
|
|
29030
|
+
const content = await readFile10(configPath, "utf-8");
|
|
28739
29031
|
const config = load(content);
|
|
28740
29032
|
if (config.repositories.length === 0)
|
|
28741
29033
|
return;
|
|
@@ -28753,7 +29045,7 @@ async function updateAgentFiles(workspacePath = process.cwd()) {
|
|
|
28753
29045
|
}
|
|
28754
29046
|
agentFiles.add("AGENTS.md");
|
|
28755
29047
|
for (const agentFile of agentFiles) {
|
|
28756
|
-
await ensureWorkspaceRules(
|
|
29048
|
+
await ensureWorkspaceRules(join14(workspacePath, agentFile), config.repositories, skillsIndexRefs);
|
|
28757
29049
|
}
|
|
28758
29050
|
}
|
|
28759
29051
|
var init_workspace_repo = __esm(() => {
|
|
@@ -28937,14 +29229,14 @@ var init_sync_state = __esm(() => {
|
|
|
28937
29229
|
});
|
|
28938
29230
|
|
|
28939
29231
|
// src/core/config-gitignore.ts
|
|
28940
|
-
import { writeFile as writeFile6, readFile as
|
|
28941
|
-
import { existsSync as
|
|
28942
|
-
import { join as
|
|
29232
|
+
import { writeFile as writeFile6, readFile as readFile11 } from "node:fs/promises";
|
|
29233
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
29234
|
+
import { join as join15 } from "node:path";
|
|
28943
29235
|
async function ensureConfigGitignore(workspacePath) {
|
|
28944
|
-
const gitignorePath =
|
|
29236
|
+
const gitignorePath = join15(workspacePath, CONFIG_DIR, ".gitignore");
|
|
28945
29237
|
let existing = "";
|
|
28946
|
-
if (
|
|
28947
|
-
existing = await
|
|
29238
|
+
if (existsSync12(gitignorePath)) {
|
|
29239
|
+
existing = await readFile11(gitignorePath, "utf-8");
|
|
28948
29240
|
}
|
|
28949
29241
|
const missing = GITIGNORE_ENTRIES.filter((entry) => !existing.split(`
|
|
28950
29242
|
`).includes(entry));
|
|
@@ -28965,19 +29257,19 @@ var init_config_gitignore = __esm(() => {
|
|
|
28965
29257
|
});
|
|
28966
29258
|
|
|
28967
29259
|
// src/core/sync-state.ts
|
|
28968
|
-
import { readFile as
|
|
28969
|
-
import { existsSync as
|
|
28970
|
-
import { join as
|
|
29260
|
+
import { readFile as readFile12, writeFile as writeFile7, mkdir as mkdir7 } from "node:fs/promises";
|
|
29261
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
29262
|
+
import { join as join16, dirname as dirname6 } from "node:path";
|
|
28971
29263
|
function getSyncStatePath(workspacePath) {
|
|
28972
|
-
return
|
|
29264
|
+
return join16(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
|
|
28973
29265
|
}
|
|
28974
29266
|
async function loadSyncState(workspacePath) {
|
|
28975
29267
|
const statePath = getSyncStatePath(workspacePath);
|
|
28976
|
-
if (!
|
|
29268
|
+
if (!existsSync13(statePath)) {
|
|
28977
29269
|
return null;
|
|
28978
29270
|
}
|
|
28979
29271
|
try {
|
|
28980
|
-
const content = await
|
|
29272
|
+
const content = await readFile12(statePath, "utf-8");
|
|
28981
29273
|
const parsed = JSON.parse(content);
|
|
28982
29274
|
const result = SyncStateSchema.safeParse(parsed);
|
|
28983
29275
|
if (!result.success) {
|
|
@@ -29052,12 +29344,12 @@ var init_sync_state2 = __esm(() => {
|
|
|
29052
29344
|
|
|
29053
29345
|
// src/core/vscode-workspace.ts
|
|
29054
29346
|
import { createHash as createHash2 } from "node:crypto";
|
|
29055
|
-
import { resolve as
|
|
29347
|
+
import { resolve as resolve10, basename as basename7, isAbsolute as isAbsolute3, relative as relative4 } from "node:path";
|
|
29056
29348
|
function normalizeWorkspacePath(path) {
|
|
29057
29349
|
return path.replace(/\\/g, "/");
|
|
29058
29350
|
}
|
|
29059
29351
|
function resolveFolderAbsolutePath(workspacePath, folderPath) {
|
|
29060
|
-
return (isAbsolute3(folderPath) ? folderPath :
|
|
29352
|
+
return (isAbsolute3(folderPath) ? folderPath : resolve10(workspacePath, folderPath)).replace(/\\/g, "/");
|
|
29061
29353
|
}
|
|
29062
29354
|
function resolveFolderDisplayPath(folderPath) {
|
|
29063
29355
|
return normalizeWorkspacePath(folderPath);
|
|
@@ -29065,7 +29357,7 @@ function resolveFolderDisplayPath(folderPath) {
|
|
|
29065
29357
|
function buildPathPlaceholderMap(repositories, workspacePath) {
|
|
29066
29358
|
const map2 = new Map;
|
|
29067
29359
|
for (const repo of repositories) {
|
|
29068
|
-
const absolutePath =
|
|
29360
|
+
const absolutePath = resolve10(workspacePath, repo.path);
|
|
29069
29361
|
map2.set(repo.path, absolutePath);
|
|
29070
29362
|
}
|
|
29071
29363
|
return map2;
|
|
@@ -29100,7 +29392,7 @@ function generateVscodeWorkspace(input) {
|
|
|
29100
29392
|
const folders = [];
|
|
29101
29393
|
const seenPaths = new Set;
|
|
29102
29394
|
folders.push({ path: "." });
|
|
29103
|
-
seenPaths.add(
|
|
29395
|
+
seenPaths.add(resolve10(workspacePath, "."));
|
|
29104
29396
|
for (const repo of repositories) {
|
|
29105
29397
|
const absolutePath = resolveFolderAbsolutePath(workspacePath, repo.path);
|
|
29106
29398
|
const entry = { path: resolveFolderDisplayPath(repo.path) };
|
|
@@ -29142,22 +29434,22 @@ function getWorkspaceOutputPath(workspacePath, vscodeConfig) {
|
|
|
29142
29434
|
const name = vscodeConfig?.output;
|
|
29143
29435
|
if (name) {
|
|
29144
29436
|
const filename = name.endsWith(".code-workspace") ? name : `${name}.code-workspace`;
|
|
29145
|
-
return
|
|
29437
|
+
return resolve10(workspacePath, filename);
|
|
29146
29438
|
}
|
|
29147
|
-
const dirName =
|
|
29148
|
-
return
|
|
29439
|
+
const dirName = basename7(resolve10(workspacePath));
|
|
29440
|
+
return resolve10(workspacePath, `${dirName}.code-workspace`);
|
|
29149
29441
|
}
|
|
29150
29442
|
function computeWorkspaceHash(content) {
|
|
29151
29443
|
return createHash2("sha256").update(content).digest("hex");
|
|
29152
29444
|
}
|
|
29153
29445
|
function reconcileVscodeWorkspaceFolders(workspacePath, codeWorkspaceFolders, lastSyncedRepos, currentRepos) {
|
|
29154
|
-
const normalizedWorkspacePath =
|
|
29446
|
+
const normalizedWorkspacePath = resolve10(workspacePath).replace(/\\/g, "/");
|
|
29155
29447
|
const codeWorkspaceAbsPaths = new Set;
|
|
29156
29448
|
const codeWorkspaceNames = new Map;
|
|
29157
29449
|
for (const folder of codeWorkspaceFolders) {
|
|
29158
29450
|
if (folder.path === ".")
|
|
29159
29451
|
continue;
|
|
29160
|
-
const absPath = (isAbsolute3(folder.path) ? folder.path :
|
|
29452
|
+
const absPath = (isAbsolute3(folder.path) ? folder.path : resolve10(workspacePath, folder.path)).replace(/\\/g, "/");
|
|
29161
29453
|
codeWorkspaceAbsPaths.add(absPath);
|
|
29162
29454
|
if (folder.name)
|
|
29163
29455
|
codeWorkspaceNames.set(absPath, folder.name);
|
|
@@ -29165,7 +29457,7 @@ function reconcileVscodeWorkspaceFolders(workspacePath, codeWorkspaceFolders, la
|
|
|
29165
29457
|
const lastSyncedSet = new Set(lastSyncedRepos.map((p) => p.replace(/\\/g, "/")));
|
|
29166
29458
|
const currentReposByAbsPath = new Map;
|
|
29167
29459
|
for (const repo of currentRepos) {
|
|
29168
|
-
const absPath =
|
|
29460
|
+
const absPath = resolve10(workspacePath, repo.path).replace(/\\/g, "/");
|
|
29169
29461
|
currentReposByAbsPath.set(absPath, repo);
|
|
29170
29462
|
}
|
|
29171
29463
|
const currentAbsPaths = new Set(currentReposByAbsPath.keys());
|
|
@@ -29216,8 +29508,8 @@ var init_vscode_workspace = __esm(() => {
|
|
|
29216
29508
|
});
|
|
29217
29509
|
|
|
29218
29510
|
// src/core/vscode-mcp.ts
|
|
29219
|
-
import { existsSync as
|
|
29220
|
-
import { join as
|
|
29511
|
+
import { existsSync as existsSync14, readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
29512
|
+
import { join as join17, dirname as dirname7 } from "node:path";
|
|
29221
29513
|
function deepEqual(a, b) {
|
|
29222
29514
|
if (a === b)
|
|
29223
29515
|
return true;
|
|
@@ -29249,17 +29541,17 @@ function getVscodeMcpConfigPath() {
|
|
|
29249
29541
|
if (!appData) {
|
|
29250
29542
|
throw new Error("APPDATA environment variable not set");
|
|
29251
29543
|
}
|
|
29252
|
-
return
|
|
29544
|
+
return join17(appData, "Code", "User", "mcp.json");
|
|
29253
29545
|
}
|
|
29254
29546
|
const home = getHomeDir();
|
|
29255
29547
|
if (platform2 === "darwin") {
|
|
29256
|
-
return
|
|
29548
|
+
return join17(home, "Library", "Application Support", "Code", "User", "mcp.json");
|
|
29257
29549
|
}
|
|
29258
|
-
return
|
|
29550
|
+
return join17(home, ".config", "Code", "User", "mcp.json");
|
|
29259
29551
|
}
|
|
29260
29552
|
function readPluginMcpConfig(pluginPath) {
|
|
29261
|
-
const mcpPath =
|
|
29262
|
-
if (!
|
|
29553
|
+
const mcpPath = join17(pluginPath, ".mcp.json");
|
|
29554
|
+
if (!existsSync14(mcpPath)) {
|
|
29263
29555
|
return null;
|
|
29264
29556
|
}
|
|
29265
29557
|
try {
|
|
@@ -29325,7 +29617,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
|
|
|
29325
29617
|
trackedServers: []
|
|
29326
29618
|
};
|
|
29327
29619
|
let existingConfig = {};
|
|
29328
|
-
if (
|
|
29620
|
+
if (existsSync14(configPath)) {
|
|
29329
29621
|
try {
|
|
29330
29622
|
const content = readFileSync(configPath, "utf-8");
|
|
29331
29623
|
existingConfig = import_json5.default.parse(content);
|
|
@@ -29376,7 +29668,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
|
|
|
29376
29668
|
if (hasChanges && !dryRun) {
|
|
29377
29669
|
existingConfig.servers = existingServers;
|
|
29378
29670
|
const dir = dirname7(configPath);
|
|
29379
|
-
if (!
|
|
29671
|
+
if (!existsSync14(dir)) {
|
|
29380
29672
|
mkdirSync2(dir, { recursive: true });
|
|
29381
29673
|
}
|
|
29382
29674
|
writeFileSync2(configPath, `${JSON.stringify(existingConfig, null, 2)}
|
|
@@ -29394,7 +29686,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
|
|
|
29394
29686
|
if (result.removed > 0 && !dryRun) {
|
|
29395
29687
|
existingConfig.servers = existingServers;
|
|
29396
29688
|
const dir = dirname7(configPath);
|
|
29397
|
-
if (!
|
|
29689
|
+
if (!existsSync14(dir)) {
|
|
29398
29690
|
mkdirSync2(dir, { recursive: true });
|
|
29399
29691
|
}
|
|
29400
29692
|
writeFileSync2(configPath, `${JSON.stringify(existingConfig, null, 2)}
|
|
@@ -29451,7 +29743,7 @@ function applyMcpProxy(servers, client, config) {
|
|
|
29451
29743
|
// src/core/native/types.ts
|
|
29452
29744
|
import { spawn as spawn2 } from "node:child_process";
|
|
29453
29745
|
function executeCommand(binary2, args, options2 = {}) {
|
|
29454
|
-
return new Promise((
|
|
29746
|
+
return new Promise((resolve11) => {
|
|
29455
29747
|
const proc = spawn2(binary2, args, {
|
|
29456
29748
|
cwd: options2.cwd,
|
|
29457
29749
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -29472,7 +29764,7 @@ function executeCommand(binary2, args, options2 = {}) {
|
|
|
29472
29764
|
return;
|
|
29473
29765
|
resolved = true;
|
|
29474
29766
|
const trimmedStderr = stderr.trim();
|
|
29475
|
-
|
|
29767
|
+
resolve11({
|
|
29476
29768
|
success: code === 0,
|
|
29477
29769
|
output: stdout.trim(),
|
|
29478
29770
|
...trimmedStderr && { error: trimmedStderr }
|
|
@@ -29482,7 +29774,7 @@ function executeCommand(binary2, args, options2 = {}) {
|
|
|
29482
29774
|
if (resolved)
|
|
29483
29775
|
return;
|
|
29484
29776
|
resolved = true;
|
|
29485
|
-
|
|
29777
|
+
resolve11({
|
|
29486
29778
|
success: false,
|
|
29487
29779
|
output: "",
|
|
29488
29780
|
error: `Failed to execute ${binary2} CLI: ${err.message}`
|
|
@@ -29501,7 +29793,7 @@ function mergeNativeSyncResults(results) {
|
|
|
29501
29793
|
var init_types2 = () => {};
|
|
29502
29794
|
|
|
29503
29795
|
// src/core/codex-mcp.ts
|
|
29504
|
-
import { existsSync as
|
|
29796
|
+
import { existsSync as existsSync15, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
29505
29797
|
import { dirname as dirname8 } from "node:path";
|
|
29506
29798
|
function buildCodexMcpAddArgs(name, config) {
|
|
29507
29799
|
if (typeof config.url === "string") {
|
|
@@ -29712,7 +30004,7 @@ function syncCodexProjectMcpConfig(validatedPlugins, options2) {
|
|
|
29712
30004
|
trackedServers: []
|
|
29713
30005
|
};
|
|
29714
30006
|
let existingContent = "";
|
|
29715
|
-
if (
|
|
30007
|
+
if (existsSync15(configPath)) {
|
|
29716
30008
|
try {
|
|
29717
30009
|
existingContent = readFileSync2(configPath, "utf-8");
|
|
29718
30010
|
} catch {
|
|
@@ -29768,7 +30060,7 @@ function syncCodexProjectMcpConfig(validatedPlugins, options2) {
|
|
|
29768
30060
|
`)}
|
|
29769
30061
|
`;
|
|
29770
30062
|
const dir = dirname8(configPath);
|
|
29771
|
-
if (!
|
|
30063
|
+
if (!existsSync15(dir)) {
|
|
29772
30064
|
mkdirSync3(dir, { recursive: true });
|
|
29773
30065
|
}
|
|
29774
30066
|
writeFileSync3(configPath, output, "utf-8");
|
|
@@ -29782,7 +30074,7 @@ var init_codex_mcp = __esm(() => {
|
|
|
29782
30074
|
});
|
|
29783
30075
|
|
|
29784
30076
|
// src/core/claude-mcp.ts
|
|
29785
|
-
import { existsSync as
|
|
30077
|
+
import { existsSync as existsSync16, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
29786
30078
|
import { dirname as dirname9 } from "node:path";
|
|
29787
30079
|
function deepEqual2(a, b) {
|
|
29788
30080
|
if (a === b)
|
|
@@ -29850,7 +30142,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
|
|
|
29850
30142
|
trackedServers: []
|
|
29851
30143
|
};
|
|
29852
30144
|
let existingConfig = {};
|
|
29853
|
-
if (
|
|
30145
|
+
if (existsSync16(configPath)) {
|
|
29854
30146
|
try {
|
|
29855
30147
|
const content = readFileSync3(configPath, "utf-8");
|
|
29856
30148
|
existingConfig = import_json52.default.parse(content);
|
|
@@ -29901,7 +30193,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
|
|
|
29901
30193
|
if (hasChanges && !dryRun) {
|
|
29902
30194
|
existingConfig.mcpServers = existingServers;
|
|
29903
30195
|
const dir = dirname9(configPath);
|
|
29904
|
-
if (!
|
|
30196
|
+
if (!existsSync16(dir)) {
|
|
29905
30197
|
mkdirSync4(dir, { recursive: true });
|
|
29906
30198
|
}
|
|
29907
30199
|
writeFileSync4(configPath, `${JSON.stringify(existingConfig, null, 2)}
|
|
@@ -29919,7 +30211,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
|
|
|
29919
30211
|
if (result.removed > 0 && !dryRun) {
|
|
29920
30212
|
existingConfig.mcpServers = existingServers;
|
|
29921
30213
|
const dir = dirname9(configPath);
|
|
29922
|
-
if (!
|
|
30214
|
+
if (!existsSync16(dir)) {
|
|
29923
30215
|
mkdirSync4(dir, { recursive: true });
|
|
29924
30216
|
}
|
|
29925
30217
|
writeFileSync4(configPath, `${JSON.stringify(existingConfig, null, 2)}
|
|
@@ -30018,41 +30310,41 @@ var init_claude_mcp = __esm(() => {
|
|
|
30018
30310
|
});
|
|
30019
30311
|
|
|
30020
30312
|
// src/core/copilot-mcp.ts
|
|
30021
|
-
import { join as
|
|
30313
|
+
import { join as join18 } from "node:path";
|
|
30022
30314
|
function getCopilotMcpConfigPath() {
|
|
30023
|
-
return
|
|
30315
|
+
return join18(getHomeDir(), ".copilot", "mcp-config.json");
|
|
30024
30316
|
}
|
|
30025
30317
|
var init_copilot_mcp = __esm(() => {
|
|
30026
30318
|
init_constants();
|
|
30027
30319
|
});
|
|
30028
30320
|
|
|
30029
30321
|
// src/core/mcp-sync.ts
|
|
30030
|
-
import { existsSync as
|
|
30031
|
-
import { join as
|
|
30322
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
30323
|
+
import { join as join19 } from "node:path";
|
|
30032
30324
|
function buildSyncSpecs(workspacePath) {
|
|
30033
30325
|
return [
|
|
30034
30326
|
{
|
|
30035
30327
|
client: "vscode",
|
|
30036
30328
|
scope: "vscode",
|
|
30037
|
-
configPath:
|
|
30329
|
+
configPath: join19(workspacePath, ".vscode", "mcp.json"),
|
|
30038
30330
|
syncFn: syncVscodeMcpConfig
|
|
30039
30331
|
},
|
|
30040
30332
|
{
|
|
30041
30333
|
client: "claude",
|
|
30042
30334
|
scope: "claude",
|
|
30043
|
-
configPath:
|
|
30335
|
+
configPath: join19(workspacePath, ".mcp.json"),
|
|
30044
30336
|
syncFn: syncClaudeMcpConfig
|
|
30045
30337
|
},
|
|
30046
30338
|
{
|
|
30047
30339
|
client: "codex",
|
|
30048
30340
|
scope: "codex",
|
|
30049
|
-
configPath:
|
|
30341
|
+
configPath: join19(workspacePath, ".codex", "config.toml"),
|
|
30050
30342
|
syncFn: syncCodexProjectMcpConfig
|
|
30051
30343
|
},
|
|
30052
30344
|
{
|
|
30053
30345
|
client: "copilot",
|
|
30054
30346
|
scope: "copilot",
|
|
30055
|
-
configPath:
|
|
30347
|
+
configPath: join19(workspacePath, ".copilot", "mcp-config.json"),
|
|
30056
30348
|
syncFn: syncClaudeMcpConfig
|
|
30057
30349
|
}
|
|
30058
30350
|
];
|
|
@@ -30107,8 +30399,8 @@ function syncMcpServers(workspacePath, validPlugins, config, previousState, sync
|
|
|
30107
30399
|
async function syncMcpOnly(workspacePath = process.cwd(), options2 = {}) {
|
|
30108
30400
|
const { offline = false, dryRun = false } = options2;
|
|
30109
30401
|
await migrateWorkspaceSkillsV1toV2(workspacePath);
|
|
30110
|
-
const configPath =
|
|
30111
|
-
if (!
|
|
30402
|
+
const configPath = join19(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
30403
|
+
if (!existsSync17(configPath)) {
|
|
30112
30404
|
return {
|
|
30113
30405
|
success: false,
|
|
30114
30406
|
mcpResults: {},
|
|
@@ -30482,9 +30774,9 @@ function padStart2(str3, len) {
|
|
|
30482
30774
|
}
|
|
30483
30775
|
|
|
30484
30776
|
// src/core/managed-repos.ts
|
|
30485
|
-
import { existsSync as
|
|
30777
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
30486
30778
|
import { mkdir as mkdir8 } from "node:fs/promises";
|
|
30487
|
-
import { dirname as dirname10, resolve as
|
|
30779
|
+
import { dirname as dirname10, resolve as resolve11 } from "node:path";
|
|
30488
30780
|
function expandHome(p) {
|
|
30489
30781
|
if (p.startsWith("~/") || p === "~") {
|
|
30490
30782
|
return p.replace("~", getHomeDir());
|
|
@@ -30565,8 +30857,8 @@ async function processManagedRepos(repositories, workspacePath, options2 = {}) {
|
|
|
30565
30857
|
continue;
|
|
30566
30858
|
}
|
|
30567
30859
|
const expandedPath = expandHome(repo.path);
|
|
30568
|
-
const absolutePath =
|
|
30569
|
-
if (!
|
|
30860
|
+
const absolutePath = resolve11(workspacePath, expandedPath);
|
|
30861
|
+
if (!existsSync18(absolutePath)) {
|
|
30570
30862
|
if (!shouldClone(repo.managed)) {
|
|
30571
30863
|
results.push({ path: repo.path, repo: repo.repo, action: "skipped" });
|
|
30572
30864
|
continue;
|
|
@@ -30617,9 +30909,9 @@ var init_managed_repos = __esm(() => {
|
|
|
30617
30909
|
});
|
|
30618
30910
|
|
|
30619
30911
|
// src/core/sync.ts
|
|
30620
|
-
import { existsSync as
|
|
30912
|
+
import { existsSync as existsSync19, readFileSync as readFileSync4, writeFileSync as writeFileSync5, lstatSync as lstatSync2 } from "node:fs";
|
|
30621
30913
|
import { rm as rm4, unlink as unlink2, rmdir, copyFile } from "node:fs/promises";
|
|
30622
|
-
import { join as
|
|
30914
|
+
import { join as join20, resolve as resolve12, dirname as dirname11, relative as relative5 } from "node:path";
|
|
30623
30915
|
function deduplicateClientsByPath(clients, clientMappings = CLIENT_MAPPINGS) {
|
|
30624
30916
|
const pathToClients = new Map;
|
|
30625
30917
|
for (const client of clients) {
|
|
@@ -30782,7 +31074,7 @@ async function selectivePurgeWorkspace(workspacePath, state, clients) {
|
|
|
30782
31074
|
const previousFiles = getPreviouslySyncedFiles(state, client);
|
|
30783
31075
|
const purgedPaths = [];
|
|
30784
31076
|
for (const filePath of previousFiles) {
|
|
30785
|
-
const fullPath =
|
|
31077
|
+
const fullPath = join20(workspacePath, filePath);
|
|
30786
31078
|
const cleanPath = fullPath.replace(/\/$/, "");
|
|
30787
31079
|
let stats;
|
|
30788
31080
|
try {
|
|
@@ -30811,8 +31103,8 @@ async function selectivePurgeWorkspace(workspacePath, state, clients) {
|
|
|
30811
31103
|
async function cleanupEmptyParents(workspacePath, filePath) {
|
|
30812
31104
|
let parentPath = dirname11(filePath);
|
|
30813
31105
|
while (parentPath && parentPath !== "." && parentPath !== "/") {
|
|
30814
|
-
const fullParentPath =
|
|
30815
|
-
if (!
|
|
31106
|
+
const fullParentPath = join20(workspacePath, parentPath);
|
|
31107
|
+
if (!existsSync19(fullParentPath)) {
|
|
30816
31108
|
parentPath = dirname11(parentPath);
|
|
30817
31109
|
continue;
|
|
30818
31110
|
}
|
|
@@ -30897,8 +31189,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
|
|
|
30897
31189
|
errors2.push(`Cannot resolve file '${file}' - no workspace.source configured`);
|
|
30898
31190
|
continue;
|
|
30899
31191
|
}
|
|
30900
|
-
const fullPath =
|
|
30901
|
-
if (!
|
|
31192
|
+
const fullPath = join20(defaultSourcePath, file);
|
|
31193
|
+
if (!existsSync19(fullPath)) {
|
|
30902
31194
|
errors2.push(`File source not found: ${fullPath}`);
|
|
30903
31195
|
}
|
|
30904
31196
|
continue;
|
|
@@ -30916,8 +31208,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
|
|
|
30916
31208
|
errors2.push(`GitHub cache not found for ${cacheKey}`);
|
|
30917
31209
|
continue;
|
|
30918
31210
|
}
|
|
30919
|
-
const fullPath =
|
|
30920
|
-
if (!
|
|
31211
|
+
const fullPath = join20(cachePath, parsed.filePath);
|
|
31212
|
+
if (!existsSync19(fullPath)) {
|
|
30921
31213
|
errors2.push(`Path not found in repository: ${cacheKey}/${parsed.filePath}`);
|
|
30922
31214
|
}
|
|
30923
31215
|
} else {
|
|
@@ -30925,13 +31217,13 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
|
|
|
30925
31217
|
if (file.source.startsWith("/")) {
|
|
30926
31218
|
fullPath = file.source;
|
|
30927
31219
|
} else if (file.source.startsWith("../")) {
|
|
30928
|
-
fullPath =
|
|
31220
|
+
fullPath = resolve12(file.source);
|
|
30929
31221
|
} else if (defaultSourcePath) {
|
|
30930
|
-
fullPath =
|
|
31222
|
+
fullPath = join20(defaultSourcePath, file.source);
|
|
30931
31223
|
} else {
|
|
30932
|
-
fullPath =
|
|
31224
|
+
fullPath = resolve12(file.source);
|
|
30933
31225
|
}
|
|
30934
|
-
if (!
|
|
31226
|
+
if (!existsSync19(fullPath)) {
|
|
30935
31227
|
errors2.push(`File source not found: ${fullPath}`);
|
|
30936
31228
|
}
|
|
30937
31229
|
}
|
|
@@ -30940,8 +31232,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
|
|
|
30940
31232
|
errors2.push(`Cannot resolve file '${file.dest}' - no workspace.source configured and no explicit source provided`);
|
|
30941
31233
|
continue;
|
|
30942
31234
|
}
|
|
30943
|
-
const fullPath =
|
|
30944
|
-
if (!
|
|
31235
|
+
const fullPath = join20(defaultSourcePath, file.dest ?? "");
|
|
31236
|
+
if (!existsSync19(fullPath)) {
|
|
30945
31237
|
errors2.push(`File source not found: ${fullPath}`);
|
|
30946
31238
|
}
|
|
30947
31239
|
}
|
|
@@ -31089,7 +31381,7 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
|
|
|
31089
31381
|
...fetchResult.error && { error: fetchResult.error }
|
|
31090
31382
|
};
|
|
31091
31383
|
}
|
|
31092
|
-
const resolvedPath2 = parsed?.subpath ?
|
|
31384
|
+
const resolvedPath2 = parsed?.subpath ? join20(fetchResult.cachePath, parsed.subpath) : fetchResult.cachePath;
|
|
31093
31385
|
return {
|
|
31094
31386
|
plugin: pluginSource,
|
|
31095
31387
|
resolved: resolvedPath2,
|
|
@@ -31098,8 +31390,8 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
|
|
|
31098
31390
|
nativeClients: []
|
|
31099
31391
|
};
|
|
31100
31392
|
}
|
|
31101
|
-
const resolvedPath =
|
|
31102
|
-
if (!
|
|
31393
|
+
const resolvedPath = resolve12(workspacePath, pluginSource);
|
|
31394
|
+
if (!existsSync19(resolvedPath)) {
|
|
31103
31395
|
return {
|
|
31104
31396
|
plugin: pluginSource,
|
|
31105
31397
|
resolved: resolvedPath,
|
|
@@ -31272,10 +31564,10 @@ function buildPluginSkillNameMaps(allSkills) {
|
|
|
31272
31564
|
return pluginMaps;
|
|
31273
31565
|
}
|
|
31274
31566
|
function generateVscodeWorkspaceFile(workspacePath, config) {
|
|
31275
|
-
const configDir =
|
|
31276
|
-
const templatePath =
|
|
31567
|
+
const configDir = join20(workspacePath, CONFIG_DIR);
|
|
31568
|
+
const templatePath = join20(configDir, VSCODE_TEMPLATE_FILE);
|
|
31277
31569
|
let template;
|
|
31278
|
-
if (
|
|
31570
|
+
if (existsSync19(templatePath)) {
|
|
31279
31571
|
try {
|
|
31280
31572
|
template = import_json53.default.parse(readFileSync4(templatePath, "utf-8"));
|
|
31281
31573
|
} catch (error) {
|
|
@@ -31427,7 +31719,7 @@ async function syncVscodeWorkspaceFile(workspacePath, config, configPath, previo
|
|
|
31427
31719
|
let updatedConfig = config;
|
|
31428
31720
|
if (previousState?.vscodeWorkspaceHash && previousState?.vscodeWorkspaceRepos) {
|
|
31429
31721
|
const outputPath = getWorkspaceOutputPath(workspacePath, config.vscode);
|
|
31430
|
-
if (
|
|
31722
|
+
if (existsSync19(outputPath)) {
|
|
31431
31723
|
const existingContent = readFileSync4(outputPath, "utf-8");
|
|
31432
31724
|
const currentHash = computeWorkspaceHash(existingContent);
|
|
31433
31725
|
if (currentHash !== previousState.vscodeWorkspaceHash) {
|
|
@@ -31460,7 +31752,7 @@ async function syncVscodeWorkspaceFile(workspacePath, config, configPath, previo
|
|
|
31460
31752
|
}
|
|
31461
31753
|
const writtenContent = generateVscodeWorkspaceFile(workspacePath, updatedConfig);
|
|
31462
31754
|
const hash = computeWorkspaceHash(writtenContent);
|
|
31463
|
-
const repos = updatedConfig.repositories.map((r) =>
|
|
31755
|
+
const repos = updatedConfig.repositories.map((r) => resolve12(workspacePath, r.path).replace(/\\/g, "/"));
|
|
31464
31756
|
return { config: updatedConfig, hash, repos };
|
|
31465
31757
|
}
|
|
31466
31758
|
async function buildSourcesProvenance(validatedPlugins, pluginEntries) {
|
|
@@ -31542,9 +31834,9 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
|
|
|
31542
31834
|
skipManaged = false
|
|
31543
31835
|
} = options2;
|
|
31544
31836
|
const sw = new Stopwatch;
|
|
31545
|
-
const configDir =
|
|
31546
|
-
const configPath =
|
|
31547
|
-
if (!
|
|
31837
|
+
const configDir = join20(workspacePath, CONFIG_DIR);
|
|
31838
|
+
const configPath = join20(configDir, WORKSPACE_CONFIG_FILE);
|
|
31839
|
+
if (!existsSync19(configPath)) {
|
|
31548
31840
|
return failedSyncResult(`${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
|
|
31549
31841
|
Run 'allagents workspace init <path>' to create a new workspace`);
|
|
31550
31842
|
}
|
|
@@ -31641,8 +31933,8 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
|
|
|
31641
31933
|
const filesToCopy = [...config.workspace.files];
|
|
31642
31934
|
if (hasRepositories && sourcePath) {
|
|
31643
31935
|
for (const agentFile of AGENT_FILES) {
|
|
31644
|
-
const agentPath =
|
|
31645
|
-
if (
|
|
31936
|
+
const agentPath = join20(sourcePath, agentFile);
|
|
31937
|
+
if (existsSync19(agentPath) && !filesToCopy.includes(agentFile)) {
|
|
31646
31938
|
filesToCopy.push(agentFile);
|
|
31647
31939
|
}
|
|
31648
31940
|
}
|
|
@@ -31682,10 +31974,10 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
|
|
|
31682
31974
|
skillsIndexRefs
|
|
31683
31975
|
});
|
|
31684
31976
|
if (hasRepositories && !dryRun && syncClients.includes("claude") && sourcePath) {
|
|
31685
|
-
const claudePath =
|
|
31686
|
-
const agentsPath =
|
|
31687
|
-
const claudeExistsInSource =
|
|
31688
|
-
if (!claudeExistsInSource &&
|
|
31977
|
+
const claudePath = join20(workspacePath, "CLAUDE.md");
|
|
31978
|
+
const agentsPath = join20(workspacePath, "AGENTS.md");
|
|
31979
|
+
const claudeExistsInSource = existsSync19(join20(sourcePath, "CLAUDE.md"));
|
|
31980
|
+
if (!claudeExistsInSource && existsSync19(agentsPath) && !existsSync19(claudePath)) {
|
|
31689
31981
|
await copyFile(agentsPath, claudePath);
|
|
31690
31982
|
}
|
|
31691
31983
|
}
|
|
@@ -31769,7 +32061,7 @@ async function seedFetchCacheFromMarketplaces(results) {
|
|
|
31769
32061
|
}
|
|
31770
32062
|
function readGitBranch(repoPath) {
|
|
31771
32063
|
try {
|
|
31772
|
-
const head = readFileSync4(
|
|
32064
|
+
const head = readFileSync4(join20(repoPath, ".git", "HEAD"), "utf-8").trim();
|
|
31773
32065
|
const prefix = "ref: refs/heads/";
|
|
31774
32066
|
return head.startsWith(prefix) ? head.slice(prefix.length) : null;
|
|
31775
32067
|
} catch {
|
|
@@ -31779,7 +32071,7 @@ function readGitBranch(repoPath) {
|
|
|
31779
32071
|
async function syncUserWorkspace(options2 = {}) {
|
|
31780
32072
|
await migrateUserWorkspaceSkillsV1toV2();
|
|
31781
32073
|
const sw = new Stopwatch;
|
|
31782
|
-
const homeDir =
|
|
32074
|
+
const homeDir = resolve12(getHomeDir());
|
|
31783
32075
|
const config = await getUserWorkspaceConfig();
|
|
31784
32076
|
if (!config) {
|
|
31785
32077
|
return {
|
|
@@ -31977,11 +32269,11 @@ var init_sync = __esm(() => {
|
|
|
31977
32269
|
});
|
|
31978
32270
|
|
|
31979
32271
|
// src/core/github-fetch.ts
|
|
31980
|
-
import { existsSync as
|
|
31981
|
-
import { join as
|
|
32272
|
+
import { existsSync as existsSync20, readFileSync as readFileSync5 } from "node:fs";
|
|
32273
|
+
import { join as join21 } from "node:path";
|
|
31982
32274
|
function readFileFromClone(tempDir, filePath) {
|
|
31983
|
-
const fullPath =
|
|
31984
|
-
if (
|
|
32275
|
+
const fullPath = join21(tempDir, filePath);
|
|
32276
|
+
if (existsSync20(fullPath)) {
|
|
31985
32277
|
return readFileSync5(fullPath, "utf-8");
|
|
31986
32278
|
}
|
|
31987
32279
|
return null;
|
|
@@ -32080,15 +32372,15 @@ var init_github_fetch = __esm(() => {
|
|
|
32080
32372
|
});
|
|
32081
32373
|
|
|
32082
32374
|
// src/core/workspace.ts
|
|
32083
|
-
import { cp as cp2, mkdir as mkdir9, readFile as
|
|
32084
|
-
import { existsSync as
|
|
32085
|
-
import { join as
|
|
32375
|
+
import { cp as cp2, mkdir as mkdir9, readFile as readFile13, writeFile as writeFile8, copyFile as copyFile2, unlink as unlink3 } from "node:fs/promises";
|
|
32376
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
32377
|
+
import { join as join22, resolve as resolve13, dirname as dirname12, relative as relative6, sep as sep2, isAbsolute as isAbsolute4 } from "node:path";
|
|
32086
32378
|
import { fileURLToPath } from "node:url";
|
|
32087
32379
|
async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
32088
|
-
const absoluteTarget =
|
|
32089
|
-
const configDir =
|
|
32090
|
-
const configPath =
|
|
32091
|
-
if (
|
|
32380
|
+
const absoluteTarget = resolve13(targetPath);
|
|
32381
|
+
const configDir = join22(absoluteTarget, CONFIG_DIR);
|
|
32382
|
+
const configPath = join22(configDir, WORKSPACE_CONFIG_FILE);
|
|
32383
|
+
if (existsSync21(configPath)) {
|
|
32092
32384
|
if (options2.force) {
|
|
32093
32385
|
await unlink3(configPath);
|
|
32094
32386
|
} else {
|
|
@@ -32099,7 +32391,7 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32099
32391
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
32100
32392
|
const currentFileDir = dirname12(currentFilePath);
|
|
32101
32393
|
const isProduction = currentFilePath.includes(`${sep2}dist${sep2}`);
|
|
32102
|
-
const defaultTemplatePath = isProduction ?
|
|
32394
|
+
const defaultTemplatePath = isProduction ? join22(currentFileDir, "templates", "default") : join22(currentFileDir, "..", "templates", "default");
|
|
32103
32395
|
let githubTempDir;
|
|
32104
32396
|
let parsedFromUrl;
|
|
32105
32397
|
let githubBasePath = "";
|
|
@@ -32138,20 +32430,20 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32138
32430
|
}
|
|
32139
32431
|
console.log(`✓ Using workspace.yaml from: ${options2.from}`);
|
|
32140
32432
|
} else {
|
|
32141
|
-
const fromPath =
|
|
32142
|
-
if (!
|
|
32433
|
+
const fromPath = resolve13(options2.from);
|
|
32434
|
+
if (!existsSync21(fromPath)) {
|
|
32143
32435
|
throw new Error(`Template not found: ${fromPath}`);
|
|
32144
32436
|
}
|
|
32145
32437
|
const { stat: stat2 } = await import("node:fs/promises");
|
|
32146
32438
|
const fromStat = await stat2(fromPath);
|
|
32147
32439
|
let sourceYamlPath;
|
|
32148
32440
|
if (fromStat.isDirectory()) {
|
|
32149
|
-
const nestedPath =
|
|
32150
|
-
const rootPath =
|
|
32151
|
-
if (
|
|
32441
|
+
const nestedPath = join22(fromPath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
32442
|
+
const rootPath = join22(fromPath, WORKSPACE_CONFIG_FILE);
|
|
32443
|
+
if (existsSync21(nestedPath)) {
|
|
32152
32444
|
sourceYamlPath = nestedPath;
|
|
32153
32445
|
sourceDir = fromPath;
|
|
32154
|
-
} else if (
|
|
32446
|
+
} else if (existsSync21(rootPath)) {
|
|
32155
32447
|
sourceYamlPath = rootPath;
|
|
32156
32448
|
sourceDir = fromPath;
|
|
32157
32449
|
} else {
|
|
@@ -32167,14 +32459,14 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32167
32459
|
sourceDir = parentDir;
|
|
32168
32460
|
}
|
|
32169
32461
|
}
|
|
32170
|
-
workspaceYamlContent = await
|
|
32462
|
+
workspaceYamlContent = await readFile13(sourceYamlPath, "utf-8");
|
|
32171
32463
|
if (sourceDir) {
|
|
32172
32464
|
const parsed2 = load(workspaceYamlContent);
|
|
32173
32465
|
const workspace = parsed2?.workspace;
|
|
32174
32466
|
if (workspace?.source) {
|
|
32175
32467
|
const source = workspace.source;
|
|
32176
32468
|
if (!isGitHubUrl(source) && !isAbsolute4(source)) {
|
|
32177
|
-
workspace.source =
|
|
32469
|
+
workspace.source = resolve13(sourceDir, source);
|
|
32178
32470
|
workspaceYamlContent = dump(parsed2, { lineWidth: -1 });
|
|
32179
32471
|
}
|
|
32180
32472
|
}
|
|
@@ -32182,11 +32474,11 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32182
32474
|
console.log(`✓ Using workspace.yaml from: ${sourceYamlPath}`);
|
|
32183
32475
|
}
|
|
32184
32476
|
} else {
|
|
32185
|
-
const defaultYamlPath =
|
|
32186
|
-
if (!
|
|
32477
|
+
const defaultYamlPath = join22(defaultTemplatePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
32478
|
+
if (!existsSync21(defaultYamlPath)) {
|
|
32187
32479
|
throw new Error(`Default template not found at: ${defaultTemplatePath}`);
|
|
32188
32480
|
}
|
|
32189
|
-
workspaceYamlContent = await
|
|
32481
|
+
workspaceYamlContent = await readFile13(defaultYamlPath, "utf-8");
|
|
32190
32482
|
}
|
|
32191
32483
|
if (options2.clients && options2.clients.length > 0) {
|
|
32192
32484
|
const configParsed = load(workspaceYamlContent);
|
|
@@ -32199,8 +32491,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32199
32491
|
const clientNames = getClientTypes(clients);
|
|
32200
32492
|
const VSCODE_TEMPLATE_FILE2 = "template.code-workspace";
|
|
32201
32493
|
if (clientNames.includes("vscode") && options2.from) {
|
|
32202
|
-
const targetTemplatePath =
|
|
32203
|
-
if (!
|
|
32494
|
+
const targetTemplatePath = join22(configDir, VSCODE_TEMPLATE_FILE2);
|
|
32495
|
+
if (!existsSync21(targetTemplatePath)) {
|
|
32204
32496
|
if (isGitHubUrl(options2.from) && githubTempDir) {
|
|
32205
32497
|
if (parsedFromUrl) {
|
|
32206
32498
|
const templatePath = githubBasePath ? `${githubBasePath}/${CONFIG_DIR}/${VSCODE_TEMPLATE_FILE2}` : `${CONFIG_DIR}/${VSCODE_TEMPLATE_FILE2}`;
|
|
@@ -32210,8 +32502,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32210
32502
|
}
|
|
32211
32503
|
}
|
|
32212
32504
|
} else if (sourceDir) {
|
|
32213
|
-
const sourceTemplatePath =
|
|
32214
|
-
if (
|
|
32505
|
+
const sourceTemplatePath = join22(sourceDir, CONFIG_DIR, VSCODE_TEMPLATE_FILE2);
|
|
32506
|
+
if (existsSync21(sourceTemplatePath)) {
|
|
32215
32507
|
await copyFile2(sourceTemplatePath, targetTemplatePath);
|
|
32216
32508
|
}
|
|
32217
32509
|
}
|
|
@@ -32224,8 +32516,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32224
32516
|
if (options2.from && isGitHubUrl(options2.from) && githubTempDir) {
|
|
32225
32517
|
if (parsedFromUrl) {
|
|
32226
32518
|
for (const agentFile of AGENT_FILES) {
|
|
32227
|
-
const targetFilePath =
|
|
32228
|
-
if (
|
|
32519
|
+
const targetFilePath = join22(absoluteTarget, agentFile);
|
|
32520
|
+
if (existsSync21(targetFilePath)) {
|
|
32229
32521
|
copiedAgentFiles.push(agentFile);
|
|
32230
32522
|
continue;
|
|
32231
32523
|
}
|
|
@@ -32240,30 +32532,30 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
32240
32532
|
} else {
|
|
32241
32533
|
const effectiveSourceDir = sourceDir ?? defaultTemplatePath;
|
|
32242
32534
|
for (const agentFile of AGENT_FILES) {
|
|
32243
|
-
const targetFilePath =
|
|
32244
|
-
if (
|
|
32535
|
+
const targetFilePath = join22(absoluteTarget, agentFile);
|
|
32536
|
+
if (existsSync21(targetFilePath)) {
|
|
32245
32537
|
copiedAgentFiles.push(agentFile);
|
|
32246
32538
|
continue;
|
|
32247
32539
|
}
|
|
32248
|
-
const sourcePath =
|
|
32249
|
-
if (
|
|
32250
|
-
const content = await
|
|
32540
|
+
const sourcePath = join22(effectiveSourceDir, agentFile);
|
|
32541
|
+
if (existsSync21(sourcePath)) {
|
|
32542
|
+
const content = await readFile13(sourcePath, "utf-8");
|
|
32251
32543
|
await writeFile8(targetFilePath, content, "utf-8");
|
|
32252
32544
|
copiedAgentFiles.push(agentFile);
|
|
32253
32545
|
}
|
|
32254
32546
|
}
|
|
32255
32547
|
}
|
|
32256
32548
|
if (copiedAgentFiles.length === 0) {
|
|
32257
|
-
await ensureWorkspaceRules(
|
|
32549
|
+
await ensureWorkspaceRules(join22(absoluteTarget, "AGENTS.md"), repositories);
|
|
32258
32550
|
copiedAgentFiles.push("AGENTS.md");
|
|
32259
32551
|
} else {
|
|
32260
32552
|
for (const agentFile of copiedAgentFiles) {
|
|
32261
|
-
await ensureWorkspaceRules(
|
|
32553
|
+
await ensureWorkspaceRules(join22(absoluteTarget, agentFile), repositories);
|
|
32262
32554
|
}
|
|
32263
32555
|
}
|
|
32264
32556
|
if (clientNames.includes("claude") && !copiedAgentFiles.includes("CLAUDE.md") && copiedAgentFiles.includes("AGENTS.md")) {
|
|
32265
|
-
const agentsPath =
|
|
32266
|
-
const claudePath =
|
|
32557
|
+
const agentsPath = join22(absoluteTarget, "AGENTS.md");
|
|
32558
|
+
const claudePath = join22(absoluteTarget, "CLAUDE.md");
|
|
32267
32559
|
await copyFile2(agentsPath, claudePath);
|
|
32268
32560
|
}
|
|
32269
32561
|
}
|
|
@@ -32308,14 +32600,14 @@ Next steps:`);
|
|
|
32308
32600
|
async function seedCacheFromClone(tempDir, owner, repo, branch) {
|
|
32309
32601
|
const cachePaths = [
|
|
32310
32602
|
getPluginCachePath(owner, repo, branch),
|
|
32311
|
-
|
|
32603
|
+
join22(getMarketplacesDir(), repo)
|
|
32312
32604
|
];
|
|
32313
32605
|
for (const cachePath of cachePaths) {
|
|
32314
|
-
if (
|
|
32606
|
+
if (existsSync21(cachePath))
|
|
32315
32607
|
continue;
|
|
32316
32608
|
try {
|
|
32317
32609
|
const parentDir = dirname12(cachePath);
|
|
32318
|
-
if (!
|
|
32610
|
+
if (!existsSync21(parentDir)) {
|
|
32319
32611
|
await mkdir9(parentDir, { recursive: true });
|
|
32320
32612
|
}
|
|
32321
32613
|
await cp2(tempDir, cachePath, { recursive: true });
|
|
@@ -32336,11 +32628,11 @@ var init_workspace = __esm(() => {
|
|
|
32336
32628
|
});
|
|
32337
32629
|
|
|
32338
32630
|
// src/core/status.ts
|
|
32339
|
-
import { existsSync as
|
|
32340
|
-
import { join as
|
|
32631
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
32632
|
+
import { join as join23 } from "node:path";
|
|
32341
32633
|
async function getWorkspaceStatus(workspacePath = process.cwd()) {
|
|
32342
|
-
const configPath =
|
|
32343
|
-
if (!
|
|
32634
|
+
const configPath = join23(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
32635
|
+
if (!existsSync22(configPath) || isUserConfigPath(workspacePath)) {
|
|
32344
32636
|
const userPlugins = await getUserPluginStatuses();
|
|
32345
32637
|
return {
|
|
32346
32638
|
success: true,
|
|
@@ -32382,7 +32674,7 @@ async function getWorkspaceStatus(workspacePath = process.cwd()) {
|
|
|
32382
32674
|
function getPluginStatus(parsed) {
|
|
32383
32675
|
if (parsed.type === "github") {
|
|
32384
32676
|
const cachePath = parsed.owner && parsed.repo ? getPluginCachePath(parsed.owner, parsed.repo) : "";
|
|
32385
|
-
const available2 = cachePath ?
|
|
32677
|
+
const available2 = cachePath ? existsSync22(cachePath) : false;
|
|
32386
32678
|
return {
|
|
32387
32679
|
source: parsed.original,
|
|
32388
32680
|
type: "github",
|
|
@@ -32392,7 +32684,7 @@ function getPluginStatus(parsed) {
|
|
|
32392
32684
|
...parsed.repo && { repo: parsed.repo }
|
|
32393
32685
|
};
|
|
32394
32686
|
}
|
|
32395
|
-
const available =
|
|
32687
|
+
const available = existsSync22(parsed.normalized);
|
|
32396
32688
|
return {
|
|
32397
32689
|
source: parsed.original,
|
|
32398
32690
|
type: "local",
|
|
@@ -34553,160 +34845,6 @@ var init_prompt_clients = __esm(() => {
|
|
|
34553
34845
|
({ autocompleteMultiselect } = exports_dist);
|
|
34554
34846
|
});
|
|
34555
34847
|
|
|
34556
|
-
// src/core/skills.ts
|
|
34557
|
-
import { existsSync as existsSync24 } from "node:fs";
|
|
34558
|
-
import { readFile as readFile14, readdir as readdir5 } from "node:fs/promises";
|
|
34559
|
-
import { join as join25, basename as basename7, resolve as resolve14 } from "node:path";
|
|
34560
|
-
async function resolvePluginPath(pluginSource, workspacePath) {
|
|
34561
|
-
if (isPluginSpec(pluginSource)) {
|
|
34562
|
-
const resolved2 = await resolvePluginSpecWithAutoRegister(pluginSource, {
|
|
34563
|
-
offline: true,
|
|
34564
|
-
workspacePath
|
|
34565
|
-
});
|
|
34566
|
-
if (!resolved2.success || !resolved2.path)
|
|
34567
|
-
return null;
|
|
34568
|
-
return {
|
|
34569
|
-
path: resolved2.path,
|
|
34570
|
-
pluginName: resolved2.pluginName
|
|
34571
|
-
};
|
|
34572
|
-
}
|
|
34573
|
-
if (isGitHubUrl(pluginSource)) {
|
|
34574
|
-
const parsed = parseGitHubUrl(pluginSource);
|
|
34575
|
-
const result = await fetchPlugin(pluginSource, {
|
|
34576
|
-
offline: true,
|
|
34577
|
-
...parsed?.branch && { branch: parsed.branch }
|
|
34578
|
-
});
|
|
34579
|
-
if (!result.success)
|
|
34580
|
-
return null;
|
|
34581
|
-
const path = parsed?.subpath ? join25(result.cachePath, parsed.subpath) : result.cachePath;
|
|
34582
|
-
return { path };
|
|
34583
|
-
}
|
|
34584
|
-
const resolved = resolve14(workspacePath, pluginSource);
|
|
34585
|
-
return existsSync24(resolved) ? { path: resolved } : null;
|
|
34586
|
-
}
|
|
34587
|
-
async function getAllSkillsFromPlugins(workspacePath = process.cwd()) {
|
|
34588
|
-
const configPath = join25(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
34589
|
-
if (!existsSync24(configPath)) {
|
|
34590
|
-
return [];
|
|
34591
|
-
}
|
|
34592
|
-
const content = await readFile14(configPath, "utf-8");
|
|
34593
|
-
const config = load(content);
|
|
34594
|
-
const isV1Fallback = config.version === undefined || config.version < 2;
|
|
34595
|
-
const disabledSkills = isV1Fallback ? new Set(config.disabledSkills ?? []) : new Set;
|
|
34596
|
-
const enabledSkills = isV1Fallback && config.enabledSkills ? new Set(config.enabledSkills) : null;
|
|
34597
|
-
const skills = [];
|
|
34598
|
-
for (const pluginEntry of config.plugins) {
|
|
34599
|
-
const pluginSource = getPluginSource(pluginEntry);
|
|
34600
|
-
const resolved = await resolvePluginPath(pluginSource, workspacePath);
|
|
34601
|
-
if (!resolved)
|
|
34602
|
-
continue;
|
|
34603
|
-
const pluginPath = resolved.path;
|
|
34604
|
-
const pluginName = resolved.pluginName ?? getPluginName(pluginPath);
|
|
34605
|
-
const skillsDir = join25(pluginPath, "skills");
|
|
34606
|
-
const pluginSkillsConfig = typeof pluginEntry === "string" ? undefined : pluginEntry.skills;
|
|
34607
|
-
const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && [...enabledSkills].some((s) => s.startsWith(`${pluginName}`));
|
|
34608
|
-
let skillEntries;
|
|
34609
|
-
if (existsSync24(skillsDir)) {
|
|
34610
|
-
const entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
34611
|
-
skillEntries = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, skillPath: join25(skillsDir, e.name) }));
|
|
34612
|
-
} else {
|
|
34613
|
-
const entries = await readdir5(pluginPath, { withFileTypes: true });
|
|
34614
|
-
const flatSkills = [];
|
|
34615
|
-
for (const entry of entries) {
|
|
34616
|
-
if (!entry.isDirectory())
|
|
34617
|
-
continue;
|
|
34618
|
-
const skillMdPath = join25(pluginPath, entry.name, "SKILL.md");
|
|
34619
|
-
if (existsSync24(skillMdPath)) {
|
|
34620
|
-
flatSkills.push({ name: entry.name, skillPath: join25(pluginPath, entry.name) });
|
|
34621
|
-
}
|
|
34622
|
-
}
|
|
34623
|
-
if (flatSkills.length > 0) {
|
|
34624
|
-
skillEntries = flatSkills;
|
|
34625
|
-
} else {
|
|
34626
|
-
const rootSkillMd = join25(pluginPath, "SKILL.md");
|
|
34627
|
-
if (existsSync24(rootSkillMd)) {
|
|
34628
|
-
const skillContent = await readFile14(rootSkillMd, "utf-8");
|
|
34629
|
-
const metadata = parseSkillMetadata(skillContent);
|
|
34630
|
-
const skillName = metadata?.name ?? basename7(pluginPath);
|
|
34631
|
-
skillEntries = [{ name: skillName, skillPath: pluginPath }];
|
|
34632
|
-
} else {
|
|
34633
|
-
skillEntries = [];
|
|
34634
|
-
}
|
|
34635
|
-
}
|
|
34636
|
-
}
|
|
34637
|
-
const pluginSkillsMode = pluginSkillsConfig === undefined ? "none" : Array.isArray(pluginSkillsConfig) ? "allowlist" : "blocklist";
|
|
34638
|
-
for (const { name, skillPath } of skillEntries) {
|
|
34639
|
-
const skillKey = `${pluginName}:${name}`;
|
|
34640
|
-
let isDisabled;
|
|
34641
|
-
if (pluginSkillsConfig !== undefined) {
|
|
34642
|
-
if (Array.isArray(pluginSkillsConfig)) {
|
|
34643
|
-
isDisabled = !pluginSkillsConfig.includes(name);
|
|
34644
|
-
} else {
|
|
34645
|
-
isDisabled = pluginSkillsConfig.exclude.includes(name);
|
|
34646
|
-
}
|
|
34647
|
-
} else if (isV1Fallback) {
|
|
34648
|
-
isDisabled = hasEnabledEntries ? !enabledSkills?.has(skillKey) : disabledSkills.has(skillKey);
|
|
34649
|
-
} else {
|
|
34650
|
-
isDisabled = false;
|
|
34651
|
-
}
|
|
34652
|
-
skills.push({
|
|
34653
|
-
name,
|
|
34654
|
-
pluginName,
|
|
34655
|
-
pluginSource,
|
|
34656
|
-
path: skillPath,
|
|
34657
|
-
disabled: isDisabled,
|
|
34658
|
-
pluginSkillsMode
|
|
34659
|
-
});
|
|
34660
|
-
}
|
|
34661
|
-
}
|
|
34662
|
-
return skills;
|
|
34663
|
-
}
|
|
34664
|
-
async function findSkillByName(skillName, workspacePath = process.cwd()) {
|
|
34665
|
-
const allSkills = await getAllSkillsFromPlugins(workspacePath);
|
|
34666
|
-
return allSkills.filter((s) => s.name === skillName);
|
|
34667
|
-
}
|
|
34668
|
-
async function discoverSkillNames(pluginPath) {
|
|
34669
|
-
if (!existsSync24(pluginPath))
|
|
34670
|
-
return [];
|
|
34671
|
-
const skillsDir = join25(pluginPath, "skills");
|
|
34672
|
-
if (existsSync24(skillsDir)) {
|
|
34673
|
-
const entries2 = await readdir5(skillsDir, { withFileTypes: true });
|
|
34674
|
-
return entries2.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
34675
|
-
}
|
|
34676
|
-
const entries = await readdir5(pluginPath, { withFileTypes: true });
|
|
34677
|
-
const flatSkills = [];
|
|
34678
|
-
for (const entry of entries) {
|
|
34679
|
-
if (!entry.isDirectory())
|
|
34680
|
-
continue;
|
|
34681
|
-
if (existsSync24(join25(pluginPath, entry.name, "SKILL.md"))) {
|
|
34682
|
-
flatSkills.push(entry.name);
|
|
34683
|
-
}
|
|
34684
|
-
}
|
|
34685
|
-
if (flatSkills.length > 0)
|
|
34686
|
-
return flatSkills;
|
|
34687
|
-
const rootSkillMd = join25(pluginPath, "SKILL.md");
|
|
34688
|
-
if (existsSync24(rootSkillMd)) {
|
|
34689
|
-
try {
|
|
34690
|
-
const content = await readFile14(rootSkillMd, "utf-8");
|
|
34691
|
-
const { parseSkillMetadata: parseSkillMetadata2 } = await Promise.resolve().then(() => (init_skill(), exports_skill));
|
|
34692
|
-
const metadata = parseSkillMetadata2(content);
|
|
34693
|
-
return [metadata?.name ?? basename7(pluginPath)];
|
|
34694
|
-
} catch {
|
|
34695
|
-
return [basename7(pluginPath)];
|
|
34696
|
-
}
|
|
34697
|
-
}
|
|
34698
|
-
return [];
|
|
34699
|
-
}
|
|
34700
|
-
var init_skills = __esm(() => {
|
|
34701
|
-
init_js_yaml();
|
|
34702
|
-
init_constants();
|
|
34703
|
-
init_workspace_config();
|
|
34704
|
-
init_plugin();
|
|
34705
|
-
init_plugin_path();
|
|
34706
|
-
init_marketplace();
|
|
34707
|
-
init_skill();
|
|
34708
|
-
});
|
|
34709
|
-
|
|
34710
34848
|
// src/core/skill-search.ts
|
|
34711
34849
|
function validateSkillSearchArgs(query, options2) {
|
|
34712
34850
|
if (query.trim().length < 2) {
|
|
@@ -34841,6 +34979,23 @@ async function runOneQuery(q3, page, limit, token, fetchFn) {
|
|
|
34841
34979
|
truncated: Boolean(parsed.incomplete_results)
|
|
34842
34980
|
};
|
|
34843
34981
|
}
|
|
34982
|
+
async function fetchPrimaryPages(q3, page, limit, token, fetchFn) {
|
|
34983
|
+
const needed = page * limit * 3;
|
|
34984
|
+
const numPages = Math.min(Math.max(1, Math.ceil(needed / SEARCH_PAGE_SIZE)), MAX_RESULTS / SEARCH_PAGE_SIZE);
|
|
34985
|
+
const items = [];
|
|
34986
|
+
let total = 0;
|
|
34987
|
+
let truncated = false;
|
|
34988
|
+
for (let currentPage = 1;currentPage <= numPages; currentPage += 1) {
|
|
34989
|
+
const result = await runOneQuery(q3, currentPage, SEARCH_PAGE_SIZE, token, fetchFn);
|
|
34990
|
+
items.push(...result.items);
|
|
34991
|
+
total = result.total;
|
|
34992
|
+
truncated = truncated || result.truncated;
|
|
34993
|
+
if (result.items.length < SEARCH_PAGE_SIZE) {
|
|
34994
|
+
break;
|
|
34995
|
+
}
|
|
34996
|
+
}
|
|
34997
|
+
return { items, total, truncated };
|
|
34998
|
+
}
|
|
34844
34999
|
async function searchSkills(query, options2 = {}, deps = {}) {
|
|
34845
35000
|
validateSkillSearchArgs(query, options2);
|
|
34846
35001
|
const fetchFn = deps.fetch ?? fetch;
|
|
@@ -34850,7 +35005,7 @@ async function searchSkills(query, options2 = {}, deps = {}) {
|
|
|
34850
35005
|
const limit = options2.limit ?? 15;
|
|
34851
35006
|
const token = await (deps.tokenResolver ?? resolveGhToken)();
|
|
34852
35007
|
const queries = buildSearchQueries(query, options2.owner);
|
|
34853
|
-
const settled = await Promise.allSettled(queries.map((entry) =>
|
|
35008
|
+
const settled = await Promise.allSettled(queries.map((entry) => entry.priority === 4 ? fetchPrimaryPages(entry.q, page, limit, token, fetchFn) : runOneQuery(entry.q, 1, SEARCH_PAGE_SIZE, token, fetchFn)));
|
|
34854
35009
|
const primaryIdx = queries.findIndex((q3) => q3.priority === 4);
|
|
34855
35010
|
const primarySettled = settled[primaryIdx];
|
|
34856
35011
|
if (primarySettled?.status === "rejected") {
|
|
@@ -34876,17 +35031,21 @@ async function searchSkills(query, options2 = {}, deps = {}) {
|
|
|
34876
35031
|
const firstSegment = item.path.split("/")[0] ?? "";
|
|
34877
35032
|
return !firstSegment.startsWith(".");
|
|
34878
35033
|
});
|
|
35034
|
+
rankByRelevance(visible, query);
|
|
35035
|
+
const workingSet = truncateForProcessing(visible, page, limit);
|
|
34879
35036
|
await Promise.all([
|
|
34880
|
-
fetchStarsForItems(
|
|
34881
|
-
enrichDescriptionsForItems(
|
|
35037
|
+
fetchStarsForItems(workingSet, token, fetchFn),
|
|
35038
|
+
enrichDescriptionsForItems(workingSet, token, fetchFn)
|
|
34882
35039
|
]);
|
|
34883
|
-
|
|
34884
|
-
|
|
35040
|
+
const filtered = filterByRelevance(workingSet, query);
|
|
35041
|
+
rankByRelevance(filtered, query);
|
|
35042
|
+
const dedupedByName = deduplicateByName(filtered);
|
|
35043
|
+
const { items: finalItems, totalPages } = paginate(dedupedByName, page, limit);
|
|
34885
35044
|
return {
|
|
34886
35045
|
query,
|
|
34887
35046
|
items: finalItems,
|
|
34888
|
-
total:
|
|
34889
|
-
truncated: buckets.some((b) => b.result.truncated) ||
|
|
35047
|
+
total: dedupedByName.length,
|
|
35048
|
+
truncated: buckets.some((b) => b.result.truncated) || totalPages > page
|
|
34890
35049
|
};
|
|
34891
35050
|
}
|
|
34892
35051
|
function dedupeItems(items) {
|
|
@@ -34901,6 +35060,73 @@ function dedupeItems(items) {
|
|
|
34901
35060
|
}
|
|
34902
35061
|
return out;
|
|
34903
35062
|
}
|
|
35063
|
+
function splitRepo(item) {
|
|
35064
|
+
const [owner = item.repo, repoName = ""] = item.repo.split("/", 2);
|
|
35065
|
+
return { owner, repoName };
|
|
35066
|
+
}
|
|
35067
|
+
function relevanceScore(item, query) {
|
|
35068
|
+
const term = query.trim().toLowerCase();
|
|
35069
|
+
const termHyphen = term.replace(/ /g, "-");
|
|
35070
|
+
const name = item.name.toLowerCase();
|
|
35071
|
+
const namespace = item.namespace.toLowerCase();
|
|
35072
|
+
const description = item.description.toLowerCase();
|
|
35073
|
+
let score = 0;
|
|
35074
|
+
if (name === term || name === termHyphen) {
|
|
35075
|
+
score += 3000;
|
|
35076
|
+
} else if (name.includes(term) || name.includes(termHyphen)) {
|
|
35077
|
+
score += 1000;
|
|
35078
|
+
}
|
|
35079
|
+
if (namespace?.includes(term)) {
|
|
35080
|
+
score += 500;
|
|
35081
|
+
}
|
|
35082
|
+
if (description.includes(term)) {
|
|
35083
|
+
score += 100;
|
|
35084
|
+
}
|
|
35085
|
+
if (item.stars > 0) {
|
|
35086
|
+
score += Math.floor(Math.sqrt(item.stars) * 30);
|
|
35087
|
+
}
|
|
35088
|
+
return score;
|
|
35089
|
+
}
|
|
35090
|
+
function rankByRelevance(items, query) {
|
|
35091
|
+
items.sort((a, b) => relevanceScore(b, query) - relevanceScore(a, query));
|
|
35092
|
+
}
|
|
35093
|
+
function filterByRelevance(items, query) {
|
|
35094
|
+
const term = query.trim().toLowerCase();
|
|
35095
|
+
const termHyphen = term.replace(/ /g, "-");
|
|
35096
|
+
return items.filter((item) => {
|
|
35097
|
+
const { owner, repoName } = splitRepo(item);
|
|
35098
|
+
return item.name.toLowerCase().includes(term) || item.name.toLowerCase().includes(termHyphen) || item.namespace.toLowerCase().includes(term) || item.description.toLowerCase().includes(term) || owner.toLowerCase().includes(term) || repoName.toLowerCase().includes(term);
|
|
35099
|
+
});
|
|
35100
|
+
}
|
|
35101
|
+
function truncateForProcessing(items, page, limit) {
|
|
35102
|
+
const maxToProcess = Math.max(page * limit * 3, limit * 3);
|
|
35103
|
+
return items.length > maxToProcess ? items.slice(0, maxToProcess) : items;
|
|
35104
|
+
}
|
|
35105
|
+
function deduplicateByName(items) {
|
|
35106
|
+
const maxPerName = 3;
|
|
35107
|
+
const counts = new Map;
|
|
35108
|
+
const out = [];
|
|
35109
|
+
for (const item of items) {
|
|
35110
|
+
const key = qualifiedName(item).toLowerCase();
|
|
35111
|
+
const count = counts.get(key) ?? 0;
|
|
35112
|
+
if (count >= maxPerName)
|
|
35113
|
+
continue;
|
|
35114
|
+
counts.set(key, count + 1);
|
|
35115
|
+
out.push(item);
|
|
35116
|
+
}
|
|
35117
|
+
return out;
|
|
35118
|
+
}
|
|
35119
|
+
function paginate(items, page, limit) {
|
|
35120
|
+
if (items.length === 0) {
|
|
35121
|
+
return { items: [], totalPages: 0 };
|
|
35122
|
+
}
|
|
35123
|
+
const totalPages = Math.ceil(items.length / limit);
|
|
35124
|
+
const start = (page - 1) * limit;
|
|
35125
|
+
return {
|
|
35126
|
+
items: items.slice(start, start + limit),
|
|
35127
|
+
totalPages
|
|
35128
|
+
};
|
|
35129
|
+
}
|
|
34904
35130
|
async function fetchStarsForItems(items, token, fetchFn) {
|
|
34905
35131
|
const uniqueRepos = [...new Set(items.map((i2) => i2.repo))];
|
|
34906
35132
|
const headers = buildGitHubApiHeaders(token);
|
|
@@ -34982,7 +35208,7 @@ function decodeGitBlob(content, encoding) {
|
|
|
34982
35208
|
return;
|
|
34983
35209
|
return Buffer.from(content.replace(/\s+/g, ""), "base64").toString("utf8");
|
|
34984
35210
|
}
|
|
34985
|
-
var OWNER_REGEX, COULD_BE_OWNER_REGEX, ENRICHMENT_CONCURRENCY = 10, SkillSearchError;
|
|
35211
|
+
var OWNER_REGEX, SEARCH_PAGE_SIZE = 100, MAX_RESULTS = 1000, COULD_BE_OWNER_REGEX, ENRICHMENT_CONCURRENCY = 10, SkillSearchError;
|
|
34986
35212
|
var init_skill_search = __esm(() => {
|
|
34987
35213
|
init_skill();
|
|
34988
35214
|
OWNER_REGEX = /^[A-Za-z0-9-]{1,39}$/;
|
|
@@ -41967,7 +42193,7 @@ var package_default;
|
|
|
41967
42193
|
var init_package = __esm(() => {
|
|
41968
42194
|
package_default = {
|
|
41969
42195
|
name: "allagents",
|
|
41970
|
-
version: "1.11.
|
|
42196
|
+
version: "1.11.6-next.1",
|
|
41971
42197
|
packageManager: "bun@1.3.12",
|
|
41972
42198
|
description: "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
|
|
41973
42199
|
type: "module",
|
|
@@ -43664,8 +43890,8 @@ init_workspace();
|
|
|
43664
43890
|
init_sync();
|
|
43665
43891
|
init_status2();
|
|
43666
43892
|
var import_cmd_ts2 = __toESM(require_cjs(), 1);
|
|
43667
|
-
import { existsSync as
|
|
43668
|
-
import { join as
|
|
43893
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
43894
|
+
import { join as join25, resolve as resolve14 } from "node:path";
|
|
43669
43895
|
|
|
43670
43896
|
// src/core/prune.ts
|
|
43671
43897
|
init_js_yaml();
|
|
@@ -43673,9 +43899,9 @@ init_constants();
|
|
|
43673
43899
|
init_marketplace();
|
|
43674
43900
|
init_user_workspace();
|
|
43675
43901
|
init_workspace_config();
|
|
43676
|
-
import { readFile as
|
|
43677
|
-
import { existsSync as
|
|
43678
|
-
import { join as
|
|
43902
|
+
import { readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
|
|
43903
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
43904
|
+
import { join as join24 } from "node:path";
|
|
43679
43905
|
async function isOrphanedPlugin(pluginSpec) {
|
|
43680
43906
|
if (!isPluginSpec(pluginSpec))
|
|
43681
43907
|
return false;
|
|
@@ -43702,9 +43928,9 @@ async function prunePlugins(plugins) {
|
|
|
43702
43928
|
}
|
|
43703
43929
|
async function pruneOrphanedPlugins(workspacePath) {
|
|
43704
43930
|
let projectResult = { removed: [], kept: [], keptEntries: [] };
|
|
43705
|
-
const projectConfigPath =
|
|
43706
|
-
if (
|
|
43707
|
-
const content = await
|
|
43931
|
+
const projectConfigPath = join24(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
43932
|
+
if (existsSync23(projectConfigPath) && !isUserConfigPath(workspacePath)) {
|
|
43933
|
+
const content = await readFile14(projectConfigPath, "utf-8");
|
|
43708
43934
|
const config = load(content);
|
|
43709
43935
|
projectResult = await prunePlugins(config.plugins);
|
|
43710
43936
|
if (projectResult.removed.length > 0) {
|
|
@@ -44090,8 +44316,8 @@ var syncCmd = import_cmd_ts2.command({
|
|
|
44090
44316
|
`);
|
|
44091
44317
|
}
|
|
44092
44318
|
const userConfigExists = !!await getUserWorkspaceConfig();
|
|
44093
|
-
const projectConfigPath =
|
|
44094
|
-
const projectConfigExists =
|
|
44319
|
+
const projectConfigPath = join25(process.cwd(), ".allagents", "workspace.yaml");
|
|
44320
|
+
const projectConfigExists = existsSync24(projectConfigPath);
|
|
44095
44321
|
if (!userConfigExists && !projectConfigExists) {
|
|
44096
44322
|
await ensureUserWorkspace();
|
|
44097
44323
|
if (isJsonMode()) {
|
|
@@ -44386,7 +44612,7 @@ var repoAddCmd = import_cmd_ts2.command({
|
|
|
44386
44612
|
},
|
|
44387
44613
|
handler: async ({ path: repoPath, description }) => {
|
|
44388
44614
|
try {
|
|
44389
|
-
const resolvedPath =
|
|
44615
|
+
const resolvedPath = resolve14(process.cwd(), repoPath);
|
|
44390
44616
|
const remote = await detectRemote(resolvedPath);
|
|
44391
44617
|
const result = await addRepository(repoPath, {
|
|
44392
44618
|
source: remote?.source,
|
|
@@ -44814,15 +45040,17 @@ var skillsRemoveMeta = {
|
|
|
44814
45040
|
};
|
|
44815
45041
|
var skillsSearchMeta = {
|
|
44816
45042
|
command: "skill search",
|
|
44817
|
-
description: "Search GitHub for skills by querying SKILL.md files via the Code Search API. Results are
|
|
45043
|
+
description: "Search GitHub for skills by querying SKILL.md files via the Code Search API. Results are ranked by relevance, with skill-name matches first. In TTY mode, shows a filter-as-you-type multi-select picker and offers to install the selected skills.",
|
|
44818
45044
|
whenToUse: 'To discover available skills from public GitHub repositories without leaving the CLI. Bridges "I want a skill that does X" → install.',
|
|
44819
45045
|
examples: [
|
|
44820
45046
|
"allagents skill search terraform",
|
|
45047
|
+
"allagents skill pr-search",
|
|
45048
|
+
'allagents skill "pr search"',
|
|
44821
45049
|
"allagents skill search terraform --owner hashicorp",
|
|
44822
45050
|
"allagents skill search docs --page 2 --limit 10",
|
|
44823
45051
|
"allagents --json skill search docs --limit 5"
|
|
44824
45052
|
],
|
|
44825
|
-
expectedOutput: "Skills
|
|
45053
|
+
expectedOutput: "Skills ranked by relevance: repo, skill name, stars, description. In TTY mode, followed by a searchable multi-select install prompt.",
|
|
44826
45054
|
positionals: [
|
|
44827
45055
|
{ name: "query", type: "string", required: true, description: "Search query (≥2 characters)." }
|
|
44828
45056
|
],
|
|
@@ -44933,6 +45161,12 @@ async function recordSourceProvenance(opts) {
|
|
|
44933
45161
|
...pinnedRef && { pinnedRef }
|
|
44934
45162
|
});
|
|
44935
45163
|
}
|
|
45164
|
+
function resolveFetchedSourcePath(source, cachePath) {
|
|
45165
|
+
if (!isGitHubUrl(source))
|
|
45166
|
+
return cachePath;
|
|
45167
|
+
const parsed = parseGitHubUrl(source);
|
|
45168
|
+
return parsed?.subpath ? join26(cachePath, parsed.subpath) : cachePath;
|
|
45169
|
+
}
|
|
44936
45170
|
function extractInlineRef(spec) {
|
|
44937
45171
|
const slashIdx = spec.indexOf("/");
|
|
44938
45172
|
if (slashIdx === -1)
|
|
@@ -45206,11 +45440,12 @@ async function installSkillFromSource(opts) {
|
|
|
45206
45440
|
if (!fetchResult.success) {
|
|
45207
45441
|
return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
|
|
45208
45442
|
}
|
|
45209
|
-
const
|
|
45443
|
+
const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
|
|
45444
|
+
const manifestResult = await parseMarketplaceManifest(sourcePath);
|
|
45210
45445
|
if (manifestResult.success) {
|
|
45211
45446
|
return installSkillViaMarketplace({ skill, from, isUser, workspacePath });
|
|
45212
45447
|
}
|
|
45213
|
-
return installSkillDirect({ skill, from, isUser, workspacePath, cachePath:
|
|
45448
|
+
return installSkillDirect({ skill, from, isUser, workspacePath, cachePath: sourcePath });
|
|
45214
45449
|
}
|
|
45215
45450
|
async function installSkillViaMarketplace(opts) {
|
|
45216
45451
|
const { skill, from, isUser, workspacePath } = opts;
|
|
@@ -45263,7 +45498,8 @@ Available skills: ${allAvailableSkills.join(", ") || "none"}`
|
|
|
45263
45498
|
}
|
|
45264
45499
|
async function installSkillDirect(opts) {
|
|
45265
45500
|
const { skill, from, isUser, workspacePath, cachePath } = opts;
|
|
45266
|
-
const
|
|
45501
|
+
const sourcePath = resolveFetchedSourcePath(from, cachePath);
|
|
45502
|
+
const availableSkills = await discoverSkillNames(sourcePath);
|
|
45267
45503
|
if (!availableSkills.includes(skill)) {
|
|
45268
45504
|
return {
|
|
45269
45505
|
success: false,
|
|
@@ -45274,6 +45510,25 @@ Available skills: ${availableSkills.join(", ") || "none"}
|
|
|
45274
45510
|
Tip: run \`allagents skill list\` to see all installed skills.`
|
|
45275
45511
|
};
|
|
45276
45512
|
}
|
|
45513
|
+
if (isGitHubUrl(from)) {
|
|
45514
|
+
const existingEnabledSkills = await getEnabledSkillsForGitHubSource(from, workspacePath);
|
|
45515
|
+
const desiredSkills = [...existingEnabledSkills];
|
|
45516
|
+
if (!desiredSkills.includes(skill))
|
|
45517
|
+
desiredSkills.push(skill);
|
|
45518
|
+
const updateResult = isUser ? await upsertUserGitHubPluginSourceAllowlist(from, desiredSkills) : await upsertGitHubPluginSourceAllowlist(from, desiredSkills, workspacePath);
|
|
45519
|
+
if (!updateResult.success) {
|
|
45520
|
+
return {
|
|
45521
|
+
success: false,
|
|
45522
|
+
error: `Failed to update plugin '${from}': ${updateResult.error ?? "Unknown error"}`
|
|
45523
|
+
};
|
|
45524
|
+
}
|
|
45525
|
+
return finishSkillEnable({
|
|
45526
|
+
skill,
|
|
45527
|
+
pluginName: extractPrimaryPluginName(updateResult.normalizedPlugin ?? from),
|
|
45528
|
+
isUser,
|
|
45529
|
+
workspacePath
|
|
45530
|
+
});
|
|
45531
|
+
}
|
|
45277
45532
|
const installResult = isUser ? await addUserPlugin(from) : await addPlugin(from, workspacePath);
|
|
45278
45533
|
if (!installResult.success) {
|
|
45279
45534
|
if (!installResult.error?.includes("already exists") && !installResult.error?.includes("duplicates existing")) {
|
|
@@ -45283,9 +45538,36 @@ Tip: run \`allagents skill list\` to see all installed skills.`
|
|
|
45283
45538
|
console.log("Plugin already installed.");
|
|
45284
45539
|
}
|
|
45285
45540
|
}
|
|
45286
|
-
const pluginName = getPluginName(
|
|
45541
|
+
const pluginName = getPluginName(sourcePath);
|
|
45287
45542
|
return applySkillAllowlist({ skill, pluginName, isUser, workspacePath });
|
|
45288
45543
|
}
|
|
45544
|
+
function extractPrimaryPluginName(source) {
|
|
45545
|
+
const parsed = isGitHubUrl(source) ? parseGitHubUrl(source) : null;
|
|
45546
|
+
if (parsed?.subpath) {
|
|
45547
|
+
const segments = parsed.subpath.split("/").filter(Boolean);
|
|
45548
|
+
const leaf = segments[segments.length - 1];
|
|
45549
|
+
if (leaf)
|
|
45550
|
+
return leaf;
|
|
45551
|
+
}
|
|
45552
|
+
return getPluginName(source);
|
|
45553
|
+
}
|
|
45554
|
+
async function getEnabledSkillsForGitHubSource(source, workspacePath) {
|
|
45555
|
+
const identity = await resolveGitHubIdentity(source);
|
|
45556
|
+
if (!identity)
|
|
45557
|
+
return [];
|
|
45558
|
+
const enabledSkills = [];
|
|
45559
|
+
const allSkills = await getAllSkillsFromPlugins(workspacePath);
|
|
45560
|
+
for (const skill of allSkills) {
|
|
45561
|
+
if (skill.disabled)
|
|
45562
|
+
continue;
|
|
45563
|
+
const skillIdentity = await resolveGitHubIdentity(skill.pluginSource);
|
|
45564
|
+
if (skillIdentity !== identity)
|
|
45565
|
+
continue;
|
|
45566
|
+
if (!enabledSkills.includes(skill.name))
|
|
45567
|
+
enabledSkills.push(skill.name);
|
|
45568
|
+
}
|
|
45569
|
+
return enabledSkills;
|
|
45570
|
+
}
|
|
45289
45571
|
async function applySkillAllowlist(opts) {
|
|
45290
45572
|
const { skill, pluginName, isUser, workspacePath } = opts;
|
|
45291
45573
|
const allSkills = await getAllSkillsFromPlugins(workspacePath);
|
|
@@ -45305,6 +45587,10 @@ async function applySkillAllowlist(opts) {
|
|
|
45305
45587
|
return { success: false, error: `Failed to configure skill allowlist: ${setModeResult.error ?? "Unknown error"}` };
|
|
45306
45588
|
}
|
|
45307
45589
|
}
|
|
45590
|
+
return finishSkillEnable({ skill, pluginName, isUser, workspacePath });
|
|
45591
|
+
}
|
|
45592
|
+
async function finishSkillEnable(opts) {
|
|
45593
|
+
const { skill, pluginName, isUser, workspacePath } = opts;
|
|
45308
45594
|
if (!isJsonMode()) {
|
|
45309
45595
|
console.log(`✓ Enabled skill: ${skill} (${pluginName})`);
|
|
45310
45596
|
}
|
|
@@ -45353,13 +45639,14 @@ async function discoverSkillsFromSource(from) {
|
|
|
45353
45639
|
if (!fetchResult.success) {
|
|
45354
45640
|
return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
|
|
45355
45641
|
}
|
|
45356
|
-
const
|
|
45642
|
+
const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
|
|
45643
|
+
const manifestResult = await parseMarketplaceManifest(sourcePath);
|
|
45357
45644
|
if (manifestResult.success) {
|
|
45358
45645
|
const all = [];
|
|
45359
45646
|
for (const plugin of manifestResult.data.plugins) {
|
|
45360
45647
|
if (typeof plugin.source === "object")
|
|
45361
45648
|
continue;
|
|
45362
|
-
const resolved = resolvePluginSourcePath(plugin.source,
|
|
45649
|
+
const resolved = resolvePluginSourcePath(plugin.source, sourcePath);
|
|
45363
45650
|
if (!existsSync25(resolved))
|
|
45364
45651
|
continue;
|
|
45365
45652
|
const skills2 = await discoverSkillsWithMetadata(resolved, plugin.name);
|
|
@@ -45367,7 +45654,7 @@ async function discoverSkillsFromSource(from) {
|
|
|
45367
45654
|
}
|
|
45368
45655
|
return { success: true, skills: all, isMarketplace: true };
|
|
45369
45656
|
}
|
|
45370
|
-
const skills = await discoverSkillsWithMetadata(
|
|
45657
|
+
const skills = await discoverSkillsWithMetadata(sourcePath);
|
|
45371
45658
|
return { success: true, skills, isMarketplace: false };
|
|
45372
45659
|
}
|
|
45373
45660
|
async function installAllSkillsFromSource(opts) {
|
|
@@ -45382,14 +45669,43 @@ async function installAllSkillsFromSource(opts) {
|
|
|
45382
45669
|
if (!fetchResult.success) {
|
|
45383
45670
|
return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
|
|
45384
45671
|
}
|
|
45385
|
-
const
|
|
45672
|
+
const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
|
|
45673
|
+
const manifestResult = await parseMarketplaceManifest(sourcePath);
|
|
45386
45674
|
if (manifestResult.success) {
|
|
45387
45675
|
return installAllViaMarketplace({ from, isUser, workspacePath, cachedPath: fetchResult.cachePath });
|
|
45388
45676
|
}
|
|
45389
|
-
const skillNames = await discoverSkillNames(
|
|
45677
|
+
const skillNames = await discoverSkillNames(sourcePath);
|
|
45390
45678
|
if (skillNames.length === 0) {
|
|
45391
45679
|
return { success: false, error: `No skills found in '${from}'.` };
|
|
45392
45680
|
}
|
|
45681
|
+
if (isGitHubUrl(from)) {
|
|
45682
|
+
const existingEnabledSkills = await getEnabledSkillsForGitHubSource(from, workspacePath);
|
|
45683
|
+
const desiredSkills = [...existingEnabledSkills];
|
|
45684
|
+
for (const skillName of skillNames) {
|
|
45685
|
+
if (!desiredSkills.includes(skillName))
|
|
45686
|
+
desiredSkills.push(skillName);
|
|
45687
|
+
}
|
|
45688
|
+
const updateResult = isUser ? await upsertUserGitHubPluginSourceAllowlist(from, desiredSkills) : await upsertGitHubPluginSourceAllowlist(from, desiredSkills, workspacePath);
|
|
45689
|
+
if (!updateResult.success) {
|
|
45690
|
+
return {
|
|
45691
|
+
success: false,
|
|
45692
|
+
error: `Failed to configure skill allowlist: ${updateResult.error ?? "Unknown error"}`
|
|
45693
|
+
};
|
|
45694
|
+
}
|
|
45695
|
+
const pluginName2 = extractPrimaryPluginName(updateResult.normalizedPlugin ?? from);
|
|
45696
|
+
if (!isJsonMode()) {
|
|
45697
|
+
console.log(`✓ Enabled ${skillNames.length} skill(s) from ${pluginName2}: ${skillNames.join(", ")}`);
|
|
45698
|
+
}
|
|
45699
|
+
const syncResult2 = isUser ? await syncUserWorkspace() : await syncWorkspace(workspacePath);
|
|
45700
|
+
if (!syncResult2.success) {
|
|
45701
|
+
return { success: false, error: "Sync failed" };
|
|
45702
|
+
}
|
|
45703
|
+
return {
|
|
45704
|
+
success: true,
|
|
45705
|
+
installed: [{ pluginName: pluginName2, skills: desiredSkills }],
|
|
45706
|
+
syncResult: syncResult2
|
|
45707
|
+
};
|
|
45708
|
+
}
|
|
45393
45709
|
const installResult = isUser ? await addUserPlugin(from) : await addPlugin(from, workspacePath);
|
|
45394
45710
|
if (!installResult.success) {
|
|
45395
45711
|
if (!installResult.error?.includes("already exists") && !installResult.error?.includes("duplicates existing")) {
|
|
@@ -45399,7 +45715,7 @@ async function installAllSkillsFromSource(opts) {
|
|
|
45399
45715
|
console.log("Plugin already installed.");
|
|
45400
45716
|
}
|
|
45401
45717
|
}
|
|
45402
|
-
const pluginName = getPluginName(
|
|
45718
|
+
const pluginName = getPluginName(sourcePath);
|
|
45403
45719
|
const setModeResult = isUser ? await setUserPluginSkillsMode(pluginName, "allowlist", skillNames) : await setPluginSkillsMode(pluginName, "allowlist", skillNames, workspacePath);
|
|
45404
45720
|
if (!setModeResult.success) {
|
|
45405
45721
|
return { success: false, error: `Failed to configure skill allowlist: ${setModeResult.error ?? "Unknown error"}` };
|
|
@@ -45954,7 +46270,7 @@ var searchCmd = import_cmd_ts3.command({
|
|
|
45954
46270
|
name: "search",
|
|
45955
46271
|
description: buildDescription(skillsSearchMeta),
|
|
45956
46272
|
args: {
|
|
45957
|
-
query: import_cmd_ts3.
|
|
46273
|
+
query: import_cmd_ts3.restPositionals({ type: import_cmd_ts3.string, displayName: "query" }),
|
|
45958
46274
|
owner: import_cmd_ts3.option({
|
|
45959
46275
|
type: import_cmd_ts3.optional(import_cmd_ts3.string),
|
|
45960
46276
|
long: "owner",
|
|
@@ -45973,6 +46289,7 @@ var searchCmd = import_cmd_ts3.command({
|
|
|
45973
46289
|
},
|
|
45974
46290
|
handler: async ({ query, owner, page, limit }) => {
|
|
45975
46291
|
try {
|
|
46292
|
+
const searchQuery = query.join(" ").trim();
|
|
45976
46293
|
const opts = {};
|
|
45977
46294
|
if (owner)
|
|
45978
46295
|
opts.owner = owner;
|
|
@@ -46002,7 +46319,7 @@ var searchCmd = import_cmd_ts3.command({
|
|
|
46002
46319
|
}
|
|
46003
46320
|
opts.limit = n;
|
|
46004
46321
|
}
|
|
46005
|
-
const result = await searchSkills(
|
|
46322
|
+
const result = await searchSkills(searchQuery, opts);
|
|
46006
46323
|
if (isJsonMode()) {
|
|
46007
46324
|
jsonOutput({
|
|
46008
46325
|
success: true,
|
|
@@ -46012,16 +46329,16 @@ var searchCmd = import_cmd_ts3.command({
|
|
|
46012
46329
|
return;
|
|
46013
46330
|
}
|
|
46014
46331
|
if (result.items.length === 0) {
|
|
46015
|
-
console.log(`No skills found for "${
|
|
46332
|
+
console.log(`No skills found for "${searchQuery}".`);
|
|
46016
46333
|
return;
|
|
46017
46334
|
}
|
|
46018
46335
|
const isTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
46019
46336
|
if (!isTTY) {
|
|
46020
|
-
printSearchResults(result.items,
|
|
46337
|
+
printSearchResults(result.items, searchQuery, result.truncated);
|
|
46021
46338
|
return;
|
|
46022
46339
|
}
|
|
46023
46340
|
const { autocompleteMultiselect: autocompleteMultiselect2, isCancel, log } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
|
|
46024
|
-
log.success(formatSkillSearchSummary(result.items.length,
|
|
46341
|
+
log.success(formatSkillSearchSummary(result.items.length, searchQuery, result.truncated));
|
|
46025
46342
|
const options2 = result.items.map((item) => ({
|
|
46026
46343
|
label: `${qualifiedName(item)} ${source_default.dim(item.repo)}`,
|
|
46027
46344
|
value: item.path,
|
|
@@ -58135,6 +58452,44 @@ var selfCmd = conciseSubcommands({
|
|
|
58135
58452
|
}
|
|
58136
58453
|
});
|
|
58137
58454
|
|
|
58455
|
+
// src/cli/skill-arg-normalizer.ts
|
|
58456
|
+
var SKILL_SUBCOMMANDS = new Set(["list", "remove", "add", "search"]);
|
|
58457
|
+
function normalizeSkillAlias(args) {
|
|
58458
|
+
if (args.length === 0)
|
|
58459
|
+
return args;
|
|
58460
|
+
return args[0] === "skills" ? ["skill", ...args.slice(1)] : args;
|
|
58461
|
+
}
|
|
58462
|
+
function shouldUseSkillSearchShorthand(rest) {
|
|
58463
|
+
const first2 = rest[0] ?? "";
|
|
58464
|
+
if (rest.length === 0)
|
|
58465
|
+
return false;
|
|
58466
|
+
if (first2.startsWith("-") || SKILL_SUBCOMMANDS.has(first2))
|
|
58467
|
+
return false;
|
|
58468
|
+
return rest.length > 1 || /[-\s]/.test(first2);
|
|
58469
|
+
}
|
|
58470
|
+
function normalizeSkillHelpArgs(args) {
|
|
58471
|
+
const normalized = normalizeSkillAlias(args);
|
|
58472
|
+
if (normalized.length === 0 || normalized[0] !== "skill") {
|
|
58473
|
+
return normalized;
|
|
58474
|
+
}
|
|
58475
|
+
const [, ...rest] = normalized;
|
|
58476
|
+
return shouldUseSkillSearchShorthand(rest) ? ["skill", "search"] : normalized;
|
|
58477
|
+
}
|
|
58478
|
+
function normalizeSkillArgs(args) {
|
|
58479
|
+
const normalized = normalizeSkillAlias(args);
|
|
58480
|
+
if (normalized.length === 0 || normalized[0] !== "skill") {
|
|
58481
|
+
return args;
|
|
58482
|
+
}
|
|
58483
|
+
const [, ...rest] = normalized;
|
|
58484
|
+
if (rest.length === 0) {
|
|
58485
|
+
return ["skill"];
|
|
58486
|
+
}
|
|
58487
|
+
if (!shouldUseSkillSearchShorthand(rest)) {
|
|
58488
|
+
return ["skill", ...rest];
|
|
58489
|
+
}
|
|
58490
|
+
return ["skill", "search", ...rest];
|
|
58491
|
+
}
|
|
58492
|
+
|
|
58138
58493
|
// src/cli/agent-help.ts
|
|
58139
58494
|
var allCommands = [
|
|
58140
58495
|
initMeta,
|
|
@@ -58189,7 +58544,7 @@ function findMetaByCommand(commandPath) {
|
|
|
58189
58544
|
}
|
|
58190
58545
|
function printAgentHelp(args, version2) {
|
|
58191
58546
|
const positional5 = args.filter((a) => !a.startsWith("-"));
|
|
58192
|
-
const normalized = positional5
|
|
58547
|
+
const normalized = normalizeSkillHelpArgs(positional5);
|
|
58193
58548
|
const commandPath = normalized.join(" ");
|
|
58194
58549
|
if (!commandPath) {
|
|
58195
58550
|
const tree = {
|
|
@@ -58242,8 +58597,8 @@ var app = conciseSubcommands({
|
|
|
58242
58597
|
var rawArgs = process.argv.slice(2);
|
|
58243
58598
|
var { args: argsNoJson, json: json2, jsonFields: jsonFields2 } = extractJsonFlag(rawArgs);
|
|
58244
58599
|
var { args: argsNoJq, jqExpr: jqExpr2 } = extractJqFlag(argsNoJson);
|
|
58245
|
-
var { args:
|
|
58246
|
-
var finalArgs =
|
|
58600
|
+
var { args: argsAfterAgentHelp, agentHelp } = extractAgentHelpFlag(argsNoJq);
|
|
58601
|
+
var finalArgs = normalizeSkillArgs(argsAfterAgentHelp);
|
|
58247
58602
|
if (jqExpr2 && !json2) {
|
|
58248
58603
|
process.stderr.write(`Error: --jq requires --json.
|
|
58249
58604
|
`);
|
|
@@ -58269,7 +58624,7 @@ if (!agentHelp && !json2 && !isWizard) {
|
|
|
58269
58624
|
`);
|
|
58270
58625
|
}
|
|
58271
58626
|
if (agentHelp) {
|
|
58272
|
-
printAgentHelp(
|
|
58627
|
+
printAgentHelp(normalizeSkillHelpArgs(argsAfterAgentHelp), package_default.version);
|
|
58273
58628
|
} else if (isWizard) {
|
|
58274
58629
|
const { runWizard: runWizard2 } = await Promise.resolve().then(() => (init_wizard(), exports_wizard));
|
|
58275
58630
|
await runWizard2();
|