ai-agent-config 1.0.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/README.md +115 -0
- package/bin/cli.js +297 -0
- package/index.js +36 -0
- package/package.json +44 -0
- package/scripts/installer.js +280 -0
- package/scripts/platforms.js +182 -0
- package/scripts/postinstall.js +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# AI Agent Config
|
|
2
|
+
|
|
3
|
+
> Universal Global Skills & Workflows for AI Coding Assistants
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@dongitran/ai-agent-config)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Install a curated collection of AI agent skills from [github.com/dongitran/ai-agent-config](https://github.com/dongitran/ai-agent-config) to your global configuration.
|
|
9
|
+
|
|
10
|
+
## How It Works
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
14
|
+
│ GitHub Repository │
|
|
15
|
+
│ github.com/dongitran/ai-agent-config │
|
|
16
|
+
│ └── .agent/ │
|
|
17
|
+
│ ├── skills/ ◄── Skills are defined here │
|
|
18
|
+
│ │ ├── code-review/ │
|
|
19
|
+
│ │ └── git-commit/ │
|
|
20
|
+
│ └── workflows/ │
|
|
21
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
22
|
+
│
|
|
23
|
+
│ ai-agent sync
|
|
24
|
+
▼
|
|
25
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
26
|
+
│ Local Cache │
|
|
27
|
+
│ ~/.ai-agent-config-cache/ │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
29
|
+
│
|
|
30
|
+
│ ai-agent install
|
|
31
|
+
▼
|
|
32
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
33
|
+
│ Platform Global Directories │
|
|
34
|
+
│ ├── ~/.claude/skills/ (Claude Code) │
|
|
35
|
+
│ ├── ~/.gemini/antigravity/skills/ (Antigravity IDE) │
|
|
36
|
+
│ ├── ~/.cursor/skills/ (Cursor) │
|
|
37
|
+
│ └── ~/.windsurf/skills/ (Windsurf) │
|
|
38
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Install the CLI globally
|
|
45
|
+
npm install -g @dongitran/ai-agent-config
|
|
46
|
+
|
|
47
|
+
# Sync skills from repository
|
|
48
|
+
ai-agent sync
|
|
49
|
+
|
|
50
|
+
# Install to your platforms
|
|
51
|
+
ai-agent install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## CLI Usage
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ai-agent help # Show all commands
|
|
58
|
+
ai-agent sync # Sync from GitHub repository
|
|
59
|
+
ai-agent install # Install to all detected platforms
|
|
60
|
+
ai-agent list # List available skills
|
|
61
|
+
ai-agent platforms # Show detected platforms
|
|
62
|
+
ai-agent uninstall # Remove installed skills
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Options
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
ai-agent install --platform claude # Install to specific platform
|
|
69
|
+
ai-agent install --skill code-review # Install specific skill
|
|
70
|
+
ai-agent install --force # Overwrite existing files
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Supported Platforms
|
|
74
|
+
|
|
75
|
+
| Platform | Global Skills Path |
|
|
76
|
+
|----------|-------------------|
|
|
77
|
+
| Claude Code | `~/.claude/skills/` |
|
|
78
|
+
| Antigravity IDE | `~/.gemini/antigravity/skills/` |
|
|
79
|
+
| Cursor | `~/.cursor/skills/` |
|
|
80
|
+
| Windsurf | `~/.windsurf/skills/` |
|
|
81
|
+
| Codex CLI | `~/.codex/skills/` |
|
|
82
|
+
|
|
83
|
+
## Available Skills
|
|
84
|
+
|
|
85
|
+
Skills are defined in the repository's `.agent/skills/` directory:
|
|
86
|
+
|
|
87
|
+
- **code-review** - Thorough code review with security, performance checks
|
|
88
|
+
- **git-commit** - Conventional commit message formatting
|
|
89
|
+
- *More coming soon...*
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Optional config at `~/.ai-agent-config.json`:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"platforms": ["claude", "antigravity"],
|
|
98
|
+
"skills": {
|
|
99
|
+
"include": ["*"],
|
|
100
|
+
"exclude": []
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Contributing
|
|
106
|
+
|
|
107
|
+
Add your skills to the repository:
|
|
108
|
+
|
|
109
|
+
1. Fork [github.com/dongitran/ai-agent-config](https://github.com/dongitran/ai-agent-config)
|
|
110
|
+
2. Create skill in `.agent/skills/your-skill/SKILL.md`
|
|
111
|
+
3. Submit a pull request
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AI Agent Config CLI
|
|
5
|
+
* Install global skills from https://github.com/dongitran/ai-agent-config
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const platforms = require("../scripts/platforms");
|
|
12
|
+
const installer = require("../scripts/installer");
|
|
13
|
+
|
|
14
|
+
const COMMANDS = {
|
|
15
|
+
init: "Initialize config file in home directory",
|
|
16
|
+
install: "Install skills to detected platforms",
|
|
17
|
+
sync: "Sync skills from GitHub repository",
|
|
18
|
+
list: "List available skills and workflows",
|
|
19
|
+
platforms: "Show detected platforms",
|
|
20
|
+
uninstall: "Remove installed skills",
|
|
21
|
+
help: "Show this help message",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function showHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
27
|
+
║ AI Agent Config CLI ║
|
|
28
|
+
║ Universal Global Skills for AI Coding Assistants ║
|
|
29
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
30
|
+
|
|
31
|
+
Usage: ai-agent <command> [options]
|
|
32
|
+
|
|
33
|
+
Commands:`);
|
|
34
|
+
|
|
35
|
+
Object.entries(COMMANDS).forEach(([cmd, desc]) => {
|
|
36
|
+
console.log(` ${cmd.padEnd(12)} ${desc}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(`
|
|
40
|
+
Options:
|
|
41
|
+
--platform <name> Target specific platform (claude, antigravity, cursor)
|
|
42
|
+
--skill <name> Install specific skill only
|
|
43
|
+
--force Overwrite existing files
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
ai-agent sync # Sync from GitHub repository
|
|
47
|
+
ai-agent install # Install all skills to detected platforms
|
|
48
|
+
ai-agent install --platform claude
|
|
49
|
+
ai-agent install --skill code-review
|
|
50
|
+
ai-agent list # List available skills
|
|
51
|
+
ai-agent platforms # Show detected platforms
|
|
52
|
+
ai-agent uninstall # Remove all installed skills
|
|
53
|
+
|
|
54
|
+
Repository: https://github.com/dongitran/ai-agent-config
|
|
55
|
+
Skills are stored in: ~/.ai-agent-config-cache/.agent/skills/
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function showPlatforms() {
|
|
60
|
+
console.log("\n🔍 Detecting platforms...\n");
|
|
61
|
+
|
|
62
|
+
const detected = platforms.detectAll();
|
|
63
|
+
|
|
64
|
+
if (detected.length === 0) {
|
|
65
|
+
console.log("No AI coding platforms detected in home directory.\n");
|
|
66
|
+
console.log("Supported platforms:");
|
|
67
|
+
platforms.SUPPORTED.forEach((p) => {
|
|
68
|
+
console.log(` - ${p.displayName} (~/${p.configDir})`);
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log("Detected platforms:\n");
|
|
74
|
+
detected.forEach((p) => {
|
|
75
|
+
console.log(` ✓ ${p.displayName}`);
|
|
76
|
+
console.log(` Skills: ${p.skillsPath}`);
|
|
77
|
+
console.log("");
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function listSkills() {
|
|
82
|
+
console.log("\n📦 Available Skills & Workflows\n");
|
|
83
|
+
|
|
84
|
+
if (!installer.isRepoCached()) {
|
|
85
|
+
console.log("⚠️ Repository not synced yet.");
|
|
86
|
+
console.log(" Run: ai-agent sync\n");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const skills = installer.getAvailableSkills();
|
|
91
|
+
const workflows = installer.getAvailableWorkflows();
|
|
92
|
+
|
|
93
|
+
console.log("Skills:");
|
|
94
|
+
if (skills.length === 0) {
|
|
95
|
+
console.log(" (no skills found)");
|
|
96
|
+
} else {
|
|
97
|
+
skills.forEach((skill) => {
|
|
98
|
+
const skillFile = path.join(installer.REPO_SKILLS_DIR, skill, "SKILL.md");
|
|
99
|
+
let desc = "";
|
|
100
|
+
if (fs.existsSync(skillFile)) {
|
|
101
|
+
const content = fs.readFileSync(skillFile, "utf-8");
|
|
102
|
+
const match = content.match(/description:\s*(.+)/i);
|
|
103
|
+
if (match) desc = `- ${match[1]}`;
|
|
104
|
+
}
|
|
105
|
+
console.log(` • ${skill} ${desc}`);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log("\nWorkflows:");
|
|
110
|
+
if (workflows.length === 0) {
|
|
111
|
+
console.log(" (no workflows found)");
|
|
112
|
+
} else {
|
|
113
|
+
workflows.forEach((wf) => {
|
|
114
|
+
console.log(` • ${wf}`);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(`\nSource: ${installer.CACHE_DIR}`);
|
|
119
|
+
console.log("");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function init(args) {
|
|
123
|
+
console.log("\n🚀 Initializing AI Agent Config...\n");
|
|
124
|
+
|
|
125
|
+
const configFile = path.join(platforms.HOME, ".ai-agent-config.json");
|
|
126
|
+
|
|
127
|
+
if (fs.existsSync(configFile) && !args.includes("--force")) {
|
|
128
|
+
console.log("⚠️ Config file already exists. Use --force to overwrite.");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const defaultConfig = {
|
|
133
|
+
platforms: ["auto"],
|
|
134
|
+
skills: {
|
|
135
|
+
include: ["*"],
|
|
136
|
+
exclude: [],
|
|
137
|
+
},
|
|
138
|
+
sync: {
|
|
139
|
+
auto: false,
|
|
140
|
+
remote: "https://github.com/dongitran/ai-agent-config",
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
fs.writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2));
|
|
145
|
+
console.log(`✓ Created ${configFile}`);
|
|
146
|
+
|
|
147
|
+
const detected = platforms.detectAll();
|
|
148
|
+
if (detected.length > 0) {
|
|
149
|
+
console.log("\nDetected platforms:");
|
|
150
|
+
detected.forEach((p) => console.log(` ✓ ${p.displayName}`));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log("\nNext steps:");
|
|
154
|
+
console.log(" 1. Run: ai-agent sync");
|
|
155
|
+
console.log(" 2. Run: ai-agent install");
|
|
156
|
+
console.log("");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function install(args) {
|
|
160
|
+
console.log("\n📥 Installing skills...\n");
|
|
161
|
+
|
|
162
|
+
const options = {
|
|
163
|
+
platform: null,
|
|
164
|
+
force: false,
|
|
165
|
+
skill: null,
|
|
166
|
+
sync: true,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < args.length; i++) {
|
|
170
|
+
if (args[i] === "--platform" && args[i + 1]) {
|
|
171
|
+
options.platform = args[++i];
|
|
172
|
+
} else if (args[i] === "--force") {
|
|
173
|
+
options.force = true;
|
|
174
|
+
} else if (args[i] === "--skill" && args[i + 1]) {
|
|
175
|
+
options.skill = args[++i];
|
|
176
|
+
} else if (args[i] === "--no-sync") {
|
|
177
|
+
options.sync = false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const result = installer.install(options);
|
|
183
|
+
|
|
184
|
+
if (result.skillsCount > 0) {
|
|
185
|
+
console.log(`\n✓ Installed ${result.skillsCount} skill(s) to ${result.platformsCount} platform(s)\n`);
|
|
186
|
+
result.details.forEach((d) => {
|
|
187
|
+
console.log(` ${d.platform}: ${d.path}`);
|
|
188
|
+
d.skills.forEach((s) => {
|
|
189
|
+
const status = s.skipped > 0 ? `(${s.copied} new, ${s.skipped} skipped)` : "";
|
|
190
|
+
console.log(` • ${s.name} ${status}`);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
console.log("\n⚠️ No skills installed.");
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`\n❌ Installation failed: ${error.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log("");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function sync(args) {
|
|
205
|
+
console.log("\n🔄 Syncing from GitHub repository...\n");
|
|
206
|
+
console.log(` Repository: ${installer.REPO_URL}`);
|
|
207
|
+
console.log(` Cache: ${installer.CACHE_DIR}\n`);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const success = installer.syncRepo();
|
|
211
|
+
|
|
212
|
+
if (success) {
|
|
213
|
+
console.log("\n✓ Sync complete!\n");
|
|
214
|
+
|
|
215
|
+
const skills = installer.getAvailableSkills();
|
|
216
|
+
const workflows = installer.getAvailableWorkflows();
|
|
217
|
+
|
|
218
|
+
console.log(` Found ${skills.length} skill(s), ${workflows.length} workflow(s)`);
|
|
219
|
+
console.log("\n Run 'ai-agent install' to install to your platforms.\n");
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error(`\n❌ Sync failed: ${error.message}`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function uninstall(args) {
|
|
228
|
+
console.log("\n🗑️ Uninstalling skills...\n");
|
|
229
|
+
|
|
230
|
+
const options = {
|
|
231
|
+
platform: null,
|
|
232
|
+
skill: null,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
for (let i = 0; i < args.length; i++) {
|
|
236
|
+
if (args[i] === "--platform" && args[i + 1]) {
|
|
237
|
+
options.platform = args[++i];
|
|
238
|
+
} else if (args[i] === "--skill" && args[i + 1]) {
|
|
239
|
+
options.skill = args[++i];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const result = installer.uninstall(options);
|
|
245
|
+
|
|
246
|
+
if (result.totalRemoved > 0) {
|
|
247
|
+
console.log(`✓ Removed ${result.totalRemoved} skill(s) from ${result.platformsCount} platform(s)\n`);
|
|
248
|
+
result.details.forEach((d) => {
|
|
249
|
+
if (d.removed > 0) {
|
|
250
|
+
console.log(` ${d.platform}: ${d.removed} removed`);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
} else {
|
|
254
|
+
console.log("ℹ️ No skills to remove.");
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(`\n❌ Uninstall failed: ${error.message}`);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.log("");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Main
|
|
265
|
+
const args = process.argv.slice(2);
|
|
266
|
+
const command = args[0];
|
|
267
|
+
|
|
268
|
+
switch (command) {
|
|
269
|
+
case "init":
|
|
270
|
+
init(args.slice(1));
|
|
271
|
+
break;
|
|
272
|
+
case "install":
|
|
273
|
+
install(args.slice(1));
|
|
274
|
+
break;
|
|
275
|
+
case "sync":
|
|
276
|
+
sync(args.slice(1));
|
|
277
|
+
break;
|
|
278
|
+
case "list":
|
|
279
|
+
listSkills();
|
|
280
|
+
break;
|
|
281
|
+
case "platforms":
|
|
282
|
+
showPlatforms();
|
|
283
|
+
break;
|
|
284
|
+
case "uninstall":
|
|
285
|
+
uninstall(args.slice(1));
|
|
286
|
+
break;
|
|
287
|
+
case "help":
|
|
288
|
+
case "--help":
|
|
289
|
+
case "-h":
|
|
290
|
+
case undefined:
|
|
291
|
+
showHelp();
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
console.error(`Unknown command: ${command}`);
|
|
295
|
+
console.log('Run "ai-agent help" for usage information.');
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Agent Config
|
|
3
|
+
* Universal Global Skills & Workflows for AI Coding Assistants
|
|
4
|
+
*
|
|
5
|
+
* @module @dongitran/ai-agent-config
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const platforms = require("./scripts/platforms");
|
|
9
|
+
const installer = require("./scripts/installer");
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
// Platform detection
|
|
13
|
+
platforms: {
|
|
14
|
+
detectAll: platforms.detectAll,
|
|
15
|
+
getByName: platforms.getByName,
|
|
16
|
+
getAllNames: platforms.getAllNames,
|
|
17
|
+
SUPPORTED: platforms.SUPPORTED,
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// Installation
|
|
21
|
+
installer: {
|
|
22
|
+
install: installer.install,
|
|
23
|
+
uninstall: installer.uninstall,
|
|
24
|
+
syncRepo: installer.syncRepo,
|
|
25
|
+
isRepoCached: installer.isRepoCached,
|
|
26
|
+
getAvailableSkills: installer.getAvailableSkills,
|
|
27
|
+
getAvailableWorkflows: installer.getAvailableWorkflows,
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Constants
|
|
31
|
+
REPO_URL: installer.REPO_URL,
|
|
32
|
+
CACHE_DIR: installer.CACHE_DIR,
|
|
33
|
+
|
|
34
|
+
// Version
|
|
35
|
+
version: require("./package.json").version,
|
|
36
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-agent-config",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Universal Global Skills & Workflows for AI Coding Assistants (Claude Code, Antigravity, Cursor)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-agent": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node scripts/postinstall.js",
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"agent",
|
|
16
|
+
"skills",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"antigravity",
|
|
19
|
+
"cursor",
|
|
20
|
+
"windsurf",
|
|
21
|
+
"copilot",
|
|
22
|
+
"workflows",
|
|
23
|
+
"coding-assistant",
|
|
24
|
+
"global-skills"
|
|
25
|
+
],
|
|
26
|
+
"author": "Dong Tran",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/dongitran/ai-agent-config.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/dongitran/ai-agent-config/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/dongitran/ai-agent-config#readme",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"bin/",
|
|
41
|
+
"scripts/",
|
|
42
|
+
"index.js"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer module for AI Agent Config
|
|
3
|
+
* Handles syncing from GitHub repo and copying skills to platform directories
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
const platforms = require("./platforms");
|
|
10
|
+
|
|
11
|
+
const REPO_URL = "https://github.com/dongitran/ai-agent-config.git";
|
|
12
|
+
const CACHE_DIR = path.join(platforms.HOME, ".ai-agent-config-cache");
|
|
13
|
+
const REPO_SKILLS_DIR = path.join(CACHE_DIR, ".agent", "skills");
|
|
14
|
+
const REPO_WORKFLOWS_DIR = path.join(CACHE_DIR, ".agent", "workflows");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Copy directory recursively
|
|
18
|
+
*/
|
|
19
|
+
function copyDir(src, dest, force = false) {
|
|
20
|
+
if (!fs.existsSync(src)) {
|
|
21
|
+
return { copied: 0, skipped: 0 };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(dest)) {
|
|
25
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let copied = 0;
|
|
29
|
+
let skipped = 0;
|
|
30
|
+
|
|
31
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
32
|
+
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const srcPath = path.join(src, entry.name);
|
|
35
|
+
const destPath = path.join(dest, entry.name);
|
|
36
|
+
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
const result = copyDir(srcPath, destPath, force);
|
|
39
|
+
copied += result.copied;
|
|
40
|
+
skipped += result.skipped;
|
|
41
|
+
} else {
|
|
42
|
+
if (fs.existsSync(destPath) && !force) {
|
|
43
|
+
skipped++;
|
|
44
|
+
} else {
|
|
45
|
+
fs.copyFileSync(srcPath, destPath);
|
|
46
|
+
copied++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { copied, skipped };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sync repository from GitHub
|
|
56
|
+
* @returns {boolean} Success status
|
|
57
|
+
*/
|
|
58
|
+
function syncRepo() {
|
|
59
|
+
try {
|
|
60
|
+
if (fs.existsSync(CACHE_DIR)) {
|
|
61
|
+
console.log(" Updating cached repository...");
|
|
62
|
+
execSync("git pull --quiet", { cwd: CACHE_DIR, stdio: "pipe" });
|
|
63
|
+
} else {
|
|
64
|
+
console.log(" Cloning repository...");
|
|
65
|
+
execSync(`git clone --quiet "${REPO_URL}" "${CACHE_DIR}"`, { stdio: "pipe" });
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error(` Failed to sync: ${error.message}`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if repo is cached
|
|
76
|
+
*/
|
|
77
|
+
function isRepoCached() {
|
|
78
|
+
return fs.existsSync(CACHE_DIR) && fs.existsSync(REPO_SKILLS_DIR);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get list of available skills from cached repo
|
|
83
|
+
*/
|
|
84
|
+
function getAvailableSkills() {
|
|
85
|
+
if (!fs.existsSync(REPO_SKILLS_DIR)) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return fs.readdirSync(REPO_SKILLS_DIR).filter((name) => {
|
|
90
|
+
const skillPath = path.join(REPO_SKILLS_DIR, name);
|
|
91
|
+
const skillFile = path.join(skillPath, "SKILL.md");
|
|
92
|
+
return fs.statSync(skillPath).isDirectory() && fs.existsSync(skillFile);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get list of available workflows from cached repo
|
|
98
|
+
*/
|
|
99
|
+
function getAvailableWorkflows() {
|
|
100
|
+
if (!fs.existsSync(REPO_WORKFLOWS_DIR)) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return fs
|
|
105
|
+
.readdirSync(REPO_WORKFLOWS_DIR)
|
|
106
|
+
.filter((name) => name.endsWith(".md"))
|
|
107
|
+
.map((name) => name.replace(".md", ""));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Install skills to a specific platform
|
|
112
|
+
*/
|
|
113
|
+
function installToPlatform(platform, options = {}) {
|
|
114
|
+
const { force = false, skill = null } = options;
|
|
115
|
+
|
|
116
|
+
const skillsPath = platforms.ensureSkillsDir(platform);
|
|
117
|
+
const results = { platform: platform.name, path: skillsPath, skills: [] };
|
|
118
|
+
|
|
119
|
+
let skillsToInstall = getAvailableSkills();
|
|
120
|
+
|
|
121
|
+
if (skill) {
|
|
122
|
+
skillsToInstall = skillsToInstall.filter((s) => s === skill);
|
|
123
|
+
if (skillsToInstall.length === 0) {
|
|
124
|
+
throw new Error(`Skill "${skill}" not found in repository`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const skillName of skillsToInstall) {
|
|
129
|
+
const srcPath = path.join(REPO_SKILLS_DIR, skillName);
|
|
130
|
+
const destPath = path.join(skillsPath, skillName);
|
|
131
|
+
|
|
132
|
+
const copyResult = copyDir(srcPath, destPath, force);
|
|
133
|
+
results.skills.push({
|
|
134
|
+
name: skillName,
|
|
135
|
+
...copyResult,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return results;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Install skills to all detected platforms
|
|
144
|
+
*/
|
|
145
|
+
function install(options = {}) {
|
|
146
|
+
const { platform = null, force = false, skill = null, sync = true } = options;
|
|
147
|
+
|
|
148
|
+
// Sync repo first if needed
|
|
149
|
+
if (sync && !isRepoCached()) {
|
|
150
|
+
console.log("\n📦 Syncing skills from repository...");
|
|
151
|
+
if (!syncRepo()) {
|
|
152
|
+
throw new Error("Failed to sync repository. Check your internet connection.");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!isRepoCached()) {
|
|
157
|
+
console.log("\n⚠️ Skills repository not cached. Run 'ai-agent sync' first.");
|
|
158
|
+
return { skillsCount: 0, platformsCount: 0, details: [] };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let targetPlatforms;
|
|
162
|
+
|
|
163
|
+
if (platform) {
|
|
164
|
+
const platformObj = platforms.getByName(platform);
|
|
165
|
+
if (!platformObj) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Unknown platform: ${platform}. Available: ${platforms.getAllNames().join(", ")}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
targetPlatforms = [platformObj];
|
|
171
|
+
} else {
|
|
172
|
+
targetPlatforms = platforms.detectAll().map((p) => platforms.getByName(p.name));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (targetPlatforms.length === 0) {
|
|
176
|
+
console.log("\n⚠️ No AI coding platforms detected.");
|
|
177
|
+
console.log(" Supported platforms:", platforms.getAllNames().join(", "));
|
|
178
|
+
console.log("\n Force install with: ai-agent install --platform <name>");
|
|
179
|
+
return { skillsCount: 0, platformsCount: 0, details: [] };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const details = [];
|
|
183
|
+
let totalSkills = 0;
|
|
184
|
+
|
|
185
|
+
for (const platformObj of targetPlatforms) {
|
|
186
|
+
try {
|
|
187
|
+
const result = installToPlatform(platformObj, { force, skill });
|
|
188
|
+
details.push(result);
|
|
189
|
+
totalSkills += result.skills.length;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(` Failed to install to ${platformObj.name}: ${error.message}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
skillsCount: totalSkills,
|
|
197
|
+
platformsCount: details.length,
|
|
198
|
+
details,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Uninstall skills from a platform
|
|
204
|
+
*/
|
|
205
|
+
function uninstallFromPlatform(platform, skill = null) {
|
|
206
|
+
const skillsPath = platform.skillsPath;
|
|
207
|
+
|
|
208
|
+
if (!fs.existsSync(skillsPath)) {
|
|
209
|
+
return { platform: platform.name, removed: 0 };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let removed = 0;
|
|
213
|
+
const ourSkills = getAvailableSkills();
|
|
214
|
+
|
|
215
|
+
if (skill) {
|
|
216
|
+
const skillPath = path.join(skillsPath, skill);
|
|
217
|
+
if (fs.existsSync(skillPath) && ourSkills.includes(skill)) {
|
|
218
|
+
fs.rmSync(skillPath, { recursive: true });
|
|
219
|
+
removed++;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
for (const skillName of ourSkills) {
|
|
223
|
+
const skillPath = path.join(skillsPath, skillName);
|
|
224
|
+
if (fs.existsSync(skillPath)) {
|
|
225
|
+
fs.rmSync(skillPath, { recursive: true });
|
|
226
|
+
removed++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return { platform: platform.name, removed };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Uninstall skills from all platforms
|
|
236
|
+
*/
|
|
237
|
+
function uninstall(options = {}) {
|
|
238
|
+
const { platform = null, skill = null } = options;
|
|
239
|
+
|
|
240
|
+
let targetPlatforms;
|
|
241
|
+
|
|
242
|
+
if (platform) {
|
|
243
|
+
const platformObj = platforms.getByName(platform);
|
|
244
|
+
if (!platformObj) {
|
|
245
|
+
throw new Error(`Unknown platform: ${platform}`);
|
|
246
|
+
}
|
|
247
|
+
targetPlatforms = [platformObj];
|
|
248
|
+
} else {
|
|
249
|
+
targetPlatforms = platforms.detectAll().map((p) => platforms.getByName(p.name));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const results = [];
|
|
253
|
+
let totalRemoved = 0;
|
|
254
|
+
|
|
255
|
+
for (const platformObj of targetPlatforms) {
|
|
256
|
+
const result = uninstallFromPlatform(platformObj, skill);
|
|
257
|
+
results.push(result);
|
|
258
|
+
totalRemoved += result.removed;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
totalRemoved,
|
|
263
|
+
platformsCount: results.length,
|
|
264
|
+
details: results,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = {
|
|
269
|
+
install,
|
|
270
|
+
uninstall,
|
|
271
|
+
syncRepo,
|
|
272
|
+
isRepoCached,
|
|
273
|
+
getAvailableSkills,
|
|
274
|
+
getAvailableWorkflows,
|
|
275
|
+
copyDir,
|
|
276
|
+
CACHE_DIR,
|
|
277
|
+
REPO_URL,
|
|
278
|
+
REPO_SKILLS_DIR,
|
|
279
|
+
REPO_WORKFLOWS_DIR,
|
|
280
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection for AI coding assistants
|
|
3
|
+
* Detects which platforms are installed and returns their global skills paths
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
|
|
10
|
+
const HOME = os.homedir();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Supported platforms configuration
|
|
14
|
+
*/
|
|
15
|
+
const SUPPORTED = [
|
|
16
|
+
{
|
|
17
|
+
name: "claude",
|
|
18
|
+
displayName: "Claude Code",
|
|
19
|
+
configDir: ".claude",
|
|
20
|
+
skillsDir: "skills",
|
|
21
|
+
commandsDir: "commands",
|
|
22
|
+
get configPath() {
|
|
23
|
+
return path.join(HOME, this.configDir);
|
|
24
|
+
},
|
|
25
|
+
get skillsPath() {
|
|
26
|
+
return path.join(HOME, this.configDir, this.skillsDir);
|
|
27
|
+
},
|
|
28
|
+
get commandsPath() {
|
|
29
|
+
return path.join(HOME, this.configDir, this.commandsDir);
|
|
30
|
+
},
|
|
31
|
+
detect() {
|
|
32
|
+
return fs.existsSync(this.configPath);
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "antigravity",
|
|
37
|
+
displayName: "Antigravity IDE",
|
|
38
|
+
configDir: ".gemini/antigravity",
|
|
39
|
+
skillsDir: "skills",
|
|
40
|
+
workflowsDir: "workflows",
|
|
41
|
+
get configPath() {
|
|
42
|
+
return path.join(HOME, this.configDir);
|
|
43
|
+
},
|
|
44
|
+
get skillsPath() {
|
|
45
|
+
return path.join(HOME, this.configDir, this.skillsDir);
|
|
46
|
+
},
|
|
47
|
+
get workflowsPath() {
|
|
48
|
+
return path.join(HOME, this.configDir, this.workflowsDir);
|
|
49
|
+
},
|
|
50
|
+
detect() {
|
|
51
|
+
// Check for .gemini directory or Antigravity app
|
|
52
|
+
return (
|
|
53
|
+
fs.existsSync(path.join(HOME, ".gemini")) ||
|
|
54
|
+
fs.existsSync("/Applications/Antigravity.app") ||
|
|
55
|
+
fs.existsSync(path.join(HOME, "Applications", "Antigravity.app"))
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "cursor",
|
|
61
|
+
displayName: "Cursor",
|
|
62
|
+
configDir: ".cursor",
|
|
63
|
+
skillsDir: "skills",
|
|
64
|
+
rulesDir: "rules",
|
|
65
|
+
get configPath() {
|
|
66
|
+
return path.join(HOME, this.configDir);
|
|
67
|
+
},
|
|
68
|
+
get skillsPath() {
|
|
69
|
+
return path.join(HOME, this.configDir, this.skillsDir);
|
|
70
|
+
},
|
|
71
|
+
get rulesPath() {
|
|
72
|
+
return path.join(HOME, this.configDir, this.rulesDir);
|
|
73
|
+
},
|
|
74
|
+
detect() {
|
|
75
|
+
return (
|
|
76
|
+
fs.existsSync(this.configPath) ||
|
|
77
|
+
fs.existsSync("/Applications/Cursor.app") ||
|
|
78
|
+
fs.existsSync(path.join(HOME, "Applications", "Cursor.app"))
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "windsurf",
|
|
84
|
+
displayName: "Windsurf",
|
|
85
|
+
configDir: ".windsurf",
|
|
86
|
+
skillsDir: "skills",
|
|
87
|
+
get configPath() {
|
|
88
|
+
return path.join(HOME, this.configDir);
|
|
89
|
+
},
|
|
90
|
+
get skillsPath() {
|
|
91
|
+
return path.join(HOME, this.configDir, this.skillsDir);
|
|
92
|
+
},
|
|
93
|
+
detect() {
|
|
94
|
+
return (
|
|
95
|
+
fs.existsSync(this.configPath) ||
|
|
96
|
+
fs.existsSync("/Applications/Windsurf.app")
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "codex",
|
|
102
|
+
displayName: "Codex CLI",
|
|
103
|
+
configDir: ".codex",
|
|
104
|
+
skillsDir: "skills",
|
|
105
|
+
get configPath() {
|
|
106
|
+
return path.join(HOME, this.configDir);
|
|
107
|
+
},
|
|
108
|
+
get skillsPath() {
|
|
109
|
+
return path.join(HOME, this.configDir, this.skillsDir);
|
|
110
|
+
},
|
|
111
|
+
detect() {
|
|
112
|
+
return fs.existsSync(this.configPath);
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "copilot",
|
|
117
|
+
displayName: "GitHub Copilot",
|
|
118
|
+
configDir: ".github",
|
|
119
|
+
instructionsFile: "copilot-instructions.md",
|
|
120
|
+
get configPath() {
|
|
121
|
+
return path.join(HOME, this.configDir);
|
|
122
|
+
},
|
|
123
|
+
get instructionsPath() {
|
|
124
|
+
return path.join(HOME, this.configDir, this.instructionsFile);
|
|
125
|
+
},
|
|
126
|
+
detect() {
|
|
127
|
+
// Copilot is usually detected via VS Code extensions
|
|
128
|
+
return fs.existsSync(this.configPath);
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Detect all installed platforms
|
|
135
|
+
* @returns {Array} Array of detected platform objects
|
|
136
|
+
*/
|
|
137
|
+
function detectAll() {
|
|
138
|
+
return SUPPORTED.filter((platform) => platform.detect()).map((platform) => ({
|
|
139
|
+
name: platform.name,
|
|
140
|
+
displayName: platform.displayName,
|
|
141
|
+
configPath: platform.configPath,
|
|
142
|
+
skillsPath: platform.skillsPath,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get platform by name
|
|
148
|
+
* @param {string} name - Platform name
|
|
149
|
+
* @returns {Object|null} Platform object or null
|
|
150
|
+
*/
|
|
151
|
+
function getByName(name) {
|
|
152
|
+
return SUPPORTED.find((p) => p.name === name.toLowerCase()) || null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Ensure skills directory exists for a platform
|
|
157
|
+
* @param {Object} platform - Platform object
|
|
158
|
+
*/
|
|
159
|
+
function ensureSkillsDir(platform) {
|
|
160
|
+
const skillsPath = platform.skillsPath;
|
|
161
|
+
if (!fs.existsSync(skillsPath)) {
|
|
162
|
+
fs.mkdirSync(skillsPath, { recursive: true });
|
|
163
|
+
}
|
|
164
|
+
return skillsPath;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get all supported platform names
|
|
169
|
+
* @returns {Array} Array of platform names
|
|
170
|
+
*/
|
|
171
|
+
function getAllNames() {
|
|
172
|
+
return SUPPORTED.map((p) => p.name);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
SUPPORTED,
|
|
177
|
+
detectAll,
|
|
178
|
+
getByName,
|
|
179
|
+
ensureSkillsDir,
|
|
180
|
+
getAllNames,
|
|
181
|
+
HOME,
|
|
182
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script for AI Agent Config
|
|
5
|
+
* Shows usage instructions after npm install
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const platforms = require("./platforms");
|
|
9
|
+
|
|
10
|
+
function main() {
|
|
11
|
+
console.log("\n╔═══════════════════════════════════════════════════════════════╗");
|
|
12
|
+
console.log("║ AI Agent Config Installed! ║");
|
|
13
|
+
console.log("╚═══════════════════════════════════════════════════════════════╝\n");
|
|
14
|
+
|
|
15
|
+
// Detect platforms
|
|
16
|
+
const detected = platforms.detectAll();
|
|
17
|
+
|
|
18
|
+
if (detected.length > 0) {
|
|
19
|
+
console.log("🔍 Detected platforms:\n");
|
|
20
|
+
detected.forEach((p) => {
|
|
21
|
+
console.log(` ✓ ${p.displayName}`);
|
|
22
|
+
});
|
|
23
|
+
console.log("");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log("📖 Quick Start:\n");
|
|
27
|
+
console.log(" 1. Sync skills from repository:");
|
|
28
|
+
console.log(" $ ai-agent sync\n");
|
|
29
|
+
console.log(" 2. Install to your platforms:");
|
|
30
|
+
console.log(" $ ai-agent install\n");
|
|
31
|
+
console.log(" 3. View available skills:");
|
|
32
|
+
console.log(" $ ai-agent list\n");
|
|
33
|
+
console.log("📦 Repository: https://github.com/dongitran/ai-agent-config\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main();
|