skill-linker 4.0.0 → 4.0.4
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 +1 -1
- package/package.json +1 -1
- package/src/commands/install.js +7 -5
- package/src/utils/agents.js +83 -58
- package/src/utils/git.js +68 -64
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ npx skill-linker list --repo skill-name --json
|
|
|
124
124
|
| **GitHub Copilot** | `.github/skills/` | `~/.copilot/skills/` |
|
|
125
125
|
| **Google Antigravity** | `.agent/skills/` | `~/.gemini/antigravity/skills/` |
|
|
126
126
|
| **Cursor** | `.cursor/skills/` | `~/.cursor/skills/` |
|
|
127
|
-
| **OpenCode** | `.opencode/
|
|
127
|
+
| **OpenCode** | `.opencode/skills/` | `~/.config/opencode/skills/` |
|
|
128
128
|
| **OpenAI Codex** | `.codex/skills/` | `~/.codex/skills/` |
|
|
129
129
|
| **Gemini CLI** | `.gemini/skills/` | `~/.gemini/skills/` |
|
|
130
130
|
| **Windsurf** | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
|
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -6,7 +6,11 @@ const {
|
|
|
6
6
|
createSymlink,
|
|
7
7
|
listDirectories,
|
|
8
8
|
} = require("../utils/file-system");
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
getAllAgents,
|
|
11
|
+
detectInstalledAgents,
|
|
12
|
+
findAgentIndex,
|
|
13
|
+
} = require("../utils/agents");
|
|
10
14
|
const { cloneOrUpdateRepo, pullRepo } = require("../utils/git");
|
|
11
15
|
|
|
12
16
|
/**
|
|
@@ -99,10 +103,8 @@ async function install(options) {
|
|
|
99
103
|
if (options.agents && options.agents.length > 0) {
|
|
100
104
|
selectedAgents = options.agents
|
|
101
105
|
.map((agentName) => {
|
|
102
|
-
const idx =
|
|
103
|
-
|
|
104
|
-
);
|
|
105
|
-
if (idx === -1) {
|
|
106
|
+
const idx = findAgentIndex(agentName);
|
|
107
|
+
if (idx === null) {
|
|
106
108
|
console.log(
|
|
107
109
|
chalk.yellow("[WARNING]"),
|
|
108
110
|
`Unknown agent: ${agentName}, skipping...`,
|
package/src/utils/agents.js
CHANGED
|
@@ -1,69 +1,93 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const os = require(
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Supported AI Agent configurations
|
|
7
|
-
* Format: { name, projectDir, globalDir }
|
|
7
|
+
* Format: { name, projectDir, globalDir, aliases }
|
|
8
8
|
*/
|
|
9
9
|
const AGENTS = [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
10
|
+
{
|
|
11
|
+
name: "Claude Code",
|
|
12
|
+
projectDir: ".claude/skills",
|
|
13
|
+
globalDir: path.join(os.homedir(), ".claude/skills"),
|
|
14
|
+
aliases: ["claude", "claude-code", "anthropic"],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "GitHub Copilot",
|
|
18
|
+
projectDir: ".github/skills",
|
|
19
|
+
globalDir: path.join(os.homedir(), ".copilot/skills"),
|
|
20
|
+
aliases: ["copilot", "github", "gh-copilot"],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Google Antigravity",
|
|
24
|
+
projectDir: ".agent/skills",
|
|
25
|
+
globalDir: path.join(os.homedir(), ".gemini/antigravity/skills"),
|
|
26
|
+
aliases: ["antigravity", "gemini-antigravity"],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "Cursor",
|
|
30
|
+
projectDir: ".cursor/skills",
|
|
31
|
+
globalDir: path.join(os.homedir(), ".cursor/skills"),
|
|
32
|
+
aliases: ["cursor"],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "OpenCode",
|
|
36
|
+
projectDir: ".opencode/skills",
|
|
37
|
+
globalDir: path.join(os.homedir(), ".config/opencode/skills"),
|
|
38
|
+
aliases: ["opencode", "open-code"],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "OpenAI Codex",
|
|
42
|
+
projectDir: ".codex/skills",
|
|
43
|
+
globalDir: path.join(os.homedir(), ".codex/skills"),
|
|
44
|
+
aliases: ["codex", "openai", "openai-codex"],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "Gemini CLI",
|
|
48
|
+
projectDir: ".gemini/skills",
|
|
49
|
+
globalDir: path.join(os.homedir(), ".gemini/skills"),
|
|
50
|
+
aliases: ["gemini", "gemini-cli", "google-gemini"],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "Windsurf",
|
|
54
|
+
projectDir: ".windsurf/skills",
|
|
55
|
+
globalDir: path.join(os.homedir(), ".codeium/windsurf/skills"),
|
|
56
|
+
aliases: ["windsurf", "codeium"],
|
|
57
|
+
},
|
|
50
58
|
];
|
|
51
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Find agent index by name or alias (case-insensitive)
|
|
62
|
+
* @param {string} name - Agent name or alias
|
|
63
|
+
* @returns {number|null} Agent index or null if not found
|
|
64
|
+
*/
|
|
65
|
+
function findAgentIndex(nameOrAlias) {
|
|
66
|
+
const lower = nameOrAlias.toLowerCase();
|
|
67
|
+
const idx = AGENTS.findIndex(
|
|
68
|
+
(agent) =>
|
|
69
|
+
agent.name.toLowerCase() === lower ||
|
|
70
|
+
(agent.aliases &&
|
|
71
|
+
agent.aliases.some((alias) => alias.toLowerCase() === lower)),
|
|
72
|
+
);
|
|
73
|
+
return idx !== -1 ? idx : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
52
76
|
/**
|
|
53
77
|
* Detect which agents are installed on the system
|
|
54
78
|
* @returns {Array} List of detected agent indices
|
|
55
79
|
*/
|
|
56
80
|
function detectInstalledAgents() {
|
|
57
|
-
|
|
81
|
+
const installed = [];
|
|
58
82
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
AGENTS.forEach((agent, index) => {
|
|
84
|
+
// Check if global directory exists
|
|
85
|
+
if (fs.existsSync(agent.globalDir)) {
|
|
86
|
+
installed.push(index);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
65
89
|
|
|
66
|
-
|
|
90
|
+
return installed;
|
|
67
91
|
}
|
|
68
92
|
|
|
69
93
|
/**
|
|
@@ -72,7 +96,7 @@ function detectInstalledAgents() {
|
|
|
72
96
|
* @returns {Object} Agent configuration
|
|
73
97
|
*/
|
|
74
98
|
function getAgent(index) {
|
|
75
|
-
|
|
99
|
+
return AGENTS[index];
|
|
76
100
|
}
|
|
77
101
|
|
|
78
102
|
/**
|
|
@@ -80,12 +104,13 @@ function getAgent(index) {
|
|
|
80
104
|
* @returns {Array} All agent configurations
|
|
81
105
|
*/
|
|
82
106
|
function getAllAgents() {
|
|
83
|
-
|
|
107
|
+
return AGENTS;
|
|
84
108
|
}
|
|
85
109
|
|
|
86
110
|
module.exports = {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
AGENTS,
|
|
112
|
+
detectInstalledAgents,
|
|
113
|
+
getAgent,
|
|
114
|
+
getAllAgents,
|
|
115
|
+
findAgentIndex,
|
|
91
116
|
};
|
package/src/utils/git.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const execa = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const os = require(
|
|
4
|
-
const { dirExists } = require(
|
|
1
|
+
const execa = require("execa");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const { dirExists } = require("./file-system");
|
|
5
5
|
|
|
6
|
-
const DEFAULT_LIB_PATH = path.join(os.homedir(),
|
|
6
|
+
const DEFAULT_LIB_PATH = path.join(os.homedir(), "Documents/AgentSkills");
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Parse GitHub URL to extract owner, repo, branch, and subpath
|
|
@@ -11,46 +11,50 @@ const DEFAULT_LIB_PATH = path.join(os.homedir(), 'Documents/AgentSkills');
|
|
|
11
11
|
* @returns {Object} Parsed components
|
|
12
12
|
*/
|
|
13
13
|
function parseGitHubUrl(url) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
let cleanUrl = url;
|
|
15
|
+
let subpath = "";
|
|
16
|
+
let branch = "main";
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
// Check for /tree/branch/path format
|
|
19
|
+
const treeMatch = url.match(/(.+)\/tree\/([^/]+)\/(.+)$/);
|
|
20
|
+
if (treeMatch) {
|
|
21
|
+
cleanUrl = treeMatch[1];
|
|
22
|
+
branch = treeMatch[2];
|
|
23
|
+
subpath = treeMatch[3];
|
|
24
|
+
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// Extract owner/repo
|
|
27
|
+
const repoMatch = cleanUrl.match(/github\.com[/:]([^/]+)\/([^/]+?)(\.git)?$/);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (!repoMatch) {
|
|
30
|
+
throw new Error("Invalid GitHub URL format");
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
return {
|
|
34
|
+
owner: repoMatch[1],
|
|
35
|
+
repo: repoMatch[2].replace(".git", ""),
|
|
36
|
+
branch,
|
|
37
|
+
subpath,
|
|
38
|
+
cleanUrl,
|
|
39
|
+
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Clone a GitHub repository
|
|
44
44
|
* @param {string} url - GitHub URL
|
|
45
45
|
* @param {string} targetPath - Target directory
|
|
46
|
+
* @param {boolean} shallow - Use shallow clone (default true)
|
|
46
47
|
* @returns {Promise<void>}
|
|
47
48
|
*/
|
|
48
|
-
async function cloneRepo(url, targetPath) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
async function cloneRepo(url, targetPath, shallow = true) {
|
|
50
|
+
try {
|
|
51
|
+
const args = shallow
|
|
52
|
+
? ["clone", "--depth", "1", url, targetPath]
|
|
53
|
+
: ["clone", url, targetPath];
|
|
54
|
+
await execa("git", args);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Failed to clone repository: ${error.message}`);
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/**
|
|
@@ -59,11 +63,11 @@ async function cloneRepo(url, targetPath) {
|
|
|
59
63
|
* @returns {Promise<void>}
|
|
60
64
|
*/
|
|
61
65
|
async function pullRepo(repoPath) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
try {
|
|
67
|
+
await execa("git", ["-C", repoPath, "pull", "--rebase"]);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(`Failed to pull repository: ${error.message}`);
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -72,37 +76,37 @@ async function pullRepo(repoPath) {
|
|
|
72
76
|
* @returns {Promise<{skillPath: string, needsUpdate: boolean}>}
|
|
73
77
|
*/
|
|
74
78
|
async function cloneOrUpdateRepo(url) {
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
const parsed = parseGitHubUrl(url);
|
|
80
|
+
const targetPath = path.join(DEFAULT_LIB_PATH, parsed.owner, parsed.repo);
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
let needsUpdate = false;
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
if (dirExists(targetPath)) {
|
|
85
|
+
// Repo exists, ask if user wants to update
|
|
86
|
+
needsUpdate = true;
|
|
87
|
+
} else {
|
|
88
|
+
// Clone new repo
|
|
89
|
+
await cloneRepo(parsed.cleanUrl, targetPath);
|
|
90
|
+
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
// Determine final skill path
|
|
93
|
+
let skillPath = targetPath;
|
|
94
|
+
if (parsed.subpath) {
|
|
95
|
+
skillPath = path.join(targetPath, parsed.subpath);
|
|
96
|
+
}
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
return {
|
|
99
|
+
skillPath,
|
|
100
|
+
targetPath,
|
|
101
|
+
needsUpdate,
|
|
102
|
+
hasSubpath: !!parsed.subpath,
|
|
103
|
+
};
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
module.exports = {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
DEFAULT_LIB_PATH,
|
|
108
|
+
parseGitHubUrl,
|
|
109
|
+
cloneRepo,
|
|
110
|
+
pullRepo,
|
|
111
|
+
cloneOrUpdateRepo,
|
|
108
112
|
};
|