agentloom 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -6
- package/dist/cli.js +11 -4
- package/dist/commands/add.js +14 -0
- package/dist/commands/delete.js +89 -3
- package/dist/commands/entity-utils.js +3 -0
- package/dist/commands/find.js +146 -12
- package/dist/commands/rule.d.ts +2 -0
- package/dist/commands/rule.js +86 -0
- package/dist/commands/sync.js +13 -4
- package/dist/commands/update.js +90 -7
- package/dist/core/agents.js +1 -1
- package/dist/core/argv.js +2 -0
- package/dist/core/commands.d.ts +12 -0
- package/dist/core/commands.js +106 -6
- package/dist/core/copy.js +12 -5
- package/dist/core/importer.d.ts +10 -0
- package/dist/core/importer.js +629 -13
- package/dist/core/lockfile.js +8 -0
- package/dist/core/manifest.js +123 -6
- package/dist/core/migration.js +655 -66
- package/dist/core/provider-entity-validation.d.ts +8 -0
- package/dist/core/provider-entity-validation.js +34 -0
- package/dist/core/provider-paths.d.ts +8 -1
- package/dist/core/provider-paths.js +69 -5
- package/dist/core/router.js +7 -1
- package/dist/core/rules.d.ts +34 -0
- package/dist/core/rules.js +149 -0
- package/dist/core/scope.js +1 -0
- package/dist/core/skills.d.ts +1 -0
- package/dist/core/skills.js +21 -2
- package/dist/core/sources.d.ts +2 -0
- package/dist/core/sources.js +34 -5
- package/dist/core/telemetry.d.ts +1 -1
- package/dist/core/telemetry.js +16 -0
- package/dist/sync/index.js +376 -18
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
const COPILOT_AGENT_FILE = /\.agent\.md$/i;
|
|
3
|
+
const COPILOT_COMMAND_FILE = /\.prompt\.md$/i;
|
|
4
|
+
const GENERIC_AGENT_FILE = /\.md$/i;
|
|
5
|
+
const GENERIC_COMMAND_FILE = /(?:\.prompt)?\.(md|mdc)$/i;
|
|
6
|
+
const GEMINI_COMMAND_FILE = /\.(toml|md|mdc)$/i;
|
|
7
|
+
const EXCLUDED_ENTITY_STEMS = new Set(["readme"]);
|
|
8
|
+
export function isProviderEntityFileName(options) {
|
|
9
|
+
const normalizedName = path.basename(options.fileName);
|
|
10
|
+
const stem = normalizeEntityStem(normalizedName);
|
|
11
|
+
if (!stem || EXCLUDED_ENTITY_STEMS.has(stem)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (options.provider === "copilot") {
|
|
15
|
+
if (options.entity === "agent") {
|
|
16
|
+
return COPILOT_AGENT_FILE.test(normalizedName);
|
|
17
|
+
}
|
|
18
|
+
return COPILOT_COMMAND_FILE.test(normalizedName);
|
|
19
|
+
}
|
|
20
|
+
if (options.entity === "agent") {
|
|
21
|
+
return GENERIC_AGENT_FILE.test(normalizedName);
|
|
22
|
+
}
|
|
23
|
+
if (options.provider === "gemini") {
|
|
24
|
+
return GEMINI_COMMAND_FILE.test(normalizedName);
|
|
25
|
+
}
|
|
26
|
+
return GENERIC_COMMAND_FILE.test(normalizedName);
|
|
27
|
+
}
|
|
28
|
+
function normalizeEntityStem(fileName) {
|
|
29
|
+
return fileName
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/\.agent\.md$/i, "")
|
|
32
|
+
.replace(/\.prompt\.(md|mdc)$/i, "")
|
|
33
|
+
.replace(/\.(md|mdc|toml)$/i, "");
|
|
34
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { Provider, ScopePaths } from "../types.js";
|
|
2
2
|
export declare function getProviderAgentsDir(paths: ScopePaths, provider: Provider): string;
|
|
3
3
|
export declare function getProviderCommandsDir(paths: ScopePaths, provider: Provider): string;
|
|
4
|
-
export declare function getProviderSkillsPaths(paths: ScopePaths, providers: Provider[]): string[];
|
|
4
|
+
export declare function getProviderSkillsPaths(paths: ScopePaths, providers: readonly Provider[]): string[];
|
|
5
|
+
export declare function getCursorRulesDir(paths: ScopePaths): string;
|
|
6
|
+
export declare function getAgentsInstructionPath(paths: ScopePaths): string;
|
|
7
|
+
export declare function getClaudeInstructionPath(paths: ScopePaths): string;
|
|
8
|
+
export declare function getGeminiInstructionPath(paths: ScopePaths): string;
|
|
9
|
+
export declare function getCopilotInstructionPath(paths: ScopePaths): string;
|
|
10
|
+
export declare function getOpenCodeInstructionPath(paths: ScopePaths): string;
|
|
11
|
+
export declare function getRuleInstructionPaths(paths: ScopePaths, providers: readonly Provider[]): string[];
|
|
5
12
|
export declare function getCursorMcpPath(paths: ScopePaths): string;
|
|
6
13
|
export declare function getClaudeMcpPath(paths: ScopePaths): string;
|
|
7
14
|
export declare function getClaudeSettingsPath(paths: ScopePaths): string;
|
|
@@ -25,7 +25,7 @@ export function getProviderAgentsDir(paths, provider) {
|
|
|
25
25
|
case "copilot":
|
|
26
26
|
return paths.scope === "local"
|
|
27
27
|
? path.join(workspaceRoot, ".github", "agents")
|
|
28
|
-
: path.join(home, ".
|
|
28
|
+
: path.join(home, ".copilot", "agents");
|
|
29
29
|
case "pi":
|
|
30
30
|
return paths.scope === "local"
|
|
31
31
|
? path.join(workspaceRoot, ".pi", "agents")
|
|
@@ -59,7 +59,7 @@ export function getProviderCommandsDir(paths, provider) {
|
|
|
59
59
|
case "copilot":
|
|
60
60
|
return paths.scope === "local"
|
|
61
61
|
? path.join(workspaceRoot, ".github", "prompts")
|
|
62
|
-
: path.join(home, ".
|
|
62
|
+
: path.join(home, ".copilot", "prompts");
|
|
63
63
|
case "pi":
|
|
64
64
|
return paths.scope === "local"
|
|
65
65
|
? path.join(workspaceRoot, ".pi", "prompts")
|
|
@@ -70,12 +70,16 @@ export function getProviderCommandsDir(paths, provider) {
|
|
|
70
70
|
}
|
|
71
71
|
export function getProviderSkillsPaths(paths, providers) {
|
|
72
72
|
const targets = new Set();
|
|
73
|
-
|
|
74
|
-
if (hasClaudeStyleProvider) {
|
|
73
|
+
if (providers.includes("claude")) {
|
|
75
74
|
targets.add(paths.scope === "local"
|
|
76
75
|
? path.join(paths.workspaceRoot, ".claude", "skills")
|
|
77
76
|
: path.join(paths.homeDir, ".claude", "skills"));
|
|
78
77
|
}
|
|
78
|
+
if (providers.includes("copilot")) {
|
|
79
|
+
targets.add(paths.scope === "local"
|
|
80
|
+
? path.join(paths.workspaceRoot, ".github", "skills")
|
|
81
|
+
: path.join(paths.homeDir, ".copilot", "skills"));
|
|
82
|
+
}
|
|
79
83
|
if (providers.includes("cursor")) {
|
|
80
84
|
targets.add(paths.scope === "local"
|
|
81
85
|
? path.join(paths.workspaceRoot, ".cursor", "skills")
|
|
@@ -88,6 +92,66 @@ export function getProviderSkillsPaths(paths, providers) {
|
|
|
88
92
|
}
|
|
89
93
|
return [...targets];
|
|
90
94
|
}
|
|
95
|
+
export function getCursorRulesDir(paths) {
|
|
96
|
+
return paths.scope === "local"
|
|
97
|
+
? path.join(paths.workspaceRoot, ".cursor", "rules")
|
|
98
|
+
: path.join(paths.homeDir, ".cursor", "rules");
|
|
99
|
+
}
|
|
100
|
+
export function getAgentsInstructionPath(paths) {
|
|
101
|
+
return path.join(paths.workspaceRoot, "AGENTS.md");
|
|
102
|
+
}
|
|
103
|
+
export function getClaudeInstructionPath(paths) {
|
|
104
|
+
return paths.scope === "local"
|
|
105
|
+
? path.join(paths.workspaceRoot, "CLAUDE.md")
|
|
106
|
+
: path.join(paths.homeDir, ".claude", "CLAUDE.md");
|
|
107
|
+
}
|
|
108
|
+
export function getGeminiInstructionPath(paths) {
|
|
109
|
+
return paths.scope === "local"
|
|
110
|
+
? path.join(paths.workspaceRoot, "GEMINI.md")
|
|
111
|
+
: path.join(paths.homeDir, ".gemini", "GEMINI.md");
|
|
112
|
+
}
|
|
113
|
+
export function getCopilotInstructionPath(paths) {
|
|
114
|
+
return paths.scope === "local"
|
|
115
|
+
? path.join(paths.workspaceRoot, ".github", "copilot-instructions.md")
|
|
116
|
+
: path.join(paths.homeDir, ".copilot", "copilot-instructions.md");
|
|
117
|
+
}
|
|
118
|
+
export function getOpenCodeInstructionPath(paths) {
|
|
119
|
+
return paths.scope === "local"
|
|
120
|
+
? path.join(paths.workspaceRoot, "AGENTS.md")
|
|
121
|
+
: path.join(paths.homeDir, ".config", "opencode", "AGENTS.md");
|
|
122
|
+
}
|
|
123
|
+
export function getRuleInstructionPaths(paths, providers) {
|
|
124
|
+
const targets = new Set();
|
|
125
|
+
if (paths.scope === "local") {
|
|
126
|
+
targets.add(getAgentsInstructionPath(paths));
|
|
127
|
+
}
|
|
128
|
+
for (const provider of providers) {
|
|
129
|
+
if (provider === "claude") {
|
|
130
|
+
targets.add(getClaudeInstructionPath(paths));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (provider === "gemini") {
|
|
134
|
+
targets.add(getGeminiInstructionPath(paths));
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (provider === "copilot") {
|
|
138
|
+
targets.add(getCopilotInstructionPath(paths));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (provider === "opencode") {
|
|
142
|
+
targets.add(getOpenCodeInstructionPath(paths));
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (provider === "codex" && paths.scope === "local") {
|
|
146
|
+
targets.add(getAgentsInstructionPath(paths));
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (provider === "pi" && paths.scope === "local") {
|
|
150
|
+
targets.add(getAgentsInstructionPath(paths));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return [...targets];
|
|
154
|
+
}
|
|
91
155
|
export function getCursorMcpPath(paths) {
|
|
92
156
|
return paths.scope === "local"
|
|
93
157
|
? path.join(paths.workspaceRoot, ".cursor", "mcp.json")
|
|
@@ -101,7 +165,7 @@ export function getClaudeMcpPath(paths) {
|
|
|
101
165
|
export function getClaudeSettingsPath(paths) {
|
|
102
166
|
return paths.scope === "local"
|
|
103
167
|
? path.join(paths.workspaceRoot, ".claude", "settings.json")
|
|
104
|
-
: path.join(paths.homeDir, ".claude.json");
|
|
168
|
+
: path.join(paths.homeDir, ".claude", "settings.json");
|
|
105
169
|
}
|
|
106
170
|
export function getOpenCodeConfigPath(paths) {
|
|
107
171
|
return paths.scope === "local"
|
package/dist/core/router.js
CHANGED
|
@@ -7,7 +7,13 @@ const AGGREGATE_VERBS = new Set([
|
|
|
7
7
|
"delete",
|
|
8
8
|
"init",
|
|
9
9
|
]);
|
|
10
|
-
const ENTITY_NOUNS = new Set([
|
|
10
|
+
const ENTITY_NOUNS = new Set([
|
|
11
|
+
"agent",
|
|
12
|
+
"command",
|
|
13
|
+
"mcp",
|
|
14
|
+
"rule",
|
|
15
|
+
"skill",
|
|
16
|
+
]);
|
|
11
17
|
const ENTITY_VERBS = new Set([
|
|
12
18
|
"add",
|
|
13
19
|
"list",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface CanonicalRuleFile {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
content: string;
|
|
7
|
+
body: string;
|
|
8
|
+
frontmatter: Record<string, unknown> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface ManagedRuleBlock {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
body: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function parseRulesDir(rulesDir: string): CanonicalRuleFile[];
|
|
18
|
+
export declare function parseRuleMarkdown(content: string, sourcePath?: string): {
|
|
19
|
+
name: string;
|
|
20
|
+
body: string;
|
|
21
|
+
frontmatter: Record<string, unknown> & {
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare function normalizeRuleSelector(selector: string): string;
|
|
26
|
+
export declare function resolveRuleSelections(rules: CanonicalRuleFile[], selectors: string[]): {
|
|
27
|
+
selected: CanonicalRuleFile[];
|
|
28
|
+
unmatched: string[];
|
|
29
|
+
};
|
|
30
|
+
export declare function renderRuleForCursor(rule: CanonicalRuleFile): string;
|
|
31
|
+
export declare function renderManagedRuleBlock(rule: CanonicalRuleFile): string;
|
|
32
|
+
export declare function parseManagedRuleBlocks(content: string): ManagedRuleBlock[];
|
|
33
|
+
export declare function upsertManagedRuleBlocks(existingContent: string, rules: CanonicalRuleFile[]): string;
|
|
34
|
+
export declare function stripRuleFileExtension(fileName: string): string;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
import YAML from "yaml";
|
|
5
|
+
import { listMarkdownFiles, slugify } from "./fs.js";
|
|
6
|
+
const MANAGED_BLOCK_PATTERN = /<!--\s*agentloom:([a-z0-9._-]+):start\s*-->[\s\S]*?<!--\s*agentloom:\1:end\s*-->\s*/gi;
|
|
7
|
+
const MANAGED_BLOCK_CAPTURE_PATTERN = /<!--\s*agentloom:([a-z0-9._-]+):start\s*-->\s*([\s\S]*?)\s*<!--\s*agentloom:\1:end\s*-->/gi;
|
|
8
|
+
export function parseRulesDir(rulesDir) {
|
|
9
|
+
if (!fs.existsSync(rulesDir))
|
|
10
|
+
return [];
|
|
11
|
+
return listMarkdownFiles(rulesDir)
|
|
12
|
+
.sort((a, b) => a.localeCompare(b))
|
|
13
|
+
.map((sourcePath) => {
|
|
14
|
+
const content = fs.readFileSync(sourcePath, "utf8");
|
|
15
|
+
const fileName = path.basename(sourcePath);
|
|
16
|
+
const parsed = parseRuleMarkdown(content, sourcePath);
|
|
17
|
+
return {
|
|
18
|
+
id: stripRuleFileExtension(fileName),
|
|
19
|
+
name: parsed.name,
|
|
20
|
+
fileName,
|
|
21
|
+
sourcePath,
|
|
22
|
+
content,
|
|
23
|
+
body: parsed.body,
|
|
24
|
+
frontmatter: parsed.frontmatter,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function parseRuleMarkdown(content, sourcePath = "<rule>") {
|
|
29
|
+
const parsed = matter(content);
|
|
30
|
+
if (!parsed.data ||
|
|
31
|
+
typeof parsed.data !== "object" ||
|
|
32
|
+
Array.isArray(parsed.data)) {
|
|
33
|
+
throw new Error(`Rule "${sourcePath}" must include YAML frontmatter with required "name".`);
|
|
34
|
+
}
|
|
35
|
+
const frontmatter = parsed.data;
|
|
36
|
+
if (typeof frontmatter.name !== "string" || frontmatter.name.trim() === "") {
|
|
37
|
+
throw new Error(`Rule "${sourcePath}" is missing required frontmatter.name.`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
name: frontmatter.name.trim(),
|
|
41
|
+
body: parsed.content.trimStart(),
|
|
42
|
+
frontmatter: {
|
|
43
|
+
...frontmatter,
|
|
44
|
+
name: frontmatter.name.trim(),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function normalizeRuleSelector(selector) {
|
|
49
|
+
const trimmed = selector.trim().replace(/^\/+/, "");
|
|
50
|
+
const withoutExt = stripRuleFileExtension(trimmed);
|
|
51
|
+
const normalized = slugify(withoutExt);
|
|
52
|
+
return normalized || withoutExt.toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
export function resolveRuleSelections(rules, selectors) {
|
|
55
|
+
const selectedById = new Map();
|
|
56
|
+
const unmatched = [];
|
|
57
|
+
for (const selector of selectors) {
|
|
58
|
+
const normalizedSelector = normalizeRuleSelector(selector);
|
|
59
|
+
if (!normalizedSelector) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const matches = rules.filter((rule) => {
|
|
63
|
+
const exactCandidates = new Set([
|
|
64
|
+
rule.id.toLowerCase(),
|
|
65
|
+
stripRuleFileExtension(rule.fileName).toLowerCase(),
|
|
66
|
+
]);
|
|
67
|
+
const slugCandidates = new Set([
|
|
68
|
+
normalizeRuleSelector(rule.id),
|
|
69
|
+
normalizeRuleSelector(stripRuleFileExtension(rule.fileName)),
|
|
70
|
+
normalizeRuleSelector(rule.name),
|
|
71
|
+
]);
|
|
72
|
+
return (exactCandidates.has(normalizedSelector) ||
|
|
73
|
+
slugCandidates.has(normalizedSelector));
|
|
74
|
+
});
|
|
75
|
+
if (matches.length === 0) {
|
|
76
|
+
unmatched.push(selector);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const match of matches) {
|
|
80
|
+
selectedById.set(match.id, match);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
selected: [...selectedById.values()],
|
|
85
|
+
unmatched,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function renderRuleForCursor(rule) {
|
|
89
|
+
const fm = YAML.stringify(rule.frontmatter, { lineWidth: 0 }).trimEnd();
|
|
90
|
+
const body = rule.body.trimStart();
|
|
91
|
+
return `---\n${fm}\n---\n\n${body}${body.endsWith("\n") ? "" : "\n"}`;
|
|
92
|
+
}
|
|
93
|
+
export function renderManagedRuleBlock(rule) {
|
|
94
|
+
const body = rule.body.trim();
|
|
95
|
+
const heading = `## ${rule.name}`;
|
|
96
|
+
const content = body ? `${heading}\n\n${body}` : heading;
|
|
97
|
+
return `<!-- agentloom:${rule.id}:start -->\n${content}\n<!-- agentloom:${rule.id}:end -->`;
|
|
98
|
+
}
|
|
99
|
+
export function parseManagedRuleBlocks(content) {
|
|
100
|
+
const blocks = [];
|
|
101
|
+
content.replace(MANAGED_BLOCK_CAPTURE_PATTERN, (_match, rawId, rawInner) => {
|
|
102
|
+
const id = String(rawId).trim();
|
|
103
|
+
const inner = String(rawInner).trim();
|
|
104
|
+
const headingMatch = inner.match(/^##\s+(.+?)(?:\r?\n([\s\S]*))?$/);
|
|
105
|
+
const name = headingMatch?.[1]?.trim() || id;
|
|
106
|
+
const body = (headingMatch?.[2] ?? inner).replace(/^\r?\n/, "").trim();
|
|
107
|
+
blocks.push({
|
|
108
|
+
id,
|
|
109
|
+
name,
|
|
110
|
+
body: headingMatch ? body : inner,
|
|
111
|
+
});
|
|
112
|
+
return "";
|
|
113
|
+
});
|
|
114
|
+
return blocks;
|
|
115
|
+
}
|
|
116
|
+
export function upsertManagedRuleBlocks(existingContent, rules) {
|
|
117
|
+
const byId = new Map(rules.map((rule) => [rule.id, rule]));
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
let nextContent = existingContent.replace(MANAGED_BLOCK_PATTERN, (match, id) => {
|
|
120
|
+
const normalizedId = String(id).trim();
|
|
121
|
+
const rule = byId.get(normalizedId);
|
|
122
|
+
if (!rule) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
if (seen.has(normalizedId)) {
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
seen.add(normalizedId);
|
|
129
|
+
const suffix = /\n$/.test(match) ? "\n" : "";
|
|
130
|
+
return `${renderManagedRuleBlock(rule)}${suffix}`;
|
|
131
|
+
});
|
|
132
|
+
const missing = rules
|
|
133
|
+
.filter((rule) => !seen.has(rule.id))
|
|
134
|
+
.map((rule) => renderManagedRuleBlock(rule));
|
|
135
|
+
if (missing.length === 0) {
|
|
136
|
+
return nextContent;
|
|
137
|
+
}
|
|
138
|
+
if (nextContent.trim().length === 0) {
|
|
139
|
+
return `${missing.join("\n\n")}\n`;
|
|
140
|
+
}
|
|
141
|
+
const trailingNewline = nextContent.endsWith("\n") ? "" : "\n";
|
|
142
|
+
return `${nextContent}${trailingNewline}\n${missing.join("\n\n")}\n`;
|
|
143
|
+
}
|
|
144
|
+
export function stripRuleFileExtension(fileName) {
|
|
145
|
+
const ext = path.extname(fileName);
|
|
146
|
+
if (!ext)
|
|
147
|
+
return fileName;
|
|
148
|
+
return fileName.slice(0, -ext.length);
|
|
149
|
+
}
|
package/dist/core/scope.js
CHANGED
|
@@ -15,6 +15,7 @@ export function buildScopePaths(cwd, scope, homeDir = os.homedir()) {
|
|
|
15
15
|
agentsRoot,
|
|
16
16
|
agentsDir: path.join(agentsRoot, "agents"),
|
|
17
17
|
commandsDir: path.join(agentsRoot, "commands"),
|
|
18
|
+
rulesDir: path.join(agentsRoot, "rules"),
|
|
18
19
|
skillsDir: path.join(agentsRoot, "skills"),
|
|
19
20
|
mcpPath: path.join(agentsRoot, "mcp.json"),
|
|
20
21
|
lockPath: path.join(agentsRoot, "agents.lock.json"),
|
package/dist/core/skills.d.ts
CHANGED
package/dist/core/skills.js
CHANGED
|
@@ -125,13 +125,21 @@ export function skillContentMatchesTarget(skill, targetDir) {
|
|
|
125
125
|
}
|
|
126
126
|
export function applySkillProviderSideEffects(options) {
|
|
127
127
|
const pathsToSymlink = getProviderSkillsPaths(options.paths, options.providers);
|
|
128
|
-
if (
|
|
128
|
+
if (options.providers.includes("copilot")) {
|
|
129
|
+
const copilotTargets = getProviderSkillsPaths(options.paths, ["copilot"]);
|
|
130
|
+
const hasCurrentCopilotTarget = copilotTargets.some((targetPath) => fs.existsSync(targetPath));
|
|
131
|
+
if (!hasCurrentCopilotTarget) {
|
|
132
|
+
pathsToSymlink.push(...getLegacyCopilotSkillDirs(options.paths));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const uniquePathsToSymlink = [...new Set(pathsToSymlink)];
|
|
136
|
+
if (uniquePathsToSymlink.length === 0)
|
|
129
137
|
return;
|
|
130
138
|
const canonicalSkillsDir = options.paths.skillsDir;
|
|
131
139
|
if (!options.dryRun) {
|
|
132
140
|
ensureDir(canonicalSkillsDir);
|
|
133
141
|
}
|
|
134
|
-
for (const targetSkillsDir of
|
|
142
|
+
for (const targetSkillsDir of uniquePathsToSymlink) {
|
|
135
143
|
enforceProviderSkillsSymlink({
|
|
136
144
|
targetSkillsDir,
|
|
137
145
|
canonicalSkillsDir,
|
|
@@ -140,6 +148,17 @@ export function applySkillProviderSideEffects(options) {
|
|
|
140
148
|
});
|
|
141
149
|
}
|
|
142
150
|
}
|
|
151
|
+
export function getLegacyCopilotSkillDirs(paths) {
|
|
152
|
+
const legacyDirs = [
|
|
153
|
+
paths.scope === "local"
|
|
154
|
+
? path.join(paths.workspaceRoot, ".claude", "skills")
|
|
155
|
+
: path.join(paths.homeDir, ".claude", "skills"),
|
|
156
|
+
];
|
|
157
|
+
if (paths.scope === "global") {
|
|
158
|
+
legacyDirs.push(path.join(paths.homeDir, ".github", "skills"));
|
|
159
|
+
}
|
|
160
|
+
return legacyDirs;
|
|
161
|
+
}
|
|
143
162
|
function enforceProviderSkillsSymlink(options) {
|
|
144
163
|
const resolvedCanonical = realPathOrResolved(options.canonicalSkillsDir);
|
|
145
164
|
const targetDir = options.targetSkillsDir;
|
package/dist/core/sources.d.ts
CHANGED
|
@@ -18,5 +18,7 @@ export declare function prepareSource(options: {
|
|
|
18
18
|
}): PreparedSource;
|
|
19
19
|
export declare function discoverSourceAgentsDir(importRoot: string): string | null;
|
|
20
20
|
export declare function discoverSourceMcpPath(importRoot: string): string | null;
|
|
21
|
+
export declare function discoverSourceCommandsDirs(importRoot: string): string[];
|
|
21
22
|
export declare function discoverSourceCommandsDir(importRoot: string): string | null;
|
|
22
23
|
export declare function discoverSourceSkillsDir(importRoot: string): string | null;
|
|
24
|
+
export declare function discoverSourceRulesDir(importRoot: string): string | null;
|
package/dist/core/sources.js
CHANGED
|
@@ -65,6 +65,10 @@ export function discoverSourceAgentsDir(importRoot) {
|
|
|
65
65
|
if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
|
|
66
66
|
return nested;
|
|
67
67
|
}
|
|
68
|
+
const githubAgents = path.join(importRoot, ".github", "agents");
|
|
69
|
+
if (fs.existsSync(githubAgents) && fs.statSync(githubAgents).isDirectory()) {
|
|
70
|
+
return githubAgents;
|
|
71
|
+
}
|
|
68
72
|
return null;
|
|
69
73
|
}
|
|
70
74
|
export function discoverSourceMcpPath(importRoot) {
|
|
@@ -76,20 +80,34 @@ export function discoverSourceMcpPath(importRoot) {
|
|
|
76
80
|
return direct;
|
|
77
81
|
return null;
|
|
78
82
|
}
|
|
79
|
-
export function
|
|
83
|
+
export function discoverSourceCommandsDirs(importRoot) {
|
|
80
84
|
const nested = path.join(importRoot, ".agents", "commands");
|
|
81
85
|
if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
|
|
82
|
-
return nested;
|
|
86
|
+
return [nested];
|
|
83
87
|
}
|
|
84
88
|
const direct = path.join(importRoot, "commands");
|
|
85
89
|
if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
|
|
86
|
-
return direct;
|
|
90
|
+
return [direct];
|
|
87
91
|
}
|
|
88
92
|
const prompts = path.join(importRoot, "prompts");
|
|
89
93
|
if (fs.existsSync(prompts) && fs.statSync(prompts).isDirectory()) {
|
|
90
|
-
return prompts;
|
|
94
|
+
return [prompts];
|
|
91
95
|
}
|
|
92
|
-
|
|
96
|
+
const providerFallbacks = [];
|
|
97
|
+
const githubPrompts = path.join(importRoot, ".github", "prompts");
|
|
98
|
+
if (fs.existsSync(githubPrompts) &&
|
|
99
|
+
fs.statSync(githubPrompts).isDirectory()) {
|
|
100
|
+
providerFallbacks.push(githubPrompts);
|
|
101
|
+
}
|
|
102
|
+
const geminiCommands = path.join(importRoot, ".gemini", "commands");
|
|
103
|
+
if (fs.existsSync(geminiCommands) &&
|
|
104
|
+
fs.statSync(geminiCommands).isDirectory()) {
|
|
105
|
+
providerFallbacks.push(geminiCommands);
|
|
106
|
+
}
|
|
107
|
+
return providerFallbacks;
|
|
108
|
+
}
|
|
109
|
+
export function discoverSourceCommandsDir(importRoot) {
|
|
110
|
+
return discoverSourceCommandsDirs(importRoot)[0] ?? null;
|
|
93
111
|
}
|
|
94
112
|
export function discoverSourceSkillsDir(importRoot) {
|
|
95
113
|
const nested = path.join(importRoot, ".agents", "skills");
|
|
@@ -106,6 +124,17 @@ export function discoverSourceSkillsDir(importRoot) {
|
|
|
106
124
|
}
|
|
107
125
|
return null;
|
|
108
126
|
}
|
|
127
|
+
export function discoverSourceRulesDir(importRoot) {
|
|
128
|
+
const nested = path.join(importRoot, ".agents", "rules");
|
|
129
|
+
if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
|
|
130
|
+
return nested;
|
|
131
|
+
}
|
|
132
|
+
const direct = path.join(importRoot, "rules");
|
|
133
|
+
if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
|
|
134
|
+
return direct;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
109
138
|
function resolveImportRoot(rootPath, subdir) {
|
|
110
139
|
if (!subdir)
|
|
111
140
|
return rootPath;
|
package/dist/core/telemetry.d.ts
CHANGED
package/dist/core/telemetry.js
CHANGED
|
@@ -47,6 +47,7 @@ export function parseGitHubSource(input) {
|
|
|
47
47
|
}
|
|
48
48
|
export function buildTelemetryItems(summary) {
|
|
49
49
|
const items = [];
|
|
50
|
+
const importedRules = summary.importedRules ?? [];
|
|
50
51
|
for (const filePath of summary.importedAgents) {
|
|
51
52
|
const name = path.basename(filePath, path.extname(filePath));
|
|
52
53
|
items.push({ entityType: "agent", name, filePath });
|
|
@@ -58,6 +59,21 @@ export function buildTelemetryItems(summary) {
|
|
|
58
59
|
for (const serverName of summary.importedMcpServers) {
|
|
59
60
|
items.push({ entityType: "mcp", name: serverName, filePath: "mcp.json" });
|
|
60
61
|
}
|
|
62
|
+
if (summary.telemetryRules && summary.telemetryRules.length > 0) {
|
|
63
|
+
for (const rule of summary.telemetryRules) {
|
|
64
|
+
items.push({
|
|
65
|
+
entityType: "rule",
|
|
66
|
+
name: rule.name,
|
|
67
|
+
filePath: rule.filePath.replace(/^\/+/, ""),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
for (const filePath of importedRules) {
|
|
73
|
+
const name = path.basename(filePath, path.extname(filePath));
|
|
74
|
+
items.push({ entityType: "rule", name, filePath });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
61
77
|
if (summary.telemetrySkills && summary.telemetrySkills.length > 0) {
|
|
62
78
|
for (const skill of summary.telemetrySkills) {
|
|
63
79
|
items.push({
|