agentinit 1.20.0 → 1.20.2

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.
@@ -0,0 +1,167 @@
1
+ import { promises as fs } from 'fs';
2
+ import { join, relative } from 'path';
3
+ const SCAN_RULES = [
4
+ {
5
+ id: 'AI001',
6
+ title: 'Prompt override language',
7
+ severity: 'medium',
8
+ regex: /\b(ignore|disregard|override|bypass)\b.{0,40}\b(previous|prior|system|safety|guardrails?|instructions?)\b/i,
9
+ },
10
+ {
11
+ id: 'AI002',
12
+ title: 'Secret or credential exfiltration',
13
+ severity: 'high',
14
+ 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,
15
+ },
16
+ {
17
+ id: 'AI003',
18
+ title: 'Destructive shell command',
19
+ severity: 'high',
20
+ 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,
21
+ },
22
+ {
23
+ id: 'AI004',
24
+ title: 'Remote shell execution pipeline',
25
+ severity: 'high',
26
+ regex: /\b(curl|wget)\b[^\n|]{0,160}\|\s*(sh|bash|zsh)\b/i,
27
+ },
28
+ {
29
+ id: 'AI005',
30
+ title: 'Hardcoded credential material',
31
+ severity: 'low',
32
+ regex: /\b(api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{8,}['"]/i,
33
+ },
34
+ {
35
+ id: 'AI006',
36
+ title: 'Unicode bidi control characters',
37
+ severity: 'low',
38
+ regex: /[\u202A-\u202E\u2066-\u2069]/,
39
+ },
40
+ ];
41
+ const MAX_TEXT_FILE_BYTES = 1024 * 1024;
42
+ const SCANNED_TEXT_EXTENSIONS = new Set([
43
+ '',
44
+ '.md',
45
+ '.txt',
46
+ '.json',
47
+ '.yaml',
48
+ '.yml',
49
+ '.toml',
50
+ '.js',
51
+ '.ts',
52
+ '.mjs',
53
+ '.cjs',
54
+ '.sh',
55
+ '.bash',
56
+ '.zsh',
57
+ '.py',
58
+ '.ps1',
59
+ ]);
60
+ const EXECUTABLE_TEXT_EXTENSIONS = new Set([
61
+ '.js',
62
+ '.ts',
63
+ '.mjs',
64
+ '.cjs',
65
+ '.sh',
66
+ '.bash',
67
+ '.zsh',
68
+ '.py',
69
+ '.ps1',
70
+ ]);
71
+ export class SkillSecurityScanner {
72
+ async scanSkill(skill) {
73
+ const findings = skill.generatedContent
74
+ ? this.scanText(skill.generatedContent, 'SKILL.md')
75
+ : await this.scanDirectory(skill.path);
76
+ const stats = findings.reduce((acc, finding) => {
77
+ acc[finding.severity] += 1;
78
+ return acc;
79
+ }, { high: 0, medium: 0, low: 0 });
80
+ return {
81
+ blocked: findings.some(finding => finding.blocking),
82
+ findings,
83
+ stats,
84
+ };
85
+ }
86
+ formatShortSummary(result) {
87
+ const parts = [
88
+ result.stats.high ? `${result.stats.high} high` : null,
89
+ result.stats.medium ? `${result.stats.medium} medium` : null,
90
+ result.stats.low ? `${result.stats.low} low` : null,
91
+ ].filter((value) => !!value);
92
+ return parts.length > 0 ? parts.join(', ') : 'no findings';
93
+ }
94
+ formatBlockingReason(result) {
95
+ const finding = result.findings.find(entry => entry.blocking)
96
+ || result.findings.find(entry => entry.severity === 'high')
97
+ || result.findings[0];
98
+ if (!finding) {
99
+ return 'Security scan failed';
100
+ }
101
+ return `Security scan failed: ${finding.title} (${finding.ruleId}) at ${finding.filePath}:${finding.line}`;
102
+ }
103
+ async scanDirectory(rootPath) {
104
+ const findings = [];
105
+ await this.walk(rootPath, rootPath, findings);
106
+ return findings;
107
+ }
108
+ async walk(rootPath, currentPath, findings) {
109
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
110
+ for (const entry of entries) {
111
+ if (entry.name === '.git' || entry.name === 'node_modules') {
112
+ continue;
113
+ }
114
+ const entryPath = join(currentPath, entry.name);
115
+ if (entry.isDirectory()) {
116
+ await this.walk(rootPath, entryPath, findings);
117
+ continue;
118
+ }
119
+ const relativePath = relative(rootPath, entryPath).replace(/\\/g, '/');
120
+ const content = await this.readTextFile(entryPath);
121
+ if (content === null) {
122
+ continue;
123
+ }
124
+ findings.push(...this.scanText(content, relativePath, {
125
+ blockHighRisk: this.isExecutableTextFile(entryPath, content),
126
+ }));
127
+ }
128
+ }
129
+ async readTextFile(filePath) {
130
+ const extension = filePath.includes('.') ? filePath.slice(filePath.lastIndexOf('.')).toLowerCase() : '';
131
+ if (!SCANNED_TEXT_EXTENSIONS.has(extension)) {
132
+ return null;
133
+ }
134
+ const buffer = await fs.readFile(filePath);
135
+ if (buffer.length > MAX_TEXT_FILE_BYTES || buffer.includes(0)) {
136
+ return null;
137
+ }
138
+ return buffer.toString('utf8');
139
+ }
140
+ isExecutableTextFile(filePath, content) {
141
+ const extension = filePath.includes('.') ? filePath.slice(filePath.lastIndexOf('.')).toLowerCase() : '';
142
+ return EXECUTABLE_TEXT_EXTENSIONS.has(extension) || content.startsWith('#!');
143
+ }
144
+ scanText(content, filePath, options = {}) {
145
+ const findings = [];
146
+ const lines = content.split(/\r?\n/);
147
+ lines.forEach((line, index) => {
148
+ for (const rule of SCAN_RULES) {
149
+ rule.regex.lastIndex = 0;
150
+ if (!rule.regex.test(line)) {
151
+ continue;
152
+ }
153
+ findings.push({
154
+ ruleId: rule.id,
155
+ title: rule.title,
156
+ severity: rule.severity,
157
+ filePath,
158
+ line: index + 1,
159
+ snippet: line.trim().slice(0, 160),
160
+ blocking: rule.severity === 'high' && options.blockHighRisk === true,
161
+ });
162
+ }
163
+ });
164
+ return findings;
165
+ }
166
+ }
167
+ //# sourceMappingURL=skillSecurityScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillSecurityScanner.js","sourceRoot":"","sources":["../../src/core/skillSecurityScanner.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import type { SkillInfo, InstalledSkill, SkillInstallResult, SkillsAddOptions, S
4
4
  export declare class SkillsManager {
5
5
  private agentManager;
6
6
  private preparedSourceContexts;
7
+ private readonly skillScanner;
7
8
  constructor(agentManager?: AgentManager);
8
9
  /**
9
10
  * Parse a source string into a structured SkillSource
@@ -14,7 +15,13 @@ export declare class SkillsManager {
14
15
  private isImplicitCatalogSkillSource;
15
16
  private resolveSourceRequest;
16
17
  private parseGitHubHttpSource;
18
+ private parseGitLabHttpSource;
19
+ private parseBitbucketHttpSource;
20
+ private parseHttpRepositorySource;
21
+ private parseSshRepositorySource;
17
22
  private parseGitHubShorthandSource;
23
+ private parseGitLabShorthandSource;
24
+ private parseBitbucketShorthandSource;
18
25
  /**
19
26
  * Parse a SKILL.md file and extract name + description from frontmatter
20
27
  */
@@ -95,6 +102,10 @@ export declare class SkillsManager {
95
102
  global?: boolean;
96
103
  }): Promise<SkillInstallResult>;
97
104
  private getCanonicalInstallPlan;
105
+ private normalizeSkillPrefix;
106
+ private withSkillPrefix;
107
+ private rewriteSkillFileName;
108
+ private applyPrefixToSkills;
98
109
  private normalizeSkillName;
99
110
  private resolveInstallPath;
100
111
  getInstallPath(skillName: string, targetDir: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"skillsManager.d.ts","sourceRoot":"","sources":["../../src/core/skillsManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skillsManager.d.ts","sourceRoot":"","sources":["../../src/core/skillsManager.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { createRelativeSymlink, fileExists, isDirectory, listFiles, readFileIfEx
9
9
  import { expandTilde } from '../utils/paths.js';
10
10
  import { AgentManager } from './agentManager.js';
11
11
  import { getConfiguredDefaultMarketplaceId, getMarketplace, getMarketplaceIds } from './marketplaceRegistry.js';
12
+ import { SkillSecurityScanner } from './skillSecurityScanner.js';
12
13
  import { SHARED_SKILLS_TARGET_ID } from '../types/skills.js';
13
14
  import { InstallLock, hashDirectory, logLockWriteWarning } from './installLock.js';
14
15
  const execFileAsync = promisify(execFile);
@@ -33,6 +34,7 @@ const SKILL_SEARCH_DIRS = [
33
34
  export class SkillsManager {
34
35
  agentManager;
35
36
  preparedSourceContexts = new Map();
37
+ skillScanner = new SkillSecurityScanner();
36
38
  constructor(agentManager) {
37
39
  this.agentManager = agentManager || new AgentManager();
38
40
  }
@@ -45,12 +47,16 @@ export class SkillsManager {
45
47
  return { type: 'local', path: source };
46
48
  }
47
49
  // Full GitHub URL
48
- const githubUrlSource = this.parseGitHubHttpSource(source);
49
- if (githubUrlSource) {
50
- return githubUrlSource;
50
+ const httpSource = this.parseHttpRepositorySource(source);
51
+ if (httpSource) {
52
+ return httpSource;
51
53
  }
52
54
  // Full git URL
53
- if (source.startsWith('git@') || source.endsWith('.git')) {
55
+ const sshSource = this.parseSshRepositorySource(source);
56
+ if (sshSource) {
57
+ return sshSource;
58
+ }
59
+ if (source.endsWith('.git')) {
54
60
  return { type: 'github', url: source };
55
61
  }
56
62
  if (options?.from) {
@@ -63,6 +69,14 @@ export class SkillsManager {
63
69
  pluginName: source,
64
70
  };
65
71
  }
72
+ const gitLabShorthandSource = this.parseGitLabShorthandSource(source);
73
+ if (gitLabShorthandSource) {
74
+ return gitLabShorthandSource;
75
+ }
76
+ const bitbucketShorthandSource = this.parseBitbucketShorthandSource(source);
77
+ if (bitbucketShorthandSource) {
78
+ return bitbucketShorthandSource;
79
+ }
66
80
  const githubShorthandSource = this.parseGitHubShorthandSource(source);
67
81
  if (githubShorthandSource?.subpath) {
68
82
  return githubShorthandSource;
@@ -163,6 +177,110 @@ export class SkillsManager {
163
177
  return null;
164
178
  }
165
179
  }
180
+ parseGitLabHttpSource(source) {
181
+ if (!source.startsWith('https://gitlab.com/') && !source.startsWith('http://gitlab.com/')) {
182
+ return null;
183
+ }
184
+ try {
185
+ const parsedUrl = new URL(source);
186
+ const segments = parsedUrl.pathname.replace(/\/+$/, '').split('/').filter(Boolean);
187
+ const dashIndex = segments.indexOf('-');
188
+ const repoBoundary = dashIndex >= 0 ? dashIndex : segments.length;
189
+ if (repoBoundary < 2) {
190
+ return null;
191
+ }
192
+ const repo = segments[repoBoundary - 1];
193
+ const owner = segments.slice(0, repoBoundary - 1).join('/');
194
+ if (!owner || !repo) {
195
+ return null;
196
+ }
197
+ let subpath;
198
+ if (dashIndex >= 0) {
199
+ const marker = segments[dashIndex + 1];
200
+ if ((marker === 'tree' || marker === 'blob') && segments.length > dashIndex + 3) {
201
+ subpath = segments.slice(dashIndex + 3).join('/');
202
+ }
203
+ }
204
+ return {
205
+ type: 'gitlab',
206
+ url: `https://gitlab.com/${owner}/${repo}.git`,
207
+ owner,
208
+ repo,
209
+ ...(subpath ? { subpath } : {}),
210
+ };
211
+ }
212
+ catch {
213
+ return null;
214
+ }
215
+ }
216
+ parseBitbucketHttpSource(source) {
217
+ if (!source.startsWith('https://bitbucket.org/') && !source.startsWith('http://bitbucket.org/')) {
218
+ return null;
219
+ }
220
+ try {
221
+ const parsedUrl = new URL(source);
222
+ const segments = parsedUrl.pathname.replace(/\/+$/, '').split('/').filter(Boolean);
223
+ if (segments.length < 2) {
224
+ return null;
225
+ }
226
+ const [owner, repo, marker, _commitish, ...rest] = segments;
227
+ if (!owner || !repo) {
228
+ return null;
229
+ }
230
+ let subpath;
231
+ if (marker === 'src' && rest.length > 0) {
232
+ subpath = rest.join('/');
233
+ }
234
+ return {
235
+ type: 'bitbucket',
236
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
237
+ owner,
238
+ repo,
239
+ ...(subpath ? { subpath } : {}),
240
+ };
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ }
246
+ parseHttpRepositorySource(source) {
247
+ return this.parseGitHubHttpSource(source)
248
+ || this.parseGitLabHttpSource(source)
249
+ || this.parseBitbucketHttpSource(source);
250
+ }
251
+ parseSshRepositorySource(source) {
252
+ const githubMatch = source.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
253
+ if (githubMatch) {
254
+ const [, owner, repo] = githubMatch;
255
+ return {
256
+ type: 'github',
257
+ url: `git@github.com:${owner}/${repo}.git`,
258
+ owner,
259
+ repo,
260
+ };
261
+ }
262
+ const gitlabMatch = source.match(/^git@gitlab\.com:(.+)\/([^/]+?)(?:\.git)?$/);
263
+ if (gitlabMatch) {
264
+ const [, owner, repo] = gitlabMatch;
265
+ return {
266
+ type: 'gitlab',
267
+ url: `git@gitlab.com:${owner}/${repo}.git`,
268
+ owner,
269
+ repo,
270
+ };
271
+ }
272
+ const bitbucketMatch = source.match(/^git@bitbucket\.org:([^/]+)\/([^/]+?)(?:\.git)?$/);
273
+ if (bitbucketMatch) {
274
+ const [, owner, repo] = bitbucketMatch;
275
+ return {
276
+ type: 'bitbucket',
277
+ url: `git@bitbucket.org:${owner}/${repo}.git`,
278
+ owner,
279
+ repo,
280
+ };
281
+ }
282
+ return null;
283
+ }
166
284
  parseGitHubShorthandSource(source) {
167
285
  const githubShorthandMatch = source.match(/^([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(?:\/(.+))?$/);
168
286
  if (!githubShorthandMatch) {
@@ -177,6 +295,58 @@ export class SkillsManager {
177
295
  ...(subpath ? { subpath } : {}),
178
296
  };
179
297
  }
298
+ parseGitLabShorthandSource(source) {
299
+ const normalized = source.startsWith('gitlab:')
300
+ ? source.slice('gitlab:'.length)
301
+ : source.startsWith('gitlab.com/')
302
+ ? source.slice('gitlab.com/'.length)
303
+ : null;
304
+ if (!normalized) {
305
+ return null;
306
+ }
307
+ const [repoSpec = normalized, subpathSpec] = normalized.split('//', 2);
308
+ const segments = repoSpec.split('/').filter(Boolean);
309
+ if (segments.length < 2) {
310
+ return null;
311
+ }
312
+ const repo = segments[segments.length - 1];
313
+ const owner = segments.slice(0, segments.length - 1).join('/');
314
+ if (!owner || !repo) {
315
+ return null;
316
+ }
317
+ return {
318
+ type: 'gitlab',
319
+ url: `https://gitlab.com/${owner}/${repo}.git`,
320
+ owner,
321
+ repo,
322
+ ...(subpathSpec ? { subpath: subpathSpec } : {}),
323
+ };
324
+ }
325
+ parseBitbucketShorthandSource(source) {
326
+ const normalized = source.startsWith('bitbucket:')
327
+ ? source.slice('bitbucket:'.length)
328
+ : source.startsWith('bitbucket.org/')
329
+ ? source.slice('bitbucket.org/'.length)
330
+ : null;
331
+ if (!normalized) {
332
+ return null;
333
+ }
334
+ const segments = normalized.split('/').filter(Boolean);
335
+ if (segments.length < 2) {
336
+ return null;
337
+ }
338
+ const [owner, repo, ...rest] = segments;
339
+ if (!owner || !repo) {
340
+ return null;
341
+ }
342
+ return {
343
+ type: 'bitbucket',
344
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
345
+ owner,
346
+ repo,
347
+ ...(rest.length > 0 ? { subpath: rest.join('/') } : {}),
348
+ };
349
+ }
180
350
  /**
181
351
  * Parse a SKILL.md file and extract name + description from frontmatter
182
352
  */
@@ -324,7 +494,7 @@ export class SkillsManager {
324
494
  }
325
495
  async resolveDiscoveryRoot(repoPath, source, sourceLabel) {
326
496
  const resolvedRepoPath = resolve(repoPath);
327
- if (source.type !== 'github' || !source.subpath) {
497
+ if ((source.type !== 'github' && source.type !== 'gitlab' && source.type !== 'bitbucket') || !source.subpath) {
328
498
  return resolvedRepoPath;
329
499
  }
330
500
  const discoveryRoot = resolve(resolvedRepoPath, source.subpath);
@@ -369,7 +539,7 @@ export class SkillsManager {
369
539
  };
370
540
  }
371
541
  let repoPath;
372
- if (resolved.type === 'github') {
542
+ if (resolved.type === 'github' || resolved.type === 'gitlab' || resolved.type === 'bitbucket') {
373
543
  if (!resolved.url) {
374
544
  throw new Error(`Invalid source: ${source}`);
375
545
  }
@@ -627,6 +797,76 @@ export class SkillsManager {
627
797
  mode: 'symlink',
628
798
  };
629
799
  }
800
+ normalizeSkillPrefix(prefix) {
801
+ const normalized = prefix?.trim() ?? '';
802
+ if (normalized.includes('/') || normalized.includes('\\')) {
803
+ throw new Error(`Invalid skill prefix: ${prefix}`);
804
+ }
805
+ return normalized;
806
+ }
807
+ withSkillPrefix(skillName, prefix) {
808
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
809
+ return normalizedPrefix ? `${normalizedPrefix}${skillName}` : skillName;
810
+ }
811
+ async rewriteSkillFileName(filePath, skillName) {
812
+ const content = await fs.readFile(filePath, 'utf8');
813
+ const parsed = matter(content);
814
+ const nextContent = matter.stringify(parsed.content, {
815
+ ...parsed.data,
816
+ name: skillName,
817
+ });
818
+ await fs.writeFile(filePath, nextContent, 'utf8');
819
+ }
820
+ async applyPrefixToSkills(skills, prefix) {
821
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
822
+ if (!normalizedPrefix) {
823
+ return { skills, cleanup: async () => { } };
824
+ }
825
+ const tempDirs = [];
826
+ try {
827
+ const prefixedSkills = await Promise.all(skills.map(async (skill) => {
828
+ const name = this.withSkillPrefix(skill.name, normalizedPrefix);
829
+ if (skill.generatedContent) {
830
+ return {
831
+ ...skill,
832
+ name,
833
+ generatedContent: matter.stringify(matter(skill.generatedContent).content, {
834
+ ...matter(skill.generatedContent).data,
835
+ name,
836
+ }),
837
+ };
838
+ }
839
+ const tempRoot = await fs.mkdtemp(join(tmpdir(), 'agentinit-prefixed-skill-'));
840
+ const tempSkillPath = join(tempRoot, basename(skill.path));
841
+ tempDirs.push(tempRoot);
842
+ await this.copyDir(skill.path, tempSkillPath);
843
+ const skillMdPath = join(tempSkillPath, 'SKILL.md');
844
+ const skillMdPathLower = join(tempSkillPath, 'skill.md');
845
+ const skillFile = (await fileExists(skillMdPath)) ? skillMdPath
846
+ : (await fileExists(skillMdPathLower)) ? skillMdPathLower
847
+ : null;
848
+ if (!skillFile) {
849
+ throw new Error(`Skill "${skill.name}" is missing SKILL.md`);
850
+ }
851
+ await this.rewriteSkillFileName(skillFile, name);
852
+ return {
853
+ ...skill,
854
+ name,
855
+ path: tempSkillPath,
856
+ };
857
+ }));
858
+ return {
859
+ skills: prefixedSkills,
860
+ cleanup: async () => {
861
+ await Promise.all(tempDirs.map(dir => fs.rm(dir, { recursive: true, force: true }).catch(() => { })));
862
+ },
863
+ };
864
+ }
865
+ catch (error) {
866
+ await Promise.all(tempDirs.map(dir => fs.rm(dir, { recursive: true, force: true }).catch(() => { })));
867
+ throw error;
868
+ }
869
+ }
630
870
  normalizeSkillName(skillName) {
631
871
  const normalized = skillName.trim();
632
872
  if (!normalized) {
@@ -732,11 +972,13 @@ export class SkillsManager {
732
972
  * Add skills from a source (GitHub repo or local path)
733
973
  */
734
974
  async addFromSource(source, projectPath, options = {}) {
975
+ const normalizedPrefix = this.normalizeSkillPrefix(options.prefix);
735
976
  const context = this.takePreparedSourceContext(source, projectPath, options.from)
736
977
  || await this.loadDiscoveredSkillsContext(source, projectPath, {
737
978
  ...(options.from !== undefined ? { from: options.from } : {}),
738
979
  ...(options.pluginName !== undefined ? { pluginName: options.pluginName } : {}),
739
980
  });
981
+ let prefixedCleanup = async () => { };
740
982
  try {
741
983
  let skills = context.skills;
742
984
  if (skills.length === 0) {
@@ -744,7 +986,37 @@ export class SkillsManager {
744
986
  }
745
987
  if (options.skills && options.skills.length > 0) {
746
988
  const names = new Set(options.skills.map(skill => skill.toLowerCase()));
747
- skills = skills.filter(skill => names.has(skill.name.toLowerCase()));
989
+ skills = skills.filter(skill => names.has(skill.name.toLowerCase())
990
+ || names.has(this.withSkillPrefix(skill.name, normalizedPrefix).toLowerCase()));
991
+ }
992
+ const prefixed = await this.applyPrefixToSkills(skills, normalizedPrefix);
993
+ skills = prefixed.skills;
994
+ prefixedCleanup = prefixed.cleanup;
995
+ const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: [...context.warnings] };
996
+ if (options.scan !== false) {
997
+ const scannedWarnings = new Set();
998
+ const scannableSkills = [];
999
+ for (const skill of skills) {
1000
+ const scan = await this.skillScanner.scanSkill(skill);
1001
+ if (scan.findings.length === 0) {
1002
+ scannableSkills.push(skill);
1003
+ continue;
1004
+ }
1005
+ if (scan.blocked && !options.allowRisky) {
1006
+ result.skipped.push({
1007
+ skill,
1008
+ reason: this.skillScanner.formatBlockingReason(scan),
1009
+ });
1010
+ continue;
1011
+ }
1012
+ const summary = this.skillScanner.formatShortSummary(scan);
1013
+ scannedWarnings.add(scan.blocked
1014
+ ? `Proceeding with "${skill.name}" despite high-risk findings: ${summary}`
1015
+ : `Security warnings for "${skill.name}": ${summary}`);
1016
+ scannableSkills.push(skill);
1017
+ }
1018
+ skills = scannableSkills;
1019
+ result.warnings.push(...scannedWarnings);
748
1020
  }
749
1021
  const installToSharedStore = options.agents?.includes(SHARED_SKILLS_TARGET_ID) ?? false;
750
1022
  const agents = await this.getTargetAgents(projectPath, options);
@@ -753,11 +1025,13 @@ export class SkillsManager {
753
1025
  installed: [],
754
1026
  updated: [],
755
1027
  unchanged: [],
756
- skipped: skills.map(skill => ({ skill, reason: 'No target agents found' })),
757
- warnings: context.warnings,
1028
+ skipped: [
1029
+ ...result.skipped,
1030
+ ...skills.map(skill => ({ skill, reason: 'No target agents found' })),
1031
+ ],
1032
+ warnings: result.warnings,
758
1033
  };
759
1034
  }
760
- const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
761
1035
  const installableAgents = [];
762
1036
  // Cache comparison results by install path to avoid re-comparing the same target
763
1037
  const comparisonCache = new Map();
@@ -885,6 +1159,7 @@ export class SkillsManager {
885
1159
  type: resolvedSource.type,
886
1160
  ...(resolvedSource.marketplace ? { marketplace: resolvedSource.marketplace } : {}),
887
1161
  ...(resolvedSource.pluginName ? { pluginName: resolvedSource.pluginName } : {}),
1162
+ ...(normalizedPrefix ? { prefix: normalizedPrefix } : {}),
888
1163
  ...(resolvedSource.url ? { url: resolvedSource.url } : {}),
889
1164
  ...(resolvedSource.path ? { path: resolve(projectPath, expandTilde(resolvedSource.path)) } : {}),
890
1165
  ...(resolvedSource.owner ? { owner: resolvedSource.owner } : {}),
@@ -918,6 +1193,7 @@ export class SkillsManager {
918
1193
  return result;
919
1194
  }
920
1195
  finally {
1196
+ await prefixedCleanup();
921
1197
  await context.cleanup();
922
1198
  }
923
1199
  }