@buiducnhat/agent-skills 0.2.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +195 -454
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import y, { stdin, stdout } from "node:process";
7
7
  import * as g from "node:readline";
8
8
  import O from "node:readline";
9
9
  import { Writable } from "node:stream";
10
- import { execSync } from "node:child_process";
10
+ import { execSync, spawn } from "node:child_process";
11
11
  import { tmpdir } from "node:os";
12
12
 
13
13
  //#region \0rolldown/runtime.js
@@ -1223,215 +1223,59 @@ ${J}${i.trimStart()}`), r = 3 + stripVTControlCharacters(i.trimStart()).length);
1223
1223
  }
1224
1224
  };
1225
1225
 
1226
- //#endregion
1227
- //#region src/apply.ts
1228
- async function runRulerApply(projectDir, agents) {
1229
- const s = Y();
1230
- s.start("Running ruler to generate agent configurations...");
1231
- const agentFlag = agents.join(",");
1232
- try {
1233
- execSync(`npx --yes @intellectronica/ruler apply --agents ${agentFlag}`, {
1234
- cwd: projectDir,
1235
- stdio: "pipe",
1236
- timeout: 12e4
1237
- });
1238
- s.stop("Generated agent configurations");
1239
- } catch {
1240
- s.stop("ruler apply encountered issues");
1241
- M.warn(`ruler apply had warnings or errors. You can run it manually:
1242
- npx @intellectronica/ruler apply --agents ${agentFlag}`);
1243
- }
1244
- }
1245
-
1246
- //#endregion
1247
- //#region src/configure.ts
1248
- function configureRulerToml(projectDir, agents) {
1249
- const tomlPath = path.join(projectDir, ".ruler", "ruler.toml");
1250
- if (!fs.existsSync(tomlPath)) {
1251
- M.warn("ruler.toml not found, skipping configuration");
1252
- return;
1253
- }
1254
- let content = fs.readFileSync(tomlPath, "utf-8");
1255
- const newLine = `default_agents = [${agents.map((a) => `"${a}"`).join(", ")}]`;
1256
- if (/^#?\s*default_agents\s*=/m.test(content)) content = content.replace(/^#?\s*default_agents\s*=.*$/m, newLine);
1257
- else {
1258
- const insertPoint = content.indexOf("\n\n");
1259
- if (insertPoint !== -1) content = content.slice(0, insertPoint) + "\n" + newLine + content.slice(insertPoint);
1260
- else content += `\n${newLine}\n`;
1261
- }
1262
- fs.writeFileSync(tomlPath, content, "utf-8");
1263
- M.info(`Configured ruler.toml with agents: ${agents.join(", ")}`);
1264
- }
1265
-
1266
1226
  //#endregion
1267
1227
  //#region src/constants.ts
1268
1228
  const REPO_URL = "https://github.com/buiducnhat/agent-skills.git";
1269
1229
  const REPO_BRANCH = "main";
1270
- const RULER_AGENTS = [
1271
- {
1272
- value: "claude",
1273
- label: "Claude Code",
1274
- hint: "Anthropic"
1275
- },
1276
- {
1277
- value: "copilot",
1278
- label: "GitHub Copilot",
1279
- hint: "GitHub"
1280
- },
1281
- {
1282
- value: "cursor",
1283
- label: "Cursor",
1284
- hint: "Cursor"
1285
- },
1286
- {
1287
- value: "windsurf",
1288
- label: "Windsurf",
1289
- hint: "Codeium"
1290
- },
1291
- {
1292
- value: "codex",
1293
- label: "OpenAI Codex CLI",
1294
- hint: "OpenAI"
1295
- },
1296
- {
1297
- value: "gemini-cli",
1298
- label: "Gemini CLI",
1299
- hint: "Google"
1300
- },
1301
- {
1302
- value: "amp",
1303
- label: "Amp",
1304
- hint: "Sourcegraph"
1305
- },
1306
- {
1307
- value: "cline",
1308
- label: "Cline",
1309
- hint: "VS Code"
1310
- },
1311
- {
1312
- value: "roo",
1313
- label: "Roo Code",
1314
- hint: "VS Code"
1315
- },
1316
- {
1317
- value: "aider",
1318
- label: "Aider",
1319
- hint: "Terminal"
1320
- },
1321
- {
1322
- value: "antigravity",
1323
- label: "Antigravity",
1324
- hint: ""
1325
- },
1326
- {
1327
- value: "pi",
1328
- label: "Pi Coding Agent",
1329
- hint: ""
1330
- },
1331
- {
1332
- value: "jules",
1333
- label: "Jules",
1334
- hint: "Google"
1335
- },
1336
- {
1337
- value: "kiro",
1338
- label: "Kiro",
1339
- hint: "AWS"
1340
- },
1341
- {
1342
- value: "kilocode",
1343
- label: "Kilo Code",
1344
- hint: "VS Code"
1345
- },
1346
- {
1347
- value: "crush",
1348
- label: "Crush",
1349
- hint: ""
1350
- },
1351
- {
1352
- value: "amazonqcli",
1353
- label: "Amazon Q CLI",
1354
- hint: "AWS"
1355
- },
1356
- {
1357
- value: "firebase",
1358
- label: "Firebase Studio",
1359
- hint: "Google"
1360
- },
1361
- {
1362
- value: "openhands",
1363
- label: "Open Hands",
1364
- hint: ""
1365
- },
1366
- {
1367
- value: "junie",
1368
- label: "Junie",
1369
- hint: "JetBrains"
1370
- },
1371
- {
1372
- value: "jetbrains-ai",
1373
- label: "JetBrains AI Assistant",
1374
- hint: "JetBrains"
1375
- },
1376
- {
1377
- value: "augmentcode",
1378
- label: "AugmentCode",
1379
- hint: ""
1380
- },
1381
- {
1382
- value: "opencode",
1383
- label: "OpenCode",
1384
- hint: ""
1385
- },
1386
- {
1387
- value: "goose",
1388
- label: "Goose",
1389
- hint: "Block"
1390
- },
1391
- {
1392
- value: "qwen",
1393
- label: "Qwen Code",
1394
- hint: "Alibaba"
1395
- },
1396
- {
1397
- value: "zed",
1398
- label: "Zed",
1399
- hint: ""
1400
- },
1401
- {
1402
- value: "trae",
1403
- label: "Trae AI",
1404
- hint: "ByteDance"
1405
- },
1406
- {
1407
- value: "warp",
1408
- label: "Warp",
1409
- hint: ""
1410
- },
1411
- {
1412
- value: "firebender",
1413
- label: "Firebender",
1414
- hint: ""
1415
- },
1416
- {
1417
- value: "factory",
1418
- label: "Factory Droid",
1419
- hint: ""
1420
- },
1421
- {
1422
- value: "mistral",
1423
- label: "Mistral Vibe",
1424
- hint: "Mistral"
1425
- }
1426
- ];
1427
- const POPULAR_AGENTS = [
1428
- "claude",
1429
- "copilot",
1430
- "cursor",
1431
- "windsurf",
1432
- "codex",
1433
- "gemini-cli"
1434
- ];
1230
+ const AGENT_SKILLS_DIRS = {
1231
+ ".claude": "claude-code",
1232
+ ".cursor": "cursor",
1233
+ ".codex": "codex",
1234
+ ".pi": "pi",
1235
+ ".gemini": "gemini-cli",
1236
+ ".agents": "amp",
1237
+ ".agent": "antigravity",
1238
+ ".roo": "roo-code",
1239
+ ".opencode": "opencode",
1240
+ ".factory": "factory-droid",
1241
+ ".vibe": "mistral-vibe",
1242
+ ".cline": "cline",
1243
+ ".goose": "goose"
1244
+ };
1245
+ const AGENT_RULES_MAP = {
1246
+ "github-copilot": "AGENTS.md",
1247
+ codex: "AGENTS.md",
1248
+ pi: "AGENTS.md",
1249
+ jules: "AGENTS.md",
1250
+ cursor: "AGENTS.md",
1251
+ amp: "AGENTS.md",
1252
+ "gemini-cli": "AGENTS.md",
1253
+ "kilo-code": "AGENTS.md",
1254
+ opencode: "AGENTS.md",
1255
+ "qwen-code": "AGENTS.md",
1256
+ "roo-code": "AGENTS.md",
1257
+ zed: "AGENTS.md",
1258
+ "factory-droid": "AGENTS.md",
1259
+ "mistral-vibe": "AGENTS.md",
1260
+ aider: "AGENTS.md",
1261
+ windsurf: "AGENTS.md",
1262
+ "claude-code": "CLAUDE.md",
1263
+ cline: ".clinerules",
1264
+ crush: "CRUSH.md",
1265
+ warp: "WARP.md",
1266
+ antigravity: ".agent/rules/ruler.md",
1267
+ "amazon-q": ".amazonq/rules/ruler_q_rules.md",
1268
+ "firebase-studio": ".idx/airules.md",
1269
+ "open-hands": ".openhands/microagents/repo.md",
1270
+ junie: ".junie/guidelines.md",
1271
+ "augment-code": ".augment/rules/ruler_augment_instructions.md",
1272
+ "trae-ai": ".trae/rules/project_rules.md",
1273
+ kiro: ".kiro/steering/ruler_kiro_instructions.md",
1274
+ "jetbrains-ai": ".aiassistant/rules/AGENTS.md",
1275
+ goose: ".goosehints"
1276
+ };
1277
+ const RULES_MARKER_START = "<!-- BEGIN agent-skills rules -->";
1278
+ const RULES_MARKER_END = "<!-- END agent-skills rules -->";
1435
1279
 
1436
1280
  //#endregion
1437
1281
  //#region src/fetch.ts
@@ -1441,7 +1285,9 @@ async function fetchTemplates() {
1441
1285
  const tempDir = mkdtempSync(path.join(tmpdir(), "agent-skills-"));
1442
1286
  try {
1443
1287
  execSync(`git clone --depth 1 --branch ${REPO_BRANCH} "${REPO_URL}" "${tempDir}"`, { stdio: "pipe" });
1444
- if (!existsSync(path.join(tempDir, "templates"))) throw new Error("templates/ directory not found in the repository");
1288
+ const templatesDir = path.join(tempDir, "templates");
1289
+ if (!existsSync(templatesDir)) throw new Error("templates/ directory not found in the repository");
1290
+ if (!existsSync(path.join(templatesDir, "AGENTS.md"))) throw new Error("templates/AGENTS.md not found in the repository");
1445
1291
  s.stop("Downloaded agent skills");
1446
1292
  return tempDir;
1447
1293
  } catch (err) {
@@ -1457,99 +1303,124 @@ function cleanupTemp(tempDir) {
1457
1303
  }
1458
1304
 
1459
1305
  //#endregion
1460
- //#region src/prompts.ts
1461
- function handleCancel(value) {
1462
- if (pD(value)) {
1463
- xe("Operation cancelled.");
1464
- process.exit(0);
1465
- }
1466
- }
1467
- async function promptExistingAction() {
1468
- const action = await ve({
1469
- message: "Existing .ruler/ directory found. What would you like to do?",
1470
- options: [{
1471
- value: "update",
1472
- label: "Update existing configuration"
1473
- }, {
1474
- value: "fresh",
1475
- label: "Fresh install (backs up and overwrites current)"
1476
- }]
1477
- });
1478
- handleCancel(action);
1479
- return action;
1306
+ //#region src/rules.ts
1307
+ function escapeRegex(str) {
1308
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1480
1309
  }
1481
- async function promptAgentSelection() {
1482
- const selected = await fe({
1483
- message: "Which AI agents do you use? (space to toggle, enter to confirm)",
1484
- options: [...RULER_AGENTS].sort((a, b) => {
1485
- const aPopular = POPULAR_AGENTS.includes(a.value);
1486
- const bPopular = POPULAR_AGENTS.includes(b.value);
1487
- if (aPopular && !bPopular) return -1;
1488
- if (!aPopular && bPopular) return 1;
1489
- return a.label.localeCompare(b.label);
1490
- }).map((a) => ({
1491
- value: a.value,
1492
- label: a.label,
1493
- hint: a.hint || void 0
1494
- })),
1495
- initialValues: ["claude"],
1496
- required: true
1497
- });
1498
- handleCancel(selected);
1499
- return selected;
1310
+ function injectRules(projectDir, agents, agentsContent) {
1311
+ const rulesFilePaths = /* @__PURE__ */ new Map();
1312
+ for (const agent of agents) {
1313
+ const rulesFile = AGENT_RULES_MAP[agent];
1314
+ if (rulesFile && !rulesFilePaths.has(rulesFile)) rulesFilePaths.set(rulesFile, agent);
1315
+ }
1316
+ const results = [];
1317
+ for (const rulesFilePath of rulesFilePaths.keys()) {
1318
+ const fullPath = path.join(projectDir, rulesFilePath);
1319
+ if (rulesFilePath.endsWith(".json")) {
1320
+ results.push({
1321
+ rulesFile: rulesFilePath,
1322
+ action: "skipped"
1323
+ });
1324
+ continue;
1325
+ }
1326
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
1327
+ const markedBlock = `\n${RULES_MARKER_START}\n${agentsContent}\n${RULES_MARKER_END}\n`;
1328
+ if (fs.existsSync(fullPath)) {
1329
+ const existing = fs.readFileSync(fullPath, "utf-8");
1330
+ const markerRegex = new RegExp(`${escapeRegex(RULES_MARKER_START)}[\\s\\S]*?${escapeRegex(RULES_MARKER_END)}`);
1331
+ if (markerRegex.test(existing)) {
1332
+ const updated = existing.replace(markerRegex, `${RULES_MARKER_START}\n${agentsContent}\n${RULES_MARKER_END}`);
1333
+ fs.writeFileSync(fullPath, updated, "utf-8");
1334
+ } else fs.writeFileSync(fullPath, existing + markedBlock, "utf-8");
1335
+ results.push({
1336
+ rulesFile: rulesFilePath,
1337
+ action: "updated"
1338
+ });
1339
+ } else {
1340
+ fs.writeFileSync(fullPath, markedBlock, "utf-8");
1341
+ results.push({
1342
+ rulesFile: rulesFilePath,
1343
+ action: "created"
1344
+ });
1345
+ }
1346
+ }
1347
+ return results;
1500
1348
  }
1501
1349
 
1502
1350
  //#endregion
1503
- //#region src/manifest.ts
1504
- const MANIFEST_FILENAME = ".library-manifest.json";
1505
- function getManifestPath(projectDir) {
1506
- return path.join(projectDir, ".ruler", "skills", MANIFEST_FILENAME);
1351
+ //#region src/skills.ts
1352
+ async function runSkillsAdd(projectDir, nonInteractive) {
1353
+ const args = nonInteractive ? [
1354
+ "skills",
1355
+ "add",
1356
+ "buiducnhat/agent-skills",
1357
+ "--skill",
1358
+ "*",
1359
+ "--all",
1360
+ "-y"
1361
+ ] : [
1362
+ "skills",
1363
+ "add",
1364
+ "buiducnhat/agent-skills",
1365
+ "--skill",
1366
+ "*"
1367
+ ];
1368
+ return new Promise((resolve) => {
1369
+ const chunks = [];
1370
+ const child = spawn("npx", args, {
1371
+ cwd: projectDir,
1372
+ stdio: [
1373
+ "inherit",
1374
+ "pipe",
1375
+ "inherit"
1376
+ ]
1377
+ });
1378
+ child.stdout.on("data", (chunk) => {
1379
+ process.stdout.write(chunk);
1380
+ chunks.push(chunk);
1381
+ });
1382
+ child.on("close", (code) => {
1383
+ const rawOutput = Buffer.concat(chunks).toString("utf-8");
1384
+ const success = code === 0;
1385
+ resolve({
1386
+ success,
1387
+ detectedAgents: success ? detectAgentsFromOutput(rawOutput) : [],
1388
+ rawOutput
1389
+ });
1390
+ });
1391
+ child.on("error", (err) => {
1392
+ resolve({
1393
+ success: false,
1394
+ detectedAgents: [],
1395
+ rawOutput: err.message
1396
+ });
1397
+ });
1398
+ });
1507
1399
  }
1508
- function readManifest(projectDir) {
1509
- const manifestPath = getManifestPath(projectDir);
1510
- if (!fs.existsSync(manifestPath)) return null;
1511
- try {
1512
- const raw = fs.readFileSync(manifestPath, "utf8");
1513
- const data = JSON.parse(raw);
1514
- if (typeof data.version !== "number" || !Array.isArray(data.skills) || !data.skills.every((s) => typeof s === "string")) return null;
1515
- return {
1516
- version: data.version,
1517
- skills: data.skills
1518
- };
1519
- } catch {
1520
- return null;
1400
+ function detectAgentsFromOutput(output) {
1401
+ const detected = /* @__PURE__ */ new Set();
1402
+ for (const [dirPrefix, agentId] of Object.entries(AGENT_SKILLS_DIRS)) {
1403
+ const dirName = dirPrefix.slice(1);
1404
+ const patterns = [
1405
+ new RegExp(`\\.${dirName}/skills/`, "i"),
1406
+ new RegExp(`Installing.*\\.${dirName}`, "i"),
1407
+ new RegExp(`Added.*\\.${dirName}`, "i"),
1408
+ new RegExp(`\\b${dirName}\\b`, "i")
1409
+ ];
1410
+ for (const pattern of patterns) if (pattern.test(output)) {
1411
+ detected.add(agentId);
1412
+ break;
1413
+ }
1521
1414
  }
1415
+ return Array.from(detected);
1522
1416
  }
1523
- function writeManifest(projectDir, skills) {
1524
- const manifestPath = getManifestPath(projectDir);
1525
- const manifestDir = path.dirname(manifestPath);
1526
- fs.mkdirSync(manifestDir, { recursive: true });
1527
- const manifest = {
1528
- version: 1,
1529
- skills: [...new Set(skills)].sort()
1530
- };
1531
- fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
1532
- }
1533
- function getTemplateSkillNames(tempDir) {
1534
- const skillsDir = path.join(tempDir, "templates", ".ruler", "skills");
1535
- if (!fs.existsSync(skillsDir)) return [];
1536
- return fs.readdirSync(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
1537
- }
1538
- function getInstalledSkillNames(projectDir) {
1539
- const skillsDir = path.join(projectDir, ".ruler", "skills");
1540
- if (!fs.existsSync(skillsDir)) return [];
1541
- return fs.readdirSync(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== MANIFEST_FILENAME).map((entry) => entry.name);
1542
- }
1543
- function computeDeprecatedSkills(oldManifest, newTemplateSkills) {
1544
- if (!oldManifest) return [];
1545
- const templateSet = new Set(newTemplateSkills);
1546
- return oldManifest.skills.filter((skill) => !templateSet.has(skill));
1547
- }
1548
- function computeCustomSkills(installedSkills, manifestSkills, templateSkills) {
1549
- const templateSet = new Set(templateSkills);
1550
- if (manifestSkills.length === 0) return installedSkills.filter((skill) => !templateSet.has(skill));
1551
- const manifestSet = new Set(manifestSkills);
1552
- return installedSkills.filter((skill) => !manifestSet.has(skill) && !templateSet.has(skill));
1417
+ function detectAgentsFromFilesystem(projectDir) {
1418
+ const detected = [];
1419
+ for (const [dirPrefix, agentId] of Object.entries(AGENT_SKILLS_DIRS)) {
1420
+ const skillsDir = path.join(projectDir, dirPrefix, "skills");
1421
+ if (fs.existsSync(skillsDir)) detected.push(agentId);
1422
+ }
1423
+ return detected;
1553
1424
  }
1554
1425
 
1555
1426
  //#endregion
@@ -1561,9 +1432,6 @@ function parseArgs(argv) {
1561
1432
  version: false
1562
1433
  };
1563
1434
  for (let i = 0; i < argv.length; i++) switch (argv[i]) {
1564
- case "--agents":
1565
- args.agents = argv[++i];
1566
- break;
1567
1435
  case "--non-interactive":
1568
1436
  args.nonInteractive = true;
1569
1437
  break;
@@ -1576,7 +1444,6 @@ function parseArgs(argv) {
1576
1444
  args.version = true;
1577
1445
  break;
1578
1446
  }
1579
- if (args.agents) args.nonInteractive = true;
1580
1447
  return args;
1581
1448
  }
1582
1449
  function copyDirectory(src, dest) {
@@ -1589,163 +1456,40 @@ function copyDirectory(src, dest) {
1589
1456
  else fs.copyFileSync(srcPath, destPath);
1590
1457
  }
1591
1458
  }
1592
- function copyDirectoryExcluding(src, dest, excludeNames) {
1593
- fs.mkdirSync(dest, { recursive: true });
1594
- const entries = fs.readdirSync(src, { withFileTypes: true });
1595
- for (const entry of entries) {
1596
- if (excludeNames.has(entry.name)) continue;
1597
- const srcPath = path.join(src, entry.name);
1598
- const destPath = path.join(dest, entry.name);
1599
- if (entry.isDirectory()) copyDirectory(srcPath, destPath);
1600
- else fs.copyFileSync(srcPath, destPath);
1601
- }
1602
- }
1603
- async function copyTemplates(tempDir, projectDir, action) {
1604
- const s = Y();
1605
- s.start("Copying templates to your project...");
1606
- const templatesDir = path.join(tempDir, "templates");
1607
- const srcRuler = path.join(templatesDir, ".ruler");
1608
- const srcClaude = path.join(templatesDir, ".claude");
1609
- const destRuler = path.join(projectDir, ".ruler");
1459
+ function copyClaudeTemplate(tempDir, projectDir) {
1460
+ const srcClaude = path.join(tempDir, "templates", ".claude");
1610
1461
  const destClaude = path.join(projectDir, ".claude");
1611
- const templateSkills = getTemplateSkillNames(tempDir);
1612
- const existingManifest = readManifest(projectDir);
1613
- const manifestSkills = existingManifest?.skills ?? [];
1614
- const installedSkills = getInstalledSkillNames(projectDir);
1615
- const deprecatedSkills = computeDeprecatedSkills(existingManifest, templateSkills);
1616
- const customSkills = computeCustomSkills(installedSkills, manifestSkills, templateSkills);
1617
- if (deprecatedSkills.length > 0) {
1618
- for (const skill of deprecatedSkills) {
1619
- const skillPath = path.join(destRuler, "skills", skill);
1620
- if (fs.existsSync(skillPath)) fs.rmSync(skillPath, {
1621
- recursive: true,
1622
- force: true
1623
- });
1624
- }
1625
- M.info(`Removing ${deprecatedSkills.length} deprecated library skill(s): ${deprecatedSkills.join(", ")}`);
1626
- }
1627
- if (action === "fresh") {
1628
- let rulerBackup;
1629
- let claudeBackup;
1630
- if (fs.existsSync(destRuler)) {
1631
- rulerBackup = `${destRuler}.backup-${Date.now()}`;
1632
- fs.renameSync(destRuler, rulerBackup);
1633
- M.info(`Backed up existing .ruler/ to ${path.basename(rulerBackup)}`);
1634
- }
1635
- if (fs.existsSync(destClaude)) {
1636
- claudeBackup = `${destClaude}.backup-${Date.now()}`;
1637
- fs.renameSync(destClaude, claudeBackup);
1638
- M.info(`Backed up existing .claude/ to ${path.basename(claudeBackup)}`);
1639
- }
1640
- if (fs.existsSync(srcRuler)) copyDirectory(srcRuler, destRuler);
1641
- if (rulerBackup && customSkills.length > 0) {
1642
- for (const skill of customSkills) {
1643
- const srcSkill = path.join(rulerBackup, "skills", skill);
1644
- const destSkill = path.join(destRuler, "skills", skill);
1645
- if (fs.existsSync(srcSkill)) copyDirectory(srcSkill, destSkill);
1646
- }
1647
- M.info(`Preserving ${customSkills.length} custom skill(s): ${customSkills.join(", ")}`);
1648
- }
1649
- if (fs.existsSync(srcClaude)) copyDirectory(srcClaude, destClaude);
1650
- } else {
1651
- if (!fs.existsSync(destRuler)) {
1652
- if (fs.existsSync(srcRuler)) copyDirectory(srcRuler, destRuler);
1653
- if (fs.existsSync(srcClaude)) copyDirectory(srcClaude, destClaude);
1654
- if (templateSkills.length > 0) writeManifest(projectDir, templateSkills);
1655
- makeScriptsExecutable(path.join(destRuler, "scripts"));
1656
- s.stop("Copied templates to project");
1657
- return;
1658
- }
1659
- if (fs.existsSync(srcRuler)) {
1660
- copyDirectoryExcluding(srcRuler, destRuler, new Set(["skills"]));
1661
- const srcSkillsDir = path.join(srcRuler, "skills");
1662
- const destSkillsDir = path.join(destRuler, "skills");
1663
- if (fs.existsSync(srcSkillsDir)) {
1664
- fs.mkdirSync(destSkillsDir, { recursive: true });
1665
- for (const skill of templateSkills) {
1666
- const srcSkill = path.join(srcSkillsDir, skill);
1667
- const destSkill = path.join(destSkillsDir, skill);
1668
- if (fs.existsSync(srcSkill)) copyDirectory(srcSkill, destSkill);
1669
- }
1670
- }
1671
- }
1672
- if (customSkills.length > 0) M.info(`Preserving ${customSkills.length} custom skill(s): ${customSkills.join(", ")}`);
1673
- if (fs.existsSync(srcClaude)) copyDirectory(srcClaude, destClaude);
1674
- }
1675
- if (templateSkills.length > 0) writeManifest(projectDir, templateSkills);
1676
- makeScriptsExecutable(path.join(destRuler, "scripts"));
1677
- s.stop("Copied templates to project");
1678
- }
1679
- function ensureRulerScripts(tempDir, projectDir) {
1680
- const srcScripts = path.join(tempDir, "templates", ".ruler", "scripts");
1681
- const destScripts = path.join(projectDir, ".ruler", "scripts");
1682
- if (!fs.existsSync(srcScripts)) return;
1683
- copyDirectory(srcScripts, destScripts);
1684
- makeScriptsExecutable(destScripts);
1685
- }
1686
- function makeScriptsExecutable(scriptsDir) {
1687
- if (!fs.existsSync(scriptsDir)) return;
1688
- for (const script of fs.readdirSync(scriptsDir).filter((fileName) => fileName.endsWith(".sh"))) fs.chmodSync(path.join(scriptsDir, script), 493);
1462
+ if (fs.existsSync(srcClaude)) copyDirectory(srcClaude, destClaude);
1689
1463
  }
1690
1464
  function printHelp() {
1691
1465
  console.log(`
1692
- @buiducnhat/agent-skills - Install AI agent skills for coding assistants
1466
+ @buiducnhat/agent-skills - Install AI agent workflow skills for coding assistants
1693
1467
 
1694
1468
  Usage: npx @buiducnhat/agent-skills [options]
1695
1469
 
1696
1470
  Options:
1697
- --agents <list> Comma-separated agent IDs (e.g., claude,cursor,copilot)
1698
- --non-interactive Skip all prompts, use defaults
1471
+ --non-interactive Skip interactive prompts (installs all skills to all agents)
1699
1472
  -h, --help Show this help message
1700
1473
  -v, --version Show version
1701
1474
 
1702
1475
  Examples:
1703
1476
  npx @buiducnhat/agent-skills
1704
- npx @buiducnhat/agent-skills --agents claude,cursor
1705
- npx @buiducnhat/agent-skills --agents claude --non-interactive
1706
-
1707
- Supported agents:
1708
- claude, copilot, cursor, windsurf, codex, gemini-cli, amp, cline, roo,
1709
- aider, antigravity, pi, jules, kiro, kilocode, crush, amazonqcli,
1710
- firebase, openhands, junie, jetbrains-ai, augmentcode, opencode,
1711
- goose, qwen, zed, trae, warp, firebender, factory, mistral
1477
+ npx @buiducnhat/agent-skills --non-interactive
1712
1478
  `);
1713
1479
  }
1714
- function printSummary(agents, projectDir) {
1480
+ function printSummary(agents, results) {
1715
1481
  M.success("Installation complete!");
1716
1482
  M.message("");
1717
1483
  M.message("What was set up:");
1718
- M.message(` .claude/ - Claude vibe coding settings`);
1719
- M.message(` .ruler/AGENTS.md - Agent instructions`);
1720
- M.message(` .ruler/ruler.toml - Ruler config (agents: ${agents.join(", ")})`);
1721
- const counts = countSkillsByType(projectDir);
1722
- if (counts.custom > 0) M.message(` .ruler/skills/ - ${counts.library} library + ${counts.custom} custom skill(s)`);
1723
- else M.message(` .ruler/skills/ - ${counts.library} workflow skills`);
1484
+ M.message(" .claude/ - Claude Code settings");
1485
+ for (const result of results) if (result.action !== "skipped") M.message(` ${result.rulesFile} - ${result.action}`);
1724
1486
  M.message("");
1725
- M.message("Agent configurations generated for:");
1487
+ M.message("Agent configurations updated for:");
1726
1488
  for (const agent of agents) M.message(` - ${agent}`);
1727
1489
  M.message("");
1728
1490
  M.message("Next steps:");
1729
- M.message(" 1. Review .ruler/AGENTS.md for agent instructions");
1730
- M.message(" 2. Customize .ruler/ruler.toml if needed");
1731
- M.message(" 3. Commit the generated files to your repository");
1732
- }
1733
- function countSkillsByType(projectDir) {
1734
- const skillsDir = path.join(projectDir, ".ruler", "skills");
1735
- if (!fs.existsSync(skillsDir)) return {
1736
- library: 0,
1737
- custom: 0,
1738
- total: 0
1739
- };
1740
- const manifest = readManifest(projectDir);
1741
- const manifestSkills = new Set(manifest?.skills ?? []);
1742
- const allSkills = fs.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1743
- const library = allSkills.filter((s) => manifestSkills.has(s)).length;
1744
- return {
1745
- library,
1746
- custom: allSkills.length - library,
1747
- total: allSkills.length
1748
- };
1491
+ M.message(" 1. Review the updated agent rules files");
1492
+ M.message(" 2. Commit the generated files to your repository");
1749
1493
  }
1750
1494
 
1751
1495
  //#endregion
@@ -1770,33 +1514,30 @@ async function main() {
1770
1514
  }
1771
1515
  Ie(import_picocolors.default.bold(import_picocolors.default.cyan(" Agent Skills Installer ")));
1772
1516
  const cwd = process.cwd();
1773
- const rulerDir = path.join(cwd, ".ruler");
1774
- const existingInstall = fs.existsSync(rulerDir);
1775
- let action = "fresh";
1776
- if (existingInstall) if (args.nonInteractive) {
1777
- action = "update";
1778
- M.info("Existing .ruler/ found, updating...");
1779
- } else action = await promptExistingAction();
1780
- let selectedAgents;
1781
- if (args.agents) {
1782
- selectedAgents = args.agents.split(",").map((a) => a.trim());
1783
- M.info(`Using agents: ${selectedAgents.join(", ")}`);
1784
- } else if (args.nonInteractive) {
1785
- selectedAgents = ["claude"];
1786
- M.info("Using default agent: claude");
1787
- } else selectedAgents = await promptAgentSelection();
1788
- if (selectedAgents.length === 0) {
1789
- xe("No agents selected.");
1517
+ M.step("Installing skills via skills CLI...");
1518
+ const skillsResult = await runSkillsAdd(cwd, args.nonInteractive);
1519
+ if (!skillsResult.success) {
1520
+ xe(import_picocolors.default.red("Skills CLI failed. See errors above.\nYou can try running manually: npx skills add buiducnhat/agent-skills --skill *"));
1790
1521
  process.exit(1);
1791
1522
  }
1523
+ let agents = detectAgentsFromOutput(skillsResult.rawOutput);
1524
+ if (agents.length === 0) {
1525
+ M.warn("Could not detect agents from skills CLI output. Scanning filesystem...");
1526
+ agents = detectAgentsFromFilesystem(cwd);
1527
+ }
1528
+ if (agents.length === 0) {
1529
+ M.warn("No agents detected. Skills may have been installed but rules injection was skipped.");
1530
+ Se(import_picocolors.default.yellow("Done. No agent rules files were updated."));
1531
+ process.exit(0);
1532
+ }
1533
+ M.info(`Detected agents: ${agents.join(", ")}`);
1792
1534
  let tempDir;
1793
1535
  try {
1794
1536
  tempDir = await fetchTemplates();
1795
- await copyTemplates(tempDir, cwd, action);
1796
- configureRulerToml(cwd, selectedAgents);
1797
- await runRulerApply(cwd, selectedAgents);
1798
- ensureRulerScripts(tempDir, cwd);
1799
- printSummary(selectedAgents, cwd);
1537
+ const agentsContent = fs.readFileSync(path.join(tempDir, "templates", "AGENTS.md"), "utf-8");
1538
+ const results = injectRules(cwd, agents, agentsContent);
1539
+ copyClaudeTemplate(tempDir, cwd);
1540
+ printSummary(agents, results);
1800
1541
  Se(import_picocolors.default.green("Done! Your AI agent skills are ready."));
1801
1542
  } catch (err) {
1802
1543
  const message = err instanceof Error ? err.message : String(err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@buiducnhat/agent-skills",
3
- "version": "0.2.0",
4
- "description": "Install AI agent skills and ruler configuration for coding assistants",
3
+ "version": "0.3.0",
4
+ "description": "Install AI agent workflow skills for coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "agent-skills": "./dist/index.js"
@@ -41,7 +41,7 @@
41
41
  "ai",
42
42
  "agent",
43
43
  "skills",
44
- "ruler",
44
+ "workflow",
45
45
  "claude",
46
46
  "copilot",
47
47
  "cursor"