agentinit 1.19.0 → 1.20.1

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 (48) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +46 -4
  3. package/dist/cli.js +1927 -509
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/lock.d.ts +3 -0
  6. package/dist/commands/lock.d.ts.map +1 -0
  7. package/dist/commands/lock.js +160 -0
  8. package/dist/commands/lock.js.map +1 -0
  9. package/dist/commands/mcp.d.ts.map +1 -1
  10. package/dist/commands/mcp.js +60 -0
  11. package/dist/commands/mcp.js.map +1 -1
  12. package/dist/commands/rules.d.ts.map +1 -1
  13. package/dist/commands/rules.js +50 -0
  14. package/dist/commands/rules.js.map +1 -1
  15. package/dist/commands/skills.d.ts.map +1 -1
  16. package/dist/commands/skills.js +299 -20
  17. package/dist/commands/skills.js.map +1 -1
  18. package/dist/core/installLock.d.ts +54 -0
  19. package/dist/core/installLock.d.ts.map +1 -0
  20. package/dist/core/installLock.js +274 -0
  21. package/dist/core/installLock.js.map +1 -0
  22. package/dist/core/skillSecurityScanner.d.ts +28 -0
  23. package/dist/core/skillSecurityScanner.d.ts.map +1 -0
  24. package/dist/core/skillSecurityScanner.js +167 -0
  25. package/dist/core/skillSecurityScanner.js.map +1 -0
  26. package/dist/core/skillsManager.d.ts +12 -0
  27. package/dist/core/skillsManager.d.ts.map +1 -1
  28. package/dist/core/skillsManager.js +373 -14
  29. package/dist/core/skillsManager.js.map +1 -1
  30. package/dist/types/index.d.ts +1 -0
  31. package/dist/types/index.d.ts.map +1 -1
  32. package/dist/types/lockfile.d.ts +68 -0
  33. package/dist/types/lockfile.d.ts.map +1 -0
  34. package/dist/types/lockfile.js +2 -0
  35. package/dist/types/lockfile.js.map +1 -0
  36. package/dist/types/plugins.d.ts +1 -1
  37. package/dist/types/plugins.d.ts.map +1 -1
  38. package/dist/types/skills.d.ts +4 -1
  39. package/dist/types/skills.d.ts.map +1 -1
  40. package/dist/utils/lockSource.d.ts +9 -0
  41. package/dist/utils/lockSource.d.ts.map +1 -0
  42. package/dist/utils/lockSource.js +59 -0
  43. package/dist/utils/lockSource.js.map +1 -0
  44. package/dist/utils/promptUtils.d.ts +13 -2
  45. package/dist/utils/promptUtils.d.ts.map +1 -1
  46. package/dist/utils/promptUtils.js +61 -3
  47. package/dist/utils/promptUtils.js.map +1 -1
  48. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8347,6 +8347,159 @@ var require_prompts3 = __commonJS((exports, module) => {
8347
8347
  module.exports = isNodeLT("8.6.0") ? require_dist() : require_lib();
8348
8348
  });
8349
8349
 
8350
+ // node_modules/kleur/colors.mjs
8351
+ var init, FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM, isTTY, $, reset, bold, dim, italic, underline, inverse, hidden, strikethrough, black, red, green, yellow, blue, magenta, cyan, white, gray, grey, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite;
8352
+ var init_colors = __esm(() => {
8353
+ init = function(x, y) {
8354
+ let rgx = new RegExp(`\\x1b\\[${y}m`, "g");
8355
+ let open = `\x1B[${x}m`, close = `\x1B[${y}m`;
8356
+ return function(txt) {
8357
+ if (!$.enabled || txt == null)
8358
+ return txt;
8359
+ return open + (~("" + txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + close;
8360
+ };
8361
+ };
8362
+ isTTY = true;
8363
+ if (typeof process !== "undefined") {
8364
+ ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
8365
+ isTTY = process.stdout && process.stdout.isTTY;
8366
+ }
8367
+ $ = {
8368
+ enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY)
8369
+ };
8370
+ reset = init(0, 0);
8371
+ bold = init(1, 22);
8372
+ dim = init(2, 22);
8373
+ italic = init(3, 23);
8374
+ underline = init(4, 24);
8375
+ inverse = init(7, 27);
8376
+ hidden = init(8, 28);
8377
+ strikethrough = init(9, 29);
8378
+ black = init(30, 39);
8379
+ red = init(31, 39);
8380
+ green = init(32, 39);
8381
+ yellow = init(33, 39);
8382
+ blue = init(34, 39);
8383
+ magenta = init(35, 39);
8384
+ cyan = init(36, 39);
8385
+ white = init(37, 39);
8386
+ gray = init(90, 39);
8387
+ grey = init(90, 39);
8388
+ bgBlack = init(40, 49);
8389
+ bgRed = init(41, 49);
8390
+ bgGreen = init(42, 49);
8391
+ bgYellow = init(43, 49);
8392
+ bgBlue = init(44, 49);
8393
+ bgMagenta = init(45, 49);
8394
+ bgCyan = init(46, 49);
8395
+ bgWhite = init(47, 49);
8396
+ });
8397
+
8398
+ // dist/utils/symbols.js
8399
+ var STATUS, TREE, BOX;
8400
+ var init_symbols = __esm(() => {
8401
+ STATUS = {
8402
+ success: "\u2713",
8403
+ error: "\u2717",
8404
+ warning: "\u26A0",
8405
+ info: "\u2139",
8406
+ debug: "\u2022"
8407
+ };
8408
+ TREE = {
8409
+ branch: "\u251C\u2500",
8410
+ last: "\u2514\u2500"
8411
+ };
8412
+ BOX = {
8413
+ topLeft: "\u250C",
8414
+ topRight: "\u2510",
8415
+ bottomLeft: "\u2514",
8416
+ bottomRight: "\u2518",
8417
+ horizontal: "\u2500",
8418
+ vertical: "\u2502"
8419
+ };
8420
+ });
8421
+
8422
+ // dist/utils/logger.js
8423
+ class Logger {
8424
+ static instance;
8425
+ static getInstance() {
8426
+ if (!Logger.instance) {
8427
+ Logger.instance = new Logger;
8428
+ }
8429
+ return Logger.instance;
8430
+ }
8431
+ info(message) {
8432
+ console.log(cyan(STATUS.info), message);
8433
+ }
8434
+ success(message) {
8435
+ console.log(green(STATUS.success), message);
8436
+ }
8437
+ warning(message) {
8438
+ console.log(yellow(STATUS.warning), message);
8439
+ }
8440
+ warn(message) {
8441
+ this.warning(message);
8442
+ }
8443
+ error(message) {
8444
+ console.log(red(STATUS.error), message);
8445
+ }
8446
+ debug(message) {
8447
+ console.log(dim(STATUS.debug), dim(message));
8448
+ }
8449
+ title(message) {
8450
+ console.log(bold(cyan(message)));
8451
+ }
8452
+ subtitle(message) {
8453
+ console.log(bold(message));
8454
+ }
8455
+ titleBox(message) {
8456
+ const w = getTerminalWidth();
8457
+ if (w < 8) {
8458
+ console.log(bold(cyan(truncateText(message, w))));
8459
+ return;
8460
+ }
8461
+ const maxInner = Math.max(4, w - 2);
8462
+ const inner = Math.min(Math.max(message.length + 4, 40), maxInner);
8463
+ const visibleMessage = truncateText(message, inner - 2);
8464
+ const padR = Math.max(0, inner - visibleMessage.length - 2);
8465
+ console.log(dim(BOX.topLeft + BOX.horizontal.repeat(inner) + BOX.topRight));
8466
+ console.log(dim(BOX.vertical) + " " + bold(cyan(visibleMessage)) + " ".repeat(padR) + " " + dim(BOX.vertical));
8467
+ console.log(dim(BOX.bottomLeft + BOX.horizontal.repeat(inner) + BOX.bottomRight));
8468
+ }
8469
+ section(title) {
8470
+ const w = getTerminalWidth();
8471
+ const lineLen = Math.max(0, w - title.length - 5);
8472
+ console.log("");
8473
+ console.log(bold(`${BOX.horizontal}${BOX.horizontal} ${title} ${BOX.horizontal.repeat(lineLen)}`));
8474
+ }
8475
+ tree(message, isLast) {
8476
+ const connector = isLast ? TREE.last : TREE.branch;
8477
+ console.log(`${connector} ${message}`);
8478
+ }
8479
+ }
8480
+ var getTerminalWidth, truncateText, logger;
8481
+ var init_logger = __esm(() => {
8482
+ init_colors();
8483
+ init_symbols();
8484
+ getTerminalWidth = function() {
8485
+ const columns = process.stdout.columns;
8486
+ return typeof columns === "number" && columns > 0 ? columns : 80;
8487
+ };
8488
+ truncateText = function(text, maxLength) {
8489
+ if (maxLength <= 0) {
8490
+ return "";
8491
+ }
8492
+ if (text.length <= maxLength) {
8493
+ return text;
8494
+ }
8495
+ if (maxLength <= 3) {
8496
+ return ".".repeat(maxLength);
8497
+ }
8498
+ return `${text.slice(0, maxLength - 3)}...`;
8499
+ };
8500
+ logger = Logger.getInstance();
8501
+ });
8502
+
8350
8503
  // dist/utils/fs.js
8351
8504
  import {promises as fs} from "fs";
8352
8505
  import {platform} from "os";
@@ -16945,6 +17098,174 @@ var init_marketplaceRegistry = __esm(() => {
16945
17098
  CUSTOM_MARKETPLACE_CACHE_TTL_MS = 3600000;
16946
17099
  });
16947
17100
 
17101
+ // dist/core/skillSecurityScanner.js
17102
+ import {promises as fs22} from "fs";
17103
+ import {join as join4, relative as relative2} from "path";
17104
+
17105
+ class SkillSecurityScanner {
17106
+ async scanSkill(skill) {
17107
+ const findings = skill.generatedContent ? this.scanText(skill.generatedContent, "SKILL.md") : await this.scanDirectory(skill.path);
17108
+ const stats = findings.reduce((acc, finding) => {
17109
+ acc[finding.severity] += 1;
17110
+ return acc;
17111
+ }, { high: 0, medium: 0, low: 0 });
17112
+ return {
17113
+ blocked: findings.some((finding) => finding.blocking),
17114
+ findings,
17115
+ stats
17116
+ };
17117
+ }
17118
+ formatShortSummary(result) {
17119
+ const parts = [
17120
+ result.stats.high ? `${result.stats.high} high` : null,
17121
+ result.stats.medium ? `${result.stats.medium} medium` : null,
17122
+ result.stats.low ? `${result.stats.low} low` : null
17123
+ ].filter((value) => !!value);
17124
+ return parts.length > 0 ? parts.join(", ") : "no findings";
17125
+ }
17126
+ formatBlockingReason(result) {
17127
+ const finding = result.findings.find((entry) => entry.blocking) || result.findings.find((entry) => entry.severity === "high") || result.findings[0];
17128
+ if (!finding) {
17129
+ return "Security scan failed";
17130
+ }
17131
+ return `Security scan failed: ${finding.title} (${finding.ruleId}) at ${finding.filePath}:${finding.line}`;
17132
+ }
17133
+ async scanDirectory(rootPath) {
17134
+ const findings = [];
17135
+ await this.walk(rootPath, rootPath, findings);
17136
+ return findings;
17137
+ }
17138
+ async walk(rootPath, currentPath, findings) {
17139
+ const entries = await fs22.readdir(currentPath, { withFileTypes: true });
17140
+ for (const entry of entries) {
17141
+ if (entry.name === ".git" || entry.name === "node_modules") {
17142
+ continue;
17143
+ }
17144
+ const entryPath = join4(currentPath, entry.name);
17145
+ if (entry.isDirectory()) {
17146
+ await this.walk(rootPath, entryPath, findings);
17147
+ continue;
17148
+ }
17149
+ const relativePath = relative2(rootPath, entryPath).replace(/\\/g, "/");
17150
+ const content = await this.readTextFile(entryPath);
17151
+ if (content === null) {
17152
+ continue;
17153
+ }
17154
+ findings.push(...this.scanText(content, relativePath, {
17155
+ blockHighRisk: this.isExecutableTextFile(entryPath, content)
17156
+ }));
17157
+ }
17158
+ }
17159
+ async readTextFile(filePath) {
17160
+ const extension = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")).toLowerCase() : "";
17161
+ if (!SCANNED_TEXT_EXTENSIONS.has(extension)) {
17162
+ return null;
17163
+ }
17164
+ const buffer = await fs22.readFile(filePath);
17165
+ if (buffer.length > MAX_TEXT_FILE_BYTES || buffer.includes(0)) {
17166
+ return null;
17167
+ }
17168
+ return buffer.toString("utf8");
17169
+ }
17170
+ isExecutableTextFile(filePath, content) {
17171
+ const extension = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")).toLowerCase() : "";
17172
+ return EXECUTABLE_TEXT_EXTENSIONS.has(extension) || content.startsWith("#!");
17173
+ }
17174
+ scanText(content, filePath, options2 = {}) {
17175
+ const findings = [];
17176
+ const lines = content.split(/\r?\n/);
17177
+ lines.forEach((line, index) => {
17178
+ for (const rule of SCAN_RULES) {
17179
+ rule.regex.lastIndex = 0;
17180
+ if (!rule.regex.test(line)) {
17181
+ continue;
17182
+ }
17183
+ findings.push({
17184
+ ruleId: rule.id,
17185
+ title: rule.title,
17186
+ severity: rule.severity,
17187
+ filePath,
17188
+ line: index + 1,
17189
+ snippet: line.trim().slice(0, 160),
17190
+ blocking: rule.severity === "high" && options2.blockHighRisk === true
17191
+ });
17192
+ }
17193
+ });
17194
+ return findings;
17195
+ }
17196
+ }
17197
+ var SCAN_RULES, MAX_TEXT_FILE_BYTES, SCANNED_TEXT_EXTENSIONS, EXECUTABLE_TEXT_EXTENSIONS;
17198
+ var init_skillSecurityScanner = __esm(() => {
17199
+ SCAN_RULES = [
17200
+ {
17201
+ id: "AI001",
17202
+ title: "Prompt override language",
17203
+ severity: "medium",
17204
+ regex: /\b(ignore|disregard|override|bypass)\b.{0,40}\b(previous|prior|system|safety|guardrails?|instructions?)\b/i
17205
+ },
17206
+ {
17207
+ id: "AI002",
17208
+ title: "Secret or credential exfiltration",
17209
+ severity: "high",
17210
+ regex: /\b(exfiltrat\w*|upload|send|post|curl|wget)\b.{0,80}\b(secret|token|key|credential|cookie|session|\.ssh|id_rsa|env(?:ironment)? variables?)\b/i
17211
+ },
17212
+ {
17213
+ id: "AI003",
17214
+ title: "Destructive shell command",
17215
+ severity: "high",
17216
+ regex: /\b(rm\s+-rf\s+\/|sudo\s+rm\s+-rf|mkfs\b|dd\s+if=\/dev\/zero|chmod\s+-R\s+777\s+\/)\b/i
17217
+ },
17218
+ {
17219
+ id: "AI004",
17220
+ title: "Remote shell execution pipeline",
17221
+ severity: "high",
17222
+ regex: /\b(curl|wget)\b[^\n|]{0,160}\|\s*(sh|bash|zsh)\b/i
17223
+ },
17224
+ {
17225
+ id: "AI005",
17226
+ title: "Hardcoded credential material",
17227
+ severity: "low",
17228
+ regex: /\b(api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{8,}['"]/i
17229
+ },
17230
+ {
17231
+ id: "AI006",
17232
+ title: "Unicode bidi control characters",
17233
+ severity: "low",
17234
+ regex: /[\u202A-\u202E\u2066-\u2069]/
17235
+ }
17236
+ ];
17237
+ MAX_TEXT_FILE_BYTES = 1024 * 1024;
17238
+ SCANNED_TEXT_EXTENSIONS = new Set([
17239
+ "",
17240
+ ".md",
17241
+ ".txt",
17242
+ ".json",
17243
+ ".yaml",
17244
+ ".yml",
17245
+ ".toml",
17246
+ ".js",
17247
+ ".ts",
17248
+ ".mjs",
17249
+ ".cjs",
17250
+ ".sh",
17251
+ ".bash",
17252
+ ".zsh",
17253
+ ".py",
17254
+ ".ps1"
17255
+ ]);
17256
+ EXECUTABLE_TEXT_EXTENSIONS = new Set([
17257
+ ".js",
17258
+ ".ts",
17259
+ ".mjs",
17260
+ ".cjs",
17261
+ ".sh",
17262
+ ".bash",
17263
+ ".zsh",
17264
+ ".py",
17265
+ ".ps1"
17266
+ ]);
17267
+ });
17268
+
16948
17269
  // dist/types/skills.js
16949
17270
  var SHARED_SKILLS_TARGET_ID, SHARED_SKILLS_TARGET_NAME;
16950
17271
  var init_skills = __esm(() => {
@@ -16952,6 +17273,280 @@ var init_skills = __esm(() => {
16952
17273
  SHARED_SKILLS_TARGET_NAME = "AGENTS.md ecosystem";
16953
17274
  });
16954
17275
 
17276
+ // dist/core/installLock.js
17277
+ import {promises as fs23} from "fs";
17278
+ import {homedir as homedir4} from "os";
17279
+ import {dirname as dirname2, join as join5, resolve as resolve7} from "path";
17280
+ import {createHash, randomUUID} from "crypto";
17281
+ function getLockEntryTargetLabel(entry) {
17282
+ return entry.scope === "global" ? "Global scope" : entry.projectPath;
17283
+ }
17284
+ function logLockWriteWarning(context, error) {
17285
+ const detail = error instanceof Error ? error.message : "unknown error";
17286
+ logger.warn(`${context}, but failed to update the install lock: ${detail}`);
17287
+ }
17288
+ async function hashDirectory(rootPath) {
17289
+ if (!await fileExists(rootPath))
17290
+ return null;
17291
+ const hash = createHash("sha256");
17292
+ const walk = async (currentPath, relativePath) => {
17293
+ const stat = await fs23.stat(currentPath);
17294
+ if (stat.isDirectory()) {
17295
+ hash.update(`dir:${relativePath}\n`);
17296
+ const entries = await fs23.readdir(currentPath);
17297
+ entries.sort();
17298
+ for (const entry of entries) {
17299
+ await walk(join5(currentPath, entry), `${relativePath}/${entry}`);
17300
+ }
17301
+ } else {
17302
+ const content = await fs23.readFile(currentPath);
17303
+ hash.update(`file:${relativePath}\n`);
17304
+ hash.update(content);
17305
+ hash.update("\n");
17306
+ }
17307
+ };
17308
+ await walk(rootPath, ".");
17309
+ return hash.digest("hex");
17310
+ }
17311
+
17312
+ class InstallLock {
17313
+ state = null;
17314
+ async load() {
17315
+ if (this.state)
17316
+ return this.state;
17317
+ const content = await readFileIfExists(getLockPath());
17318
+ if (!content) {
17319
+ this.state = { version: 1, entries: [] };
17320
+ return this.state;
17321
+ }
17322
+ try {
17323
+ const parsed = JSON.parse(content);
17324
+ if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
17325
+ this.state = parsed;
17326
+ } else {
17327
+ this.state = { version: 1, entries: [] };
17328
+ }
17329
+ } catch {
17330
+ this.state = { version: 1, entries: [] };
17331
+ }
17332
+ return this.state;
17333
+ }
17334
+ async save() {
17335
+ if (!this.state)
17336
+ return;
17337
+ const lockPath = getLockPath();
17338
+ await fs23.mkdir(dirname2(lockPath), { recursive: true });
17339
+ await fs23.writeFile(lockPath, JSON.stringify(this.state, null, 2) + "\n", {
17340
+ encoding: "utf8",
17341
+ mode: 384
17342
+ });
17343
+ await fs23.chmod(lockPath, 384).catch(() => {
17344
+ });
17345
+ }
17346
+ async recordSkill(params) {
17347
+ const state = await this.load();
17348
+ const metadata = {
17349
+ kind: "skill",
17350
+ installPath: params.installPath,
17351
+ ...params.canonicalPath ? { canonicalPath: params.canonicalPath } : {},
17352
+ mode: params.mode
17353
+ };
17354
+ const entry = createEntry({
17355
+ kind: "skill",
17356
+ action: params.action,
17357
+ name: params.name,
17358
+ projectPath: resolve7(params.projectPath),
17359
+ agents: params.agents,
17360
+ scope: params.scope,
17361
+ source: params.source,
17362
+ ...params.contentHash ? { contentHash: params.contentHash } : {},
17363
+ metadata
17364
+ });
17365
+ state.entries.push(entry);
17366
+ await this.save();
17367
+ return entry;
17368
+ }
17369
+ async recordMcp(params) {
17370
+ const state = await this.load();
17371
+ const metadata = {
17372
+ kind: "mcp",
17373
+ configPath: params.configPath,
17374
+ serverType: params.serverType,
17375
+ ...params.command ? { command: params.command } : {},
17376
+ ...params.url ? { url: sanitizeUrlForLock(params.url) } : {}
17377
+ };
17378
+ const entry = createEntry({
17379
+ kind: "mcp",
17380
+ action: params.action,
17381
+ name: params.name,
17382
+ projectPath: resolve7(params.projectPath),
17383
+ agents: params.agents,
17384
+ scope: params.scope,
17385
+ source: params.source,
17386
+ metadata
17387
+ });
17388
+ state.entries.push(entry);
17389
+ await this.save();
17390
+ return entry;
17391
+ }
17392
+ async recordRules(params) {
17393
+ const state = await this.load();
17394
+ const metadata = {
17395
+ kind: "rules",
17396
+ configPath: params.configPath,
17397
+ templateIds: params.templateIds,
17398
+ ruleCount: params.ruleCount
17399
+ };
17400
+ const entry = createEntry({
17401
+ kind: "rules",
17402
+ action: params.action,
17403
+ name: params.name,
17404
+ projectPath: resolve7(params.projectPath),
17405
+ agents: params.agents,
17406
+ scope: params.scope,
17407
+ source: params.source,
17408
+ metadata
17409
+ });
17410
+ state.entries.push(entry);
17411
+ await this.save();
17412
+ return entry;
17413
+ }
17414
+ async query(options2 = {}) {
17415
+ const state = await this.load();
17416
+ return state.entries.filter((entry) => {
17417
+ if (options2.kind && entry.kind !== options2.kind)
17418
+ return false;
17419
+ if (options2.name && entry.name.toLowerCase() !== options2.name.toLowerCase())
17420
+ return false;
17421
+ if (options2.projectPath && entry.projectPath !== resolve7(options2.projectPath))
17422
+ return false;
17423
+ if (options2.agent && !entry.agents.includes(options2.agent))
17424
+ return false;
17425
+ if (options2.scope && entry.scope !== options2.scope)
17426
+ return false;
17427
+ if (options2.action && entry.action !== options2.action)
17428
+ return false;
17429
+ return true;
17430
+ });
17431
+ }
17432
+ async getCurrentState(options2 = {}) {
17433
+ const entries = await this.query(options2);
17434
+ const groups = new Map;
17435
+ for (const entry of entries) {
17436
+ const key = this.getCurrentStateKey(entry);
17437
+ groups.set(key, entry);
17438
+ }
17439
+ return [...groups.values()].filter((e) => e.action !== "remove");
17440
+ }
17441
+ getCurrentStateKey(entry) {
17442
+ const agents = [...entry.agents].sort().join(",");
17443
+ return [
17444
+ entry.kind,
17445
+ entry.name.toLowerCase(),
17446
+ getEntryTargetKey(entry),
17447
+ entry.scope,
17448
+ agents,
17449
+ this.getLocationKey(entry)
17450
+ ].join(":");
17451
+ }
17452
+ getLocationKey(entry) {
17453
+ switch (entry.metadata.kind) {
17454
+ case "skill":
17455
+ return entry.metadata.canonicalPath || entry.metadata.installPath;
17456
+ case "mcp":
17457
+ return entry.metadata.configPath;
17458
+ case "rules":
17459
+ return `${entry.metadata.configPath}:${entry.metadata.templateIds.join(",")}`;
17460
+ }
17461
+ }
17462
+ async getProjectPaths() {
17463
+ const state = await this.load();
17464
+ return [...new Set(state.entries.filter((entry) => entry.scope === "project").map((entry) => entry.projectPath))];
17465
+ }
17466
+ async findProjectsWithSkill(skillName) {
17467
+ return this.getCurrentState({ kind: "skill", name: skillName });
17468
+ }
17469
+ async findStaleProjects() {
17470
+ const projectPaths = await this.getProjectPaths();
17471
+ const stale = [];
17472
+ for (const projectPath of projectPaths) {
17473
+ if (!await fileExists(projectPath)) {
17474
+ stale.push(projectPath);
17475
+ }
17476
+ }
17477
+ return stale;
17478
+ }
17479
+ async pruneStaleEntries() {
17480
+ const stale = await this.findStaleProjects();
17481
+ let entriesRemoved = 0;
17482
+ if (stale.length === 0) {
17483
+ return { prunedProjects: [], entriesRemoved: 0 };
17484
+ }
17485
+ const state = await this.load();
17486
+ const staleSet = new Set(stale);
17487
+ const before = state.entries.length;
17488
+ state.entries = state.entries.filter((e) => !staleSet.has(e.projectPath));
17489
+ entriesRemoved = before - state.entries.length;
17490
+ if (entriesRemoved > 0) {
17491
+ await this.save();
17492
+ }
17493
+ return { prunedProjects: stale, entriesRemoved };
17494
+ }
17495
+ async checkDrift(entry) {
17496
+ if (!entry.contentHash) {
17497
+ return { entry, status: "match" };
17498
+ }
17499
+ if (entry.metadata.kind !== "skill") {
17500
+ return { entry, status: "match" };
17501
+ }
17502
+ const installPath = entry.metadata.installPath;
17503
+ if (!await fileExists(installPath)) {
17504
+ return { entry, status: "missing" };
17505
+ }
17506
+ const currentHash = await hashDirectory(installPath);
17507
+ if (!currentHash) {
17508
+ return { entry, status: "missing" };
17509
+ }
17510
+ return {
17511
+ entry,
17512
+ status: currentHash === entry.contentHash ? "match" : "drift",
17513
+ currentHash
17514
+ };
17515
+ }
17516
+ }
17517
+ var getLockPath, getEntryTargetKey, createEntry, sanitizeUrlForLock, LOCK_FILE, GLOBAL_TARGET_KEY;
17518
+ var init_installLock = __esm(() => {
17519
+ init_fs();
17520
+ init_logger();
17521
+ getLockPath = function() {
17522
+ return join5(homedir4(), ".agentinit", LOCK_FILE);
17523
+ };
17524
+ getEntryTargetKey = function(entry) {
17525
+ return entry.scope === "global" ? GLOBAL_TARGET_KEY : entry.projectPath;
17526
+ };
17527
+ createEntry = function(base) {
17528
+ return {
17529
+ id: randomUUID(),
17530
+ timestamp: new Date().toISOString(),
17531
+ ...base
17532
+ };
17533
+ };
17534
+ sanitizeUrlForLock = function(url) {
17535
+ try {
17536
+ const parsed = new URL(url);
17537
+ parsed.username = "";
17538
+ parsed.password = "";
17539
+ parsed.search = "";
17540
+ parsed.hash = "";
17541
+ return parsed.toString();
17542
+ } catch {
17543
+ return url.split(/[?#]/)[0] || url;
17544
+ }
17545
+ };
17546
+ LOCK_FILE = "lock.json";
17547
+ GLOBAL_TARGET_KEY = "__agentinit_global__";
17548
+ });
17549
+
16955
17550
  // dist/core/mcpFilter.js
16956
17551
  class MCPFilter {
16957
17552
  static filterForAgent(agent, servers) {
@@ -17066,9 +17661,9 @@ __export(exports_pluginManager, {
17066
17661
  }
17067
17662
  }
17068
17663
  });
17069
- import {resolve as resolve7, join as join4, basename as basename2, relative as relative2, dirname as dirname2} from "path";
17070
- import {promises as fs22} from "fs";
17071
- import {homedir as homedir4} from "os";
17664
+ import {resolve as resolve8, join as join6, basename as basename2, relative as relative3, dirname as dirname3} from "path";
17665
+ import {promises as fs25} from "fs";
17666
+ import {homedir as homedir5} from "os";
17072
17667
 
17073
17668
  class MultipleBundlePluginsError extends Error {
17074
17669
  entries;
@@ -17112,7 +17707,7 @@ class PluginManager {
17112
17707
  }
17113
17708
  async cleanupLoadedPluginContext(context) {
17114
17709
  if (context?.tempDir) {
17115
- await fs22.rm(context.tempDir, { recursive: true, force: true }).catch(() => {
17710
+ await fs25.rm(context.tempDir, { recursive: true, force: true }).catch(() => {
17116
17711
  });
17117
17712
  }
17118
17713
  }
@@ -17190,7 +17785,7 @@ class PluginManager {
17190
17785
  return "unverified";
17191
17786
  }
17192
17787
  async resolvePreparedPluginDir(pluginDir, source) {
17193
- const claudeMarketplaceManifestPath = join4(pluginDir, ".claude-plugin", "marketplace.json");
17788
+ const claudeMarketplaceManifestPath = join6(pluginDir, ".claude-plugin", "marketplace.json");
17194
17789
  if (!await fileExists(claudeMarketplaceManifestPath)) {
17195
17790
  return { pluginDir, warnings: [] };
17196
17791
  }
@@ -17225,8 +17820,8 @@ class PluginManager {
17225
17820
  }
17226
17821
  selectedEntry = matched;
17227
17822
  }
17228
- const selectedPluginDir = resolve7(pluginDir, selectedEntry.source);
17229
- const relativePath = relative2(resolve7(pluginDir), selectedPluginDir);
17823
+ const selectedPluginDir = resolve8(pluginDir, selectedEntry.source);
17824
+ const relativePath = relative3(resolve8(pluginDir), selectedPluginDir);
17230
17825
  if (relativePath.startsWith("..") || relativePath.includes("/../") || relativePath.includes("\\..\\")) {
17231
17826
  throw new Error(`Invalid bundled plugin source path "${selectedEntry.source}" in ${claudeMarketplaceManifestPath}`);
17232
17827
  }
@@ -17293,7 +17888,7 @@ class PluginManager {
17293
17888
  tempDir = await this.skillsManager.cloneRepo(resolved.url);
17294
17889
  pluginDir = tempDir;
17295
17890
  } else {
17296
- pluginDir = resolve7(resolved.path || source);
17891
+ pluginDir = resolve8(resolved.path || source);
17297
17892
  if (!await fileExists(pluginDir)) {
17298
17893
  throw new Error(`Local path not found: ${pluginDir}`);
17299
17894
  }
@@ -17338,15 +17933,15 @@ class PluginManager {
17338
17933
  }
17339
17934
  async getClaudeNativeFeatureKinds(pluginDir, manifest) {
17340
17935
  const featureChecks = await Promise.all([
17341
- (async () => !!manifest.commands || await isDirectory(join4(pluginDir, "commands")))(),
17342
- (async () => !!manifest.hooks || await isDirectory(join4(pluginDir, "hooks")))(),
17343
- (async () => !!manifest.agents || await isDirectory(join4(pluginDir, "agents")))(),
17344
- isDirectory(join4(pluginDir, "skills")),
17345
- (async () => !!manifest.mcpServers || await fileExists(join4(pluginDir, ".mcp.json")))(),
17346
- isDirectory(join4(pluginDir, "prompts")),
17347
- isDirectory(join4(pluginDir, "schemas")),
17348
- isDirectory(join4(pluginDir, "scripts")),
17349
- isDirectory(join4(pluginDir, "templates"))
17936
+ (async () => !!manifest.commands || await isDirectory(join6(pluginDir, "commands")))(),
17937
+ (async () => !!manifest.hooks || await isDirectory(join6(pluginDir, "hooks")))(),
17938
+ (async () => !!manifest.agents || await isDirectory(join6(pluginDir, "agents")))(),
17939
+ isDirectory(join6(pluginDir, "skills")),
17940
+ (async () => !!manifest.mcpServers || await fileExists(join6(pluginDir, ".mcp.json")))(),
17941
+ isDirectory(join6(pluginDir, "prompts")),
17942
+ isDirectory(join6(pluginDir, "schemas")),
17943
+ isDirectory(join6(pluginDir, "scripts")),
17944
+ isDirectory(join6(pluginDir, "templates"))
17350
17945
  ]);
17351
17946
  return [
17352
17947
  ...featureChecks[0] ? ["commands"] : [],
@@ -17364,7 +17959,7 @@ class PluginManager {
17364
17959
  if (plugin.format !== "claude") {
17365
17960
  return null;
17366
17961
  }
17367
- const manifestContent = await readFileIfExists(join4(pluginDir, ".claude-plugin", "plugin.json"));
17962
+ const manifestContent = await readFileIfExists(join6(pluginDir, ".claude-plugin", "plugin.json"));
17368
17963
  if (!manifestContent) {
17369
17964
  return null;
17370
17965
  }
@@ -17384,7 +17979,7 @@ class PluginManager {
17384
17979
  return {
17385
17980
  namespace,
17386
17981
  pluginKey: `${plugin.name}@${namespace}`,
17387
- installPath: join4(homedir4(), ".claude", "plugins", "cache", namespace, plugin.name, versionDir),
17982
+ installPath: join6(homedir5(), ".claude", "plugins", "cache", namespace, plugin.name, versionDir),
17388
17983
  marketplacePath: getClaudeMarketplaceInstallPath(namespace),
17389
17984
  features
17390
17985
  };
@@ -17448,12 +18043,12 @@ class PluginManager {
17448
18043
  await writeFile(getClaudeSettingsPath(), JSON.stringify(state, null, 2));
17449
18044
  }
17450
18045
  async findClaudeMarketplaceRoot(pluginDir) {
17451
- let currentDir = resolve7(pluginDir);
18046
+ let currentDir = resolve8(pluginDir);
17452
18047
  while (true) {
17453
- if (await fileExists(join4(currentDir, ".claude-plugin", "marketplace.json"))) {
18048
+ if (await fileExists(join6(currentDir, ".claude-plugin", "marketplace.json"))) {
17454
18049
  return currentDir;
17455
18050
  }
17456
- const parentDir = dirname2(currentDir);
18051
+ const parentDir = dirname3(currentDir);
17457
18052
  if (parentDir === currentDir) {
17458
18053
  return null;
17459
18054
  }
@@ -17461,7 +18056,7 @@ class PluginManager {
17461
18056
  }
17462
18057
  }
17463
18058
  async readClaudeMarketplaceManifest(marketplaceDir) {
17464
- const manifestContent = await readFileIfExists(join4(marketplaceDir, ".claude-plugin", "marketplace.json"));
18059
+ const manifestContent = await readFileIfExists(join6(marketplaceDir, ".claude-plugin", "marketplace.json"));
17465
18060
  if (!manifestContent) {
17466
18061
  return null;
17467
18062
  }
@@ -17480,19 +18075,19 @@ class PluginManager {
17480
18075
  }
17481
18076
  }
17482
18077
  async saveClaudeMarketplaceManifest(marketplaceDir, manifest) {
17483
- await fs22.mkdir(join4(marketplaceDir, ".claude-plugin"), { recursive: true });
17484
- await writeFile(join4(marketplaceDir, ".claude-plugin", "marketplace.json"), JSON.stringify(manifest, null, 2));
18078
+ await fs25.mkdir(join6(marketplaceDir, ".claude-plugin"), { recursive: true });
18079
+ await writeFile(join6(marketplaceDir, ".claude-plugin", "marketplace.json"), JSON.stringify(manifest, null, 2));
17485
18080
  }
17486
18081
  resolveMarketplaceSourcePath(baseDir, relativePath) {
17487
- const resolvedPath = resolve7(baseDir, relativePath);
17488
- const relativePathFromBase = relative2(resolve7(baseDir), resolvedPath);
18082
+ const resolvedPath = resolve8(baseDir, relativePath);
18083
+ const relativePathFromBase = relative3(resolve8(baseDir), resolvedPath);
17489
18084
  if (relativePathFromBase.startsWith("..") || relativePathFromBase.includes("/../") || relativePathFromBase.includes("\\..\\")) {
17490
18085
  throw new Error(`Invalid marketplace source path "${relativePath}" in ${baseDir}`);
17491
18086
  }
17492
18087
  return resolvedPath;
17493
18088
  }
17494
18089
  async readClaudePluginMetadata(pluginDir) {
17495
- const manifestContent = await readFileIfExists(join4(pluginDir, ".claude-plugin", "plugin.json"));
18090
+ const manifestContent = await readFileIfExists(join6(pluginDir, ".claude-plugin", "plugin.json"));
17496
18091
  if (!manifestContent) {
17497
18092
  return {};
17498
18093
  }
@@ -17508,11 +18103,11 @@ class PluginManager {
17508
18103
  }
17509
18104
  }
17510
18105
  async copyClaudeMarketplacePlugin(sourcePluginDir, marketplacePath, pluginName) {
17511
- const marketplacePluginPath = join4(marketplacePath, "plugins", pluginName);
17512
- await fs22.mkdir(dirname2(marketplacePluginPath), { recursive: true });
17513
- await fs22.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
18106
+ const marketplacePluginPath = join6(marketplacePath, "plugins", pluginName);
18107
+ await fs25.mkdir(dirname3(marketplacePluginPath), { recursive: true });
18108
+ await fs25.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
17514
18109
  });
17515
- await fs22.cp(sourcePluginDir, marketplacePluginPath, { recursive: true, dereference: true });
18110
+ await fs25.cp(sourcePluginDir, marketplacePluginPath, { recursive: true, dereference: true });
17516
18111
  return marketplacePluginPath;
17517
18112
  }
17518
18113
  getClaudeMarketplaceSource(plugin, marketplaceRoot, marketplacePath) {
@@ -17533,7 +18128,7 @@ class PluginManager {
17533
18128
  if (plugin.source.type === "local") {
17534
18129
  return {
17535
18130
  source: "directory",
17536
- path: resolve7(marketplaceRoot || plugin.source.path || marketplacePath)
18131
+ path: resolve8(marketplaceRoot || plugin.source.path || marketplacePath)
17537
18132
  };
17538
18133
  }
17539
18134
  return {
@@ -17544,7 +18139,7 @@ class PluginManager {
17544
18139
  async materializeClaudeMarketplace(plugin, pluginDir, target) {
17545
18140
  const marketplaceRoot = await this.findClaudeMarketplaceRoot(pluginDir);
17546
18141
  const marketplaceSource = this.getClaudeMarketplaceSource(plugin, marketplaceRoot, target.marketplacePath);
17547
- await fs22.mkdir(target.marketplacePath, { recursive: true });
18142
+ await fs25.mkdir(target.marketplacePath, { recursive: true });
17548
18143
  const existingManifest = await this.readClaudeMarketplaceManifest(target.marketplacePath);
17549
18144
  const sourceManifest = marketplaceRoot ? await this.readClaudeMarketplaceManifest(marketplaceRoot) : null;
17550
18145
  const mergedManifest = {
@@ -17616,10 +18211,10 @@ class PluginManager {
17616
18211
  warnings.push(`Skipped native Claude plugin install because Claude already has "${plugin.name}" installed as ${conflictingKey}.`);
17617
18212
  return { installed, skipped, warnings };
17618
18213
  }
17619
- await fs22.rm(nativeTarget.installPath, { recursive: true, force: true }).catch(() => {
18214
+ await fs25.rm(nativeTarget.installPath, { recursive: true, force: true }).catch(() => {
17620
18215
  });
17621
- await fs22.mkdir(dirname2(nativeTarget.installPath), { recursive: true });
17622
- await fs22.cp(pluginDir, nativeTarget.installPath, { recursive: true, dereference: true });
18216
+ await fs25.mkdir(dirname3(nativeTarget.installPath), { recursive: true });
18217
+ await fs25.cp(pluginDir, nativeTarget.installPath, { recursive: true, dereference: true });
17623
18218
  const marketplace = await this.materializeClaudeMarketplace(plugin, pluginDir, nativeTarget);
17624
18219
  const now = new Date().toISOString();
17625
18220
  claudeInstalled.plugins[nativeTarget.pluginKey] = [{
@@ -17659,7 +18254,7 @@ class PluginManager {
17659
18254
  };
17660
18255
  await this.saveClaudeKnownMarketplaces(knownMarketplaces);
17661
18256
  for (const entry of legacyEntries) {
17662
- await fs22.rm(entry.installPath, { recursive: true, force: true }).catch(() => {
18257
+ await fs25.rm(entry.installPath, { recursive: true, force: true }).catch(() => {
17663
18258
  });
17664
18259
  }
17665
18260
  for (const legacyKey of legacyKeys) {
@@ -17707,7 +18302,7 @@ class PluginManager {
17707
18302
  delete knownMarketplaces[marketplaceNamespace];
17708
18303
  await this.saveClaudeKnownMarketplaces(knownMarketplaces);
17709
18304
  }
17710
- await fs22.rm(getClaudeMarketplaceInstallPath(marketplaceNamespace), { recursive: true, force: true }).catch(() => {
18305
+ await fs25.rm(getClaudeMarketplaceInstallPath(marketplaceNamespace), { recursive: true, force: true }).catch(() => {
17711
18306
  });
17712
18307
  } else if (marketplaceNamespace) {
17713
18308
  const pluginName = component.pluginKey.split("@")[0] || "";
@@ -17715,14 +18310,14 @@ class PluginManager {
17715
18310
  const manifest = await this.readClaudeMarketplaceManifest(marketplacePath);
17716
18311
  if (pluginName && manifest) {
17717
18312
  manifest.plugins = (manifest.plugins || []).filter((entry) => entry.name !== pluginName);
17718
- const marketplacePluginPath = join4(marketplacePath, "plugins", pluginName);
17719
- await fs22.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
18313
+ const marketplacePluginPath = join6(marketplacePath, "plugins", pluginName);
18314
+ await fs25.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
17720
18315
  });
17721
18316
  await this.saveClaudeMarketplaceManifest(marketplacePath, manifest);
17722
18317
  }
17723
18318
  }
17724
18319
  await this.saveClaudeSettings(claudeSettings);
17725
- await fs22.rm(component.installPath, { recursive: true, force: true }).catch(() => {
18320
+ await fs25.rm(component.installPath, { recursive: true, force: true }).catch(() => {
17726
18321
  });
17727
18322
  return true;
17728
18323
  }
@@ -17795,7 +18390,7 @@ class PluginManager {
17795
18390
  throw new Error(`Unknown marketplace: ${registryId}. Available: ${this.getMarketplaceIds().join(", ")}`);
17796
18391
  }
17797
18392
  const cacheDir = getMarketplaceCacheDir(registryId);
17798
- const cacheMetaPath = join4(cacheDir, ".agentinit-cache-meta.json");
18393
+ const cacheMetaPath = join6(cacheDir, ".agentinit-cache-meta.json");
17799
18394
  const cacheMeta = await this.readMarketplaceCacheMeta(cacheMetaPath);
17800
18395
  if (cacheMeta?.repoUrl === registry.repoUrl) {
17801
18396
  const age = Date.now() - cacheMeta.fetchedAt;
@@ -17803,10 +18398,10 @@ class PluginManager {
17803
18398
  return cacheDir;
17804
18399
  }
17805
18400
  }
17806
- if (await fileExists(join4(cacheDir, ".git"))) {
18401
+ if (await fileExists(join6(cacheDir, ".git"))) {
17807
18402
  const originUrl = await this.getMarketplaceCacheOriginUrl(cacheDir);
17808
18403
  if (originUrl !== registry.repoUrl) {
17809
- await fs22.rm(cacheDir, { recursive: true, force: true });
18404
+ await fs25.rm(cacheDir, { recursive: true, force: true });
17810
18405
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
17811
18406
  } else {
17812
18407
  const { execFile } = await import("child_process");
@@ -17815,15 +18410,15 @@ class PluginManager {
17815
18410
  try {
17816
18411
  await exec("git", ["pull", "--ff-only"], { cwd: cacheDir, timeout: 30000 });
17817
18412
  } catch {
17818
- await fs22.rm(cacheDir, { recursive: true, force: true });
18413
+ await fs25.rm(cacheDir, { recursive: true, force: true });
17819
18414
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
17820
18415
  }
17821
18416
  }
17822
18417
  } else {
17823
18418
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
17824
18419
  }
17825
- await fs22.mkdir(cacheDir, { recursive: true });
17826
- await fs22.writeFile(cacheMetaPath, JSON.stringify({
18420
+ await fs25.mkdir(cacheDir, { recursive: true });
18421
+ await fs25.writeFile(cacheMetaPath, JSON.stringify({
17827
18422
  fetchedAt: Date.now(),
17828
18423
  repoUrl: registry.repoUrl
17829
18424
  }));
@@ -17834,7 +18429,7 @@ class PluginManager {
17834
18429
  return null;
17835
18430
  }
17836
18431
  try {
17837
- const meta = JSON.parse(await fs22.readFile(cacheMetaPath, "utf8"));
18432
+ const meta = JSON.parse(await fs25.readFile(cacheMetaPath, "utf8"));
17838
18433
  if (typeof meta.fetchedAt !== "number") {
17839
18434
  return null;
17840
18435
  }
@@ -17862,11 +18457,11 @@ class PluginManager {
17862
18457
  }
17863
18458
  }
17864
18459
  async cloneMarketplace(repoUrl, dest) {
17865
- await fs22.mkdir(dest, { recursive: true });
18460
+ await fs25.mkdir(dest, { recursive: true });
17866
18461
  const { execFile } = await import("child_process");
17867
18462
  const { promisify } = await import("util");
17868
18463
  const exec = promisify(execFile);
17869
- await fs22.rm(dest, { recursive: true, force: true }).catch(() => {
18464
+ await fs25.rm(dest, { recursive: true, force: true }).catch(() => {
17870
18465
  });
17871
18466
  await exec("git", ["clone", "--depth", "1", repoUrl, dest], { timeout: 60000 });
17872
18467
  }
@@ -17876,7 +18471,7 @@ class PluginManager {
17876
18471
  throw new Error(`Unknown marketplace: ${registryId}`);
17877
18472
  const cacheDir = await this.ensureMarketplaceCache(registryId);
17878
18473
  for (const dir of registry.pluginDirs) {
17879
- const pluginPath = join4(cacheDir, dir, name);
18474
+ const pluginPath = join6(cacheDir, dir, name);
17880
18475
  if (await isDirectory(pluginPath)) {
17881
18476
  return pluginPath;
17882
18477
  }
@@ -17892,7 +18487,7 @@ class PluginManager {
17892
18487
  const cacheDir = await this.ensureMarketplaceCache(registryId);
17893
18488
  const results = [];
17894
18489
  for (const dir of registry.pluginDirs) {
17895
- const fullDir = join4(cacheDir, dir);
18490
+ const fullDir = join6(cacheDir, dir);
17896
18491
  if (!await isDirectory(fullDir))
17897
18492
  continue;
17898
18493
  const cat = getMarketplaceCategoryForDir(dir);
@@ -17902,26 +18497,26 @@ class PluginManager {
17902
18497
  for (const entry of entries) {
17903
18498
  if (entry.startsWith("."))
17904
18499
  continue;
17905
- const entryPath = join4(fullDir, entry);
18500
+ const entryPath = join6(fullDir, entry);
17906
18501
  if (!await isDirectory(entryPath))
17907
18502
  continue;
17908
- const manifestPath = join4(entryPath, ".claude-plugin", "plugin.json");
18503
+ const manifestPath = join6(entryPath, ".claude-plugin", "plugin.json");
17909
18504
  let name = entry;
17910
18505
  let description = "";
17911
18506
  let version = "0.0.0";
17912
18507
  if (await fileExists(manifestPath)) {
17913
18508
  try {
17914
- const manifest = JSON.parse(await fs22.readFile(manifestPath, "utf8"));
18509
+ const manifest = JSON.parse(await fs25.readFile(manifestPath, "utf8"));
17915
18510
  name = manifest.name || entry;
17916
18511
  description = manifest.description || "";
17917
18512
  version = manifest.version || "0.0.0";
17918
18513
  } catch {
17919
18514
  }
17920
18515
  } else {
17921
- const skillMdPath = join4(entryPath, "SKILL.md");
18516
+ const skillMdPath = join6(entryPath, "SKILL.md");
17922
18517
  if (await fileExists(skillMdPath)) {
17923
18518
  try {
17924
- const parsed = import_gray_matter.default(await fs22.readFile(skillMdPath, "utf8"));
18519
+ const parsed = import_gray_matter.default(await fs25.readFile(skillMdPath, "utf8"));
17925
18520
  if (parsed.data.name)
17926
18521
  name = parsed.data.name;
17927
18522
  if (parsed.data.description)
@@ -17929,10 +18524,10 @@ class PluginManager {
17929
18524
  } catch {
17930
18525
  }
17931
18526
  } else {
17932
- const mcpPath = join4(entryPath, ".mcp.json");
18527
+ const mcpPath = join6(entryPath, ".mcp.json");
17933
18528
  if (await fileExists(mcpPath)) {
17934
18529
  try {
17935
- const mcpConfig = JSON.parse(await fs22.readFile(mcpPath, "utf8"));
18530
+ const mcpConfig = JSON.parse(await fs25.readFile(mcpPath, "utf8"));
17936
18531
  const serverNames = Object.keys(mcpConfig.mcpServers || mcpConfig);
17937
18532
  if (serverNames.length > 0) {
17938
18533
  description = `MCP server(s): ${serverNames.join(", ")}`;
@@ -17954,10 +18549,10 @@ class PluginManager {
17954
18549
  return results.sort((a, b) => a.name.localeCompare(b.name));
17955
18550
  }
17956
18551
  async detectFormat(pluginDir) {
17957
- if (await fileExists(join4(pluginDir, ".claude-plugin", "plugin.json"))) {
18552
+ if (await fileExists(join6(pluginDir, ".claude-plugin", "plugin.json"))) {
17958
18553
  return "claude";
17959
18554
  }
17960
- if (await fileExists(join4(pluginDir, ".cursor-plugin", "plugin.json"))) {
18555
+ if (await fileExists(join6(pluginDir, ".cursor-plugin", "plugin.json"))) {
17961
18556
  return "cursor";
17962
18557
  }
17963
18558
  return "generic";
@@ -17974,7 +18569,7 @@ class PluginManager {
17974
18569
  }
17975
18570
  }
17976
18571
  async parseClaudePlugin(pluginDir, source) {
17977
- const manifestPath = join4(pluginDir, ".claude-plugin", "plugin.json");
18572
+ const manifestPath = join6(pluginDir, ".claude-plugin", "plugin.json");
17978
18573
  const manifestContent = await readFileIfExists(manifestPath);
17979
18574
  if (!manifestContent) {
17980
18575
  throw new Error(`Missing .claude-plugin/plugin.json in ${pluginDir}`);
@@ -17985,10 +18580,10 @@ class PluginManager {
17985
18580
  const convertedSkills = await this.convertCommandsToSkills(pluginDir, manifest);
17986
18581
  skills.push(...convertedSkills);
17987
18582
  const mcpServers = await this.parseMcpJson(pluginDir);
17988
- if (await isDirectory(join4(pluginDir, "hooks")) || manifest.hooks) {
18583
+ if (await isDirectory(join6(pluginDir, "hooks")) || manifest.hooks) {
17989
18584
  warnings.push("Hooks (hooks/) are Claude Code-specific");
17990
18585
  }
17991
- if (await isDirectory(join4(pluginDir, "agents")) || manifest.agents) {
18586
+ if (await isDirectory(join6(pluginDir, "agents")) || manifest.agents) {
17992
18587
  warnings.push("Agent definitions (agents/) are Claude Code-specific");
17993
18588
  }
17994
18589
  return {
@@ -18003,7 +18598,7 @@ class PluginManager {
18003
18598
  };
18004
18599
  }
18005
18600
  async parseCursorPlugin(pluginDir, source) {
18006
- const manifestPath = join4(pluginDir, ".cursor-plugin", "plugin.json");
18601
+ const manifestPath = join6(pluginDir, ".cursor-plugin", "plugin.json");
18007
18602
  const manifestContent = await readFileIfExists(manifestPath);
18008
18603
  if (!manifestContent) {
18009
18604
  throw new Error(`Missing .cursor-plugin/plugin.json in ${pluginDir}`);
@@ -18042,7 +18637,7 @@ class PluginManager {
18042
18637
  };
18043
18638
  }
18044
18639
  async parseMcpJson(pluginDir) {
18045
- const mcpPath = join4(pluginDir, ".mcp.json");
18640
+ const mcpPath = join6(pluginDir, ".mcp.json");
18046
18641
  const content = await readFileIfExists(mcpPath);
18047
18642
  if (!content)
18048
18643
  return [];
@@ -18086,10 +18681,10 @@ class PluginManager {
18086
18681
  if (manifest.commands) {
18087
18682
  const cmds = Array.isArray(manifest.commands) ? manifest.commands : [manifest.commands];
18088
18683
  for (const cmd of cmds) {
18089
- commandsDirs.push(resolve7(pluginDir, cmd));
18684
+ commandsDirs.push(resolve8(pluginDir, cmd));
18090
18685
  }
18091
18686
  } else {
18092
- commandsDirs.push(join4(pluginDir, "commands"));
18687
+ commandsDirs.push(join6(pluginDir, "commands"));
18093
18688
  }
18094
18689
  for (const commandsDir of commandsDirs) {
18095
18690
  if (!await isDirectory(commandsDir))
@@ -18098,7 +18693,7 @@ class PluginManager {
18098
18693
  for (const entry of entries) {
18099
18694
  if (!entry.endsWith(".md"))
18100
18695
  continue;
18101
- const cmdPath = join4(commandsDir, entry);
18696
+ const cmdPath = join6(commandsDir, entry);
18102
18697
  const skill = await this.convertSingleCommandToSkill(cmdPath, manifest.name);
18103
18698
  if (skill)
18104
18699
  skills.push(skill);
@@ -18412,7 +19007,7 @@ ${body.trim()}
18412
19007
  }
18413
19008
  if (!removedSkillPaths.has(skill.path)) {
18414
19009
  try {
18415
- await fs22.rm(skill.path, { recursive: true, force: true });
19010
+ await fs25.rm(skill.path, { recursive: true, force: true });
18416
19011
  removedSkillPaths.add(skill.path);
18417
19012
  } catch {
18418
19013
  details.push(`Could not remove skill path: ${skill.path}`);
@@ -18421,7 +19016,7 @@ ${body.trim()}
18421
19016
  }
18422
19017
  if (skill.canonicalPath && skill.canonicalPath !== skill.path && !removedCanonicalPaths.has(skill.canonicalPath) && !sharedCanonicalPath) {
18423
19018
  try {
18424
- await fs22.rm(skill.canonicalPath, { recursive: true, force: true });
19019
+ await fs25.rm(skill.canonicalPath, { recursive: true, force: true });
18425
19020
  removedCanonicalPaths.add(skill.canonicalPath);
18426
19021
  } catch {
18427
19022
  details.push(`Could not remove canonical skill path: ${skill.canonicalPath}`);
@@ -18527,39 +19122,40 @@ var init_pluginManager = __esm(() => {
18527
19122
  init_skillsManager();
18528
19123
  init_userConfig();
18529
19124
  getMarketplaceCacheDir = function(registryId) {
18530
- return join4(homedir4(), ".agentinit", "marketplace-cache", registryId);
19125
+ return join6(homedir5(), ".agentinit", "marketplace-cache", registryId);
18531
19126
  };
18532
19127
  getRegistryPath = function(projectPath, global3) {
18533
19128
  if (global3) {
18534
- return join4(homedir4(), ".agentinit", "plugins.json");
19129
+ return join6(homedir5(), ".agentinit", "plugins.json");
18535
19130
  }
18536
- return join4(projectPath, ".agentinit", "plugins.json");
19131
+ return join6(projectPath, ".agentinit", "plugins.json");
18537
19132
  };
18538
19133
  getClaudeInstalledPluginsPath = function() {
18539
- return join4(homedir4(), ".claude", "plugins", "installed_plugins.json");
19134
+ return join6(homedir5(), ".claude", "plugins", "installed_plugins.json");
18540
19135
  };
18541
19136
  getClaudeKnownMarketplacesPath = function() {
18542
- return join4(homedir4(), ".claude", "plugins", "known_marketplaces.json");
19137
+ return join6(homedir5(), ".claude", "plugins", "known_marketplaces.json");
18543
19138
  };
18544
19139
  getClaudeMarketplaceInstallPath = function(namespace) {
18545
- return join4(homedir4(), ".claude", "plugins", "marketplaces", namespace);
19140
+ return join6(homedir5(), ".claude", "plugins", "marketplaces", namespace);
18546
19141
  };
18547
19142
  getClaudeSettingsPath = function() {
18548
- return join4(homedir4(), ".claude", "settings.json");
19143
+ return join6(homedir5(), ".claude", "settings.json");
18549
19144
  };
18550
19145
  });
18551
19146
 
18552
19147
  // dist/core/skillsManager.js
18553
- import {resolve as resolve8, join as join5, relative as relative3, basename as basename3, dirname as dirname3} from "path";
18554
- import {promises as fs24} from "fs";
18555
- import {homedir as homedir5, tmpdir} from "os";
19148
+ import {resolve as resolve9, join as join7, relative as relative4, basename as basename3, dirname as dirname4} from "path";
19149
+ import {promises as fs27} from "fs";
19150
+ import {homedir as homedir6, tmpdir} from "os";
18556
19151
  import {execFile} from "child_process";
18557
- import {createHash} from "crypto";
19152
+ import {createHash as createHash2} from "crypto";
18558
19153
  import {promisify} from "util";
18559
19154
 
18560
19155
  class SkillsManager {
18561
19156
  agentManager;
18562
19157
  preparedSourceContexts = new Map;
19158
+ skillScanner = new SkillSecurityScanner;
18563
19159
  constructor(agentManager3) {
18564
19160
  this.agentManager = agentManager3 || new AgentManager;
18565
19161
  }
@@ -18567,11 +19163,15 @@ class SkillsManager {
18567
19163
  if (source.startsWith(".") || source.startsWith("/") || source.startsWith("~")) {
18568
19164
  return { type: "local", path: source };
18569
19165
  }
18570
- const githubUrlSource = this.parseGitHubHttpSource(source);
18571
- if (githubUrlSource) {
18572
- return githubUrlSource;
19166
+ const httpSource = this.parseHttpRepositorySource(source);
19167
+ if (httpSource) {
19168
+ return httpSource;
18573
19169
  }
18574
- if (source.startsWith("git@") || source.endsWith(".git")) {
19170
+ const sshSource = this.parseSshRepositorySource(source);
19171
+ if (sshSource) {
19172
+ return sshSource;
19173
+ }
19174
+ if (source.endsWith(".git")) {
18575
19175
  return { type: "github", url: source };
18576
19176
  }
18577
19177
  if (options2?.from) {
@@ -18584,6 +19184,14 @@ class SkillsManager {
18584
19184
  pluginName: source
18585
19185
  };
18586
19186
  }
19187
+ const gitLabShorthandSource = this.parseGitLabShorthandSource(source);
19188
+ if (gitLabShorthandSource) {
19189
+ return gitLabShorthandSource;
19190
+ }
19191
+ const bitbucketShorthandSource = this.parseBitbucketShorthandSource(source);
19192
+ if (bitbucketShorthandSource) {
19193
+ return bitbucketShorthandSource;
19194
+ }
18587
19195
  const githubShorthandSource = this.parseGitHubShorthandSource(source);
18588
19196
  if (githubShorthandSource?.subpath) {
18589
19197
  return githubShorthandSource;
@@ -18673,6 +19281,106 @@ class SkillsManager {
18673
19281
  return null;
18674
19282
  }
18675
19283
  }
19284
+ parseGitLabHttpSource(source) {
19285
+ if (!source.startsWith("https://gitlab.com/") && !source.startsWith("http://gitlab.com/")) {
19286
+ return null;
19287
+ }
19288
+ try {
19289
+ const parsedUrl = new URL(source);
19290
+ const segments = parsedUrl.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
19291
+ const dashIndex = segments.indexOf("-");
19292
+ const repoBoundary = dashIndex >= 0 ? dashIndex : segments.length;
19293
+ if (repoBoundary < 2) {
19294
+ return null;
19295
+ }
19296
+ const repo = segments[repoBoundary - 1];
19297
+ const owner = segments.slice(0, repoBoundary - 1).join("/");
19298
+ if (!owner || !repo) {
19299
+ return null;
19300
+ }
19301
+ let subpath;
19302
+ if (dashIndex >= 0) {
19303
+ const marker = segments[dashIndex + 1];
19304
+ if ((marker === "tree" || marker === "blob") && segments.length > dashIndex + 3) {
19305
+ subpath = segments.slice(dashIndex + 3).join("/");
19306
+ }
19307
+ }
19308
+ return {
19309
+ type: "gitlab",
19310
+ url: `https://gitlab.com/${owner}/${repo}.git`,
19311
+ owner,
19312
+ repo,
19313
+ ...subpath ? { subpath } : {}
19314
+ };
19315
+ } catch {
19316
+ return null;
19317
+ }
19318
+ }
19319
+ parseBitbucketHttpSource(source) {
19320
+ if (!source.startsWith("https://bitbucket.org/") && !source.startsWith("http://bitbucket.org/")) {
19321
+ return null;
19322
+ }
19323
+ try {
19324
+ const parsedUrl = new URL(source);
19325
+ const segments = parsedUrl.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
19326
+ if (segments.length < 2) {
19327
+ return null;
19328
+ }
19329
+ const [owner, repo, marker, _commitish, ...rest] = segments;
19330
+ if (!owner || !repo) {
19331
+ return null;
19332
+ }
19333
+ let subpath;
19334
+ if (marker === "src" && rest.length > 0) {
19335
+ subpath = rest.join("/");
19336
+ }
19337
+ return {
19338
+ type: "bitbucket",
19339
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
19340
+ owner,
19341
+ repo,
19342
+ ...subpath ? { subpath } : {}
19343
+ };
19344
+ } catch {
19345
+ return null;
19346
+ }
19347
+ }
19348
+ parseHttpRepositorySource(source) {
19349
+ return this.parseGitHubHttpSource(source) || this.parseGitLabHttpSource(source) || this.parseBitbucketHttpSource(source);
19350
+ }
19351
+ parseSshRepositorySource(source) {
19352
+ const githubMatch = source.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
19353
+ if (githubMatch) {
19354
+ const [, owner, repo] = githubMatch;
19355
+ return {
19356
+ type: "github",
19357
+ url: `git@github.com:${owner}/${repo}.git`,
19358
+ owner,
19359
+ repo
19360
+ };
19361
+ }
19362
+ const gitlabMatch = source.match(/^git@gitlab\.com:(.+)\/([^/]+?)(?:\.git)?$/);
19363
+ if (gitlabMatch) {
19364
+ const [, owner, repo] = gitlabMatch;
19365
+ return {
19366
+ type: "gitlab",
19367
+ url: `git@gitlab.com:${owner}/${repo}.git`,
19368
+ owner,
19369
+ repo
19370
+ };
19371
+ }
19372
+ const bitbucketMatch = source.match(/^git@bitbucket\.org:([^/]+)\/([^/]+?)(?:\.git)?$/);
19373
+ if (bitbucketMatch) {
19374
+ const [, owner, repo] = bitbucketMatch;
19375
+ return {
19376
+ type: "bitbucket",
19377
+ url: `git@bitbucket.org:${owner}/${repo}.git`,
19378
+ owner,
19379
+ repo
19380
+ };
19381
+ }
19382
+ return null;
19383
+ }
18676
19384
  parseGitHubShorthandSource(source) {
18677
19385
  const githubShorthandMatch = source.match(/^([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(?:\/(.+))?$/);
18678
19386
  if (!githubShorthandMatch) {
@@ -18687,6 +19395,50 @@ class SkillsManager {
18687
19395
  ...subpath ? { subpath } : {}
18688
19396
  };
18689
19397
  }
19398
+ parseGitLabShorthandSource(source) {
19399
+ const normalized = source.startsWith("gitlab:") ? source.slice("gitlab:".length) : source.startsWith("gitlab.com/") ? source.slice("gitlab.com/".length) : null;
19400
+ if (!normalized) {
19401
+ return null;
19402
+ }
19403
+ const [repoSpec = normalized, subpathSpec] = normalized.split("//", 2);
19404
+ const segments = repoSpec.split("/").filter(Boolean);
19405
+ if (segments.length < 2) {
19406
+ return null;
19407
+ }
19408
+ const repo = segments[segments.length - 1];
19409
+ const owner = segments.slice(0, segments.length - 1).join("/");
19410
+ if (!owner || !repo) {
19411
+ return null;
19412
+ }
19413
+ return {
19414
+ type: "gitlab",
19415
+ url: `https://gitlab.com/${owner}/${repo}.git`,
19416
+ owner,
19417
+ repo,
19418
+ ...subpathSpec ? { subpath: subpathSpec } : {}
19419
+ };
19420
+ }
19421
+ parseBitbucketShorthandSource(source) {
19422
+ const normalized = source.startsWith("bitbucket:") ? source.slice("bitbucket:".length) : source.startsWith("bitbucket.org/") ? source.slice("bitbucket.org/".length) : null;
19423
+ if (!normalized) {
19424
+ return null;
19425
+ }
19426
+ const segments = normalized.split("/").filter(Boolean);
19427
+ if (segments.length < 2) {
19428
+ return null;
19429
+ }
19430
+ const [owner, repo, ...rest] = segments;
19431
+ if (!owner || !repo) {
19432
+ return null;
19433
+ }
19434
+ return {
19435
+ type: "bitbucket",
19436
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
19437
+ owner,
19438
+ repo,
19439
+ ...rest.length > 0 ? { subpath: rest.join("/") } : {}
19440
+ };
19441
+ }
18690
19442
  async parseSkillMd(filePath) {
18691
19443
  const content = await readFileIfExists(filePath);
18692
19444
  if (!content)
@@ -18705,34 +19457,34 @@ class SkillsManager {
18705
19457
  const skills2 = [];
18706
19458
  const seen = new Set;
18707
19459
  for (const searchDir of SKILL_SEARCH_DIRS) {
18708
- const fullDir = resolve8(repoPath, searchDir);
19460
+ const fullDir = resolve9(repoPath, searchDir);
18709
19461
  if (!await fileExists(fullDir))
18710
19462
  continue;
18711
- const directSkillMd = join5(fullDir, "SKILL.md");
19463
+ const directSkillMd = join7(fullDir, "SKILL.md");
18712
19464
  if (await fileExists(directSkillMd)) {
18713
19465
  const parsed = await this.parseSkillMd(directSkillMd);
18714
19466
  if (parsed && !seen.has(parsed.name)) {
18715
19467
  seen.add(parsed.name);
18716
- skills2.push({ ...parsed, path: resolve8(fullDir) });
19468
+ skills2.push({ ...parsed, path: resolve9(fullDir) });
18717
19469
  }
18718
19470
  }
18719
- const directSkillMdLower = join5(fullDir, "skill.md");
19471
+ const directSkillMdLower = join7(fullDir, "skill.md");
18720
19472
  if (await fileExists(directSkillMdLower)) {
18721
19473
  const parsed = await this.parseSkillMd(directSkillMdLower);
18722
19474
  if (parsed && !seen.has(parsed.name)) {
18723
19475
  seen.add(parsed.name);
18724
- skills2.push({ ...parsed, path: resolve8(fullDir) });
19476
+ skills2.push({ ...parsed, path: resolve9(fullDir) });
18725
19477
  }
18726
19478
  }
18727
19479
  if (!await isDirectory(fullDir))
18728
19480
  continue;
18729
19481
  const entries = await listFiles(fullDir);
18730
19482
  for (const entry of entries) {
18731
- const entryPath = join5(fullDir, entry);
19483
+ const entryPath = join7(fullDir, entry);
18732
19484
  if (!await isDirectory(entryPath))
18733
19485
  continue;
18734
- const skillMdPath = join5(entryPath, "SKILL.md");
18735
- const skillMdPathLower = join5(entryPath, "skill.md");
19486
+ const skillMdPath = join7(entryPath, "SKILL.md");
19487
+ const skillMdPathLower = join7(entryPath, "skill.md");
18736
19488
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
18737
19489
  if (!skillFile)
18738
19490
  continue;
@@ -18746,14 +19498,14 @@ class SkillsManager {
18746
19498
  return skills2;
18747
19499
  }
18748
19500
  async cloneRepo(url) {
18749
- const tempDir = await fs24.mkdtemp(join5(tmpdir(), "agentinit-skills-"));
18750
- await fs24.rm(tempDir, { recursive: true, force: true });
19501
+ const tempDir = await fs27.mkdtemp(join7(tmpdir(), "agentinit-skills-"));
19502
+ await fs27.rm(tempDir, { recursive: true, force: true });
18751
19503
  try {
18752
19504
  await execFileAsync("git", ["clone", "--depth", "1", url, tempDir], {
18753
19505
  timeout: 60000
18754
19506
  });
18755
19507
  } catch (error) {
18756
- await fs24.rm(tempDir, { recursive: true, force: true }).catch(() => {
19508
+ await fs27.rm(tempDir, { recursive: true, force: true }).catch(() => {
18757
19509
  });
18758
19510
  throw new Error(`Failed to clone ${url}: ${error.message}`);
18759
19511
  }
@@ -18811,15 +19563,15 @@ class SkillsManager {
18811
19563
  if (!tempDir) {
18812
19564
  return;
18813
19565
  }
18814
- await fs24.rm(tempDir, { recursive: true, force: true }).catch(() => {
19566
+ await fs27.rm(tempDir, { recursive: true, force: true }).catch(() => {
18815
19567
  });
18816
19568
  }
18817
19569
  async resolveDiscoveryRoot(repoPath, source, sourceLabel) {
18818
- const resolvedRepoPath = resolve8(repoPath);
18819
- if (source.type !== "github" || !source.subpath) {
19570
+ const resolvedRepoPath = resolve9(repoPath);
19571
+ if (source.type !== "github" && source.type !== "gitlab" && source.type !== "bitbucket" || !source.subpath) {
18820
19572
  return resolvedRepoPath;
18821
19573
  }
18822
- const discoveryRoot = resolve8(resolvedRepoPath, source.subpath);
19574
+ const discoveryRoot = resolve9(resolvedRepoPath, source.subpath);
18823
19575
  if (!this.isWithinPath(resolvedRepoPath, discoveryRoot)) {
18824
19576
  throw new Error(`Invalid GitHub source path "${source.subpath}" in ${sourceLabel}`);
18825
19577
  }
@@ -18837,7 +19589,7 @@ class SkillsManager {
18837
19589
  return realDiscoveryRoot;
18838
19590
  }
18839
19591
  if (basename3(realDiscoveryRoot).toLowerCase() === "skill.md") {
18840
- return dirname3(realDiscoveryRoot);
19592
+ return dirname4(realDiscoveryRoot);
18841
19593
  }
18842
19594
  throw new Error(`GitHub source must reference a skill directory or SKILL.md: ${sourceLabel}`);
18843
19595
  }
@@ -18861,14 +19613,14 @@ class SkillsManager {
18861
19613
  };
18862
19614
  }
18863
19615
  let repoPath;
18864
- if (resolved.type === "github") {
19616
+ if (resolved.type === "github" || resolved.type === "gitlab" || resolved.type === "bitbucket") {
18865
19617
  if (!resolved.url) {
18866
19618
  throw new Error(`Invalid source: ${source}`);
18867
19619
  }
18868
19620
  tempDir = await this.cloneRepo(resolved.url);
18869
19621
  repoPath = tempDir;
18870
19622
  } else {
18871
- repoPath = resolve8(resolved.path || source);
19623
+ repoPath = resolve9(resolved.path || source);
18872
19624
  if (!await fileExists(repoPath)) {
18873
19625
  throw this.getMissingLocalPathError(source, repoPath);
18874
19626
  }
@@ -18955,30 +19707,30 @@ class SkillsManager {
18955
19707
  async installSkill(skillPath, skillName, targetDir, copy = false) {
18956
19708
  const normalizedSkillName = this.normalizeSkillName(skillName);
18957
19709
  const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
18958
- await fs24.mkdir(resolve8(targetDir), { recursive: true });
19710
+ await fs27.mkdir(resolve9(targetDir), { recursive: true });
18959
19711
  if (await fileExists(destPath)) {
18960
- await fs24.rm(destPath, { recursive: true, force: true });
19712
+ await fs27.rm(destPath, { recursive: true, force: true });
18961
19713
  }
18962
19714
  if (copy) {
18963
19715
  await this.copyDir(skillPath, destPath);
18964
19716
  } else {
18965
- await fs24.symlink(skillPath, destPath, "dir");
19717
+ await fs27.symlink(skillPath, destPath, "dir");
18966
19718
  }
18967
19719
  return destPath;
18968
19720
  }
18969
19721
  async installSkillFromContent(skillName, skillContent, targetDir) {
18970
19722
  const normalizedSkillName = this.normalizeSkillName(skillName);
18971
19723
  const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
18972
- await fs24.mkdir(resolve8(targetDir), { recursive: true });
19724
+ await fs27.mkdir(resolve9(targetDir), { recursive: true });
18973
19725
  if (await fileExists(destPath)) {
18974
- await fs24.rm(destPath, { recursive: true, force: true });
19726
+ await fs27.rm(destPath, { recursive: true, force: true });
18975
19727
  }
18976
- await fs24.mkdir(destPath, { recursive: true });
18977
- await fs24.writeFile(join5(destPath, "SKILL.md"), skillContent, "utf8");
19728
+ await fs27.mkdir(destPath, { recursive: true });
19729
+ await fs27.writeFile(join7(destPath, "SKILL.md"), skillContent, "utf8");
18978
19730
  return destPath;
18979
19731
  }
18980
19732
  getCanonicalSkillsDir(projectPath, global3 = false) {
18981
- return global3 ? resolve8(homedir5(), ".agents/skills") : resolve8(projectPath, ".agents/skills");
19733
+ return global3 ? resolve9(homedir6(), ".agents/skills") : resolve9(projectPath, ".agents/skills");
18982
19734
  }
18983
19735
  async getInstallPlan(skillName, agent, projectPath, options2 = {}) {
18984
19736
  const normalizedSkillName = this.normalizeSkillName(skillName);
@@ -19021,7 +19773,7 @@ class SkillsManager {
19021
19773
  ...options2.global !== undefined ? { global: options2.global } : {},
19022
19774
  ...options2.copy !== undefined ? { copy: options2.copy } : {}
19023
19775
  });
19024
- return this.compareSkillSnapshot(skill, plan.canonicalPath || plan.path);
19776
+ return this.compareSkillInstallStatus(skill, plan);
19025
19777
  }
19026
19778
  async installSkillForAgent(skillPath, skillName, agent, projectPath, options2 = {}) {
19027
19779
  const plan = await this.getInstallPlan(skillName, agent, projectPath, options2);
@@ -19068,7 +19820,7 @@ class SkillsManager {
19068
19820
  throw new Error(`Missing canonical path for ${skillName}`);
19069
19821
  }
19070
19822
  await this.cleanAndCreateDirectory(canonicalPath);
19071
- await fs24.writeFile(join5(canonicalPath, "SKILL.md"), skillContent, "utf8");
19823
+ await fs27.writeFile(join7(canonicalPath, "SKILL.md"), skillContent, "utf8");
19072
19824
  if (plan.path === canonicalPath) {
19073
19825
  return plan;
19074
19826
  }
@@ -19091,7 +19843,7 @@ class SkillsManager {
19091
19843
  async installSkillFromContentToCanonicalStore(skillName, skillContent, projectPath, options2 = {}) {
19092
19844
  const plan = this.getCanonicalInstallPlan(skillName, projectPath, options2);
19093
19845
  await this.cleanAndCreateDirectory(plan.path);
19094
- await fs24.writeFile(join5(plan.path, "SKILL.md"), skillContent, "utf8");
19846
+ await fs27.writeFile(join7(plan.path, "SKILL.md"), skillContent, "utf8");
19095
19847
  return plan;
19096
19848
  }
19097
19849
  getCanonicalInstallPlan(skillName, projectPath, options2 = {}) {
@@ -19103,6 +19855,76 @@ class SkillsManager {
19103
19855
  mode: "symlink"
19104
19856
  };
19105
19857
  }
19858
+ normalizeSkillPrefix(prefix) {
19859
+ const normalized = prefix?.trim() ?? "";
19860
+ if (normalized.includes("/") || normalized.includes("\\")) {
19861
+ throw new Error(`Invalid skill prefix: ${prefix}`);
19862
+ }
19863
+ return normalized;
19864
+ }
19865
+ withSkillPrefix(skillName, prefix) {
19866
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
19867
+ return normalizedPrefix ? `${normalizedPrefix}${skillName}` : skillName;
19868
+ }
19869
+ async rewriteSkillFileName(filePath, skillName) {
19870
+ const content = await fs27.readFile(filePath, "utf8");
19871
+ const parsed = import_gray_matter2.default(content);
19872
+ const nextContent = import_gray_matter2.default.stringify(parsed.content, {
19873
+ ...parsed.data,
19874
+ name: skillName
19875
+ });
19876
+ await fs27.writeFile(filePath, nextContent, "utf8");
19877
+ }
19878
+ async applyPrefixToSkills(skills2, prefix) {
19879
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
19880
+ if (!normalizedPrefix) {
19881
+ return { skills: skills2, cleanup: async () => {
19882
+ } };
19883
+ }
19884
+ const tempDirs = [];
19885
+ try {
19886
+ const prefixedSkills = await Promise.all(skills2.map(async (skill) => {
19887
+ const name = this.withSkillPrefix(skill.name, normalizedPrefix);
19888
+ if (skill.generatedContent) {
19889
+ return {
19890
+ ...skill,
19891
+ name,
19892
+ generatedContent: import_gray_matter2.default.stringify(import_gray_matter2.default(skill.generatedContent).content, {
19893
+ ...import_gray_matter2.default(skill.generatedContent).data,
19894
+ name
19895
+ })
19896
+ };
19897
+ }
19898
+ const tempRoot = await fs27.mkdtemp(join7(tmpdir(), "agentinit-prefixed-skill-"));
19899
+ const tempSkillPath = join7(tempRoot, basename3(skill.path));
19900
+ tempDirs.push(tempRoot);
19901
+ await this.copyDir(skill.path, tempSkillPath);
19902
+ const skillMdPath = join7(tempSkillPath, "SKILL.md");
19903
+ const skillMdPathLower = join7(tempSkillPath, "skill.md");
19904
+ const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19905
+ if (!skillFile) {
19906
+ throw new Error(`Skill "${skill.name}" is missing SKILL.md`);
19907
+ }
19908
+ await this.rewriteSkillFileName(skillFile, name);
19909
+ return {
19910
+ ...skill,
19911
+ name,
19912
+ path: tempSkillPath
19913
+ };
19914
+ }));
19915
+ return {
19916
+ skills: prefixedSkills,
19917
+ cleanup: async () => {
19918
+ await Promise.all(tempDirs.map((dir) => fs27.rm(dir, { recursive: true, force: true }).catch(() => {
19919
+ })));
19920
+ }
19921
+ };
19922
+ } catch (error) {
19923
+ await Promise.all(tempDirs.map((dir) => fs27.rm(dir, { recursive: true, force: true }).catch(() => {
19924
+ })));
19925
+ throw error;
19926
+ }
19927
+ }
19106
19928
  normalizeSkillName(skillName) {
19107
19929
  const normalized = skillName.trim();
19108
19930
  if (!normalized) {
@@ -19114,9 +19936,9 @@ class SkillsManager {
19114
19936
  return normalized;
19115
19937
  }
19116
19938
  resolveInstallPath(targetDir, skillName) {
19117
- const resolvedTargetDir = resolve8(targetDir);
19118
- const destPath = resolve8(resolvedTargetDir, skillName);
19119
- const relativePath = relative3(resolvedTargetDir, destPath);
19939
+ const resolvedTargetDir = resolve9(targetDir);
19940
+ const destPath = resolve9(resolvedTargetDir, skillName);
19941
+ const relativePath = relative4(resolvedTargetDir, destPath);
19120
19942
  if (relativePath === "" || relativePath.startsWith("..") || relativePath.includes("/../") || relativePath.includes("\\..\\")) {
19121
19943
  throw new Error(`Refusing to install skill outside target directory: ${skillName}`);
19122
19944
  }
@@ -19133,18 +19955,18 @@ class SkillsManager {
19133
19955
  async createDirectorySnapshot(rootPath) {
19134
19956
  if (!await fileExists(rootPath))
19135
19957
  return null;
19136
- const hash = createHash("sha256");
19958
+ const hash = createHash2("sha256");
19137
19959
  const walk = async (currentPath, relativePath) => {
19138
- const stat = await fs24.stat(currentPath);
19960
+ const stat = await fs27.stat(currentPath);
19139
19961
  if (!stat.isDirectory()) {
19140
- const content = await fs24.readFile(currentPath);
19962
+ const content = await fs27.readFile(currentPath);
19141
19963
  this.updateSnapshotWithFile(hash, relativePath, content);
19142
19964
  return;
19143
19965
  }
19144
19966
  hash.update(`dir:${relativePath || "."}\n`);
19145
- const entries = (await fs24.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19967
+ const entries = (await fs27.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19146
19968
  for (const entry of entries) {
19147
- await walk(join5(currentPath, entry), relativePath ? join5(relativePath, entry) : entry);
19969
+ await walk(join7(currentPath, entry), relativePath ? join7(relativePath, entry) : entry);
19148
19970
  }
19149
19971
  };
19150
19972
  await walk(rootPath, "");
@@ -19155,7 +19977,7 @@ class SkillsManager {
19155
19977
  }
19156
19978
  async getNewSkillSnapshot(skill) {
19157
19979
  if (skill.generatedContent) {
19158
- const hash = createHash("sha256");
19980
+ const hash = createHash2("sha256");
19159
19981
  hash.update("dir:.\n");
19160
19982
  this.updateSnapshotWithFile(hash, "SKILL.md", skill.generatedContent.trim());
19161
19983
  return hash.digest("hex");
@@ -19171,23 +19993,43 @@ class SkillsManager {
19171
19993
  return "new";
19172
19994
  return existing === incoming ? "unchanged" : "changed";
19173
19995
  }
19996
+ async compareSkillInstallStatus(skill, plan) {
19997
+ const incoming = await this.getNewSkillSnapshot(skill);
19998
+ if (incoming === null)
19999
+ return "new";
20000
+ const existingAtTarget = await this.readExistingSkillSnapshot(plan.path);
20001
+ if (existingAtTarget !== null) {
20002
+ return existingAtTarget === incoming ? "unchanged" : "changed";
20003
+ }
20004
+ if (!plan.canonicalPath || plan.canonicalPath === plan.path) {
20005
+ return "new";
20006
+ }
20007
+ const existingAtCanonical = await this.readExistingSkillSnapshot(plan.canonicalPath);
20008
+ if (existingAtCanonical === null) {
20009
+ return "new";
20010
+ }
20011
+ return existingAtCanonical === incoming ? "new" : "changed";
20012
+ }
19174
20013
  async cleanAndCreateDirectory(path) {
19175
- await fs24.rm(path, { recursive: true, force: true }).catch(() => {
20014
+ await fs27.rm(path, { recursive: true, force: true }).catch(() => {
19176
20015
  });
19177
- await fs24.mkdir(path, { recursive: true });
20016
+ await fs27.mkdir(path, { recursive: true });
19178
20017
  }
19179
20018
  isWithinPath(basePath, targetPath) {
19180
- const relativePath = relative3(resolve8(basePath), resolve8(targetPath));
20019
+ const relativePath = relative4(resolve9(basePath), resolve9(targetPath));
19181
20020
  return relativePath === "" || !relativePath.startsWith("..") && !relativePath.includes("/../") && !relativePath.includes("\\..\\");
19182
20021
  }
19183
20022
  async copyDir(src, dest) {
19184
- await fs24.cp(src, dest, { recursive: true, dereference: true });
20023
+ await fs27.cp(src, dest, { recursive: true, dereference: true });
19185
20024
  }
19186
20025
  async addFromSource(source, projectPath, options2 = {}) {
20026
+ const normalizedPrefix = this.normalizeSkillPrefix(options2.prefix);
19187
20027
  const context = this.takePreparedSourceContext(source, projectPath, options2.from) || await this.loadDiscoveredSkillsContext(source, projectPath, {
19188
20028
  ...options2.from !== undefined ? { from: options2.from } : {},
19189
20029
  ...options2.pluginName !== undefined ? { pluginName: options2.pluginName } : {}
19190
20030
  });
20031
+ let prefixedCleanup = async () => {
20032
+ };
19191
20033
  try {
19192
20034
  let skills2 = context.skills;
19193
20035
  if (skills2.length === 0) {
@@ -19195,7 +20037,34 @@ class SkillsManager {
19195
20037
  }
19196
20038
  if (options2.skills && options2.skills.length > 0) {
19197
20039
  const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
19198
- skills2 = skills2.filter((skill) => names.has(skill.name.toLowerCase()));
20040
+ skills2 = skills2.filter((skill) => names.has(skill.name.toLowerCase()) || names.has(this.withSkillPrefix(skill.name, normalizedPrefix).toLowerCase()));
20041
+ }
20042
+ const prefixed = await this.applyPrefixToSkills(skills2, normalizedPrefix);
20043
+ skills2 = prefixed.skills;
20044
+ prefixedCleanup = prefixed.cleanup;
20045
+ const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: [...context.warnings] };
20046
+ if (options2.scan !== false) {
20047
+ const scannedWarnings = new Set;
20048
+ const scannableSkills = [];
20049
+ for (const skill of skills2) {
20050
+ const scan = await this.skillScanner.scanSkill(skill);
20051
+ if (scan.findings.length === 0) {
20052
+ scannableSkills.push(skill);
20053
+ continue;
20054
+ }
20055
+ if (scan.blocked && !options2.allowRisky) {
20056
+ result.skipped.push({
20057
+ skill,
20058
+ reason: this.skillScanner.formatBlockingReason(scan)
20059
+ });
20060
+ continue;
20061
+ }
20062
+ const summary = this.skillScanner.formatShortSummary(scan);
20063
+ scannedWarnings.add(scan.blocked ? `Proceeding with "${skill.name}" despite high-risk findings: ${summary}` : `Security warnings for "${skill.name}": ${summary}`);
20064
+ scannableSkills.push(skill);
20065
+ }
20066
+ skills2 = scannableSkills;
20067
+ result.warnings.push(...scannedWarnings);
19199
20068
  }
19200
20069
  const installToSharedStore = options2.agents?.includes(SHARED_SKILLS_TARGET_ID) ?? false;
19201
20070
  const agents = await this.getTargetAgents(projectPath, options2);
@@ -19204,11 +20073,13 @@ class SkillsManager {
19204
20073
  installed: [],
19205
20074
  updated: [],
19206
20075
  unchanged: [],
19207
- skipped: skills2.map((skill) => ({ skill, reason: "No target agents found" })),
19208
- warnings: context.warnings
20076
+ skipped: [
20077
+ ...result.skipped,
20078
+ ...skills2.map((skill) => ({ skill, reason: "No target agents found" }))
20079
+ ],
20080
+ warnings: result.warnings
19209
20081
  };
19210
20082
  }
19211
- const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19212
20083
  const installableAgents = [];
19213
20084
  const comparisonCache = new Map;
19214
20085
  const pendingUpdates = [];
@@ -19260,10 +20131,10 @@ class SkillsManager {
19260
20131
  ...options2.copy !== undefined ? { copy: options2.copy } : {}
19261
20132
  };
19262
20133
  const plan = await this.getInstallPlan(skill.name, agent, projectPath, installOptions);
19263
- const comparisonPath = plan.canonicalPath || plan.path;
20134
+ const comparisonPath = plan.path;
19264
20135
  let comparison = comparisonCache.get(comparisonPath);
19265
20136
  if (comparison === undefined) {
19266
- comparison = await this.compareSkillSnapshot(skill, comparisonPath);
20137
+ comparison = await this.compareSkillInstallStatus(skill, plan);
19267
20138
  comparisonCache.set(comparisonPath, comparison);
19268
20139
  }
19269
20140
  if (comparison === "unchanged") {
@@ -19310,8 +20181,47 @@ class SkillsManager {
19310
20181
  }
19311
20182
  }
19312
20183
  }
20184
+ try {
20185
+ const lock = new InstallLock;
20186
+ const fromOption = options2.from;
20187
+ const { source: resolvedSource } = this.resolveSourceRequest(source, ...fromOption ? [{ from: fromOption }] : []);
20188
+ const lockSource = {
20189
+ type: resolvedSource.type,
20190
+ ...resolvedSource.marketplace ? { marketplace: resolvedSource.marketplace } : {},
20191
+ ...resolvedSource.pluginName ? { pluginName: resolvedSource.pluginName } : {},
20192
+ ...normalizedPrefix ? { prefix: normalizedPrefix } : {},
20193
+ ...resolvedSource.url ? { url: resolvedSource.url } : {},
20194
+ ...resolvedSource.path ? { path: resolve9(projectPath, expandTilde(resolvedSource.path)) } : {},
20195
+ ...resolvedSource.owner ? { owner: resolvedSource.owner } : {},
20196
+ ...resolvedSource.repo ? { repo: resolvedSource.repo } : {},
20197
+ ...resolvedSource.subpath ? { subpath: resolvedSource.subpath } : {}
20198
+ };
20199
+ const recordEntries = async (entries, action) => {
20200
+ for (const entry of entries) {
20201
+ const hashPath = entry.canonicalPath || entry.path;
20202
+ const contentHash = await hashDirectory(hashPath);
20203
+ await lock.recordSkill({
20204
+ action,
20205
+ name: entry.skill.name,
20206
+ projectPath: resolve9(projectPath),
20207
+ agents: [entry.agent],
20208
+ scope: options2.global ? "global" : "project",
20209
+ source: lockSource,
20210
+ installPath: entry.path,
20211
+ mode: entry.mode,
20212
+ ...entry.canonicalPath ? { canonicalPath: entry.canonicalPath } : {},
20213
+ ...contentHash ? { contentHash } : {}
20214
+ });
20215
+ }
20216
+ };
20217
+ await recordEntries(result.installed, "install");
20218
+ await recordEntries(result.updated, "update");
20219
+ } catch (error) {
20220
+ logLockWriteWarning("Skills changed successfully", error);
20221
+ }
19313
20222
  return result;
19314
20223
  } finally {
20224
+ await prefixedCleanup();
19315
20225
  await context.cleanup();
19316
20226
  }
19317
20227
  }
@@ -19334,11 +20244,11 @@ class SkillsManager {
19334
20244
  continue;
19335
20245
  const entries = await listFiles(dir);
19336
20246
  for (const entry of entries) {
19337
- const entryPath = join5(dir, entry);
20247
+ const entryPath = join7(dir, entry);
19338
20248
  if (!await isDirectory(entryPath))
19339
20249
  continue;
19340
- const skillMdPath = join5(entryPath, "SKILL.md");
19341
- const skillMdPathLower = join5(entryPath, "skill.md");
20250
+ const skillMdPath = join7(entryPath, "SKILL.md");
20251
+ const skillMdPathLower = join7(entryPath, "skill.md");
19342
20252
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19343
20253
  if (!skillFile)
19344
20254
  continue;
@@ -19348,7 +20258,7 @@ class SkillsManager {
19348
20258
  let isSymlink = false;
19349
20259
  let canonicalPath;
19350
20260
  try {
19351
- const stat = await fs24.lstat(entryPath);
20261
+ const stat = await fs27.lstat(entryPath);
19352
20262
  isSymlink = stat.isSymbolicLink();
19353
20263
  const canonicalBase = this.getCanonicalSkillsDir(projectPath, scope === "global");
19354
20264
  const [resolvedEntryPath, resolvedCanonicalBase] = await Promise.all([
@@ -19358,7 +20268,7 @@ class SkillsManager {
19358
20268
  if (this.isWithinPath(resolvedCanonicalBase, resolvedEntryPath)) {
19359
20269
  canonicalPath = resolvedEntryPath;
19360
20270
  } else if (this.isWithinPath(canonicalBase, entryPath)) {
19361
- canonicalPath = resolve8(entryPath);
20271
+ canonicalPath = resolve9(entryPath);
19362
20272
  }
19363
20273
  } catch {
19364
20274
  }
@@ -19378,21 +20288,21 @@ class SkillsManager {
19378
20288
  const includeSharedTarget = !options2.agents || options2.agents.includes(SHARED_SKILLS_TARGET_ID);
19379
20289
  if (includeSharedTarget) {
19380
20290
  const scopes = options2.global ? ["global"] : ["project", "global"];
19381
- const referencedCanonicalPaths = new Set(installed.map((entry) => entry.canonicalPath).filter((value) => !!value).map((value) => resolve8(value)));
20291
+ const referencedCanonicalPaths = new Set(installed.map((entry) => entry.canonicalPath).filter((value) => !!value).map((value) => resolve9(value)));
19382
20292
  for (const scope of scopes) {
19383
20293
  const canonicalDir = this.getCanonicalSkillsDir(projectPath, scope === "global");
19384
20294
  if (!await fileExists(canonicalDir))
19385
20295
  continue;
19386
20296
  const entries = await listFiles(canonicalDir);
19387
20297
  for (const entry of entries) {
19388
- const entryPath = join5(canonicalDir, entry);
20298
+ const entryPath = join7(canonicalDir, entry);
19389
20299
  if (!await isDirectory(entryPath))
19390
20300
  continue;
19391
- const resolvedEntryPath = resolve8(entryPath);
20301
+ const resolvedEntryPath = resolve9(entryPath);
19392
20302
  if (referencedCanonicalPaths.has(resolvedEntryPath))
19393
20303
  continue;
19394
- const skillMdPath = join5(entryPath, "SKILL.md");
19395
- const skillMdPathLower = join5(entryPath, "skill.md");
20304
+ const skillMdPath = join7(entryPath, "SKILL.md");
20305
+ const skillMdPathLower = join7(entryPath, "skill.md");
19396
20306
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19397
20307
  if (!skillFile)
19398
20308
  continue;
@@ -19459,7 +20369,7 @@ class SkillsManager {
19459
20369
  }
19460
20370
  if (!removedPaths.has(entry.path)) {
19461
20371
  try {
19462
- await fs24.rm(entry.path, { recursive: true, force: true });
20372
+ await fs27.rm(entry.path, { recursive: true, force: true });
19463
20373
  removedPaths.add(entry.path);
19464
20374
  } catch {
19465
20375
  skipped.push({
@@ -19472,7 +20382,7 @@ class SkillsManager {
19472
20382
  if (entry.canonicalPath && entry.canonicalPath !== entry.path && !removedCanonicalPaths.has(entry.canonicalPath)) {
19473
20383
  const stillReferenced = remainingEntries.some((other) => other.name.toLowerCase() === entry.name.toLowerCase() && other.canonicalPath === entry.canonicalPath);
19474
20384
  if (!stillReferenced) {
19475
- await fs24.rm(entry.canonicalPath, { recursive: true, force: true }).catch(() => {
20385
+ await fs27.rm(entry.canonicalPath, { recursive: true, force: true }).catch(() => {
19476
20386
  });
19477
20387
  removedCanonicalPaths.add(entry.canonicalPath);
19478
20388
  }
@@ -19485,6 +20395,29 @@ class SkillsManager {
19485
20395
  notFound.push(name);
19486
20396
  }
19487
20397
  }
20398
+ try {
20399
+ if (targetedEntries.length > 0) {
20400
+ const lock = new InstallLock;
20401
+ for (const entry of targetedEntries) {
20402
+ const entryKey = `${entry.agent}:${entry.name}`;
20403
+ if (!removed.includes(entryKey))
20404
+ continue;
20405
+ await lock.recordSkill({
20406
+ action: "remove",
20407
+ name: entry.name,
20408
+ projectPath: resolve9(projectPath),
20409
+ agents: [entry.agent],
20410
+ scope: entry.scope,
20411
+ source: { type: "local", path: entry.path },
20412
+ installPath: entry.path,
20413
+ mode: entry.mode,
20414
+ ...entry.canonicalPath ? { canonicalPath: entry.canonicalPath } : {}
20415
+ });
20416
+ }
20417
+ }
20418
+ } catch (error) {
20419
+ logLockWriteWarning("Skills removed successfully", error);
20420
+ }
19488
20421
  return { removed, notFound, skipped };
19489
20422
  }
19490
20423
  }
@@ -19492,9 +20425,12 @@ var import_gray_matter2, execFileAsync, DEFAULT_SKILLS_CATALOG, SKILL_SEARCH_DIR
19492
20425
  var init_skillsManager = __esm(() => {
19493
20426
  import_gray_matter2 = __toESM(require_gray_matter(), 1);
19494
20427
  init_fs();
20428
+ init_paths();
19495
20429
  init_agentManager();
19496
20430
  init_marketplaceRegistry();
20431
+ init_skillSecurityScanner();
19497
20432
  init_skills();
20433
+ init_installLock();
19498
20434
  execFileAsync = promisify(execFile);
19499
20435
  DEFAULT_SKILLS_CATALOG = {
19500
20436
  owner: "vercel-labs",
@@ -20378,51 +21314,51 @@ var require_uri_all = __commonJS((exports, module) => {
20378
21314
  }
20379
21315
  return uriTokens.join("");
20380
21316
  }
20381
- function resolveComponents(base2, relative6) {
21317
+ function resolveComponents(base2, relative7) {
20382
21318
  var options2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
20383
21319
  var skipNormalization = arguments[3];
20384
21320
  var target = {};
20385
21321
  if (!skipNormalization) {
20386
21322
  base2 = parse4(serialize(base2, options2), options2);
20387
- relative6 = parse4(serialize(relative6, options2), options2);
21323
+ relative7 = parse4(serialize(relative7, options2), options2);
20388
21324
  }
20389
21325
  options2 = options2 || {};
20390
- if (!options2.tolerant && relative6.scheme) {
20391
- target.scheme = relative6.scheme;
20392
- target.userinfo = relative6.userinfo;
20393
- target.host = relative6.host;
20394
- target.port = relative6.port;
20395
- target.path = removeDotSegments(relative6.path || "");
20396
- target.query = relative6.query;
21326
+ if (!options2.tolerant && relative7.scheme) {
21327
+ target.scheme = relative7.scheme;
21328
+ target.userinfo = relative7.userinfo;
21329
+ target.host = relative7.host;
21330
+ target.port = relative7.port;
21331
+ target.path = removeDotSegments(relative7.path || "");
21332
+ target.query = relative7.query;
20397
21333
  } else {
20398
- if (relative6.userinfo !== undefined || relative6.host !== undefined || relative6.port !== undefined) {
20399
- target.userinfo = relative6.userinfo;
20400
- target.host = relative6.host;
20401
- target.port = relative6.port;
20402
- target.path = removeDotSegments(relative6.path || "");
20403
- target.query = relative6.query;
21334
+ if (relative7.userinfo !== undefined || relative7.host !== undefined || relative7.port !== undefined) {
21335
+ target.userinfo = relative7.userinfo;
21336
+ target.host = relative7.host;
21337
+ target.port = relative7.port;
21338
+ target.path = removeDotSegments(relative7.path || "");
21339
+ target.query = relative7.query;
20404
21340
  } else {
20405
- if (!relative6.path) {
21341
+ if (!relative7.path) {
20406
21342
  target.path = base2.path;
20407
- if (relative6.query !== undefined) {
20408
- target.query = relative6.query;
21343
+ if (relative7.query !== undefined) {
21344
+ target.query = relative7.query;
20409
21345
  } else {
20410
21346
  target.query = base2.query;
20411
21347
  }
20412
21348
  } else {
20413
- if (relative6.path.charAt(0) === "/") {
20414
- target.path = removeDotSegments(relative6.path);
21349
+ if (relative7.path.charAt(0) === "/") {
21350
+ target.path = removeDotSegments(relative7.path);
20415
21351
  } else {
20416
21352
  if ((base2.userinfo !== undefined || base2.host !== undefined || base2.port !== undefined) && !base2.path) {
20417
- target.path = "/" + relative6.path;
21353
+ target.path = "/" + relative7.path;
20418
21354
  } else if (!base2.path) {
20419
- target.path = relative6.path;
21355
+ target.path = relative7.path;
20420
21356
  } else {
20421
- target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative6.path;
21357
+ target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative7.path;
20422
21358
  }
20423
21359
  target.path = removeDotSegments(target.path);
20424
21360
  }
20425
- target.query = relative6.query;
21361
+ target.query = relative7.query;
20426
21362
  }
20427
21363
  target.userinfo = base2.userinfo;
20428
21364
  target.host = base2.host;
@@ -20430,10 +21366,10 @@ var require_uri_all = __commonJS((exports, module) => {
20430
21366
  }
20431
21367
  target.scheme = base2.scheme;
20432
21368
  }
20433
- target.fragment = relative6.fragment;
21369
+ target.fragment = relative7.fragment;
20434
21370
  return target;
20435
21371
  }
20436
- function resolve12(baseURI, relativeURI, options2) {
21372
+ function resolve13(baseURI, relativeURI, options2) {
20437
21373
  var schemelessOptions = assign({ scheme: "null" }, options2);
20438
21374
  return serialize(resolveComponents(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true), schemelessOptions);
20439
21375
  }
@@ -20701,7 +21637,7 @@ var require_uri_all = __commonJS((exports, module) => {
20701
21637
  exports2.removeDotSegments = removeDotSegments;
20702
21638
  exports2.serialize = serialize;
20703
21639
  exports2.resolveComponents = resolveComponents;
20704
- exports2.resolve = resolve12;
21640
+ exports2.resolve = resolve13;
20705
21641
  exports2.normalize = normalize;
20706
21642
  exports2.equal = equal;
20707
21643
  exports2.escapeComponent = escapeComponent;
@@ -20887,7 +21823,7 @@ var require_util3 = __commonJS((exports, module) => {
20887
21823
  var path = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
20888
21824
  return joinPaths(currentPath, path);
20889
21825
  };
20890
- var getData = function($data, lvl, paths5) {
21826
+ var getData = function($data, lvl, paths6) {
20891
21827
  var up, jsonPointer, data, matches;
20892
21828
  if ($data === "")
20893
21829
  return "rootData";
@@ -20905,7 +21841,7 @@ var require_util3 = __commonJS((exports, module) => {
20905
21841
  if (jsonPointer == "#") {
20906
21842
  if (up >= lvl)
20907
21843
  throw new Error("Cannot access property/index " + up + " levels up, current level is " + lvl);
20908
- return paths5[lvl - up];
21844
+ return paths6[lvl - up];
20909
21845
  }
20910
21846
  if (up > lvl)
20911
21847
  throw new Error("Cannot access data " + up + " levels up, current level is " + lvl);
@@ -21064,13 +22000,13 @@ var require_json_schema_traverse = __commonJS((exports, module) => {
21064
22000
 
21065
22001
  // node_modules/ajv/lib/compile/resolve.js
21066
22002
  var require_resolve = __commonJS((exports, module) => {
21067
- var resolve12 = function(compile, root, ref) {
22003
+ var resolve13 = function(compile, root, ref) {
21068
22004
  var refVal = this._refs[ref];
21069
22005
  if (typeof refVal == "string") {
21070
22006
  if (this._refs[refVal])
21071
22007
  refVal = this._refs[refVal];
21072
22008
  else
21073
- return resolve12.call(this, compile, root, refVal);
22009
+ return resolve13.call(this, compile, root, refVal);
21074
22010
  }
21075
22011
  refVal = refVal || this._schemas[ref];
21076
22012
  if (refVal instanceof SchemaObject) {
@@ -21275,13 +22211,13 @@ var require_resolve = __commonJS((exports, module) => {
21275
22211
  var util6 = require_util3();
21276
22212
  var SchemaObject = require_schema_obj();
21277
22213
  var traverse = require_json_schema_traverse();
21278
- module.exports = resolve12;
21279
- resolve12.normalizeId = normalizeId;
21280
- resolve12.fullPath = getFullPath;
21281
- resolve12.url = resolveUrl;
21282
- resolve12.ids = resolveIds;
21283
- resolve12.inlineRef = inlineRef;
21284
- resolve12.schema = resolveSchema;
22214
+ module.exports = resolve13;
22215
+ resolve13.normalizeId = normalizeId;
22216
+ resolve13.fullPath = getFullPath;
22217
+ resolve13.url = resolveUrl;
22218
+ resolve13.ids = resolveIds;
22219
+ resolve13.inlineRef = inlineRef;
22220
+ resolve13.schema = resolveSchema;
21285
22221
  var PREVENT_SCOPE_CHANGE = util6.toHash(["properties", "patternProperties", "enum", "dependencies", "definitions"]);
21286
22222
  var SIMPLE_INLINED = util6.toHash([
21287
22223
  "type",
@@ -21312,15 +22248,15 @@ var require_error_classes = __commonJS((exports, module) => {
21312
22248
  };
21313
22249
  var MissingRefError = function(baseId, ref, message) {
21314
22250
  this.message = message || MissingRefError.message(baseId, ref);
21315
- this.missingRef = resolve12.url(baseId, ref);
21316
- this.missingSchema = resolve12.normalizeId(resolve12.fullPath(this.missingRef));
22251
+ this.missingRef = resolve13.url(baseId, ref);
22252
+ this.missingSchema = resolve13.normalizeId(resolve13.fullPath(this.missingRef));
21317
22253
  };
21318
22254
  var errorSubclass = function(Subclass) {
21319
22255
  Subclass.prototype = Object.create(Error.prototype);
21320
22256
  Subclass.prototype.constructor = Subclass;
21321
22257
  return Subclass;
21322
22258
  };
21323
- var resolve12 = require_resolve();
22259
+ var resolve13 = require_resolve();
21324
22260
  module.exports = {
21325
22261
  Validation: errorSubclass(ValidationError),
21326
22262
  MissingRef: errorSubclass(MissingRefError)
@@ -21914,7 +22850,7 @@ var require_compile = __commonJS((exports, module) => {
21914
22850
  RULES,
21915
22851
  validate: validateGenerator,
21916
22852
  util: util6,
21917
- resolve: resolve12,
22853
+ resolve: resolve13,
21918
22854
  resolveRef,
21919
22855
  usePattern,
21920
22856
  useDefault,
@@ -21953,7 +22889,7 @@ var require_compile = __commonJS((exports, module) => {
21953
22889
  return validate2;
21954
22890
  }
21955
22891
  function resolveRef(baseId2, ref, isRoot) {
21956
- ref = resolve12.url(baseId2, ref);
22892
+ ref = resolve13.url(baseId2, ref);
21957
22893
  var refIndex = refs[ref];
21958
22894
  var _refVal, refCode;
21959
22895
  if (refIndex !== undefined) {
@@ -21970,11 +22906,11 @@ var require_compile = __commonJS((exports, module) => {
21970
22906
  }
21971
22907
  }
21972
22908
  refCode = addLocalRef(ref);
21973
- var v2 = resolve12.call(self, localCompile, root, ref);
22909
+ var v2 = resolve13.call(self, localCompile, root, ref);
21974
22910
  if (v2 === undefined) {
21975
22911
  var localSchema = localRefs && localRefs[ref];
21976
22912
  if (localSchema) {
21977
- v2 = resolve12.inlineRef(localSchema, opts.inlineRefs) ? localSchema : compile.call(self, localSchema, root, localRefs, baseId2);
22913
+ v2 = resolve13.inlineRef(localSchema, opts.inlineRefs) ? localSchema : compile.call(self, localSchema, root, localRefs, baseId2);
21978
22914
  }
21979
22915
  }
21980
22916
  if (v2 === undefined) {
@@ -22116,7 +23052,7 @@ var require_compile = __commonJS((exports, module) => {
22116
23052
  code += statement(i, arr);
22117
23053
  return code;
22118
23054
  };
22119
- var resolve12 = require_resolve();
23055
+ var resolve13 = require_resolve();
22120
23056
  var util6 = require_util3();
22121
23057
  var errorClasses = require_error_classes();
22122
23058
  var stableStringify = require_fast_json_stable_stringify();
@@ -25365,7 +26301,7 @@ var require_ajv = __commonJS((exports, module) => {
25365
26301
  var id = this._getId(schema2);
25366
26302
  if (id !== undefined && typeof id != "string")
25367
26303
  throw new Error("schema id must be string");
25368
- key = resolve12.normalizeId(key || id);
26304
+ key = resolve13.normalizeId(key || id);
25369
26305
  checkUnique(this, key);
25370
26306
  this._schemas[key] = this._addSchema(schema2, _skipValidation, _meta, true);
25371
26307
  return this;
@@ -25411,7 +26347,7 @@ var require_ajv = __commonJS((exports, module) => {
25411
26347
  }
25412
26348
  };
25413
26349
  var _getSchemaFragment = function(self, ref) {
25414
- var res = resolve12.schema.call(self, { schema: {} }, ref);
26350
+ var res = resolve13.schema.call(self, { schema: {} }, ref);
25415
26351
  if (res) {
25416
26352
  var { schema: schema2, root, baseId } = res;
25417
26353
  var v = compileSchema.call(self, schema2, root, undefined, baseId);
@@ -25427,7 +26363,7 @@ var require_ajv = __commonJS((exports, module) => {
25427
26363
  }
25428
26364
  };
25429
26365
  var _getSchemaObj = function(self, keyRef) {
25430
- keyRef = resolve12.normalizeId(keyRef);
26366
+ keyRef = resolve13.normalizeId(keyRef);
25431
26367
  return self._schemas[keyRef] || self._refs[keyRef] || self._fragments[keyRef];
25432
26368
  };
25433
26369
  var removeSchema = function(schemaKeyRef) {
@@ -25455,7 +26391,7 @@ var require_ajv = __commonJS((exports, module) => {
25455
26391
  this._cache.del(cacheKey);
25456
26392
  var id = this._getId(schemaKeyRef);
25457
26393
  if (id) {
25458
- id = resolve12.normalizeId(id);
26394
+ id = resolve13.normalizeId(id);
25459
26395
  delete this._schemas[id];
25460
26396
  delete this._refs[id];
25461
26397
  }
@@ -25480,14 +26416,14 @@ var require_ajv = __commonJS((exports, module) => {
25480
26416
  if (cached)
25481
26417
  return cached;
25482
26418
  shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false;
25483
- var id = resolve12.normalizeId(this._getId(schema2));
26419
+ var id = resolve13.normalizeId(this._getId(schema2));
25484
26420
  if (id && shouldAddSchema)
25485
26421
  checkUnique(this, id);
25486
26422
  var willValidate = this._opts.validateSchema !== false && !skipValidation;
25487
26423
  var recursiveMeta;
25488
- if (willValidate && !(recursiveMeta = id && id == resolve12.normalizeId(schema2.$schema)))
26424
+ if (willValidate && !(recursiveMeta = id && id == resolve13.normalizeId(schema2.$schema)))
25489
26425
  this.validateSchema(schema2, true);
25490
- var localRefs = resolve12.ids.call(this, schema2);
26426
+ var localRefs = resolve13.ids.call(this, schema2);
25491
26427
  var schemaObj = new SchemaObject({
25492
26428
  id,
25493
26429
  schema: schema2,
@@ -25634,21 +26570,21 @@ var require_ajv = __commonJS((exports, module) => {
25634
26570
  return metaOpts;
25635
26571
  };
25636
26572
  var setLogger = function(self) {
25637
- var logger5 = self._opts.logger;
25638
- if (logger5 === false) {
26573
+ var logger6 = self._opts.logger;
26574
+ if (logger6 === false) {
25639
26575
  self.logger = { log: noop, warn: noop, error: noop };
25640
26576
  } else {
25641
- if (logger5 === undefined)
25642
- logger5 = console;
25643
- if (!(typeof logger5 == "object" && logger5.log && logger5.warn && logger5.error))
26577
+ if (logger6 === undefined)
26578
+ logger6 = console;
26579
+ if (!(typeof logger6 == "object" && logger6.log && logger6.warn && logger6.error))
25644
26580
  throw new Error("logger must implement log, warn and error methods");
25645
- self.logger = logger5;
26581
+ self.logger = logger6;
25646
26582
  }
25647
26583
  };
25648
26584
  var noop = function() {
25649
26585
  };
25650
26586
  var compileSchema = require_compile();
25651
- var resolve12 = require_resolve();
26587
+ var resolve13 = require_resolve();
25652
26588
  var Cache = require_cache();
25653
26589
  var SchemaObject = require_schema_obj();
25654
26590
  var stableStringify = require_fast_json_stable_stringify();
@@ -25709,27 +26645,27 @@ var require_windows = __commonJS((exports, module) => {
25709
26645
  return checkPathExt(path, options2);
25710
26646
  };
25711
26647
  var isexe = function(path, options2, cb) {
25712
- fs31.stat(path, function(er, stat) {
26648
+ fs34.stat(path, function(er, stat) {
25713
26649
  cb(er, er ? false : checkStat(stat, path, options2));
25714
26650
  });
25715
26651
  };
25716
26652
  var sync = function(path, options2) {
25717
- return checkStat(fs31.statSync(path), path, options2);
26653
+ return checkStat(fs34.statSync(path), path, options2);
25718
26654
  };
25719
26655
  module.exports = isexe;
25720
26656
  isexe.sync = sync;
25721
- var fs31 = __require("fs");
26657
+ var fs34 = __require("fs");
25722
26658
  });
25723
26659
 
25724
26660
  // node_modules/isexe/mode.js
25725
26661
  var require_mode = __commonJS((exports, module) => {
25726
26662
  var isexe = function(path, options2, cb) {
25727
- fs31.stat(path, function(er, stat) {
26663
+ fs34.stat(path, function(er, stat) {
25728
26664
  cb(er, er ? false : checkStat(stat, options2));
25729
26665
  });
25730
26666
  };
25731
26667
  var sync = function(path, options2) {
25732
- return checkStat(fs31.statSync(path), options2);
26668
+ return checkStat(fs34.statSync(path), options2);
25733
26669
  };
25734
26670
  var checkStat = function(stat, options2) {
25735
26671
  return stat.isFile() && checkMode(stat, options2);
@@ -25749,7 +26685,7 @@ var require_mode = __commonJS((exports, module) => {
25749
26685
  };
25750
26686
  module.exports = isexe;
25751
26687
  isexe.sync = sync;
25752
- var fs31 = __require("fs");
26688
+ var fs34 = __require("fs");
25753
26689
  });
25754
26690
 
25755
26691
  // node_modules/isexe/index.js
@@ -25763,12 +26699,12 @@ var require_isexe = __commonJS((exports, module) => {
25763
26699
  if (typeof Promise !== "function") {
25764
26700
  throw new TypeError("callback not provided");
25765
26701
  }
25766
- return new Promise(function(resolve12, reject) {
26702
+ return new Promise(function(resolve13, reject) {
25767
26703
  isexe(path, options2 || {}, function(er, is) {
25768
26704
  if (er) {
25769
26705
  reject(er);
25770
26706
  } else {
25771
- resolve12(is);
26707
+ resolve13(is);
25772
26708
  }
25773
26709
  });
25774
26710
  });
@@ -25794,7 +26730,7 @@ var require_isexe = __commonJS((exports, module) => {
25794
26730
  }
25795
26731
  }
25796
26732
  };
25797
- var fs31 = __require("fs");
26733
+ var fs34 = __require("fs");
25798
26734
  var core2;
25799
26735
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
25800
26736
  core2 = require_windows();
@@ -25839,27 +26775,27 @@ var require_which = __commonJS((exports, module) => {
25839
26775
  opt = {};
25840
26776
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
25841
26777
  const found = [];
25842
- const step = (i) => new Promise((resolve12, reject) => {
26778
+ const step = (i) => new Promise((resolve13, reject) => {
25843
26779
  if (i === pathEnv.length)
25844
- return opt.all && found.length ? resolve12(found) : reject(getNotFoundError(cmd));
26780
+ return opt.all && found.length ? resolve13(found) : reject(getNotFoundError(cmd));
25845
26781
  const ppRaw = pathEnv[i];
25846
26782
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
25847
26783
  const pCmd = path.join(pathPart, cmd);
25848
26784
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
25849
- resolve12(subStep(p, i, 0));
26785
+ resolve13(subStep(p, i, 0));
25850
26786
  });
25851
- const subStep = (p, i, ii) => new Promise((resolve12, reject) => {
26787
+ const subStep = (p, i, ii) => new Promise((resolve13, reject) => {
25852
26788
  if (ii === pathExt.length)
25853
- return resolve12(step(i + 1));
26789
+ return resolve13(step(i + 1));
25854
26790
  const ext = pathExt[ii];
25855
26791
  isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
25856
26792
  if (!er && is) {
25857
26793
  if (opt.all)
25858
26794
  found.push(p + ext);
25859
26795
  else
25860
- return resolve12(p + ext);
26796
+ return resolve13(p + ext);
25861
26797
  }
25862
- return resolve12(subStep(p, i, ii + 1));
26798
+ return resolve13(subStep(p, i, ii + 1));
25863
26799
  });
25864
26800
  });
25865
26801
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -26001,14 +26937,14 @@ var require_readShebang = __commonJS((exports, module) => {
26001
26937
  const buffer = Buffer.alloc(size);
26002
26938
  let fd;
26003
26939
  try {
26004
- fd = fs31.openSync(command, "r");
26005
- fs31.readSync(fd, buffer, 0, size, 0);
26006
- fs31.closeSync(fd);
26940
+ fd = fs34.openSync(command, "r");
26941
+ fs34.readSync(fd, buffer, 0, size, 0);
26942
+ fs34.closeSync(fd);
26007
26943
  } catch (e) {
26008
26944
  }
26009
26945
  return shebangCommand(buffer.toString());
26010
26946
  };
26011
- var fs31 = __require("fs");
26947
+ var fs34 = __require("fs");
26012
26948
  var shebangCommand = require_shebang_command();
26013
26949
  module.exports = readShebang;
26014
26950
  });
@@ -27461,155 +28397,7 @@ function ora(options2) {
27461
28397
 
27462
28398
  // dist/commands/init.js
27463
28399
  var import_prompts = __toESM(require_prompts3(), 1);
27464
-
27465
- // node_modules/kleur/colors.mjs
27466
- var init = function(x, y) {
27467
- let rgx = new RegExp(`\\x1b\\[${y}m`, "g");
27468
- let open = `\x1B[${x}m`, close = `\x1B[${y}m`;
27469
- return function(txt) {
27470
- if (!$.enabled || txt == null)
27471
- return txt;
27472
- return open + (~("" + txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + close;
27473
- };
27474
- };
27475
- var FORCE_COLOR;
27476
- var NODE_DISABLE_COLORS;
27477
- var NO_COLOR;
27478
- var TERM;
27479
- var isTTY = true;
27480
- if (typeof process !== "undefined") {
27481
- ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
27482
- isTTY = process.stdout && process.stdout.isTTY;
27483
- }
27484
- var $ = {
27485
- enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY)
27486
- };
27487
- var reset = init(0, 0);
27488
- var bold = init(1, 22);
27489
- var dim = init(2, 22);
27490
- var italic = init(3, 23);
27491
- var underline = init(4, 24);
27492
- var inverse = init(7, 27);
27493
- var hidden = init(8, 28);
27494
- var strikethrough = init(9, 29);
27495
- var black = init(30, 39);
27496
- var red = init(31, 39);
27497
- var green = init(32, 39);
27498
- var yellow = init(33, 39);
27499
- var blue = init(34, 39);
27500
- var magenta = init(35, 39);
27501
- var cyan = init(36, 39);
27502
- var white = init(37, 39);
27503
- var gray = init(90, 39);
27504
- var grey = init(90, 39);
27505
- var bgBlack = init(40, 49);
27506
- var bgRed = init(41, 49);
27507
- var bgGreen = init(42, 49);
27508
- var bgYellow = init(43, 49);
27509
- var bgBlue = init(44, 49);
27510
- var bgMagenta = init(45, 49);
27511
- var bgCyan = init(46, 49);
27512
- var bgWhite = init(47, 49);
27513
-
27514
- // dist/utils/symbols.js
27515
- var STATUS = {
27516
- success: "\u2713",
27517
- error: "\u2717",
27518
- warning: "\u26A0",
27519
- info: "\u2139",
27520
- debug: "\u2022"
27521
- };
27522
- var TREE = {
27523
- branch: "\u251C\u2500",
27524
- last: "\u2514\u2500"
27525
- };
27526
- var BOX = {
27527
- topLeft: "\u250C",
27528
- topRight: "\u2510",
27529
- bottomLeft: "\u2514",
27530
- bottomRight: "\u2518",
27531
- horizontal: "\u2500",
27532
- vertical: "\u2502"
27533
- };
27534
-
27535
- // dist/utils/logger.js
27536
- var getTerminalWidth = function() {
27537
- const columns = process.stdout.columns;
27538
- return typeof columns === "number" && columns > 0 ? columns : 80;
27539
- };
27540
- var truncateText = function(text, maxLength) {
27541
- if (maxLength <= 0) {
27542
- return "";
27543
- }
27544
- if (text.length <= maxLength) {
27545
- return text;
27546
- }
27547
- if (maxLength <= 3) {
27548
- return ".".repeat(maxLength);
27549
- }
27550
- return `${text.slice(0, maxLength - 3)}...`;
27551
- };
27552
-
27553
- class Logger {
27554
- static instance;
27555
- static getInstance() {
27556
- if (!Logger.instance) {
27557
- Logger.instance = new Logger;
27558
- }
27559
- return Logger.instance;
27560
- }
27561
- info(message) {
27562
- console.log(cyan(STATUS.info), message);
27563
- }
27564
- success(message) {
27565
- console.log(green(STATUS.success), message);
27566
- }
27567
- warning(message) {
27568
- console.log(yellow(STATUS.warning), message);
27569
- }
27570
- warn(message) {
27571
- this.warning(message);
27572
- }
27573
- error(message) {
27574
- console.log(red(STATUS.error), message);
27575
- }
27576
- debug(message) {
27577
- console.log(dim(STATUS.debug), dim(message));
27578
- }
27579
- title(message) {
27580
- console.log(bold(cyan(message)));
27581
- }
27582
- subtitle(message) {
27583
- console.log(bold(message));
27584
- }
27585
- titleBox(message) {
27586
- const w = getTerminalWidth();
27587
- if (w < 8) {
27588
- console.log(bold(cyan(truncateText(message, w))));
27589
- return;
27590
- }
27591
- const maxInner = Math.max(4, w - 2);
27592
- const inner = Math.min(Math.max(message.length + 4, 40), maxInner);
27593
- const visibleMessage = truncateText(message, inner - 2);
27594
- const padR = Math.max(0, inner - visibleMessage.length - 2);
27595
- console.log(dim(BOX.topLeft + BOX.horizontal.repeat(inner) + BOX.topRight));
27596
- console.log(dim(BOX.vertical) + " " + bold(cyan(visibleMessage)) + " ".repeat(padR) + " " + dim(BOX.vertical));
27597
- console.log(dim(BOX.bottomLeft + BOX.horizontal.repeat(inner) + BOX.bottomRight));
27598
- }
27599
- section(title) {
27600
- const w = getTerminalWidth();
27601
- const lineLen = Math.max(0, w - title.length - 5);
27602
- console.log("");
27603
- console.log(bold(`${BOX.horizontal}${BOX.horizontal} ${title} ${BOX.horizontal.repeat(lineLen)}`));
27604
- }
27605
- tree(message, isLast) {
27606
- const connector = isLast ? TREE.last : TREE.branch;
27607
- console.log(`${connector} ${message}`);
27608
- }
27609
- }
27610
- var logger = Logger.getInstance();
27611
-
27612
- // dist/commands/init.js
28400
+ init_logger();
27613
28401
  init_fs();
27614
28402
 
27615
28403
  // dist/core/agentDetector.js
@@ -28270,6 +29058,7 @@ var getProjectName = function(cwd) {
28270
29058
  };
28271
29059
 
28272
29060
  // dist/commands/detect.js
29061
+ init_logger();
28273
29062
  init_skillsManager();
28274
29063
  init_agentManager();
28275
29064
  async function detectCommand(options2) {
@@ -28338,12 +29127,13 @@ async function detectCommand(options2) {
28338
29127
  }
28339
29128
 
28340
29129
  // dist/commands/sync.js
28341
- import {relative as relative5} from "path";
29130
+ init_logger();
29131
+ import {relative as relative6} from "path";
28342
29132
 
28343
29133
  // dist/core/propagator.js
28344
29134
  var import_gray_matter3 = __toESM(require_gray_matter(), 1);
28345
- import {promises as fs26} from "fs";
28346
- import {resolve as resolve9} from "path";
29135
+ import {promises as fs29} from "fs";
29136
+ import {resolve as resolve10} from "path";
28347
29137
 
28348
29138
  // node_modules/js-yaml/dist/js-yaml.mjs
28349
29139
  var isNothing = function(subject) {
@@ -30990,7 +31780,7 @@ class Propagator {
30990
31780
  warnings: [],
30991
31781
  resolvedTargets: []
30992
31782
  };
30993
- const agentsPath = resolve9(projectPath, "agents.md");
31783
+ const agentsPath = resolve10(projectPath, "agents.md");
30994
31784
  if (!await fileExists(agentsPath)) {
30995
31785
  result.success = false;
30996
31786
  result.errors.push("agents.md not found. Run `agentinit init` first.");
@@ -31075,7 +31865,7 @@ class Propagator {
31075
31865
  return [...generatedFiles.values()];
31076
31866
  }
31077
31867
  mergeGeneratedFile(projectPath, generatedFiles, output, agentId) {
31078
- const outputPath = resolve9(projectPath, output.path);
31868
+ const outputPath = resolve10(projectPath, output.path);
31079
31869
  const nextKind = output.kind ?? "file";
31080
31870
  const nextContent = nextKind === "file" || output.content !== undefined ? normalizeContent(output.content || "") : undefined;
31081
31871
  const existing = generatedFiles.get(outputPath);
@@ -31110,14 +31900,14 @@ class Propagator {
31110
31900
  resolvedTargets: []
31111
31901
  };
31112
31902
  const exists = await fileExists(generatedFile.path);
31113
- const existingStats = exists ? await fs26.lstat(generatedFile.path).catch(() => null) : null;
31903
+ const existingStats = exists ? await fs29.lstat(generatedFile.path).catch(() => null) : null;
31114
31904
  const existingContent = exists && !existingStats?.isSymbolicLink() ? await readFileIfExists(generatedFile.path) : null;
31115
31905
  const existingTarget = existingStats?.isSymbolicLink() ? await readSymlinkTarget(generatedFile.path) : null;
31116
31906
  if (options2.managedState && !options2.dryRun) {
31117
31907
  await options2.managedState.trackGeneratedPath(generatedFile.path, {
31118
31908
  kind: "file",
31119
31909
  source: "sync",
31120
- ignorePath: resolve9(projectPath, generatedFile.ignorePath)
31910
+ ignorePath: resolve10(projectPath, generatedFile.ignorePath)
31121
31911
  });
31122
31912
  }
31123
31913
  if (generatedFile.kind === "file" && exists && !existingStats?.isSymbolicLink() && existingContent === generatedFile.content) {
@@ -31141,12 +31931,12 @@ class Propagator {
31141
31931
  if (!options2.dryRun) {
31142
31932
  if (generatedFile.kind === "file") {
31143
31933
  if (existingStats?.isSymbolicLink()) {
31144
- await fs26.rm(generatedFile.path, { force: true }).catch(() => {
31934
+ await fs29.rm(generatedFile.path, { force: true }).catch(() => {
31145
31935
  });
31146
31936
  }
31147
31937
  await writeFile(generatedFile.path, generatedFile.content || "");
31148
31938
  } else {
31149
- const symlinkCreated = await createRelativeSymlink(resolve9(projectPath, generatedFile.target || ""), generatedFile.path);
31939
+ const symlinkCreated = await createRelativeSymlink(resolve10(projectPath, generatedFile.target || ""), generatedFile.path);
31150
31940
  if (!symlinkCreated) {
31151
31941
  await writeFile(generatedFile.path, generatedFile.content || "");
31152
31942
  result.warnings.push(`Could not create symlink for ${generatedFile.path}; wrote a copied file instead.`);
@@ -31251,7 +32041,7 @@ class Propagator {
31251
32041
  });
31252
32042
  }
31253
32043
  async buildAiderConfig(projectPath) {
31254
- const configPath = resolve9(projectPath, ".aider.conf.yml");
32044
+ const configPath = resolve10(projectPath, ".aider.conf.yml");
31255
32045
  const existingContent = await readFileIfExists(configPath);
31256
32046
  let document = {};
31257
32047
  if (existingContent) {
@@ -31318,14 +32108,14 @@ ${content}
31318
32108
 
31319
32109
  // dist/core/managedState.js
31320
32110
  init_fs();
31321
- import {promises as fs28} from "fs";
31322
- import {dirname as dirname4, join as join6, relative as relative4, resolve as resolve10} from "path";
32111
+ import {promises as fs31} from "fs";
32112
+ import {dirname as dirname5, join as join8, relative as relative5, resolve as resolve11} from "path";
31323
32113
  var toPosixPath = function(value) {
31324
32114
  return value.replace(/\\/g, "/");
31325
32115
  };
31326
32116
  async function pathType(targetPath) {
31327
32117
  try {
31328
- const stat = await fs28.lstat(targetPath);
32118
+ const stat = await fs31.lstat(targetPath);
31329
32119
  if (stat.isSymbolicLink()) {
31330
32120
  return "symlink";
31331
32121
  }
@@ -31335,20 +32125,20 @@ async function pathType(targetPath) {
31335
32125
  }
31336
32126
  }
31337
32127
  async function copyDirectory(src, dest) {
31338
- await fs28.mkdir(dest, { recursive: true });
31339
- const entries = await fs28.readdir(src, { withFileTypes: true });
32128
+ await fs31.mkdir(dest, { recursive: true });
32129
+ const entries = await fs31.readdir(src, { withFileTypes: true });
31340
32130
  for (const entry of entries) {
31341
- const srcPath = join6(src, entry.name);
31342
- const destPath = join6(dest, entry.name);
32131
+ const srcPath = join8(src, entry.name);
32132
+ const destPath = join8(dest, entry.name);
31343
32133
  if (entry.isDirectory()) {
31344
32134
  await copyDirectory(srcPath, destPath);
31345
32135
  } else if (entry.isSymbolicLink()) {
31346
- const target = await fs28.readlink(srcPath);
31347
- await fs28.mkdir(dirname4(destPath), { recursive: true });
31348
- await fs28.symlink(target, destPath);
32136
+ const target = await fs31.readlink(srcPath);
32137
+ await fs31.mkdir(dirname5(destPath), { recursive: true });
32138
+ await fs31.symlink(target, destPath);
31349
32139
  } else {
31350
- await fs28.mkdir(dirname4(destPath), { recursive: true });
31351
- await fs28.copyFile(srcPath, destPath);
32140
+ await fs31.mkdir(dirname5(destPath), { recursive: true });
32141
+ await fs31.copyFile(srcPath, destPath);
31352
32142
  }
31353
32143
  }
31354
32144
  }
@@ -31357,14 +32147,14 @@ async function copyPath(src, dest) {
31357
32147
  if (!type2) {
31358
32148
  return;
31359
32149
  }
31360
- await fs28.mkdir(dirname4(dest), { recursive: true });
32150
+ await fs31.mkdir(dirname5(dest), { recursive: true });
31361
32151
  if (type2 === "symlink") {
31362
- const target = await fs28.readlink(src);
31363
- await fs28.symlink(target, dest);
32152
+ const target = await fs31.readlink(src);
32153
+ await fs31.symlink(target, dest);
31364
32154
  } else if (type2 === "directory") {
31365
32155
  await copyDirectory(src, dest);
31366
32156
  } else {
31367
- await fs28.copyFile(src, dest);
32157
+ await fs31.copyFile(src, dest);
31368
32158
  }
31369
32159
  }
31370
32160
  var MANAGED_STATE_FILE = "managed-state.json";
@@ -31378,11 +32168,11 @@ class ManagedStateStore {
31378
32168
  this.state = state;
31379
32169
  }
31380
32170
  static async open(projectPath) {
31381
- const agentInitDir = join6(projectPath, ".agentinit");
31382
- const statePath = join6(agentInitDir, MANAGED_STATE_FILE);
32171
+ const agentInitDir = join8(projectPath, ".agentinit");
32172
+ const statePath = join8(agentInitDir, MANAGED_STATE_FILE);
31383
32173
  const emptyState = { version: 1, entries: [] };
31384
32174
  try {
31385
- const raw = await fs28.readFile(statePath, "utf8");
32175
+ const raw = await fs31.readFile(statePath, "utf8");
31386
32176
  const parsed = JSON.parse(raw);
31387
32177
  if (!parsed || parsed.version !== 1 || !Array.isArray(parsed.entries)) {
31388
32178
  return new ManagedStateStore(projectPath, emptyState);
@@ -31393,17 +32183,17 @@ class ManagedStateStore {
31393
32183
  }
31394
32184
  }
31395
32185
  get agentInitDir() {
31396
- return join6(this.projectPath, ".agentinit");
32186
+ return join8(this.projectPath, ".agentinit");
31397
32187
  }
31398
32188
  get stateFilePath() {
31399
- return join6(this.agentInitDir, MANAGED_STATE_FILE);
32189
+ return join8(this.agentInitDir, MANAGED_STATE_FILE);
31400
32190
  }
31401
32191
  get backupsDir() {
31402
- return join6(this.agentInitDir, BACKUPS_DIR);
32192
+ return join8(this.agentInitDir, BACKUPS_DIR);
31403
32193
  }
31404
32194
  normalizeRelativePath(targetPath, preserveTrailingSlash = false) {
31405
32195
  const hasTrailingSlash = preserveTrailingSlash && /[\\/]$/.test(targetPath);
31406
- const relativePath = relative4(this.projectPath, resolve10(targetPath));
32196
+ const relativePath = relative5(this.projectPath, resolve11(targetPath));
31407
32197
  const normalizedPath = toPosixPath(relativePath);
31408
32198
  if (hasTrailingSlash && normalizedPath) {
31409
32199
  return normalizedPath.endsWith("/") ? normalizedPath : `${normalizedPath}/`;
@@ -31421,14 +32211,14 @@ class ManagedStateStore {
31421
32211
  }
31422
32212
  if (type2 === "symlink") {
31423
32213
  try {
31424
- const backupLinkTarget = await fs28.readlink(targetPath);
32214
+ const backupLinkTarget = await fs31.readlink(targetPath);
31425
32215
  return { backupLinkTarget };
31426
32216
  } catch {
31427
32217
  return {};
31428
32218
  }
31429
32219
  }
31430
32220
  const relativeTargetPath = this.normalizeRelativePath(targetPath);
31431
- const backupPath = join6(this.backupsDir, relativeTargetPath);
32221
+ const backupPath = join8(this.backupsDir, relativeTargetPath);
31432
32222
  if (!await fileExists(backupPath)) {
31433
32223
  await copyPath(targetPath, backupPath);
31434
32224
  }
@@ -31463,20 +32253,20 @@ class ManagedStateStore {
31463
32253
  if (this.state.entries.length === 0) {
31464
32254
  return [];
31465
32255
  }
31466
- const paths5 = new Set([
32256
+ const paths6 = new Set([
31467
32257
  ".agentinit/managed-state.json",
31468
32258
  ".agentinit/backups/"
31469
32259
  ]);
31470
32260
  for (const entry of this.state.entries) {
31471
32261
  if (entry.ignorePath) {
31472
- paths5.add(entry.ignorePath);
32262
+ paths6.add(entry.ignorePath);
31473
32263
  }
31474
32264
  }
31475
- return [...paths5];
32265
+ return [...paths6];
31476
32266
  }
31477
32267
  async save() {
31478
- await fs28.mkdir(this.agentInitDir, { recursive: true });
31479
- await fs28.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2), "utf8");
32268
+ await fs31.mkdir(this.agentInitDir, { recursive: true });
32269
+ await fs31.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2), "utf8");
31480
32270
  }
31481
32271
  async revertAll(options2 = {}) {
31482
32272
  const summary = {
@@ -31486,34 +32276,34 @@ class ManagedStateStore {
31486
32276
  };
31487
32277
  const entries = [...this.state.entries].sort((a, b) => b.path.length - a.path.length);
31488
32278
  for (const entry of entries) {
31489
- const absolutePath = resolve10(this.projectPath, entry.path);
31490
- const backupPath = entry.backupPath ? resolve10(this.projectPath, entry.backupPath) : null;
32279
+ const absolutePath = resolve11(this.projectPath, entry.path);
32280
+ const backupPath = entry.backupPath ? resolve11(this.projectPath, entry.backupPath) : null;
31491
32281
  const backupLinkTarget = entry.backupLinkTarget;
31492
32282
  if (entry.existedBefore && backupLinkTarget !== undefined) {
31493
32283
  if (!options2.dryRun) {
31494
- await fs28.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32284
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31495
32285
  });
31496
- await fs28.mkdir(dirname4(absolutePath), { recursive: true });
31497
- await fs28.symlink(backupLinkTarget, absolutePath);
32286
+ await fs31.mkdir(dirname5(absolutePath), { recursive: true });
32287
+ await fs31.symlink(backupLinkTarget, absolutePath);
31498
32288
  }
31499
32289
  summary.restored++;
31500
32290
  } else if (entry.existedBefore && backupPath && await fileExists(backupPath)) {
31501
32291
  if (!options2.dryRun) {
31502
- await fs28.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32292
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31503
32293
  });
31504
32294
  await copyPath(backupPath, absolutePath);
31505
32295
  }
31506
32296
  summary.restored++;
31507
32297
  } else {
31508
32298
  if (!options2.dryRun) {
31509
- await fs28.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32299
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31510
32300
  });
31511
32301
  }
31512
32302
  summary.removed++;
31513
32303
  }
31514
32304
  if (!options2.keepBackups && backupPath && await fileExists(backupPath)) {
31515
32305
  if (!options2.dryRun) {
31516
- await fs28.rm(backupPath, { recursive: true, force: true }).catch(() => {
32306
+ await fs31.rm(backupPath, { recursive: true, force: true }).catch(() => {
31517
32307
  });
31518
32308
  }
31519
32309
  summary.backupsRemoved++;
@@ -31521,16 +32311,16 @@ class ManagedStateStore {
31521
32311
  }
31522
32312
  if (!options2.dryRun) {
31523
32313
  this.state.entries.length = 0;
31524
- await fs28.rm(this.stateFilePath, { force: true }).catch(() => {
32314
+ await fs31.rm(this.stateFilePath, { force: true }).catch(() => {
31525
32315
  });
31526
32316
  if (!options2.keepBackups) {
31527
- await fs28.rm(this.backupsDir, { recursive: true, force: true }).catch(() => {
32317
+ await fs31.rm(this.backupsDir, { recursive: true, force: true }).catch(() => {
31528
32318
  });
31529
32319
  }
31530
32320
  try {
31531
- const remainingEntries = await fs28.readdir(this.agentInitDir);
32321
+ const remainingEntries = await fs31.readdir(this.agentInitDir);
31532
32322
  if (remainingEntries.length === 0) {
31533
- await fs28.rm(this.agentInitDir, { recursive: true, force: true });
32323
+ await fs31.rm(this.agentInitDir, { recursive: true, force: true });
31534
32324
  }
31535
32325
  } catch {
31536
32326
  }
@@ -31580,7 +32370,7 @@ async function syncCommand(options2) {
31580
32370
  for (const change of result.changes) {
31581
32371
  const action = change.action === "created" ? "\u2795" : change.action === "updated" ? "\uD83D\uDCDD" : "\uD83D\uDCBE";
31582
32372
  const names = change.agents.map((id) => agentManager5.getAgentById(id)?.name || id).join(", ");
31583
- logger.info(` ${action} ${relative5(cwd, change.file) || change.file}`);
32373
+ logger.info(` ${action} ${relative6(cwd, change.file) || change.file}`);
31584
32374
  logger.info(` Agents: ${names}`);
31585
32375
  }
31586
32376
  if (options2.backup && result.changes.some((c) => c.action === "backed_up")) {
@@ -31609,7 +32399,9 @@ async function syncCommand(options2) {
31609
32399
  }
31610
32400
 
31611
32401
  // dist/commands/apply.js
31612
- import {relative as relative6} from "path";
32402
+ init_colors();
32403
+ init_logger();
32404
+ import {relative as relative7} from "path";
31613
32405
 
31614
32406
  // dist/types/index.js
31615
32407
  var MCPServerType;
@@ -31875,12 +32667,12 @@ class MCPParser {
31875
32667
  // dist/constants/mcp.js
31876
32668
  import {readFileSync as readFileSync2} from "fs";
31877
32669
  import {fileURLToPath} from "url";
31878
- import {dirname as dirname5, join as join7} from "path";
32670
+ import {dirname as dirname6, join as join9} from "path";
31879
32671
  var getPackageVersion = function() {
31880
32672
  try {
31881
32673
  const __filename2 = fileURLToPath(import.meta.url);
31882
- const __dirname2 = dirname5(__filename2);
31883
- const packageJsonPath = join7(__dirname2, "../../package.json");
32674
+ const __dirname2 = dirname6(__filename2);
32675
+ const packageJsonPath = join9(__dirname2, "../../package.json");
31884
32676
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
31885
32677
  return packageJson.version || "1.0.0";
31886
32678
  } catch {
@@ -31911,17 +32703,17 @@ import {readFileSync as readFileSync4} from "fs";
31911
32703
 
31912
32704
  // dist/core/rulesTemplateLoader.js
31913
32705
  var toml = __toESM(require_toml(), 1);
31914
- import {resolve as resolve11, dirname as dirname6} from "path";
32706
+ import {resolve as resolve12, dirname as dirname7} from "path";
31915
32707
  import {fileURLToPath as fileURLToPath2} from "url";
31916
32708
  import {readFileSync as readFileSync3, readdirSync, existsSync as existsSync2} from "fs";
31917
32709
  var __filename2 = fileURLToPath2(import.meta.url);
31918
- var __dirname2 = dirname6(__filename2);
32710
+ var __dirname2 = dirname7(__filename2);
31919
32711
 
31920
32712
  class RulesTemplateLoader {
31921
32713
  templatesPath;
31922
32714
  templates = new Map;
31923
32715
  constructor() {
31924
- this.templatesPath = resolve11(__dirname2, "../templates/rules");
32716
+ this.templatesPath = resolve12(__dirname2, "../templates/rules");
31925
32717
  this.loadTemplates();
31926
32718
  }
31927
32719
  loadTemplates() {
@@ -31931,7 +32723,7 @@ class RulesTemplateLoader {
31931
32723
  const files = readdirSync(this.templatesPath).filter((file) => file.endsWith(".toml"));
31932
32724
  for (const file of files) {
31933
32725
  try {
31934
- const filePath = resolve11(this.templatesPath, file);
32726
+ const filePath = resolve12(this.templatesPath, file);
31935
32727
  const content = readFileSync3(filePath, "utf-8");
31936
32728
  const parsed = toml.default.parse(content);
31937
32729
  const template = {
@@ -37503,7 +38295,7 @@ class Protocol {
37503
38295
  }
37504
38296
  request(request, resultSchema, options2) {
37505
38297
  const { relatedRequestId, resumptionToken, onresumptiontoken } = options2 !== null && options2 !== undefined ? options2 : {};
37506
- return new Promise((resolve12, reject) => {
38298
+ return new Promise((resolve13, reject) => {
37507
38299
  var _a, _b, _c, _d, _e, _f;
37508
38300
  if (!this._transport) {
37509
38301
  reject(new Error("Not connected"));
@@ -37554,7 +38346,7 @@ class Protocol {
37554
38346
  }
37555
38347
  try {
37556
38348
  const result = resultSchema.parse(response.result);
37557
- resolve12(result);
38349
+ resolve13(result);
37558
38350
  } catch (error) {
37559
38351
  reject(error);
37560
38352
  }
@@ -37934,7 +38726,7 @@ class StdioClientTransport {
37934
38726
  if (this._process) {
37935
38727
  throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
37936
38728
  }
37937
- return new Promise((resolve12, reject) => {
38729
+ return new Promise((resolve13, reject) => {
37938
38730
  var _a, _b, _c, _d, _e;
37939
38731
  this._process = import_cross_spawn.default(this._serverParams.command, (_a = this._serverParams.args) !== null && _a !== undefined ? _a : [], {
37940
38732
  env: {
@@ -37957,7 +38749,7 @@ class StdioClientTransport {
37957
38749
  (_b2 = this.onerror) === null || _b2 === undefined || _b2.call(this, error);
37958
38750
  });
37959
38751
  this._process.on("spawn", () => {
37960
- resolve12();
38752
+ resolve13();
37961
38753
  });
37962
38754
  this._process.on("close", (_code) => {
37963
38755
  var _a2;
@@ -38012,16 +38804,16 @@ class StdioClientTransport {
38012
38804
  this._readBuffer.clear();
38013
38805
  }
38014
38806
  send(message) {
38015
- return new Promise((resolve12) => {
38807
+ return new Promise((resolve13) => {
38016
38808
  var _a;
38017
38809
  if (!((_a = this._process) === null || _a === undefined ? undefined : _a.stdin)) {
38018
38810
  throw new Error("Not connected");
38019
38811
  }
38020
38812
  const json2 = serializeMessage(message);
38021
38813
  if (this._process.stdin.write(json2)) {
38022
- resolve12();
38814
+ resolve13();
38023
38815
  } else {
38024
- this._process.stdin.once("drain", resolve12);
38816
+ this._process.stdin.once("drain", resolve13);
38025
38817
  }
38026
38818
  });
38027
38819
  }
@@ -39443,7 +40235,7 @@ class SSEClientTransport {
39443
40235
  _startOrAuth() {
39444
40236
  var _a, _b, _c;
39445
40237
  const fetchImpl = (_c = (_b = (_a = this === null || this === undefined ? undefined : this._eventSourceInit) === null || _a === undefined ? undefined : _a.fetch) !== null && _b !== undefined ? _b : this._fetch) !== null && _c !== undefined ? _c : fetch;
39446
- return new Promise((resolve12, reject) => {
40238
+ return new Promise((resolve13, reject) => {
39447
40239
  this._eventSource = new EventSource(this._url.href, {
39448
40240
  ...this._eventSourceInit,
39449
40241
  fetch: async (url, init2) => {
@@ -39463,7 +40255,7 @@ class SSEClientTransport {
39463
40255
  this._eventSource.onerror = (event) => {
39464
40256
  var _a2;
39465
40257
  if (event.code === 401 && this._authProvider) {
39466
- this._authThenStart().then(resolve12, reject);
40258
+ this._authThenStart().then(resolve13, reject);
39467
40259
  return;
39468
40260
  }
39469
40261
  const error = new SseError(event.code, event.message, event);
@@ -39486,7 +40278,7 @@ class SSEClientTransport {
39486
40278
  this.close();
39487
40279
  return;
39488
40280
  }
39489
- resolve12();
40281
+ resolve13();
39490
40282
  });
39491
40283
  this._eventSource.onmessage = (event) => {
39492
40284
  var _a2, _b2;
@@ -39563,8 +40355,11 @@ class SSEClientTransport {
39563
40355
 
39564
40356
  // dist/core/mcpClient.js
39565
40357
  init_lib();
40358
+ init_colors();
40359
+ init_logger();
39566
40360
 
39567
40361
  // dist/utils/packageVersion.js
40362
+ init_logger();
39568
40363
  function extractExplicitVersion(packageSpec) {
39569
40364
  const versionMatch = packageSpec.match(/@([\d]+\.[\d]+\.[\d]+(?:[-+].+)?$)/);
39570
40365
  if (versionMatch && versionMatch[1]) {
@@ -40150,11 +40945,11 @@ class MCPVerifier {
40150
40945
  }
40151
40946
 
40152
40947
  // dist/core/gitignoreManager.js
40153
- import {promises as fs31} from "fs";
40154
- import {dirname as dirname7, join as join8} from "path";
40948
+ import {promises as fs34} from "fs";
40949
+ import {dirname as dirname8, join as join10} from "path";
40155
40950
  var normalizeIgnorePath = function(projectPath, value) {
40156
- const relative6 = value.startsWith(projectPath) ? value.slice(projectPath.length + 1) : value;
40157
- const normalized = relative6.replace(/\\/g, "/").replace(/^\/+/, "");
40951
+ const relative7 = value.startsWith(projectPath) ? value.slice(projectPath.length + 1) : value;
40952
+ const normalized = relative7.replace(/\\/g, "/").replace(/^\/+/, "");
40158
40953
  if (!normalized) {
40159
40954
  return normalized;
40160
40955
  }
@@ -40197,13 +40992,13 @@ var updateManagedBlock = function(existingContent, entries) {
40197
40992
  }
40198
40993
  return content;
40199
40994
  };
40200
- async function updateManagedIgnoreFile(projectPath, paths5, options2 = {}) {
40201
- const ignoreFile = options2.local ? join8(".git", "info", "exclude") : ".gitignore";
40202
- const ignoreFilePath = join8(projectPath, ignoreFile);
40995
+ async function updateManagedIgnoreFile(projectPath, paths6, options2 = {}) {
40996
+ const ignoreFile = options2.local ? join10(".git", "info", "exclude") : ".gitignore";
40997
+ const ignoreFilePath = join10(projectPath, ignoreFile);
40203
40998
  if (options2.local) {
40204
- const gitDir = join8(projectPath, ".git");
40999
+ const gitDir = join10(projectPath, ".git");
40205
41000
  try {
40206
- const stat = await fs31.stat(gitDir);
41001
+ const stat = await fs34.stat(gitDir);
40207
41002
  if (!stat.isDirectory()) {
40208
41003
  throw new Error;
40209
41004
  }
@@ -40211,24 +41006,24 @@ async function updateManagedIgnoreFile(projectPath, paths5, options2 = {}) {
40211
41006
  throw new Error("Cannot update .git/info/exclude because this project is not a Git repository");
40212
41007
  }
40213
41008
  }
40214
- const normalizedPaths = [...new Set(paths5.map((path) => normalizeIgnorePath(projectPath, path)).filter(Boolean))].sort();
41009
+ const normalizedPaths = [...new Set(paths6.map((path) => normalizeIgnorePath(projectPath, path)).filter(Boolean))].sort();
40215
41010
  let existingContent = "";
40216
41011
  try {
40217
- existingContent = await fs31.readFile(ignoreFilePath, "utf8");
41012
+ existingContent = await fs34.readFile(ignoreFilePath, "utf8");
40218
41013
  } catch {
40219
41014
  existingContent = "";
40220
41015
  }
40221
41016
  const updatedContent = updateManagedBlock(existingContent, normalizedPaths);
40222
- await fs31.mkdir(dirname7(ignoreFilePath), { recursive: true });
40223
- await fs31.writeFile(ignoreFilePath, updatedContent, "utf8");
41017
+ await fs34.mkdir(dirname8(ignoreFilePath), { recursive: true });
41018
+ await fs34.writeFile(ignoreFilePath, updatedContent, "utf8");
40224
41019
  return ignoreFilePath;
40225
41020
  }
40226
41021
  async function removeManagedIgnoreBlock(projectPath, options2 = {}) {
40227
- const ignoreFile = options2.local ? join8(".git", "info", "exclude") : ".gitignore";
40228
- const ignoreFilePath = join8(projectPath, ignoreFile);
41022
+ const ignoreFile = options2.local ? join10(".git", "info", "exclude") : ".gitignore";
41023
+ const ignoreFilePath = join10(projectPath, ignoreFile);
40229
41024
  let content;
40230
41025
  try {
40231
- content = await fs31.readFile(ignoreFilePath, "utf8");
41026
+ content = await fs34.readFile(ignoreFilePath, "utf8");
40232
41027
  } catch {
40233
41028
  return false;
40234
41029
  }
@@ -40244,13 +41039,13 @@ async function removeManagedIgnoreBlock(projectPath, options2 = {}) {
40244
41039
  const afterBlock = content.slice(endIndex + END_MARKER.length).replace(/^\n+/, "");
40245
41040
  let nextContent = `${beforeBlock}${afterBlock}`.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "");
40246
41041
  if (nextContent.trim() === "") {
40247
- await fs31.rm(ignoreFilePath, { force: true }).catch(() => {
41042
+ await fs34.rm(ignoreFilePath, { force: true }).catch(() => {
40248
41043
  });
40249
41044
  } else {
40250
41045
  if (!nextContent.endsWith("\n")) {
40251
41046
  nextContent += "\n";
40252
41047
  }
40253
- await fs31.writeFile(ignoreFilePath, nextContent, "utf8");
41048
+ await fs34.writeFile(ignoreFilePath, nextContent, "utf8");
40254
41049
  }
40255
41050
  return true;
40256
41051
  }
@@ -40261,12 +41056,12 @@ var END_MARKER = "# END AgentInit Generated Files";
40261
41056
  init_agentManager();
40262
41057
  init_skillsManager();
40263
41058
  init_fs();
40264
- import {dirname as dirname8, join as join9} from "path";
41059
+ import {dirname as dirname9, join as join11} from "path";
40265
41060
  async function discoverProjectSkills(projectPath, skillsManager4) {
40266
41061
  const sources = [];
40267
41062
  const skills2 = new Map;
40268
41063
  for (const sourceDir of PROJECT_SKILL_SOURCE_DIRS) {
40269
- const absoluteSourceDir = join9(projectPath, sourceDir);
41064
+ const absoluteSourceDir = join11(projectPath, sourceDir);
40270
41065
  if (!await fileExists(absoluteSourceDir)) {
40271
41066
  continue;
40272
41067
  }
@@ -40339,7 +41134,7 @@ async function applyProjectSkills(projectPath, targetAgentIds, managedState2, op
40339
41134
  await managedState2.trackGeneratedPath(generatedPath, {
40340
41135
  kind: "directory",
40341
41136
  source: "skills",
40342
- ignorePath: `${dirname8(generatedPath)}/`
41137
+ ignorePath: `${dirname9(generatedPath)}/`
40343
41138
  });
40344
41139
  }
40345
41140
  }
@@ -40450,7 +41245,7 @@ async function applyProjectCommand(options2) {
40450
41245
  if (change.action === "backed_up")
40451
41246
  return;
40452
41247
  const names = change.agents.map((id) => agentManager7.getAgentById(id)?.name || id).join(", ");
40453
- logger.info(` ${relative6(cwd, change.file) || change.file}`);
41248
+ logger.info(` ${relative7(cwd, change.file) || change.file}`);
40454
41249
  logger.info(` Agents: ${names}`);
40455
41250
  });
40456
41251
  }
@@ -40468,7 +41263,7 @@ async function applyProjectCommand(options2) {
40468
41263
  installsByPath.set(item.path, existing);
40469
41264
  }
40470
41265
  for (const [path, details] of installsByPath) {
40471
- logger.info(` ${relative6(cwd, path) || path}`);
41266
+ logger.info(` ${relative7(cwd, path) || path}`);
40472
41267
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
40473
41268
  logger.info(` Skills: ${[...details.skills].join(", ")}`);
40474
41269
  }
@@ -40910,6 +41705,7 @@ var LEGACY_APPLY_FLAGS = new Set([
40910
41705
  ]);
40911
41706
 
40912
41707
  // dist/commands/verifyMcp.js
41708
+ init_logger();
40913
41709
  init_agentManager();
40914
41710
  async function verifyMcpCommand(args) {
40915
41711
  const cwd = process.cwd();
@@ -41115,6 +41911,7 @@ async function verifyMcpCommand(args) {
41115
41911
  }
41116
41912
 
41117
41913
  // dist/commands/revert.js
41914
+ init_logger();
41118
41915
  async function revertCommand(options2) {
41119
41916
  const cwd = process.cwd();
41120
41917
  logger.titleBox("AgentInit Revert");
@@ -41159,6 +41956,7 @@ async function revertCommand(options2) {
41159
41956
  }
41160
41957
 
41161
41958
  // dist/utils/colors.js
41959
+ init_colors();
41162
41960
  var colorsEnabled = function() {
41163
41961
  const env2 = process.env || {};
41164
41962
  const isTTY2 = !!process.stdout?.isTTY;
@@ -41172,6 +41970,7 @@ function orange(text) {
41172
41970
  }
41173
41971
 
41174
41972
  // dist/commands/config.js
41973
+ init_logger();
41175
41974
  init_marketplaceRegistry();
41176
41975
  init_userConfig();
41177
41976
  var sortConfig = function(config) {
@@ -41348,12 +42147,15 @@ var BUILT_IN_VERIFIED_REPOS = new Set(getBuiltInVerifiedGithubRepos());
41348
42147
 
41349
42148
  // dist/commands/skills.js
41350
42149
  var import_prompts3 = __toESM(require_prompts3(), 1);
41351
- import {homedir as homedir6} from "os";
41352
- import {relative as relative7, resolve as resolve12} from "path";
42150
+ init_colors();
42151
+ init_logger();
42152
+ import {homedir as homedir7} from "os";
42153
+ import {relative as relative8, resolve as resolve13} from "path";
41353
42154
 
41354
42155
  // dist/utils/promptUtils.js
41355
42156
  var import_prompts2 = __toESM(require_prompts3(), 1);
41356
42157
  import {createRequire as createRequire2} from "module";
42158
+ init_logger();
41357
42159
  function enableUppercaseToggleAllForMultiselectPrompt() {
41358
42160
  if (uppercaseToggleAllPatched) {
41359
42161
  return;
@@ -41367,7 +42169,22 @@ function enableUppercaseToggleAllForMultiselectPrompt() {
41367
42169
  }
41368
42170
  const originalHandler = prototype._;
41369
42171
  prototype._ = function agentinitMultiselectHandler(input, key) {
41370
- return originalHandler.call(this, input === "A" ? "a" : input, key);
42172
+ if (this.__agentinitHotkeyBusy) {
42173
+ return;
42174
+ }
42175
+ const normalizedInput = input === "A" ? "a" : input;
42176
+ const hotkeyHandler = this.__agentinitHotkeys?.[normalizedInput] || this.__agentinitHotkeys?.[input];
42177
+ if (hotkeyHandler) {
42178
+ this.__agentinitHotkeyBusy = true;
42179
+ Promise.resolve(hotkeyHandler(this, normalizedInput, key)).finally(() => {
42180
+ this.__agentinitHotkeyBusy = false;
42181
+ if (typeof this.render === "function") {
42182
+ this.render();
42183
+ }
42184
+ });
42185
+ return;
42186
+ }
42187
+ return originalHandler.call(this, normalizedInput, key);
41371
42188
  };
41372
42189
  Object.defineProperty(prototype, "__agentinitUppercaseToggleAllPatched", {
41373
42190
  value: true,
@@ -41380,12 +42197,52 @@ function enableUppercaseToggleAllForMultiselectPrompt() {
41380
42197
  }
41381
42198
  async function promptMultiselect(options2) {
41382
42199
  enableUppercaseToggleAllForMultiselectPrompt();
41383
- return import_prompts2.default({
42200
+ const userOnRender = options2.onRenderPrompt;
42201
+ const hint = options2.hint;
42202
+ let requestedAction;
42203
+ let promptInstance;
42204
+ const closeWithCurrentSelection = (prompt) => {
42205
+ if (typeof prompt.close !== "function") {
42206
+ return false;
42207
+ }
42208
+ prompt.done = true;
42209
+ prompt.aborted = false;
42210
+ prompt.fire?.();
42211
+ prompt.render?.();
42212
+ prompt.out?.write("\n");
42213
+ prompt.close();
42214
+ return true;
42215
+ };
42216
+ const hotkeys = options2.hotkeys ? Object.fromEntries(Object.entries(options2.hotkeys).map(([key, handler]) => [
42217
+ key,
42218
+ (prompt, input, pressedKey) => handler({
42219
+ prompt,
42220
+ requestAction: (action) => {
42221
+ requestedAction = action;
42222
+ },
42223
+ closeWithCurrentSelection: () => closeWithCurrentSelection(prompt)
42224
+ }, input, pressedKey)
42225
+ ])) : undefined;
42226
+ const response = await import_prompts2.default({
41384
42227
  ...options2,
41385
42228
  type: "multiselect",
41386
42229
  instructions: options2.instructions ?? false,
41387
- hint: options2.hint ?? MULTISELECT_TOGGLE_ALL_HINT
42230
+ hint: typeof hint === "function" ? hint() : hint ?? MULTISELECT_TOGGLE_ALL_HINT,
42231
+ onRender() {
42232
+ promptInstance = this;
42233
+ if (hotkeys) {
42234
+ this.__agentinitHotkeys = hotkeys;
42235
+ } else {
42236
+ delete this.__agentinitHotkeys;
42237
+ }
42238
+ this.hint = typeof hint === "function" ? hint() : hint ?? MULTISELECT_TOGGLE_ALL_HINT;
42239
+ userOnRender?.(this);
42240
+ }
41388
42241
  });
42242
+ if (requestedAction) {
42243
+ response.__agentinitAction = requestedAction;
42244
+ }
42245
+ return response;
41389
42246
  }
41390
42247
  async function selectBundlePlugins(entries, actionLabel, options2 = {}) {
41391
42248
  if (options2.selectAll) {
@@ -41418,11 +42275,86 @@ init_skillsManager();
41418
42275
  init_pluginManager();
41419
42276
  init_marketplaceRegistry();
41420
42277
  init_agentManager();
42278
+ init_installLock();
42279
+ init_fs();
42280
+
42281
+ // dist/utils/lockSource.js
42282
+ function lockSourceToSpecifier(source) {
42283
+ if (source.type === "marketplace" && source.marketplace && source.pluginName) {
42284
+ return {
42285
+ source: source.pluginName,
42286
+ from: source.marketplace,
42287
+ ...source.prefix ? { prefix: source.prefix } : {}
42288
+ };
42289
+ }
42290
+ if (source.type === "github") {
42291
+ if (source.owner && source.repo) {
42292
+ return {
42293
+ source: source.subpath ? `${source.owner}/${source.repo}/${source.subpath}` : `${source.owner}/${source.repo}`,
42294
+ ...source.prefix ? { prefix: source.prefix } : {}
42295
+ };
42296
+ }
42297
+ if (source.url) {
42298
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42299
+ }
42300
+ }
42301
+ if (source.type === "gitlab") {
42302
+ if (source.owner && source.repo) {
42303
+ const prefix = `gitlab:${source.owner}/${source.repo}`;
42304
+ return {
42305
+ source: source.subpath ? `${prefix}//${source.subpath}` : prefix,
42306
+ ...source.prefix ? { prefix: source.prefix } : {}
42307
+ };
42308
+ }
42309
+ if (source.url) {
42310
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42311
+ }
42312
+ }
42313
+ if (source.type === "bitbucket") {
42314
+ if (source.owner && source.repo) {
42315
+ const prefix = `bitbucket:${source.owner}/${source.repo}`;
42316
+ return {
42317
+ source: source.subpath ? `${prefix}/${source.subpath}` : prefix,
42318
+ ...source.prefix ? { prefix: source.prefix } : {}
42319
+ };
42320
+ }
42321
+ if (source.url) {
42322
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42323
+ }
42324
+ }
42325
+ if (source.type === "local" && source.path) {
42326
+ return { source: source.path, ...source.prefix ? { prefix: source.prefix } : {} };
42327
+ }
42328
+ return null;
42329
+ }
42330
+ function formatLockSource(source) {
42331
+ const specifier = lockSourceToSpecifier(source);
42332
+ if (!specifier) {
42333
+ return source.type;
42334
+ }
42335
+ const formatted = specifier.from ? `${specifier.from}/${specifier.source}` : specifier.source;
42336
+ return specifier.prefix ? `${formatted} [prefix: ${specifier.prefix}]` : formatted;
42337
+ }
42338
+
42339
+ // dist/commands/skills.js
41421
42340
  init_skills();
42341
+ var normalizeSkillPrefix = function(prefix) {
42342
+ return prefix?.trim() ?? "";
42343
+ };
42344
+ var matchesSelectedSkillName = function(skillName, selectedNames, prefix) {
42345
+ if (selectedNames.has(skillName.toLowerCase())) {
42346
+ return true;
42347
+ }
42348
+ const normalizedPrefix = normalizeSkillPrefix(prefix);
42349
+ if (!normalizedPrefix) {
42350
+ return false;
42351
+ }
42352
+ return selectedNames.has(`${normalizedPrefix}${skillName}`.toLowerCase());
42353
+ };
41422
42354
  function registerSkillsCommand(program2) {
41423
42355
  const marketplaceHelp = getMarketplaceIds().join(", ");
41424
42356
  const skills3 = program2.command("skills").description("Manage agent skills");
41425
- skills3.command("add <source>").description("Add skills from a marketplace, GitHub repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("--copy", "Copy skill files instead of symlinking").option("-y, --yes", "Skip prompts, auto-detect project-configured agents, and apply available skill updates").action(async (source, options2) => {
42357
+ skills3.command("add <source>").description("Add skills from a marketplace, hosted Git repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("--copy", "Copy skill files instead of symlinking").option("--prefix <prefix>", "Prefix installed skill names").option("--no-scan", "Skip security scanning before installation").option("--allow-risky", "Install even if scanning finds high-risk patterns").option("-y, --yes", "Skip prompts, auto-detect project-configured agents, and apply available skill updates").action(async (source, options2) => {
41426
42358
  logger.titleBox("AgentInit Skills");
41427
42359
  const agentManager9 = new AgentManager;
41428
42360
  const skillsManager5 = new SkillsManager(agentManager9);
@@ -41501,11 +42433,12 @@ function registerSkillsCommand(program2) {
41501
42433
  let targetAgents = options2.agent;
41502
42434
  let targetGlobal = options2.global;
41503
42435
  let selectedSkillNames = options2.skill;
42436
+ let installPrefix = options2.prefix;
41504
42437
  if (!options2.yes && (!selectedSkillNames || selectedSkillNames.length === 0) && preparedSkills.length > 1) {
41505
42438
  if (selectedPluginNames && selectedPluginNames.length > 1) {
41506
42439
  logger.info("Multiple bundled plugins selected; installing all skills from each selected plugin. Use --skill to filter by skill name.");
41507
42440
  } else {
41508
- const skillSelection = await resolveInteractiveSkillSelection(preparedSkills);
42441
+ const skillSelection = await resolveInteractiveSkillSelection(preparedSkills, installPrefix);
41509
42442
  if (skillSelection.aborted) {
41510
42443
  await skillsManager5.discardPreparedSource(source, process.cwd(), {
41511
42444
  from: options2.from
@@ -41513,11 +42446,12 @@ function registerSkillsCommand(program2) {
41513
42446
  return;
41514
42447
  }
41515
42448
  selectedSkillNames = skillSelection.skills;
42449
+ installPrefix = skillSelection.prefix;
41516
42450
  }
41517
42451
  }
41518
42452
  if (!targetAgents && !options2.yes) {
41519
42453
  const selectedSkillNameSet = selectedSkillNames && selectedSkillNames.length > 0 ? new Set(selectedSkillNames.map((name) => name.toLowerCase())) : undefined;
41520
- const filteredPreviewSkills = selectedSkillNameSet ? preparedSkills.filter((skill) => selectedSkillNameSet.has(skill.name.toLowerCase())) : preparedSkills;
42454
+ const filteredPreviewSkills = selectedSkillNameSet ? preparedSkills.filter((skill) => matchesSelectedSkillName(skill.name, selectedSkillNameSet, installPrefix)) : preparedSkills;
41521
42455
  const selection = await resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, process.cwd(), {
41522
42456
  from: options2.from,
41523
42457
  global: options2.global,
@@ -41550,7 +42484,10 @@ function registerSkillsCommand(program2) {
41550
42484
  ...targetGlobal !== undefined ? { global: targetGlobal } : {},
41551
42485
  ...targetAgents !== undefined ? { agents: targetAgents } : {},
41552
42486
  ...selectedSkillNames !== undefined ? { skills: selectedSkillNames } : {},
42487
+ ...installPrefix !== undefined ? { prefix: installPrefix } : {},
41553
42488
  ...options2.copy !== undefined ? { copy: options2.copy } : {},
42489
+ ...options2.scan !== undefined ? { scan: options2.scan } : {},
42490
+ ...options2.allowRisky !== undefined ? { allowRisky: options2.allowRisky } : {},
41554
42491
  ...pluginName !== undefined ? { pluginName } : {},
41555
42492
  ...options2.yes !== undefined ? { yes: options2.yes } : {},
41556
42493
  ...confirmUpdate !== undefined ? { confirmUpdate } : {}
@@ -41609,7 +42546,7 @@ function registerSkillsCommand(program2) {
41609
42546
  const mode = skill.mode === "symlink" ? " (canonical)" : "";
41610
42547
  const scope = skill.scope === "global" ? " [global]" : "";
41611
42548
  logger.info(`\n ${green(skill.name)} - ${skill.description}${scope}${mode}`);
41612
- logger.info(` Path: ${relative7(process.cwd(), skill.path) || skill.path}`);
42549
+ logger.info(` Path: ${relative8(process.cwd(), skill.path) || skill.path}`);
41613
42550
  logger.info(` Agents: ${[...skill.agents].join(", ")}`);
41614
42551
  }
41615
42552
  });
@@ -41648,7 +42585,207 @@ function registerSkillsCommand(program2) {
41648
42585
  logger.info("Nothing to remove.");
41649
42586
  }
41650
42587
  });
42588
+ skills3.command("update [name]").description("Update installed skills from their original source").option("--everywhere", "Update across all tracked projects (uses global lockfile)").option("-d, --dry-run", "Show what would be updated without making changes").action(async (name, options2) => {
42589
+ logger.titleBox("AgentInit Skills");
42590
+ const installLock3 = new InstallLock;
42591
+ if (options2.everywhere) {
42592
+ if (!name) {
42593
+ logger.error("Skill name is required with --everywhere.");
42594
+ logger.info("Usage: agentinit skills update <name> --everywhere");
42595
+ return;
42596
+ }
42597
+ const entries = await installLock3.findProjectsWithSkill(name);
42598
+ if (entries.length === 0) {
42599
+ logger.info(`No tracked installations found for skill "${name}".`);
42600
+ logger.info(dim("Install the skill first, then update with --everywhere."));
42601
+ return;
42602
+ }
42603
+ logger.info(`Found ${cyan(String(entries.length))} tracked target(s) with skill "${green(name)}":\n`);
42604
+ const staleProjects = [];
42605
+ const updateTargets = [];
42606
+ for (const entry of entries) {
42607
+ const targetLabel = getLockEntryTargetLabel(entry);
42608
+ if (entry.scope !== "global" && !await fileExists(entry.projectPath)) {
42609
+ staleProjects.push(entry.projectPath);
42610
+ logger.info(` ${red("x")} ${targetLabel} ${dim("(missing)")}`);
42611
+ } else {
42612
+ updateTargets.push(entry);
42613
+ logger.info(` ${green("+")} ${targetLabel} ${dim(`[${entry.agents.join(", ")}]`)}`);
42614
+ }
42615
+ }
42616
+ if (staleProjects.length > 0) {
42617
+ logger.info("");
42618
+ logger.warn(`${staleProjects.length} project(s) no longer exist. Run "agentinit lock prune" to clean up.`);
42619
+ }
42620
+ if (updateTargets.length === 0) {
42621
+ logger.info("\nNo valid projects to update.");
42622
+ return;
42623
+ }
42624
+ if (options2.dryRun) {
42625
+ logger.info(dim(`
42626
+ Dry run \u2014 no changes made.`));
42627
+ return;
42628
+ }
42629
+ logger.info("");
42630
+ const spinner = ora("Updating skills across projects...").start();
42631
+ let updatedCount = 0;
42632
+ let unchangedCount = 0;
42633
+ let failedCount = 0;
42634
+ for (const entry of updateTargets) {
42635
+ spinner.text = `Updating in ${getLockEntryTargetLabel(entry)}...`;
42636
+ try {
42637
+ const sourceString = lockSourceToString(entry.source);
42638
+ if (!sourceString) {
42639
+ failedCount++;
42640
+ logger.info(` ${red("x")} ${getLockEntryTargetLabel(entry)}: cannot reconstruct source`);
42641
+ continue;
42642
+ }
42643
+ const agentManager9 = new AgentManager;
42644
+ const skillsManager5 = new SkillsManager(agentManager9);
42645
+ const result = await skillsManager5.addFromSource(sourceString.source, entry.projectPath, {
42646
+ ...sourceString.from ? { from: sourceString.from } : {},
42647
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42648
+ agents: entry.agents,
42649
+ global: entry.scope === "global",
42650
+ skills: [name],
42651
+ yes: true
42652
+ });
42653
+ if (result.updated.length > 0) {
42654
+ updatedCount++;
42655
+ logger.info(` ${green("~")} ${getLockEntryTargetLabel(entry)}: updated`);
42656
+ } else if (result.installed.length > 0) {
42657
+ updatedCount++;
42658
+ logger.info(` ${green("+")} ${getLockEntryTargetLabel(entry)}: installed`);
42659
+ } else {
42660
+ unchangedCount++;
42661
+ logger.info(` ${dim("-")} ${getLockEntryTargetLabel(entry)}: ${dim("unchanged")}`);
42662
+ }
42663
+ } catch (error) {
42664
+ failedCount++;
42665
+ logger.info(` ${red("x")} ${getLockEntryTargetLabel(entry)}: ${error instanceof Error ? error.message : "unknown error"}`);
42666
+ }
42667
+ }
42668
+ if (updatedCount > 0) {
42669
+ spinner.succeed(`Updated "${name}" in ${updatedCount} target(s)`);
42670
+ } else {
42671
+ spinner.info(`"${name}" is already up to date in all tracked targets`);
42672
+ }
42673
+ if (unchangedCount > 0)
42674
+ logger.info(dim(` ${unchangedCount} unchanged`));
42675
+ if (failedCount > 0)
42676
+ logger.info(red(` ${failedCount} failed`));
42677
+ } else {
42678
+ const cwd = process.cwd();
42679
+ const agentManager9 = new AgentManager;
42680
+ const skillsManager5 = new SkillsManager(agentManager9);
42681
+ if (name) {
42682
+ const entries = await installLock3.getCurrentState({
42683
+ kind: "skill",
42684
+ name,
42685
+ projectPath: resolve13(cwd),
42686
+ scope: "project"
42687
+ });
42688
+ if (entries.length === 0) {
42689
+ logger.info(`Skill "${name}" not tracked in the lockfile for this project.`);
42690
+ logger.info(dim("Try: agentinit skills add <source> or use --everywhere for global installs."));
42691
+ return;
42692
+ }
42693
+ if (options2.dryRun) {
42694
+ logger.info(`Would update "${name}" in ${entries.length} tracked target(s):`);
42695
+ for (const entry of entries) {
42696
+ const sourceString = lockSourceToString(entry.source);
42697
+ logger.info(` ${green(entry.name)} ${dim(`[${entry.agents.join(", ")}]`)} ${sourceString ? dim(sourceString.source) : red("unavailable source")}`);
42698
+ }
42699
+ return;
42700
+ }
42701
+ const spinner = ora(`Updating skill "${name}"...`).start();
42702
+ let updatedCount = 0;
42703
+ let unchangedCount = 0;
42704
+ let failedCount = 0;
42705
+ for (const entry of entries) {
42706
+ const sourceString = lockSourceToString(entry.source);
42707
+ if (!sourceString) {
42708
+ failedCount++;
42709
+ continue;
42710
+ }
42711
+ try {
42712
+ const result = await skillsManager5.addFromSource(sourceString.source, cwd, {
42713
+ ...sourceString.from ? { from: sourceString.from } : {},
42714
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42715
+ agents: entry.agents,
42716
+ global: entry.scope === "global",
42717
+ skills: [name],
42718
+ yes: true
42719
+ });
42720
+ if (result.updated.length > 0 || result.installed.length > 0) {
42721
+ updatedCount++;
42722
+ } else {
42723
+ unchangedCount++;
42724
+ }
42725
+ } catch {
42726
+ failedCount++;
42727
+ }
42728
+ }
42729
+ if (updatedCount > 0) {
42730
+ spinner.succeed(`Updated "${name}" in ${updatedCount} target(s)`);
42731
+ } else {
42732
+ spinner.info(`"${name}" is already up to date`);
42733
+ }
42734
+ if (unchangedCount > 0)
42735
+ logger.info(dim(` ${unchangedCount} unchanged`));
42736
+ if (failedCount > 0)
42737
+ logger.info(red(` ${failedCount} failed`));
42738
+ } else {
42739
+ const entries = await installLock3.getCurrentState({
42740
+ kind: "skill",
42741
+ projectPath: resolve13(cwd),
42742
+ scope: "project"
42743
+ });
42744
+ if (entries.length === 0) {
42745
+ logger.info("No skills tracked in the lockfile for this project.");
42746
+ return;
42747
+ }
42748
+ if (options2.dryRun) {
42749
+ logger.info(`Would update ${entries.length} skill(s):`);
42750
+ for (const entry of entries) {
42751
+ logger.info(` ${green(entry.name)} ${dim(`[${entry.agents.join(", ")}]`)}`);
42752
+ }
42753
+ return;
42754
+ }
42755
+ const spinner = ora("Updating all skills...").start();
42756
+ let updatedCount = 0;
42757
+ for (const entry of entries) {
42758
+ const sourceString = lockSourceToString(entry.source);
42759
+ if (!sourceString)
42760
+ continue;
42761
+ spinner.text = `Updating "${entry.name}"...`;
42762
+ try {
42763
+ const result = await skillsManager5.addFromSource(sourceString.source, cwd, {
42764
+ ...sourceString.from ? { from: sourceString.from } : {},
42765
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42766
+ agents: entry.agents,
42767
+ global: entry.scope === "global",
42768
+ skills: [entry.name],
42769
+ yes: true
42770
+ });
42771
+ if (result.updated.length > 0 || result.installed.length > 0) {
42772
+ updatedCount++;
42773
+ }
42774
+ } catch {
42775
+ }
42776
+ }
42777
+ if (updatedCount > 0) {
42778
+ spinner.succeed(`Updated ${updatedCount} skill(s)`);
42779
+ } else {
42780
+ spinner.info("All skills are up to date");
42781
+ }
42782
+ }
42783
+ }
42784
+ });
41651
42785
  }
42786
+ var lockSourceToString = function(source) {
42787
+ return lockSourceToSpecifier(source);
42788
+ };
41652
42789
  async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, projectPath, options2) {
41653
42790
  let installGlobal = !!options2.global;
41654
42791
  if (!options2.global) {
@@ -41721,23 +42858,57 @@ async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, sou
41721
42858
  }
41722
42859
  return selection;
41723
42860
  }
41724
- async function resolveInteractiveSkillSelection(skills3) {
41725
- const response = await promptMultiselect({
41726
- name: "skills",
41727
- message: `Select skills to install (${skills3.length} found):`,
41728
- min: 1,
41729
- choices: skills3.map((skill) => ({
41730
- title: skill.name,
41731
- value: skill.name,
41732
- description: skill.description,
41733
- selected: true
41734
- }))
41735
- });
41736
- if (!response.skills || response.skills.length === 0) {
41737
- logger.info("No skills selected. Aborting.");
41738
- return { aborted: true };
42861
+ var formatSkillSelectionHint = function(prefix) {
42862
+ return `Press Space to select, A to select or deselect all, p to edit prefix, then Enter to confirm. Prefix: "${prefix}"`;
42863
+ };
42864
+ async function resolveInteractiveSkillSelection(skills3, initialPrefix) {
42865
+ let prefix = initialPrefix ?? "";
42866
+ let selectedSkills = new Set(skills3.map((skill) => skill.name));
42867
+ const promptForPrefix = async () => {
42868
+ const response = await import_prompts3.default({
42869
+ type: "text",
42870
+ name: "prefix",
42871
+ message: "Prefix to prepend to installed skill names:",
42872
+ initial: prefix
42873
+ });
42874
+ if (typeof response.prefix === "string") {
42875
+ prefix = response.prefix;
42876
+ }
42877
+ };
42878
+ const requestPrefixEdit = (controls) => {
42879
+ controls.requestAction("edit-prefix");
42880
+ controls.closeWithCurrentSelection();
42881
+ };
42882
+ while (true) {
42883
+ const response = await promptMultiselect({
42884
+ name: "skills",
42885
+ message: `Select skills to install (${skills3.length} found):`,
42886
+ min: 1,
42887
+ hint: () => formatSkillSelectionHint(prefix),
42888
+ hotkeys: {
42889
+ p: requestPrefixEdit,
42890
+ P: requestPrefixEdit
42891
+ },
42892
+ choices: skills3.map((skill) => ({
42893
+ title: skill.name,
42894
+ value: skill.name,
42895
+ description: skill.description,
42896
+ selected: selectedSkills.has(skill.name)
42897
+ }))
42898
+ });
42899
+ if (response.skills && response.skills.length > 0) {
42900
+ selectedSkills = new Set(response.skills);
42901
+ }
42902
+ if (response.__agentinitAction === "edit-prefix") {
42903
+ await promptForPrefix();
42904
+ continue;
42905
+ }
42906
+ if (!response.skills || response.skills.length === 0) {
42907
+ logger.info("No skills selected. Aborting.");
42908
+ return { aborted: true };
42909
+ }
42910
+ return { skills: response.skills, prefix };
41739
42911
  }
41740
- return { skills: response.skills };
41741
42912
  }
41742
42913
  async function getDetectedSkillGroups(agentManager9, projectPath, global3) {
41743
42914
  const detectedAgents = (await agentManager9.detectAgents(projectPath)).map((entry) => entry.agent);
@@ -41772,7 +42943,7 @@ var buildSkillGroups = function(agents, projectPath, global3) {
41772
42943
  dirToAgents.set(skillsDir, existing);
41773
42944
  }
41774
42945
  return Array.from(dirToAgents.entries()).map(([dir, groupedAgents]) => {
41775
- const canonicalShared = resolve12(dir) === getCanonicalSkillsDirForScope(projectPath, !!global3) && groupedAgents.every((agent) => agent.getProjectSkillsStandard() === "agents");
42946
+ const canonicalShared = resolve13(dir) === getCanonicalSkillsDirForScope(projectPath, !!global3) && groupedAgents.every((agent) => agent.getProjectSkillsStandard() === "agents");
41776
42947
  return {
41777
42948
  dir,
41778
42949
  displayDir: formatSkillsDir(projectPath, dir),
@@ -41795,7 +42966,7 @@ var prependCanonicalGlobalGroup = function(agentManager9, projectPath, groups) {
41795
42966
  if (sharedAgents.length === 0) {
41796
42967
  return groups;
41797
42968
  }
41798
- const existingCanonicalIndex = groups.findIndex((group) => resolve12(group.dir) === canonicalDir);
42969
+ const existingCanonicalIndex = groups.findIndex((group) => resolve13(group.dir) === canonicalDir);
41799
42970
  if (existingCanonicalIndex >= 0) {
41800
42971
  const existingCanonical = groups[existingCanonicalIndex];
41801
42972
  const remaining = groups.filter((_, index) => index !== existingCanonicalIndex);
@@ -41831,9 +43002,9 @@ var prependCanonicalGlobalGroup = function(agentManager9, projectPath, groups) {
41831
43002
  var formatSkillsDir = function(projectPath, dir) {
41832
43003
  const normalizedDir = dir.replace(/\\/g, "/").replace(/\/?$/, "/");
41833
43004
  const normalizedProjectPath = projectPath.replace(/\\/g, "/");
41834
- const normalizedHome = homedir6().replace(/\\/g, "/");
43005
+ const normalizedHome = homedir7().replace(/\\/g, "/");
41835
43006
  if (normalizedDir.startsWith(`${normalizedProjectPath}/`)) {
41836
- return `${relative7(projectPath, dir).replace(/\\/g, "/").replace(/\/?$/, "/")}`;
43007
+ return `${relative8(projectPath, dir).replace(/\\/g, "/").replace(/\/?$/, "/")}`;
41837
43008
  }
41838
43009
  if (normalizedDir.startsWith(`${normalizedHome}/`)) {
41839
43010
  return normalizedDir.replace(normalizedHome, "~");
@@ -41842,7 +43013,7 @@ var formatSkillsDir = function(projectPath, dir) {
41842
43013
  };
41843
43014
  var formatPromptPath = function(path) {
41844
43015
  const normalizedPath = path.replace(/\\/g, "/").replace(/\/?$/, "/");
41845
- const normalizedHome = homedir6().replace(/\\/g, "/");
43016
+ const normalizedHome = homedir7().replace(/\\/g, "/");
41846
43017
  if (normalizedPath === `${normalizedHome}/`) {
41847
43018
  return "~/";
41848
43019
  }
@@ -41852,10 +43023,10 @@ var formatPromptPath = function(path) {
41852
43023
  return normalizedPath;
41853
43024
  };
41854
43025
  var getCanonicalGlobalSkillsDir = function() {
41855
- return resolve12(homedir6(), ".agents/skills");
43026
+ return resolve13(homedir7(), ".agents/skills");
41856
43027
  };
41857
43028
  var getCanonicalSkillsDirForScope = function(projectPath, global3) {
41858
- return global3 ? getCanonicalGlobalSkillsDir() : resolve12(projectPath, ".agents/skills");
43029
+ return global3 ? getCanonicalGlobalSkillsDir() : resolve13(projectPath, ".agents/skills");
41859
43030
  };
41860
43031
  var getCanonicalGlobalSkillsDisplayPath = function() {
41861
43032
  return formatPromptPath(getCanonicalGlobalSkillsDir());
@@ -42045,7 +43216,7 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
42045
43216
  byPath.set(path, existing);
42046
43217
  }
42047
43218
  for (const [path, details] of byPath) {
42048
- logger.info(` ${relative7(process.cwd(), path) || path}`);
43219
+ logger.info(` ${relative8(process.cwd(), path) || path}`);
42049
43220
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
42050
43221
  logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
42051
43222
  }
@@ -42061,7 +43232,7 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
42061
43232
  byPathUpdated.set(path, existing);
42062
43233
  }
42063
43234
  for (const [path, details] of byPathUpdated) {
42064
- logger.info(` ${relative7(process.cwd(), path) || path}`);
43235
+ logger.info(` ${relative8(process.cwd(), path) || path}`);
42065
43236
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
42066
43237
  logger.info(` Skills: ${yellow(String(details.skills.size))} updated (${[...details.skills].join(", ")})`);
42067
43238
  }
@@ -42089,8 +43260,11 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
42089
43260
  };
42090
43261
 
42091
43262
  // dist/commands/mcp.js
43263
+ init_colors();
43264
+ init_logger();
42092
43265
  init_mcpFilter();
42093
43266
  init_agentManager();
43267
+ init_installLock();
42094
43268
  var filterMcpArgs = function(args) {
42095
43269
  const filtered = [];
42096
43270
  let i = 0;
@@ -42282,6 +43456,28 @@ var registerAddCommand = function(mcp) {
42282
43456
  logger.info(` ${cyan(server.name)} (${typeLabel}): ${server.url}`);
42283
43457
  }
42284
43458
  });
43459
+ try {
43460
+ const lock = new InstallLock;
43461
+ const lockSource2 = { type: "local" };
43462
+ for (const plan of plans) {
43463
+ for (const server of plan.servers) {
43464
+ await lock.recordMcp({
43465
+ action: "install",
43466
+ name: server.name,
43467
+ projectPath: cwd,
43468
+ agents: [plan.agent.id],
43469
+ scope: plan.isGlobal ? "global" : "project",
43470
+ source: lockSource2,
43471
+ configPath: plan.configPath,
43472
+ serverType: server.type,
43473
+ ...server.command ? { command: server.command } : {},
43474
+ ...server.url ? { url: server.url } : {}
43475
+ });
43476
+ }
43477
+ }
43478
+ } catch (error) {
43479
+ logLockWriteWarning("MCP servers were added successfully", error);
43480
+ }
42285
43481
  }
42286
43482
  } catch (error) {
42287
43483
  spinner.fail("Failed to add MCP servers");
@@ -42375,8 +43571,12 @@ var registerRemoveCommand = function(mcp) {
42375
43571
  }
42376
43572
  let removedCount = 0;
42377
43573
  let failedCount = 0;
43574
+ const removedTargets = [];
42378
43575
  for (const { agent, isGlobal: global3 } of targets) {
42379
43576
  try {
43577
+ const configPath = global3 ? agent.getGlobalMcpPath() : agent.getNativeMcpPath(cwd);
43578
+ const existingServers = global3 ? await agent.getGlobalMCPServers().catch(() => []) : await agent.getMCPServers(cwd).catch(() => []);
43579
+ const existingServer = existingServers.find((server) => server.name.toLowerCase() === name.toLowerCase());
42380
43580
  let removed;
42381
43581
  if (global3) {
42382
43582
  removed = await agent.removeGlobalMCPServer(name);
@@ -42385,6 +43585,14 @@ var registerRemoveCommand = function(mcp) {
42385
43585
  }
42386
43586
  if (removed) {
42387
43587
  removedCount++;
43588
+ if (configPath) {
43589
+ removedTargets.push({
43590
+ agent,
43591
+ isGlobal: global3,
43592
+ configPath,
43593
+ ...existingServer ? { server: existingServer } : {}
43594
+ });
43595
+ }
42388
43596
  logger.info(` ${green("-")} ${agent.name}: removed "${name}"`);
42389
43597
  } else {
42390
43598
  logger.info(` ${yellow("~")} ${agent.name}: "${name}" not found`);
@@ -42396,6 +43604,25 @@ var registerRemoveCommand = function(mcp) {
42396
43604
  }
42397
43605
  if (removedCount > 0) {
42398
43606
  spinner.succeed(`Removed "${name}" from ${removedCount} agent(s)`);
43607
+ try {
43608
+ const lock = new InstallLock;
43609
+ for (const { agent, isGlobal: global3, configPath, server } of removedTargets) {
43610
+ await lock.recordMcp({
43611
+ action: "remove",
43612
+ name,
43613
+ projectPath: cwd,
43614
+ agents: [agent.id],
43615
+ scope: global3 ? "global" : "project",
43616
+ source: { type: "local" },
43617
+ configPath,
43618
+ serverType: server?.type || "stdio",
43619
+ ...server?.command ? { command: server.command } : {},
43620
+ ...server?.url ? { url: server.url } : {}
43621
+ });
43622
+ }
43623
+ } catch (error) {
43624
+ logLockWriteWarning("MCP servers were removed successfully", error);
43625
+ }
42399
43626
  } else if (failedCount > 0) {
42400
43627
  spinner.fail(`Failed to remove "${name}"`);
42401
43628
  } else {
@@ -42584,8 +43811,11 @@ var KNOWN_ADD_OPTIONS = {
42584
43811
  };
42585
43812
 
42586
43813
  // dist/commands/rules.js
43814
+ init_colors();
43815
+ init_logger();
42587
43816
  init_agentManager();
42588
43817
  init_fs();
43818
+ init_installLock();
42589
43819
  var colorizeTokenCount2 = function(tokenCount) {
42590
43820
  if (tokenCount <= TOKEN_COUNT_THRESHOLDS.LOW)
42591
43821
  return green(tokenCount.toString());
@@ -42774,6 +44004,29 @@ function registerRulesCommand(program2) {
42774
44004
  }
42775
44005
  }
42776
44006
  }
44007
+ try {
44008
+ const lock = new InstallLock;
44009
+ for (const { agent, result } of results) {
44010
+ if (!result.success || result.rulesApplied === 0)
44011
+ continue;
44012
+ const sections = result.mergedSections || uniqueSections;
44013
+ for (const section of sections) {
44014
+ await lock.recordRules({
44015
+ action: "install",
44016
+ name: section.templateId,
44017
+ projectPath: cwd,
44018
+ agents: [agent.id],
44019
+ scope: isGlobal ? "global" : "project",
44020
+ source: { type: "local" },
44021
+ configPath: result.configPath || "",
44022
+ templateIds: [section.templateId],
44023
+ ruleCount: section.rules.length
44024
+ });
44025
+ }
44026
+ }
44027
+ } catch (error) {
44028
+ logLockWriteWarning("Rules were applied successfully", error);
44029
+ }
42777
44030
  } catch (error) {
42778
44031
  spinner.fail("Failed to apply rules");
42779
44032
  logger.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -42836,6 +44089,7 @@ function registerRulesCommand(program2) {
42836
44089
  const agents = await resolveTargetAgents(agentManager11, options2, cwd);
42837
44090
  const spinner = ora("Removing rules...").start();
42838
44091
  let totalRemoved = 0;
44092
+ const removedSections = [];
42839
44093
  try {
42840
44094
  for (const agent of agents) {
42841
44095
  if (!agent.capabilities.rules) {
@@ -42859,6 +44113,9 @@ function registerRulesCommand(program2) {
42859
44113
  const updatedContent = await agent.applyRulesConfig(configPath, buildAppliedRules(remaining), content);
42860
44114
  await writeFile(configPath, updatedContent);
42861
44115
  totalRemoved += removed.length;
44116
+ for (const section of removed) {
44117
+ removedSections.push({ agent, configPath, section });
44118
+ }
42862
44119
  for (const s of removed) {
42863
44120
  logger.info(` ${agent.name}: removed ${green(s.templateName)} (${s.rules.length} rules)`);
42864
44121
  }
@@ -42868,6 +44125,24 @@ function registerRulesCommand(program2) {
42868
44125
  logger.info('Run "agentinit rules list" to see current sections.');
42869
44126
  } else {
42870
44127
  spinner.succeed(`Removed ${totalRemoved} rule section${totalRemoved !== 1 ? "s" : ""}`);
44128
+ try {
44129
+ const lock = new InstallLock;
44130
+ for (const { agent, configPath, section } of removedSections) {
44131
+ await lock.recordRules({
44132
+ action: "remove",
44133
+ name: section.templateId,
44134
+ projectPath: cwd,
44135
+ agents: [agent.id],
44136
+ scope: isGlobal ? "global" : "project",
44137
+ source: { type: "local" },
44138
+ configPath,
44139
+ templateIds: [section.templateId],
44140
+ ruleCount: 0
44141
+ });
44142
+ }
44143
+ } catch (error) {
44144
+ logLockWriteWarning("Rules were removed successfully", error);
44145
+ }
42871
44146
  }
42872
44147
  } catch (error) {
42873
44148
  spinner.fail("Failed to remove rules");
@@ -42879,8 +44154,9 @@ function registerRulesCommand(program2) {
42879
44154
 
42880
44155
  // dist/commands/plugins.js
42881
44156
  var import_prompts4 = __toESM(require_prompts3(), 1);
42882
- import {homedir as homedir7} from "os";
42883
- import {dirname as dirname9, relative as relative8, resolve as resolve13} from "path";
44157
+ import {homedir as homedir8} from "os";
44158
+ import {dirname as dirname10, relative as relative9, resolve as resolve14} from "path";
44159
+ init_logger();
42884
44160
  init_pluginManager();
42885
44161
  init_agentManager();
42886
44162
  init_marketplaceRegistry();
@@ -43232,7 +44508,7 @@ function registerPluginsCommand(program2) {
43232
44508
  }
43233
44509
  var formatPathForDisplay = function(pathValue, projectPath) {
43234
44510
  if (pathValue.startsWith(`${projectPath}/`)) {
43235
- return relative8(projectPath, pathValue) || ".";
44511
+ return relative9(projectPath, pathValue) || ".";
43236
44512
  }
43237
44513
  const homePrefix = `${process.env.HOME || ""}/`;
43238
44514
  if (homePrefix !== "/" && pathValue.startsWith(homePrefix)) {
@@ -43422,7 +44698,7 @@ var renderPluginWarnings = function(previewOrResult, projectPath) {
43422
44698
  var renderInstalledComponents = function(result, agentManager12, projectPath) {
43423
44699
  const skillGroups = new Map;
43424
44700
  for (const item of result.skills.installed) {
43425
- const targetDir = dirname9(item.path);
44701
+ const targetDir = dirname10(item.path);
43426
44702
  const existing = skillGroups.get(targetDir) || { agents: new Set, skillNames: new Set };
43427
44703
  existing.agents.add(item.agent);
43428
44704
  existing.skillNames.add(item.name);
@@ -43500,7 +44776,7 @@ var buildGlobalPluginGroups = function(agentManager12, projectPath) {
43500
44776
  compatibleAgentNames: [],
43501
44777
  kind: "native"
43502
44778
  }));
43503
- const canonicalDir = resolve13(homedir7(), ".agents/skills");
44779
+ const canonicalDir = resolve14(homedir8(), ".agents/skills");
43504
44780
  const sharedAgents = agentManager12.getAllAgents().filter((agent) => agent.supportsSkills() && agent.getProjectSkillsStandard() === "agents" && !!agent.getSkillsDir(projectPath, true));
43505
44781
  const claudeGroups = nativeGroups.filter((group) => group.agents.some((agent) => agent.id === "claude"));
43506
44782
  const otherGroups = nativeGroups.filter((group) => !group.agents.some((agent) => agent.id === "claude"));
@@ -43610,7 +44886,148 @@ async function interactiveAgentSelect(pluginManager3, agentManager12, projectPat
43610
44886
  } : undefined;
43611
44887
  }
43612
44888
 
44889
+ // dist/commands/lock.js
44890
+ init_colors();
44891
+ init_logger();
44892
+ init_installLock();
44893
+ import {resolve as resolve15} from "path";
44894
+ var formatTimestamp = function(iso) {
44895
+ const date = new Date(iso);
44896
+ return date.toLocaleDateString("en-US", {
44897
+ month: "short",
44898
+ day: "numeric",
44899
+ year: "numeric"
44900
+ });
44901
+ };
44902
+ var formatKind = function(kind) {
44903
+ switch (kind) {
44904
+ case "skill":
44905
+ return cyan("skill");
44906
+ case "mcp":
44907
+ return yellow("mcp");
44908
+ case "rules":
44909
+ return green("rules");
44910
+ }
44911
+ };
44912
+ var formatSource = function(entry) {
44913
+ return formatLockSource(entry.source);
44914
+ };
44915
+ function registerLockCommand(program2) {
44916
+ const lock = program2.command("lock").description("View and manage the global install lockfile");
44917
+ lock.command("list").alias("ls").description("List all tracked installations across projects").option("--kind <kind>", "Filter by kind: skill, mcp, rules").option("--project <path>", "Filter by project path").option("--agent <agent>", "Filter by agent ID").option("--scope <scope>", "Filter by scope: project or global").action(async (options2) => {
44918
+ const installLock6 = new InstallLock;
44919
+ const queryOptions = {};
44920
+ if (options2.kind)
44921
+ queryOptions.kind = options2.kind;
44922
+ if (options2.project)
44923
+ queryOptions.projectPath = resolve15(options2.project);
44924
+ if (options2.agent)
44925
+ queryOptions.agent = options2.agent;
44926
+ if (options2.scope)
44927
+ queryOptions.scope = options2.scope;
44928
+ const entries = await installLock6.getCurrentState(queryOptions);
44929
+ if (entries.length === 0) {
44930
+ logger.info("No installations tracked in the lockfile.");
44931
+ logger.info(dim("Install skills, MCPs, or rules to start tracking."));
44932
+ return;
44933
+ }
44934
+ const byProject = new Map;
44935
+ for (const entry of entries) {
44936
+ const key = entry.scope === "global" ? "global" : entry.projectPath;
44937
+ const group = byProject.get(key) || { label: getLockEntryTargetLabel(entry), entries: [] };
44938
+ group.entries.push(entry);
44939
+ byProject.set(key, group);
44940
+ }
44941
+ for (const { label, entries: projectEntries } of byProject.values()) {
44942
+ logger.info(`\n${cyan(label)}`);
44943
+ for (const entry of projectEntries) {
44944
+ const kind = formatKind(entry.kind);
44945
+ const scope = entry.scope === "global" ? dim(" (global)") : "";
44946
+ const agents = dim(`[${entry.agents.join(", ")}]`);
44947
+ const date = dim(formatTimestamp(entry.timestamp));
44948
+ const source = dim(formatSource(entry));
44949
+ logger.info(` ${kind} ${green(entry.name)} ${agents}${scope} ${source} ${date}`);
44950
+ }
44951
+ }
44952
+ logger.info("");
44953
+ logger.info(dim(`${entries.length} installation(s) across ${byProject.size} target(s)`));
44954
+ });
44955
+ lock.command("status").description("Show summary of tracked installations").option("--check-drift", "Check for modified skill files (compares content hashes)").action(async (options2) => {
44956
+ const installLock6 = new InstallLock;
44957
+ const entries = await installLock6.getCurrentState();
44958
+ if (entries.length === 0) {
44959
+ logger.info("No installations tracked in the lockfile.");
44960
+ return;
44961
+ }
44962
+ const projects = new Set(entries.filter((entry) => entry.scope === "project").map((entry) => entry.projectPath));
44963
+ const globalTargets = entries.filter((entry) => entry.scope === "global");
44964
+ const skills3 = entries.filter((e) => e.kind === "skill");
44965
+ const mcps = entries.filter((e) => e.kind === "mcp");
44966
+ const rules = entries.filter((e) => e.kind === "rules");
44967
+ logger.info(`Projects: ${cyan(projects.size.toString())}`);
44968
+ logger.info(`Global targets: ${cyan(globalTargets.length.toString())}`);
44969
+ logger.info(`Skills: ${green(skills3.length.toString())}`);
44970
+ logger.info(`MCPs: ${yellow(mcps.length.toString())}`);
44971
+ logger.info(`Rules: ${green(rules.length.toString())}`);
44972
+ const stale = await installLock6.findStaleProjects();
44973
+ if (stale.length > 0) {
44974
+ logger.info("");
44975
+ logger.info(`${red("Stale projects:")} ${stale.length}`);
44976
+ for (const path of stale) {
44977
+ logger.info(` ${red("x")} ${path}`);
44978
+ }
44979
+ logger.info(dim('Run "agentinit lock prune" to clean up.'));
44980
+ }
44981
+ if (options2.checkDrift) {
44982
+ logger.info("");
44983
+ logger.info("Checking for drift...");
44984
+ let driftCount = 0;
44985
+ let missingCount = 0;
44986
+ for (const entry of skills3) {
44987
+ if (!entry.contentHash)
44988
+ continue;
44989
+ const result = await installLock6.checkDrift(entry);
44990
+ if (result.status === "drift") {
44991
+ driftCount++;
44992
+ logger.info(` ${yellow("~")} ${entry.name} in ${entry.projectPath}`);
44993
+ } else if (result.status === "missing") {
44994
+ missingCount++;
44995
+ logger.info(` ${red("x")} ${entry.name} in ${entry.projectPath} ${dim("(missing)")}`);
44996
+ }
44997
+ }
44998
+ if (driftCount === 0 && missingCount === 0) {
44999
+ logger.info(` ${green("All skills match their installed hashes.")}`);
45000
+ } else {
45001
+ if (driftCount > 0)
45002
+ logger.info(` ${driftCount} skill(s) modified since install`);
45003
+ if (missingCount > 0)
45004
+ logger.info(` ${missingCount} skill(s) missing from disk`);
45005
+ }
45006
+ }
45007
+ });
45008
+ lock.command("prune").description("Remove entries for projects that no longer exist on disk").option("-d, --dry-run", "Show what would be removed without changing the lockfile").action(async (options2) => {
45009
+ const installLock6 = new InstallLock;
45010
+ const stale = await installLock6.findStaleProjects();
45011
+ if (stale.length === 0) {
45012
+ logger.info("No stale projects found. Lockfile is clean.");
45013
+ return;
45014
+ }
45015
+ logger.info(`Found ${stale.length} stale project(s):`);
45016
+ for (const path of stale) {
45017
+ logger.info(` ${red("x")} ${path}`);
45018
+ }
45019
+ if (options2.dryRun) {
45020
+ logger.info(dim(`
45021
+ Dry run \u2014 no changes made.`));
45022
+ return;
45023
+ }
45024
+ const result = await installLock6.pruneStaleEntries();
45025
+ logger.info(`\nRemoved ${result.entriesRemoved} entries for ${result.prunedProjects.length} stale project(s).`);
45026
+ });
45027
+ }
45028
+
43613
45029
  // dist/cli.js
45030
+ init_logger();
43614
45031
  var program2 = new Command;
43615
45032
  program2.name("agentinit").description("A CLI tool for managing and configuring AI coding agents").version("1.0.1");
43616
45033
  registerSkillsCommand(program2);
@@ -43618,6 +45035,7 @@ registerMcpCommand(program2);
43618
45035
  registerRulesCommand(program2);
43619
45036
  registerPluginsCommand(program2);
43620
45037
  registerConfigCommand(program2);
45038
+ registerLockCommand(program2);
43621
45039
  program2.command("init").description("Initialize agents.md configuration for the current project").option("-f, --force", "Overwrite existing configuration").option("-t, --template <template>", "Use specific template (web, cli, library)").action(initCommand);
43622
45040
  program2.command("detect").description("Detect current project stack and existing agent configurations").option("-v, --verbose", "Show detailed detection results").action(detectCommand);
43623
45041
  program2.command("sync").description("Sync agents.md with agent-specific configuration files").option("-a, --agent <agents...>", "Target specific agent(s)").option("-d, --dry-run", "Show what would be changed without making changes").option("-b, --backup", "Create backup before syncing").action(syncCommand);