agentinit 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +127 -104
- package/dist/agents/Agent.d.ts +44 -0
- package/dist/agents/Agent.d.ts.map +1 -1
- package/dist/agents/Agent.js +111 -22
- package/dist/agents/Agent.js.map +1 -1
- package/dist/agents/AiderAgent.d.ts +9 -0
- package/dist/agents/AiderAgent.d.ts.map +1 -0
- package/dist/agents/AiderAgent.js +135 -0
- package/dist/agents/AiderAgent.js.map +1 -0
- package/dist/agents/ClaudeAgent.d.ts +4 -0
- package/dist/agents/ClaudeAgent.d.ts.map +1 -1
- package/dist/agents/ClaudeAgent.js +33 -18
- package/dist/agents/ClaudeAgent.js.map +1 -1
- package/dist/agents/ClaudeDesktopAgent.d.ts +5 -0
- package/dist/agents/ClaudeDesktopAgent.d.ts.map +1 -1
- package/dist/agents/ClaudeDesktopAgent.js +31 -1
- package/dist/agents/ClaudeDesktopAgent.js.map +1 -1
- package/dist/agents/ClineAgent.d.ts +8 -0
- package/dist/agents/ClineAgent.d.ts.map +1 -0
- package/dist/agents/ClineAgent.js +42 -0
- package/dist/agents/ClineAgent.js.map +1 -0
- package/dist/agents/CodexCliAgent.d.ts +4 -0
- package/dist/agents/CodexCliAgent.d.ts.map +1 -1
- package/dist/agents/CodexCliAgent.js +38 -8
- package/dist/agents/CodexCliAgent.js.map +1 -1
- package/dist/agents/CopilotAgent.d.ts +9 -0
- package/dist/agents/CopilotAgent.d.ts.map +1 -0
- package/dist/agents/CopilotAgent.js +131 -0
- package/dist/agents/CopilotAgent.js.map +1 -0
- package/dist/agents/CursorAgent.d.ts +4 -4
- package/dist/agents/CursorAgent.d.ts.map +1 -1
- package/dist/agents/CursorAgent.js +32 -43
- package/dist/agents/CursorAgent.js.map +1 -1
- package/dist/agents/DroidAgent.d.ts +5 -0
- package/dist/agents/DroidAgent.d.ts.map +1 -1
- package/dist/agents/DroidAgent.js +37 -18
- package/dist/agents/DroidAgent.js.map +1 -1
- package/dist/agents/GeminiCliAgent.d.ts +4 -0
- package/dist/agents/GeminiCliAgent.d.ts.map +1 -1
- package/dist/agents/GeminiCliAgent.js +37 -8
- package/dist/agents/GeminiCliAgent.js.map +1 -1
- package/dist/agents/MarkdownRulesAgent.d.ts +13 -0
- package/dist/agents/MarkdownRulesAgent.d.ts.map +1 -0
- package/dist/agents/MarkdownRulesAgent.js +53 -0
- package/dist/agents/MarkdownRulesAgent.js.map +1 -0
- package/dist/agents/RooCodeAgent.d.ts +9 -0
- package/dist/agents/RooCodeAgent.d.ts.map +1 -0
- package/dist/agents/RooCodeAgent.js +131 -0
- package/dist/agents/RooCodeAgent.js.map +1 -0
- package/dist/agents/WindsurfAgent.d.ts +9 -0
- package/dist/agents/WindsurfAgent.d.ts.map +1 -0
- package/dist/agents/WindsurfAgent.js +127 -0
- package/dist/agents/WindsurfAgent.js.map +1 -0
- package/dist/agents/ZedAgent.d.ts +9 -0
- package/dist/agents/ZedAgent.d.ts.map +1 -0
- package/dist/agents/ZedAgent.js +127 -0
- package/dist/agents/ZedAgent.js.map +1 -0
- package/dist/cli.js +30587 -11981
- package/dist/cli.js.map +1 -1
- package/dist/commands/apply.d.ts +10 -0
- package/dist/commands/apply.d.ts.map +1 -1
- package/dist/commands/apply.js +134 -3
- package/dist/commands/apply.js.map +1 -1
- package/dist/commands/detect.d.ts.map +1 -1
- package/dist/commands/detect.js +25 -0
- package/dist/commands/detect.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts +2 -7
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +541 -110
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/plugins.d.ts +3 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +309 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/revert.d.ts +7 -0
- package/dist/commands/revert.d.ts.map +1 -0
- package/dist/commands/revert.js +48 -0
- package/dist/commands/revert.js.map +1 -0
- package/dist/commands/rules.d.ts +3 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +354 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +179 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +18 -1
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/verifyMcp.js +1 -1
- package/dist/commands/verifyMcp.js.map +1 -1
- package/dist/core/agentDetector.d.ts.map +1 -1
- package/dist/core/agentDetector.js +8 -2
- package/dist/core/agentDetector.js.map +1 -1
- package/dist/core/agentManager.d.ts.map +1 -1
- package/dist/core/agentManager.js +12 -0
- package/dist/core/agentManager.js.map +1 -1
- package/dist/core/gitignoreManager.d.ts +8 -0
- package/dist/core/gitignoreManager.d.ts.map +1 -0
- package/dist/core/gitignoreManager.js +114 -0
- package/dist/core/gitignoreManager.js.map +1 -0
- package/dist/core/managedState.d.ts +42 -0
- package/dist/core/managedState.d.ts.map +1 -0
- package/dist/core/managedState.js +194 -0
- package/dist/core/managedState.js.map +1 -0
- package/dist/core/pluginManager.d.ts +134 -0
- package/dist/core/pluginManager.d.ts.map +1 -0
- package/dist/core/pluginManager.js +845 -0
- package/dist/core/pluginManager.js.map +1 -0
- package/dist/core/projectSkills.d.ts +19 -0
- package/dist/core/projectSkills.d.ts.map +1 -0
- package/dist/core/projectSkills.js +105 -0
- package/dist/core/projectSkills.js.map +1 -0
- package/dist/core/propagator.d.ts +8 -1
- package/dist/core/propagator.d.ts.map +1 -1
- package/dist/core/propagator.js +179 -36
- package/dist/core/propagator.js.map +1 -1
- package/dist/core/rulesApplicator.d.ts +0 -4
- package/dist/core/rulesApplicator.d.ts.map +1 -1
- package/dist/core/rulesApplicator.js +8 -39
- package/dist/core/rulesApplicator.js.map +1 -1
- package/dist/core/rulesTemplateLoader.js +2 -2
- package/dist/core/rulesTemplateLoader.js.map +1 -1
- package/dist/core/skillsManager.d.ts +61 -0
- package/dist/core/skillsManager.d.ts.map +1 -0
- package/dist/core/skillsManager.js +407 -0
- package/dist/core/skillsManager.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/plugins.d.ts +161 -0
- package/dist/types/plugins.d.ts.map +1 -0
- package/dist/types/plugins.js +2 -0
- package/dist/types/plugins.js.map +1 -0
- package/dist/types/skills.d.ts +50 -0
- package/dist/types/skills.d.ts.map +1 -0
- package/dist/types/skills.js +2 -0
- package/dist/types/skills.js.map +1 -0
- package/package.json +7 -2
- package/dist/agentinit-1.7.0.tgz +0 -0
- package/dist/registry/mcpRegistry.d.ts +0 -12
- package/dist/registry/mcpRegistry.d.ts.map +0 -1
- package/dist/registry/mcpRegistry.js +0 -114
- package/dist/registry/mcpRegistry.js.map +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { resolve } from 'path';
|
|
2
1
|
import { countTokens } from 'contextcalc';
|
|
3
2
|
import { readFileIfExists, writeFile, ensureDirectoryExists } from '../utils/fs.js';
|
|
4
3
|
export class RulesApplicator {
|
|
@@ -17,8 +16,8 @@ export class RulesApplicator {
|
|
|
17
16
|
};
|
|
18
17
|
}
|
|
19
18
|
const configPath = isGlobal
|
|
20
|
-
? agent.
|
|
21
|
-
:
|
|
19
|
+
? agent.getGlobalRulesPath()
|
|
20
|
+
: agent.getProjectRulesPath(projectPath);
|
|
22
21
|
if (!configPath) {
|
|
23
22
|
return {
|
|
24
23
|
success: false,
|
|
@@ -28,24 +27,18 @@ export class RulesApplicator {
|
|
|
28
27
|
errors: [`Could not determine config path for ${agent.name}`]
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
|
-
// Get existing sections and rules before applying new ones using agent methods
|
|
32
30
|
const existingContent = await readFileIfExists(configPath) || '';
|
|
33
31
|
const existingSections = agent.extractExistingSections(existingContent);
|
|
34
32
|
const existingRules = agent.extractExistingRules(existingContent);
|
|
35
33
|
const previousTokenCount = this.countRulesTokens(existingRules);
|
|
36
|
-
// Merge new sections with existing ones
|
|
37
34
|
const mergedSections = this.mergeSections(existingSections, rules.sections);
|
|
38
35
|
const allMergedRules = mergedSections.flatMap(section => section.rules);
|
|
39
|
-
// Determine which rules are new vs existing
|
|
40
36
|
const existingSet = new Set(existingRules);
|
|
41
37
|
const newlyApplied = allMergedRules.filter(rule => !existingSet.has(rule));
|
|
42
38
|
const existing = allMergedRules.filter(rule => existingSet.has(rule));
|
|
43
|
-
// Apply rules based on agent type using merged sections
|
|
44
39
|
await this.applyRulesByAgentType(agent, allMergedRules, configPath, mergedSections);
|
|
45
|
-
// Count tokens in the applied rules (now using merged rules)
|
|
46
40
|
const tokenCount = this.countRulesTokens(allMergedRules);
|
|
47
41
|
const tokenDiff = tokenCount - previousTokenCount;
|
|
48
|
-
// Count total file tokens after applying rules
|
|
49
42
|
const totalFileTokens = await this.countTotalFileTokens(configPath);
|
|
50
43
|
return {
|
|
51
44
|
success: true,
|
|
@@ -56,10 +49,10 @@ export class RulesApplicator {
|
|
|
56
49
|
tokenDiff,
|
|
57
50
|
totalFileTokens,
|
|
58
51
|
existingRules: existing,
|
|
59
|
-
newlyApplied
|
|
52
|
+
newlyApplied,
|
|
60
53
|
existingCount: existing.length,
|
|
61
54
|
newlyAppliedCount: newlyApplied.length,
|
|
62
|
-
mergedSections
|
|
55
|
+
mergedSections
|
|
63
56
|
};
|
|
64
57
|
}
|
|
65
58
|
catch (error) {
|
|
@@ -78,7 +71,6 @@ export class RulesApplicator {
|
|
|
78
71
|
async applyRulesByAgentType(agent, rules, configPath, sections) {
|
|
79
72
|
await ensureDirectoryExists(configPath);
|
|
80
73
|
const existing = await readFileIfExists(configPath) || '';
|
|
81
|
-
// Create AppliedRules object for the agent
|
|
82
74
|
const appliedRules = {
|
|
83
75
|
templateRules: rules,
|
|
84
76
|
rawRules: [],
|
|
@@ -87,9 +79,7 @@ export class RulesApplicator {
|
|
|
87
79
|
merged: rules,
|
|
88
80
|
sections: sections || []
|
|
89
81
|
};
|
|
90
|
-
// Delegate to agent-specific implementation
|
|
91
82
|
const newContent = await agent.applyRulesConfig(configPath, appliedRules, existing);
|
|
92
|
-
// Write the new content
|
|
93
83
|
await writeFile(configPath, newContent);
|
|
94
84
|
}
|
|
95
85
|
/**
|
|
@@ -97,7 +87,6 @@ export class RulesApplicator {
|
|
|
97
87
|
*/
|
|
98
88
|
mergeSections(existingSections, newSections) {
|
|
99
89
|
const merged = new Map();
|
|
100
|
-
// Add all existing sections
|
|
101
90
|
for (const section of existingSections) {
|
|
102
91
|
merged.set(section.templateId, {
|
|
103
92
|
templateId: section.templateId,
|
|
@@ -105,20 +94,19 @@ export class RulesApplicator {
|
|
|
105
94
|
rules: [...section.rules]
|
|
106
95
|
});
|
|
107
96
|
}
|
|
108
|
-
// Add or merge new sections
|
|
109
97
|
for (const section of newSections) {
|
|
110
98
|
if (merged.has(section.templateId)) {
|
|
111
|
-
// Merge rules, deduplicating
|
|
112
99
|
const existing = merged.get(section.templateId);
|
|
100
|
+
if (!existing) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
113
103
|
const allRules = [...existing.rules, ...section.rules];
|
|
114
|
-
const uniqueRules = [...new Set(allRules)];
|
|
115
104
|
merged.set(section.templateId, {
|
|
116
105
|
...existing,
|
|
117
|
-
rules:
|
|
106
|
+
rules: [...new Set(allRules)]
|
|
118
107
|
});
|
|
119
108
|
}
|
|
120
109
|
else {
|
|
121
|
-
// Add new section
|
|
122
110
|
merged.set(section.templateId, {
|
|
123
111
|
templateId: section.templateId,
|
|
124
112
|
templateName: section.templateName,
|
|
@@ -150,32 +138,13 @@ export class RulesApplicator {
|
|
|
150
138
|
try {
|
|
151
139
|
if (rules.length === 0)
|
|
152
140
|
return 0;
|
|
153
|
-
// Join all rules with bullet points as they would appear in the config
|
|
154
141
|
const rulesText = rules.map(rule => `- ${rule}`).join('\n');
|
|
155
142
|
return countTokens(rulesText);
|
|
156
143
|
}
|
|
157
144
|
catch (error) {
|
|
158
|
-
// If token counting fails, return 0 to avoid breaking the functionality
|
|
159
145
|
console.warn('Failed to count tokens:', error);
|
|
160
146
|
return 0;
|
|
161
147
|
}
|
|
162
148
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Get the appropriate rules config path for an agent
|
|
165
|
-
*/
|
|
166
|
-
getAgentRulesPath(agent, projectPath) {
|
|
167
|
-
// Use the same path as the agent's native config for rules
|
|
168
|
-
// Each agent will handle rules in their own config file
|
|
169
|
-
switch (agent.id) {
|
|
170
|
-
case 'claude':
|
|
171
|
-
return resolve(projectPath, 'CLAUDE.md');
|
|
172
|
-
case 'cursor':
|
|
173
|
-
return resolve(projectPath, '.cursorrules');
|
|
174
|
-
case 'claude-desktop':
|
|
175
|
-
return resolve(projectPath, '.claude_desktop_config.json');
|
|
176
|
-
default:
|
|
177
|
-
return resolve(projectPath, `${agent.id}_rules.md`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
149
|
}
|
|
181
150
|
//# sourceMappingURL=rulesApplicator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rulesApplicator.js","sourceRoot":"","sources":["../../src/core/rulesApplicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"rulesApplicator.js","sourceRoot":"","sources":["../../src/core/rulesApplicator.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,8 @@ export class RulesTemplateLoader {
|
|
|
8
8
|
templatesPath;
|
|
9
9
|
templates = new Map();
|
|
10
10
|
constructor() {
|
|
11
|
-
// In
|
|
12
|
-
this.templatesPath = resolve(__dirname, 'templates/rules');
|
|
11
|
+
// In both src/ and dist/, templates live one directory above this file.
|
|
12
|
+
this.templatesPath = resolve(__dirname, '../templates/rules');
|
|
13
13
|
this.loadTemplates();
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rulesTemplateLoader.js","sourceRoot":"","sources":["../../src/core/rulesTemplateLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC3D,OAAO,IAAI,MAAM,aAAa,CAAC;AAG/B,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAatC,MAAM,OAAO,mBAAmB;IACb,aAAa,CAAS;IAC/B,SAAS,GAA8B,IAAI,GAAG,EAAE,CAAC;IAEzD;QACE,
|
|
1
|
+
{"version":3,"file":"rulesTemplateLoader.js","sourceRoot":"","sources":["../../src/core/rulesTemplateLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AgentManager } from './agentManager.js';
|
|
2
|
+
import type { Agent } from '../agents/Agent.js';
|
|
3
|
+
import type { SkillInfo, InstalledSkill, SkillsAddOptions, SkillsAddResult, SkillsListOptions, SkillsRemoveOptions, SkillSource } from '../types/skills.js';
|
|
4
|
+
export declare class SkillsManager {
|
|
5
|
+
private agentManager;
|
|
6
|
+
constructor(agentManager?: AgentManager);
|
|
7
|
+
/**
|
|
8
|
+
* Parse a source string into a structured SkillSource
|
|
9
|
+
*/
|
|
10
|
+
resolveSource(source: string): SkillSource;
|
|
11
|
+
/**
|
|
12
|
+
* Parse a SKILL.md file and extract name + description from frontmatter
|
|
13
|
+
*/
|
|
14
|
+
parseSkillMd(filePath: string): Promise<{
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
} | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Discover all skills in a given directory (cloned repo or local path)
|
|
20
|
+
*/
|
|
21
|
+
discoverSkills(repoPath: string): Promise<SkillInfo[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Clone a GitHub repository to a temp directory
|
|
24
|
+
*/
|
|
25
|
+
cloneRepo(url: string): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Get target agents based on options
|
|
28
|
+
*/
|
|
29
|
+
getTargetAgents(projectPath: string, options: {
|
|
30
|
+
agents?: string[];
|
|
31
|
+
global?: boolean;
|
|
32
|
+
}): Promise<Agent[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Install a skill directory into an agent's skills directory
|
|
35
|
+
*/
|
|
36
|
+
installSkill(skillPath: string, skillName: string, targetDir: string, copy?: boolean): Promise<string>;
|
|
37
|
+
installSkillFromContent(skillName: string, skillContent: string, targetDir: string): Promise<string>;
|
|
38
|
+
private normalizeSkillName;
|
|
39
|
+
private resolveInstallPath;
|
|
40
|
+
getInstallPath(skillName: string, targetDir: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Recursively copy a directory
|
|
43
|
+
*/
|
|
44
|
+
private copyDir;
|
|
45
|
+
/**
|
|
46
|
+
* Add skills from a source (GitHub repo or local path)
|
|
47
|
+
*/
|
|
48
|
+
addFromSource(source: string, projectPath: string, options?: SkillsAddOptions): Promise<SkillsAddResult>;
|
|
49
|
+
/**
|
|
50
|
+
* List all installed skills
|
|
51
|
+
*/
|
|
52
|
+
listInstalled(projectPath: string, options?: SkillsListOptions): Promise<InstalledSkill[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Remove installed skills by name
|
|
55
|
+
*/
|
|
56
|
+
remove(skillNames: string[], projectPath: string, options?: SkillsRemoveOptions): Promise<{
|
|
57
|
+
removed: string[];
|
|
58
|
+
notFound: string[];
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=skillsManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillsManager.d.ts","sourceRoot":"","sources":["../../src/core/skillsManager.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAkB5B,qBAAa,aAAa;IACxB,OAAO,CAAC,YAAY,CAAe;gBAEvB,YAAY,CAAC,EAAE,YAAY;IAIvC;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW;IAsC1C;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAc3F;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAyD5D;;OAEG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB7C;;OAEG;IACG,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAe9G;;OAEG;IACG,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,OAAe,GACpB,OAAO,CAAC,MAAM,CAAC;IAmBZ,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC;IAelB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,kBAAkB;IAY1B,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAI5D;;OAEG;YACW,OAAO;IAgBrB;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC;IA+E3B;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAyDpG;;OAEG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CA2CtD"}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { resolve, join, relative } from 'path';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { execFile } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import matter from 'gray-matter';
|
|
7
|
+
import { readFileIfExists, listFiles, fileExists, isDirectory } from '../utils/fs.js';
|
|
8
|
+
import { AgentManager } from './agentManager.js';
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
/**
|
|
11
|
+
* Standard directories where skills are discovered in a repository
|
|
12
|
+
* Compatible with the open agent skills ecosystem (vercel-labs/skills)
|
|
13
|
+
*/
|
|
14
|
+
const SKILL_SEARCH_DIRS = [
|
|
15
|
+
'.',
|
|
16
|
+
'skills',
|
|
17
|
+
'skills/.curated',
|
|
18
|
+
'skills/.experimental',
|
|
19
|
+
'.agents/skills',
|
|
20
|
+
'.claude/skills',
|
|
21
|
+
'.factory/skills',
|
|
22
|
+
];
|
|
23
|
+
export class SkillsManager {
|
|
24
|
+
agentManager;
|
|
25
|
+
constructor(agentManager) {
|
|
26
|
+
this.agentManager = agentManager || new AgentManager();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse a source string into a structured SkillSource
|
|
30
|
+
*/
|
|
31
|
+
resolveSource(source) {
|
|
32
|
+
// Local path
|
|
33
|
+
if (source.startsWith('.') || source.startsWith('/') || source.startsWith('~')) {
|
|
34
|
+
return { type: 'local', path: source };
|
|
35
|
+
}
|
|
36
|
+
// Full GitHub URL
|
|
37
|
+
if (source.startsWith('https://github.com/') || source.startsWith('http://github.com/')) {
|
|
38
|
+
const url = source.replace(/\.git$/, '');
|
|
39
|
+
const match = url.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
40
|
+
return {
|
|
41
|
+
type: 'github',
|
|
42
|
+
url: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
|
|
43
|
+
owner: match?.[1],
|
|
44
|
+
repo: match?.[2]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Full git URL
|
|
48
|
+
if (source.startsWith('git@') || source.endsWith('.git')) {
|
|
49
|
+
return { type: 'github', url: source };
|
|
50
|
+
}
|
|
51
|
+
// GitHub shorthand: owner/repo
|
|
52
|
+
if (/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/.test(source)) {
|
|
53
|
+
const [owner, repo] = source.split('/');
|
|
54
|
+
return {
|
|
55
|
+
type: 'github',
|
|
56
|
+
url: `https://github.com/${owner}/${repo}.git`,
|
|
57
|
+
owner,
|
|
58
|
+
repo
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Fallback: treat as local path
|
|
62
|
+
return { type: 'local', path: source };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parse a SKILL.md file and extract name + description from frontmatter
|
|
66
|
+
*/
|
|
67
|
+
async parseSkillMd(filePath) {
|
|
68
|
+
const content = await readFileIfExists(filePath);
|
|
69
|
+
if (!content)
|
|
70
|
+
return null;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = matter(content);
|
|
73
|
+
const { name, description } = parsed.data;
|
|
74
|
+
if (!name || !description)
|
|
75
|
+
return null;
|
|
76
|
+
return { name: String(name), description: String(description) };
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Discover all skills in a given directory (cloned repo or local path)
|
|
84
|
+
*/
|
|
85
|
+
async discoverSkills(repoPath) {
|
|
86
|
+
const skills = [];
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
for (const searchDir of SKILL_SEARCH_DIRS) {
|
|
89
|
+
const fullDir = resolve(repoPath, searchDir);
|
|
90
|
+
if (!(await fileExists(fullDir)))
|
|
91
|
+
continue;
|
|
92
|
+
// Check if this directory itself has a SKILL.md
|
|
93
|
+
const directSkillMd = join(fullDir, 'SKILL.md');
|
|
94
|
+
if (await fileExists(directSkillMd)) {
|
|
95
|
+
// Check lowercase variant too
|
|
96
|
+
const parsed = await this.parseSkillMd(directSkillMd);
|
|
97
|
+
if (parsed && !seen.has(parsed.name)) {
|
|
98
|
+
seen.add(parsed.name);
|
|
99
|
+
skills.push({ ...parsed, path: resolve(fullDir) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Also check lowercase
|
|
103
|
+
const directSkillMdLower = join(fullDir, 'skill.md');
|
|
104
|
+
if (await fileExists(directSkillMdLower)) {
|
|
105
|
+
const parsed = await this.parseSkillMd(directSkillMdLower);
|
|
106
|
+
if (parsed && !seen.has(parsed.name)) {
|
|
107
|
+
seen.add(parsed.name);
|
|
108
|
+
skills.push({ ...parsed, path: resolve(fullDir) });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Check subdirectories for skill folders
|
|
112
|
+
if (!(await isDirectory(fullDir)))
|
|
113
|
+
continue;
|
|
114
|
+
const entries = await listFiles(fullDir);
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const entryPath = join(fullDir, entry);
|
|
117
|
+
if (!(await isDirectory(entryPath)))
|
|
118
|
+
continue;
|
|
119
|
+
// Look for SKILL.md in subdirectory
|
|
120
|
+
const skillMdPath = join(entryPath, 'SKILL.md');
|
|
121
|
+
const skillMdPathLower = join(entryPath, 'skill.md');
|
|
122
|
+
const skillFile = (await fileExists(skillMdPath)) ? skillMdPath
|
|
123
|
+
: (await fileExists(skillMdPathLower)) ? skillMdPathLower
|
|
124
|
+
: null;
|
|
125
|
+
if (!skillFile)
|
|
126
|
+
continue;
|
|
127
|
+
const parsed = await this.parseSkillMd(skillFile);
|
|
128
|
+
if (parsed && !seen.has(parsed.name)) {
|
|
129
|
+
seen.add(parsed.name);
|
|
130
|
+
skills.push({ ...parsed, path: entryPath });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return skills;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Clone a GitHub repository to a temp directory
|
|
138
|
+
*/
|
|
139
|
+
async cloneRepo(url) {
|
|
140
|
+
const tempDir = join(tmpdir(), `agentinit-skills-${Date.now()}`);
|
|
141
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
142
|
+
try {
|
|
143
|
+
await execFileAsync('git', ['clone', '--depth', '1', url, tempDir], {
|
|
144
|
+
timeout: 60000
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
149
|
+
throw new Error(`Failed to clone ${url}: ${error.message}`);
|
|
150
|
+
}
|
|
151
|
+
return tempDir;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get target agents based on options
|
|
155
|
+
*/
|
|
156
|
+
async getTargetAgents(projectPath, options) {
|
|
157
|
+
if (options.agents && options.agents.length > 0) {
|
|
158
|
+
const agents = [];
|
|
159
|
+
for (const id of options.agents) {
|
|
160
|
+
const agent = this.agentManager.getAgentById(id);
|
|
161
|
+
if (agent)
|
|
162
|
+
agents.push(agent);
|
|
163
|
+
}
|
|
164
|
+
return agents;
|
|
165
|
+
}
|
|
166
|
+
// Auto-detect agents in the project
|
|
167
|
+
const detected = await this.agentManager.detectAgents(projectPath);
|
|
168
|
+
return detected.map(d => d.agent);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Install a skill directory into an agent's skills directory
|
|
172
|
+
*/
|
|
173
|
+
async installSkill(skillPath, skillName, targetDir, copy = false) {
|
|
174
|
+
const normalizedSkillName = this.normalizeSkillName(skillName);
|
|
175
|
+
const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
|
|
176
|
+
await fs.mkdir(resolve(targetDir), { recursive: true });
|
|
177
|
+
// Remove existing if present
|
|
178
|
+
if (await fileExists(destPath)) {
|
|
179
|
+
await fs.rm(destPath, { recursive: true, force: true });
|
|
180
|
+
}
|
|
181
|
+
if (copy) {
|
|
182
|
+
await this.copyDir(skillPath, destPath);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
await fs.symlink(skillPath, destPath, 'dir');
|
|
186
|
+
}
|
|
187
|
+
return destPath;
|
|
188
|
+
}
|
|
189
|
+
async installSkillFromContent(skillName, skillContent, targetDir) {
|
|
190
|
+
const normalizedSkillName = this.normalizeSkillName(skillName);
|
|
191
|
+
const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
|
|
192
|
+
await fs.mkdir(resolve(targetDir), { recursive: true });
|
|
193
|
+
if (await fileExists(destPath)) {
|
|
194
|
+
await fs.rm(destPath, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
await fs.mkdir(destPath, { recursive: true });
|
|
197
|
+
await fs.writeFile(join(destPath, 'SKILL.md'), skillContent, 'utf8');
|
|
198
|
+
return destPath;
|
|
199
|
+
}
|
|
200
|
+
normalizeSkillName(skillName) {
|
|
201
|
+
const normalized = skillName.trim();
|
|
202
|
+
if (!normalized) {
|
|
203
|
+
throw new Error('Skill name cannot be empty');
|
|
204
|
+
}
|
|
205
|
+
if (normalized === '.' || normalized === '..' || normalized.includes('/') || normalized.includes('\\')) {
|
|
206
|
+
throw new Error(`Invalid skill name: ${skillName}`);
|
|
207
|
+
}
|
|
208
|
+
return normalized;
|
|
209
|
+
}
|
|
210
|
+
resolveInstallPath(targetDir, skillName) {
|
|
211
|
+
const resolvedTargetDir = resolve(targetDir);
|
|
212
|
+
const destPath = resolve(resolvedTargetDir, skillName);
|
|
213
|
+
const relativePath = relative(resolvedTargetDir, destPath);
|
|
214
|
+
if (relativePath === '' || relativePath.startsWith('..') || relativePath.includes('/../') || relativePath.includes('\\..\\')) {
|
|
215
|
+
throw new Error(`Refusing to install skill outside target directory: ${skillName}`);
|
|
216
|
+
}
|
|
217
|
+
return destPath;
|
|
218
|
+
}
|
|
219
|
+
getInstallPath(skillName, targetDir) {
|
|
220
|
+
return this.resolveInstallPath(targetDir, this.normalizeSkillName(skillName));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Recursively copy a directory
|
|
224
|
+
*/
|
|
225
|
+
async copyDir(src, dest) {
|
|
226
|
+
await fs.mkdir(dest, { recursive: true });
|
|
227
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
228
|
+
for (const entry of entries) {
|
|
229
|
+
const srcPath = join(src, entry.name);
|
|
230
|
+
const destPath = join(dest, entry.name);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
await this.copyDir(srcPath, destPath);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
await fs.copyFile(srcPath, destPath);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Add skills from a source (GitHub repo or local path)
|
|
241
|
+
*/
|
|
242
|
+
async addFromSource(source, projectPath, options = {}) {
|
|
243
|
+
const resolved = this.resolveSource(source);
|
|
244
|
+
let repoPath;
|
|
245
|
+
let tempDir = null;
|
|
246
|
+
if (resolved.type === 'github') {
|
|
247
|
+
if (!resolved.url)
|
|
248
|
+
throw new Error(`Invalid source: ${source}`);
|
|
249
|
+
tempDir = await this.cloneRepo(resolved.url);
|
|
250
|
+
repoPath = tempDir;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
repoPath = resolve(resolved.path || source);
|
|
254
|
+
if (!(await fileExists(repoPath))) {
|
|
255
|
+
throw new Error(`Local path not found: ${repoPath}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
// Discover skills
|
|
260
|
+
let skills = await this.discoverSkills(repoPath);
|
|
261
|
+
if (skills.length === 0) {
|
|
262
|
+
return { installed: [], skipped: [] };
|
|
263
|
+
}
|
|
264
|
+
// Filter by name if specified
|
|
265
|
+
if (options.skills && options.skills.length > 0) {
|
|
266
|
+
const names = new Set(options.skills.map(s => s.toLowerCase()));
|
|
267
|
+
skills = skills.filter(s => names.has(s.name.toLowerCase()));
|
|
268
|
+
}
|
|
269
|
+
// Get target agents
|
|
270
|
+
const agents = await this.getTargetAgents(projectPath, options);
|
|
271
|
+
if (agents.length === 0) {
|
|
272
|
+
return {
|
|
273
|
+
installed: [],
|
|
274
|
+
skipped: skills.map(s => ({ skill: s, reason: 'No target agents found' }))
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const result = { installed: [], skipped: [] };
|
|
278
|
+
for (const skill of skills) {
|
|
279
|
+
for (const agent of agents) {
|
|
280
|
+
if (!agent.supportsSkills()) {
|
|
281
|
+
result.skipped.push({ skill, reason: `${agent.name} does not support skills` });
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const skillsDir = agent.getSkillsDir(projectPath, options.global);
|
|
285
|
+
if (!skillsDir) {
|
|
286
|
+
result.skipped.push({ skill, reason: `No skills directory for ${agent.name}` });
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
// For GitHub repos, always copy (symlinks to temp dirs would break)
|
|
291
|
+
const shouldCopy = options.copy || resolved.type === 'github';
|
|
292
|
+
const installedPath = await this.installSkill(skill.path, skill.name, skillsDir, shouldCopy);
|
|
293
|
+
result.installed.push({ skill, agent: agent.id, path: installedPath });
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
result.skipped.push({ skill, reason: error.message });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
// Clean up temp directory
|
|
304
|
+
if (tempDir) {
|
|
305
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* List all installed skills
|
|
311
|
+
*/
|
|
312
|
+
async listInstalled(projectPath, options = {}) {
|
|
313
|
+
const agents = await this.getTargetAgents(projectPath, options);
|
|
314
|
+
const installed = [];
|
|
315
|
+
for (const agent of agents) {
|
|
316
|
+
if (!agent.supportsSkills())
|
|
317
|
+
continue;
|
|
318
|
+
// Check both project and global scopes
|
|
319
|
+
const scopes = [];
|
|
320
|
+
if (!options.global) {
|
|
321
|
+
scopes.push({ scope: 'project', dir: agent.getSkillsDir(projectPath, false) });
|
|
322
|
+
}
|
|
323
|
+
scopes.push({ scope: 'global', dir: agent.getSkillsDir(projectPath, true) });
|
|
324
|
+
for (const { scope, dir } of scopes) {
|
|
325
|
+
if (!dir || !(await fileExists(dir)))
|
|
326
|
+
continue;
|
|
327
|
+
const entries = await listFiles(dir);
|
|
328
|
+
for (const entry of entries) {
|
|
329
|
+
const entryPath = join(dir, entry);
|
|
330
|
+
if (!(await isDirectory(entryPath)))
|
|
331
|
+
continue;
|
|
332
|
+
// Look for SKILL.md
|
|
333
|
+
const skillMdPath = join(entryPath, 'SKILL.md');
|
|
334
|
+
const skillMdPathLower = join(entryPath, 'skill.md');
|
|
335
|
+
const skillFile = (await fileExists(skillMdPath)) ? skillMdPath
|
|
336
|
+
: (await fileExists(skillMdPathLower)) ? skillMdPathLower
|
|
337
|
+
: null;
|
|
338
|
+
if (!skillFile)
|
|
339
|
+
continue;
|
|
340
|
+
const parsed = await this.parseSkillMd(skillFile);
|
|
341
|
+
if (!parsed)
|
|
342
|
+
continue;
|
|
343
|
+
// Check if it's a symlink
|
|
344
|
+
let isSymlink = false;
|
|
345
|
+
try {
|
|
346
|
+
const stat = await fs.lstat(entryPath);
|
|
347
|
+
isSymlink = stat.isSymbolicLink();
|
|
348
|
+
}
|
|
349
|
+
catch { }
|
|
350
|
+
installed.push({
|
|
351
|
+
name: parsed.name,
|
|
352
|
+
description: parsed.description,
|
|
353
|
+
path: entryPath,
|
|
354
|
+
agent: agent.id,
|
|
355
|
+
scope,
|
|
356
|
+
isSymlink
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return installed;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Remove installed skills by name
|
|
365
|
+
*/
|
|
366
|
+
async remove(skillNames, projectPath, options = {}) {
|
|
367
|
+
const agents = await this.getTargetAgents(projectPath, options);
|
|
368
|
+
const removed = [];
|
|
369
|
+
const notFound = [];
|
|
370
|
+
const namesLower = new Set(skillNames.map(n => n.toLowerCase()));
|
|
371
|
+
for (const agent of agents) {
|
|
372
|
+
if (!agent.supportsSkills())
|
|
373
|
+
continue;
|
|
374
|
+
const scopes = [];
|
|
375
|
+
if (!options.global) {
|
|
376
|
+
scopes.push({ scope: 'project', dir: agent.getSkillsDir(projectPath, false) });
|
|
377
|
+
}
|
|
378
|
+
if (options.global) {
|
|
379
|
+
scopes.push({ scope: 'global', dir: agent.getSkillsDir(projectPath, true) });
|
|
380
|
+
}
|
|
381
|
+
for (const { dir } of scopes) {
|
|
382
|
+
if (!dir || !(await fileExists(dir)))
|
|
383
|
+
continue;
|
|
384
|
+
const entries = await listFiles(dir);
|
|
385
|
+
for (const entry of entries) {
|
|
386
|
+
if (!namesLower.has(entry.toLowerCase()))
|
|
387
|
+
continue;
|
|
388
|
+
const entryPath = join(dir, entry);
|
|
389
|
+
try {
|
|
390
|
+
await fs.rm(entryPath, { recursive: true, force: true });
|
|
391
|
+
removed.push(`${agent.id}:${entry}`);
|
|
392
|
+
}
|
|
393
|
+
catch { }
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// Check which names were not found anywhere
|
|
398
|
+
const removedNames = new Set(removed.map(r => r.split(':')[1]?.toLowerCase()));
|
|
399
|
+
for (const name of skillNames) {
|
|
400
|
+
if (!removedNames.has(name.toLowerCase())) {
|
|
401
|
+
notFound.push(name);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return { removed, notFound };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=skillsManager.js.map
|