psadk 1.3.3 → 1.3.5

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.
@@ -11,3 +11,6 @@ export * from './lib/tools/PSWriteFileTool';
11
11
  export * from './lib/tools/PSSubAgentTool';
12
12
  export * from './lib/tools/PSMemoryTool';
13
13
  export * from './lib/tools/PSClarifyTool';
14
+ export * from './lib/tools/PSSkillTool';
15
+ export * from './lib/skills/types';
16
+ export * from './lib/skills/utils';
@@ -27,4 +27,7 @@ __exportStar(require("./lib/tools/PSWriteFileTool"), exports);
27
27
  __exportStar(require("./lib/tools/PSSubAgentTool"), exports);
28
28
  __exportStar(require("./lib/tools/PSMemoryTool"), exports);
29
29
  __exportStar(require("./lib/tools/PSClarifyTool"), exports);
30
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZDQUEyQjtBQUMzQixnREFBOEI7QUFDOUIsc0RBQW9DO0FBQ3BDLHNEQUFvQztBQUNwQyxvREFBa0M7QUFDbEMsb0RBQWtDO0FBQ2xDLHVEQUFxQztBQUNyQyxnRUFBOEM7QUFDOUMsNkRBQTJDO0FBQzNDLDhEQUE0QztBQUM1Qyw2REFBMkM7QUFDM0MsMkRBQXlDO0FBQ3pDLDREQUEwQyJ9
30
+ __exportStar(require("./lib/tools/PSSkillTool"), exports);
31
+ __exportStar(require("./lib/skills/types"), exports);
32
+ __exportStar(require("./lib/skills/utils"), exports);
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZDQUEyQjtBQUMzQixnREFBOEI7QUFDOUIsc0RBQW9DO0FBQ3BDLHNEQUFvQztBQUNwQyxvREFBa0M7QUFDbEMsb0RBQWtDO0FBQ2xDLHVEQUFxQztBQUNyQyxnRUFBOEM7QUFDOUMsNkRBQTJDO0FBQzNDLDhEQUE0QztBQUM1Qyw2REFBMkM7QUFDM0MsMkRBQXlDO0FBQ3pDLDREQUEwQztBQUMxQywwREFBd0M7QUFDeEMscURBQW1DO0FBQ25DLHFEQUFtQyJ9
@@ -1,11 +1,11 @@
1
1
  export type AgentLoggerType = 'debug' | 'info' | 'error' | 'warn' | 'done';
2
2
  export interface IStreamLogger {
3
3
  log(type: string, message: string, ...optionalParams: any[]): number;
4
- debug(message: string, ...optionalParams: any[]): void;
5
- info(message: string, ...optionalParams: any[]): void;
6
- error(message: string, ...optionalParams: any[]): void;
7
- warn(message: string, ...optionalParams: any[]): void;
8
- done(message: string, ...optionalParams: any[]): void;
4
+ debug(message: string, ...optionalParams: any[]): number;
5
+ info(message: string, ...optionalParams: any[]): number;
6
+ error(message: string, ...optionalParams: any[]): number;
7
+ warn(message: string, ...optionalParams: any[]): number;
8
+ done(message: string, ...optionalParams: any[]): number;
9
9
  }
10
10
  export declare class PSAgentLogger implements IStreamLogger {
11
11
  static id: number;
@@ -2,6 +2,7 @@ declare const SKILL_CONSTANTS: {
2
2
  SKILL_WATCH_PATTERN: string;
3
3
  SKILLS_FOLDER_WATCH_PATTERN: string;
4
4
  SLINGSHOT_DIR: string;
5
+ AGENTS_DIR: string;
5
6
  SKILLS_DIR: string;
6
7
  GLOBAL_SKILLS_DIR: string;
7
8
  SKILL_DEFINITION_FILE: string;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const STATIC_CONSTANTS = {
7
7
  // Directory names
8
8
  SLINGSHOT_DIR: '.slingshot',
9
+ AGENTS_DIR: '.agents', // Base directory for npx skills CLI
9
10
  SKILLS_DIR: 'skills',
10
11
  GLOBAL_SKILLS_DIR: 'skills', // For extension global path
11
12
  // File names
@@ -21,4 +22,4 @@ const SKILL_CONSTANTS = Object.assign(Object.assign({}, STATIC_CONSTANTS), {
21
22
  // Watch pattern for folder deletions - watch the entire skills directory
22
23
  SKILLS_FOLDER_WATCH_PATTERN: `${STATIC_CONSTANTS.SLINGSHOT_DIR}/${STATIC_CONSTANTS.SKILLS_DIR}/**` });
23
24
  exports.default = SKILL_CONSTANTS;
24
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9za2lsbHMvY29uc3RhbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7O0dBRUc7QUFDSCxNQUFNLGdCQUFnQixHQUFHO0lBQ3ZCLGtCQUFrQjtJQUNsQixhQUFhLEVBQUUsWUFBWTtJQUMzQixVQUFVLEVBQUUsUUFBUTtJQUNwQixpQkFBaUIsRUFBRSxRQUFRLEVBQUUsNEJBQTRCO0lBRXpELGFBQWE7SUFDYixxQkFBcUIsRUFBRSxVQUFVO0lBQ2pDLGFBQWEsRUFBRSxxQkFBcUI7SUFDcEMsVUFBVSxFQUFFLG1CQUFtQjtJQUUvQixTQUFTO0lBQ1QsY0FBYyxFQUFFLEdBQUcsRUFBRSxlQUFlO0NBQ3JDLENBQUM7QUFFRixNQUFNLGVBQWUsbUNBQ2hCLGdCQUFnQjtJQUNuQix3Q0FBd0M7SUFDeEMsbUJBQW1CLEVBQUUsR0FBRyxnQkFBZ0IsQ0FBQyxhQUFhLElBQUksZ0JBQWdCLENBQUMsVUFBVSxPQUFPLGdCQUFnQixDQUFDLHFCQUFxQixFQUFFO0lBQ3BJLHlFQUF5RTtJQUN6RSwyQkFBMkIsRUFBRSxHQUFHLGdCQUFnQixDQUFDLGFBQWEsSUFBSSxnQkFBZ0IsQ0FBQyxVQUFVLEtBQUssR0FDbkcsQ0FBQztBQUVGLGtCQUFlLGVBQWUsQ0FBQyJ9
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9za2lsbHMvY29uc3RhbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7O0dBRUc7QUFDSCxNQUFNLGdCQUFnQixHQUFHO0lBQ3ZCLGtCQUFrQjtJQUNsQixhQUFhLEVBQUUsWUFBWTtJQUMzQixVQUFVLEVBQUUsU0FBUyxFQUFFLG9DQUFvQztJQUMzRCxVQUFVLEVBQUUsUUFBUTtJQUNwQixpQkFBaUIsRUFBRSxRQUFRLEVBQUUsNEJBQTRCO0lBRXpELGFBQWE7SUFDYixxQkFBcUIsRUFBRSxVQUFVO0lBQ2pDLGFBQWEsRUFBRSxxQkFBcUI7SUFDcEMsVUFBVSxFQUFFLG1CQUFtQjtJQUUvQixTQUFTO0lBQ1QsY0FBYyxFQUFFLEdBQUcsRUFBRSxlQUFlO0NBQ3JDLENBQUM7QUFFRixNQUFNLGVBQWUsbUNBQ2hCLGdCQUFnQjtJQUNuQix3Q0FBd0M7SUFDeEMsbUJBQW1CLEVBQUUsR0FBRyxnQkFBZ0IsQ0FBQyxhQUFhLElBQUksZ0JBQWdCLENBQUMsVUFBVSxPQUFPLGdCQUFnQixDQUFDLHFCQUFxQixFQUFFO0lBQ3BJLHlFQUF5RTtJQUN6RSwyQkFBMkIsRUFBRSxHQUFHLGdCQUFnQixDQUFDLGFBQWEsSUFBSSxnQkFBZ0IsQ0FBQyxVQUFVLEtBQUssR0FDbkcsQ0FBQztBQUVGLGtCQUFlLGVBQWUsQ0FBQyJ9
@@ -1,5 +1,10 @@
1
+ import { SkillMetadata } from './types';
1
2
  /**
2
- * Parses a SKILL.md file to extract metadata for the registry.
3
+ * Parses a SKILL.md file content to extract all metadata using gray-matter
4
+ */
5
+ export declare function parseSkillMetadataFromContent(content: string, skillPath: string): SkillMetadata;
6
+ /**
7
+ * Parses a SKILL.md file to extract metadata for the registry (legacy format)
3
8
  * @param {string} filePath - The absolute path to the SKILL.md file.
4
9
  * @returns {Object|null} - Returns { name, description, folderPath } or null if invalid.
5
10
  */
@@ -3,12 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseSkillMetadataFromContent = parseSkillMetadataFromContent;
6
7
  exports.default = parseSkillMetadata;
7
8
  const node_fs_1 = __importDefault(require("node:fs"));
8
9
  const node_path_1 = __importDefault(require("node:path"));
9
- const yaml_1 = __importDefault(require("yaml"));
10
+ const gray_matter_1 = __importDefault(require("gray-matter"));
10
11
  /**
11
- * Parses a SKILL.md file to extract metadata for the registry.
12
+ * Parses a SKILL.md file content to extract all metadata using gray-matter
13
+ */
14
+ function parseSkillMetadataFromContent(content, skillPath) {
15
+ const parsed = (0, gray_matter_1.default)(content);
16
+ return {
17
+ name: parsed.data.name || 'unknown',
18
+ description: parsed.data.description || 'No description available',
19
+ version: parsed.data.version,
20
+ author: parsed.data.author,
21
+ license: parsed.data.license,
22
+ path: skillPath,
23
+ };
24
+ }
25
+ /**
26
+ * Parses a SKILL.md file to extract metadata for the registry (legacy format)
12
27
  * @param {string} filePath - The absolute path to the SKILL.md file.
13
28
  * @returns {Object|null} - Returns { name, description, folderPath } or null if invalid.
14
29
  */
@@ -18,23 +33,18 @@ function parseSkillMetadata(filePath) {
18
33
  return null;
19
34
  }
20
35
  const fileContent = node_fs_1.default.readFileSync(filePath, 'utf8');
21
- // Use gray-matter to parse YAML frontmatter safely
22
- const data = yaml_1.default.parse(fileContent);
23
- // Validation: Name is required by the spec
24
- if (!data || !data.name) {
25
- return null;
26
- }
27
- const trimmedName = data.name.trim();
28
- const trimmedDescription = data.description ? data.description.trim() : '';
36
+ const skillPath = node_path_1.default.dirname(filePath);
37
+ // Parse using the unified function
38
+ const fullMetadata = parseSkillMetadataFromContent(fileContent, skillPath);
39
+ // Return legacy format for backward compatibility
29
40
  return {
30
- name: trimmedName,
31
- description: trimmedDescription || 'No description provided.',
32
- // We store the directory path, not the file path, as that's the "Skill Root"
33
- folderPath: node_path_1.default.dirname(filePath),
41
+ name: fullMetadata.name,
42
+ description: fullMetadata.description,
43
+ folderPath: fullMetadata.path,
34
44
  };
35
45
  }
36
46
  catch (_a) {
37
47
  return null;
38
48
  }
39
49
  }
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tpbGxQYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy9za2lsbFBhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVVBLHFDQThCQztBQXhDRCxzREFBeUI7QUFDekIsMERBQTZCO0FBRTdCLGdEQUF3QjtBQUV4Qjs7OztHQUlHO0FBQ0gsU0FBd0Isa0JBQWtCLENBQ3hDLFFBQWdCO0lBRWhCLElBQUksQ0FBQztRQUNILElBQUksQ0FBQyxpQkFBRSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQzdCLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLGlCQUFFLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUV0RCxtREFBbUQ7UUFDbkQsTUFBTSxJQUFJLEdBQUcsY0FBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUVyQywyQ0FBMkM7UUFDM0MsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN4QixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3JDLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBRTNFLE9BQU87WUFDTCxJQUFJLEVBQUUsV0FBVztZQUNqQixXQUFXLEVBQUUsa0JBQWtCLElBQUksMEJBQTBCO1lBQzdELDZFQUE2RTtZQUM3RSxVQUFVLEVBQUUsbUJBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO1NBQ25DLENBQUM7SUFDSixDQUFDO0lBQUMsV0FBTSxDQUFDO1FBQ1AsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0FBQ0gsQ0FBQyJ9
50
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tpbGxQYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy9za2lsbFBhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVVBLHNFQWNDO0FBT0QscUNBdUJDO0FBdERELHNEQUF5QjtBQUN6QiwwREFBNkI7QUFFN0IsOERBQWlDO0FBSWpDOztHQUVHO0FBQ0gsU0FBZ0IsNkJBQTZCLENBQzNDLE9BQWUsRUFDZixTQUFpQjtJQUVqQixNQUFNLE1BQU0sR0FBRyxJQUFBLHFCQUFNLEVBQUMsT0FBTyxDQUFDLENBQUM7SUFFL0IsT0FBTztRQUNMLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxTQUFTO1FBQ25DLFdBQVcsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFdBQVcsSUFBSSwwQkFBMEI7UUFDbEUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTztRQUM1QixNQUFNLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNO1FBQzFCLE9BQU8sRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU87UUFDNUIsSUFBSSxFQUFFLFNBQVM7S0FDaEIsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBd0Isa0JBQWtCLENBQ3hDLFFBQWdCO0lBRWhCLElBQUksQ0FBQztRQUNILElBQUksQ0FBQyxpQkFBRSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQzdCLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLGlCQUFFLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUN0RCxNQUFNLFNBQVMsR0FBRyxtQkFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUV6QyxtQ0FBbUM7UUFDbkMsTUFBTSxZQUFZLEdBQUcsNkJBQTZCLENBQUMsV0FBVyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRTNFLGtEQUFrRDtRQUNsRCxPQUFPO1lBQ0wsSUFBSSxFQUFFLFlBQVksQ0FBQyxJQUFJO1lBQ3ZCLFdBQVcsRUFBRSxZQUFZLENBQUMsV0FBVztZQUNyQyxVQUFVLEVBQUUsWUFBWSxDQUFDLElBQUk7U0FDOUIsQ0FBQztJQUNKLENBQUM7SUFBQyxXQUFNLENBQUM7UUFDUCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDIn0=
@@ -0,0 +1,61 @@
1
+ export interface SkillContent {
2
+ metadata: SkillFrontmatter;
3
+ content: string;
4
+ rawContent: string;
5
+ }
6
+ export interface SkillFrontmatter {
7
+ name: string;
8
+ description: string;
9
+ version: string;
10
+ author: string;
11
+ dependencies?: string[];
12
+ }
13
+ export interface SkillSourceConfig {
14
+ source: string;
15
+ sourceType?: 'git' | 'npm' | 'local';
16
+ version?: string;
17
+ branch?: string;
18
+ }
19
+ export interface SkillSourcesObject {
20
+ [skillName: string]: SkillSourceConfig;
21
+ }
22
+ export interface PSSkillToolConfig {
23
+ skillsPath: string;
24
+ allowedSkills?: string[];
25
+ skillSources?: SkillSourcesObject;
26
+ autoInstall?: boolean;
27
+ }
28
+ export interface PSSkillToolExecutionConfig {
29
+ skillsPath: string;
30
+ allowedSkills?: string[];
31
+ }
32
+ export interface SkillInstallationOptions {
33
+ source: string;
34
+ skillsPath: string;
35
+ timeout?: number;
36
+ }
37
+ export interface SkillInvocation {
38
+ skill_name: string;
39
+ arguments?: string;
40
+ }
41
+ export interface SkillMetadata {
42
+ name: string;
43
+ description: string;
44
+ path: string;
45
+ version?: string;
46
+ author?: string;
47
+ license?: string;
48
+ }
49
+ export interface SkillExecutionResult {
50
+ status: 'success' | 'error';
51
+ skill_name: string;
52
+ content?: string;
53
+ metadata?: {
54
+ name: string;
55
+ description: string;
56
+ version: string;
57
+ author: string;
58
+ };
59
+ message: string;
60
+ error?: string;
61
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -1,3 +1,5 @@
1
+ import { IStreamLogger } from '../PSAgentLogger';
2
+ import { SkillContent, SkillInstallationOptions, SkillMetadata, SkillSourcesObject } from './types';
1
3
  /**
2
4
  * Gets the slingshot directories for both workspace and user levels.
3
5
  * Creates the directories if they don't exist.
@@ -8,3 +10,57 @@ export default function getSlingshotDirectories(repoRoot: string): {
8
10
  workspaceLevelSlingshotDir: string | null;
9
11
  userLevelSlingshotDir: string | null;
10
12
  };
13
+ /**
14
+ * Validates skill name for security (prevents path traversal)
15
+ */
16
+ export declare function validateSkillName(skillName: string): boolean;
17
+ /**
18
+ * Validates skill source URL for security (prevents command injection)
19
+ */
20
+ export declare function validateSkillSource(source: string): boolean;
21
+ /**
22
+ * Ensures skills directory exists
23
+ */
24
+ export declare function ensureSkillsDirectory(skillsPath: string): Promise<void>;
25
+ /**
26
+ * Gets the path to a skill's SKILL.md file
27
+ * Skills are stored in the .agents/skills subdirectory as created by npx skills CLI
28
+ */
29
+ export declare function getSkillFilePath(skillsPath: string, skillName: string): string;
30
+ /**
31
+ * Checks if a skill exists locally
32
+ */
33
+ export declare function skillExists(skillsPath: string, skillName: string): boolean;
34
+ /**
35
+ * Reads skill content and parses frontmatter using skillParser
36
+ */
37
+ export declare function readSkillContent(skillsPath: string, skillName: string): Promise<SkillContent>;
38
+ /**
39
+ * Scans a directory for installed skills and returns metadata
40
+ */
41
+ export declare function scanInstalledSkills(skillsPath: string): Promise<Map<string, SkillMetadata>>;
42
+ /**
43
+ * Normalizes skill source URLs to handle common formatting issues
44
+ */
45
+ export declare function normalizeSkillSource(source: string): string;
46
+ /**
47
+ * Extract skill name from source URL
48
+ */
49
+ export declare function extractSkillName(source: string): string;
50
+ /**
51
+ * Install a skill using npx skills command (simplified)
52
+ */
53
+ export declare function installSkill(options: SkillInstallationOptions): Promise<SkillMetadata>;
54
+ /**
55
+ * Converts skill metadata to XML format for system prompts
56
+ */
57
+ export declare function convertSkillsToSystemPromptXml(skills: SkillMetadata[]): string;
58
+ /**
59
+ * Get skills XML from a skills directory scan
60
+ */
61
+ export declare function getSkillsSystemPromptFromDirectory(skillsPath: string): Promise<string>;
62
+ /**
63
+ * Setup skills directory and get system prompt
64
+ * Accepts skill sources as an object mapping skill names to source configurations
65
+ */
66
+ export declare function setupSkillsAndGetPrompt(skillsPath: string, sources?: SkillSourcesObject, log?: IStreamLogger): Promise<string>;
@@ -4,10 +4,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = getSlingshotDirectories;
7
+ exports.validateSkillName = validateSkillName;
8
+ exports.validateSkillSource = validateSkillSource;
9
+ exports.ensureSkillsDirectory = ensureSkillsDirectory;
10
+ exports.getSkillFilePath = getSkillFilePath;
11
+ exports.skillExists = skillExists;
12
+ exports.readSkillContent = readSkillContent;
13
+ exports.scanInstalledSkills = scanInstalledSkills;
14
+ exports.normalizeSkillSource = normalizeSkillSource;
15
+ exports.extractSkillName = extractSkillName;
16
+ exports.installSkill = installSkill;
17
+ exports.convertSkillsToSystemPromptXml = convertSkillsToSystemPromptXml;
18
+ exports.getSkillsSystemPromptFromDirectory = getSkillsSystemPromptFromDirectory;
19
+ exports.setupSkillsAndGetPrompt = setupSkillsAndGetPrompt;
20
+ const fs_1 = require("fs");
7
21
  const node_fs_1 = __importDefault(require("node:fs"));
8
22
  const node_os_1 = __importDefault(require("node:os"));
9
23
  const node_path_1 = __importDefault(require("node:path"));
24
+ const gray_matter_1 = __importDefault(require("gray-matter"));
25
+ const debuglogger_1 = __importDefault(require("../utils/debuglogger"));
10
26
  const constants_1 = __importDefault(require("./constants"));
27
+ const skillParser_1 = require("./skillParser");
28
+ const skillsXMLMetaDataGenerator_1 = __importDefault(require("./skillsXMLMetaDataGenerator"));
11
29
  /**
12
30
  * Gets the slingshot directories for both workspace and user levels.
13
31
  * Creates the directories if they don't exist.
@@ -45,4 +63,327 @@ function getSlingshotDirectories(repoRoot) {
45
63
  return result;
46
64
  }
47
65
  }
48
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVlBLDBDQThDQztBQTFERCxzREFBeUI7QUFDekIsc0RBQXlCO0FBQ3pCLDBEQUE2QjtBQUU3Qiw0REFBMEM7QUFFMUM7Ozs7O0dBS0c7QUFDSCxTQUF3Qix1QkFBdUIsQ0FBQyxRQUFnQjtJQUM5RCxNQUFNLE1BQU0sR0FHUjtRQUNGLDBCQUEwQixFQUFFLElBQUk7UUFDaEMscUJBQXFCLEVBQUUsSUFBSTtLQUM1QixDQUFDO0lBRUYsSUFBSSxDQUFDO1FBQ0gsMkNBQTJDO1FBQzNDLElBQUksUUFBUSxFQUFFLENBQUM7WUFDYixNQUFNLHFCQUFxQixHQUFHLG1CQUFJLENBQUMsSUFBSSxDQUNyQyxRQUFRLEVBQ1IsbUJBQWUsQ0FBQyxhQUFhLENBQzlCLENBQUM7WUFFRix1REFBdUQ7WUFDdkQsSUFBSSxDQUFDLGlCQUFFLENBQUMsVUFBVSxDQUFDLHFCQUFxQixDQUFDLEVBQUUsQ0FBQztnQkFDMUMsaUJBQUUsQ0FBQyxTQUFTLENBQUMscUJBQXFCLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUMzRCxDQUFDO1lBRUQsTUFBTSxDQUFDLDBCQUEwQixHQUFHLHFCQUFxQixDQUFDO1FBQzVELENBQUM7UUFFRCxxRUFBcUU7UUFDckUsTUFBTSxXQUFXLEdBQUcsaUJBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUVqQyxNQUFNLGdCQUFnQixHQUFHLG1CQUFJLENBQUMsSUFBSSxDQUNoQyxXQUFXLEVBQ1gsbUJBQWUsQ0FBQyxhQUFhLENBQzlCLENBQUM7UUFFRixrREFBa0Q7UUFDbEQsSUFBSSxDQUFDLGlCQUFFLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztZQUNyQyxpQkFBRSxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELENBQUM7UUFFRCxNQUFNLENBQUMscUJBQXFCLEdBQUcsZ0JBQWdCLENBQUM7UUFFaEQsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZiwrREFBK0Q7UUFDL0QsT0FBTyxDQUFDLEtBQUssQ0FBQyx1Q0FBdUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUM5RCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0FBQ0gsQ0FBQyJ9
66
+ const dlog = (0, debuglogger_1.default)('PSAI:SkillUtils:');
67
+ // Extract skill-related constants from centralized constants
68
+ const { AGENTS_DIR, SKILLS_DIR, SKILL_DEFINITION_FILE } = constants_1.default;
69
+ // Map to track ongoing installations to prevent race conditions
70
+ const ongoingInstallations = new Map();
71
+ /**
72
+ * Validates skill name for security (prevents path traversal)
73
+ */
74
+ function validateSkillName(skillName) {
75
+ // Skill name should not contain path separators or special characters
76
+ const validPattern = /^[a-zA-Z0-9_-]+$/;
77
+ return validPattern.test(skillName) && !skillName.includes('..');
78
+ }
79
+ /**
80
+ * Validates skill source URL for security (prevents command injection)
81
+ */
82
+ function validateSkillSource(source) {
83
+ if (!source || typeof source !== 'string') {
84
+ return false;
85
+ }
86
+ const trimmed = source.trim();
87
+ // Check for common malicious patterns
88
+ const dangerousPatterns = [
89
+ /[;&|`$(){}[\]\\]/, // Shell metacharacters
90
+ /\.\./, // Path traversal
91
+ /^-/, // Starting with dash (could be interpreted as flag)
92
+ /\s/, // Whitespace (could break command parsing)
93
+ // eslint-disable-next-line no-control-regex
94
+ /[\x00-\x1f\x7f]/, // Control characters
95
+ ];
96
+ for (const pattern of dangerousPatterns) {
97
+ if (pattern.test(trimmed)) {
98
+ return false;
99
+ }
100
+ }
101
+ // Only allow specific URL patterns
102
+ const allowedPatterns = [
103
+ /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(@[a-zA-Z0-9_-]+)?$/, // owner/repo@skill
104
+ /^github\.com\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(@[a-zA-Z0-9_-]+)?$/, // github.com/owner/repo@skill
105
+ /^https:\/\/github\.com\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(@[a-zA-Z0-9_-]+)?$/, // Full GitHub URL
106
+ ];
107
+ return allowedPatterns.some((pattern) => pattern.test(trimmed));
108
+ }
109
+ /**
110
+ * Ensures skills directory exists
111
+ */
112
+ async function ensureSkillsDirectory(skillsPath) {
113
+ await fs_1.promises.mkdir(skillsPath, { recursive: true });
114
+ }
115
+ /**
116
+ * Gets the path to a skill's SKILL.md file
117
+ * Skills are stored in the .agents/skills subdirectory as created by npx skills CLI
118
+ */
119
+ function getSkillFilePath(skillsPath, skillName) {
120
+ if (!validateSkillName(skillName)) {
121
+ throw new Error(`Invalid skill name: ${skillName}`);
122
+ }
123
+ return node_path_1.default.join(skillsPath, AGENTS_DIR, SKILLS_DIR, skillName, SKILL_DEFINITION_FILE);
124
+ }
125
+ /**
126
+ * Checks if a skill exists locally
127
+ */
128
+ function skillExists(skillsPath, skillName) {
129
+ const skillFilePath = getSkillFilePath(skillsPath, skillName);
130
+ return node_fs_1.default.existsSync(skillFilePath);
131
+ }
132
+ /**
133
+ * Reads skill content and parses frontmatter using skillParser
134
+ */
135
+ async function readSkillContent(skillsPath, skillName) {
136
+ const skillFilePath = getSkillFilePath(skillsPath, skillName);
137
+ const rawContent = await fs_1.promises.readFile(skillFilePath, 'utf-8');
138
+ const parsed = (0, gray_matter_1.default)(rawContent);
139
+ // Create SkillFrontmatter with user-friendly defaults (different from SkillMetadata)
140
+ const metadata = {
141
+ name: parsed.data.name || skillName,
142
+ description: parsed.data.description || 'No description provided',
143
+ version: parsed.data.version || '1.0.0',
144
+ author: parsed.data.author || 'Unknown',
145
+ dependencies: parsed.data.dependencies || [],
146
+ };
147
+ return {
148
+ metadata,
149
+ content: parsed.content,
150
+ rawContent,
151
+ };
152
+ }
153
+ /**
154
+ * Scans a directory for installed skills and returns metadata
155
+ */
156
+ async function scanInstalledSkills(skillsPath) {
157
+ const installedSkills = new Map();
158
+ try {
159
+ await fs_1.promises.access(skillsPath);
160
+ }
161
+ catch (_a) {
162
+ return installedSkills;
163
+ }
164
+ try {
165
+ // Skills are located in .agents/skills directory (created by npx skills CLI)
166
+ const skillsDir = node_path_1.default.join(skillsPath, AGENTS_DIR, SKILLS_DIR);
167
+ dlog.info(`Scanning skills directory: ${skillsDir}`);
168
+ try {
169
+ await fs_1.promises.access(skillsDir);
170
+ }
171
+ catch (_b) {
172
+ dlog.info(`Skills directory does not exist: ${skillsDir}`);
173
+ return installedSkills;
174
+ }
175
+ const entries = await fs_1.promises.readdir(skillsDir, {
176
+ withFileTypes: true,
177
+ });
178
+ dlog.info(`Found entries:`, entries.map((e) => `${e.name} (${e.isDirectory() ? 'dir' : 'file'})`));
179
+ for (const entry of entries) {
180
+ if (entry.isDirectory()) {
181
+ const skillDir = node_path_1.default.join(skillsDir, entry.name);
182
+ dlog.info(`Checking skill directory: ${skillDir}`);
183
+ const metadata = await loadSkillMetadata(skillDir);
184
+ dlog.info(`Metadata loaded:`, metadata ? 'success' : 'failed');
185
+ if (metadata) {
186
+ metadata.path = skillDir;
187
+ installedSkills.set(entry.name, metadata);
188
+ dlog.info(`Added skill: ${entry.name}`);
189
+ }
190
+ }
191
+ }
192
+ dlog.info(`Total skills found: ${installedSkills.size}`);
193
+ dlog.info(`Skill names:`, Array.from(installedSkills.keys()));
194
+ }
195
+ catch (error) {
196
+ dlog.error(`Error scanning skills:`, error);
197
+ }
198
+ return installedSkills;
199
+ }
200
+ /**
201
+ * Load skill metadata from SKILL.md file using skillParser
202
+ */
203
+ async function loadSkillMetadata(skillDir) {
204
+ const skillMdPath = node_path_1.default.join(skillDir, SKILL_DEFINITION_FILE);
205
+ try {
206
+ const fileContent = await fs_1.promises.readFile(skillMdPath, 'utf-8');
207
+ return (0, skillParser_1.parseSkillMetadataFromContent)(fileContent, skillDir);
208
+ }
209
+ catch (_a) {
210
+ return null;
211
+ }
212
+ }
213
+ /**
214
+ * Normalizes skill source URLs to handle common formatting issues
215
+ */
216
+ function normalizeSkillSource(source) {
217
+ // Validate input first for security
218
+ if (!validateSkillSource(source)) {
219
+ throw new Error(`Invalid or potentially unsafe skill source: ${source}`);
220
+ }
221
+ let normalized = source.trim();
222
+ // Remove .git suffix if present (common mistake)
223
+ normalized = normalized.replace(/\.git$/, '');
224
+ // Remove https:// prefix if present (npx skills prefers short format)
225
+ normalized = normalized.replace(/^https:\/\//, '');
226
+ // Remove github.com/ prefix (npx skills prefers owner/repo format)
227
+ normalized = normalized.replace(/^github\.com\//, '');
228
+ return normalized;
229
+ }
230
+ /**
231
+ * Extract skill name from source URL
232
+ */
233
+ function extractSkillName(source) {
234
+ // Handle formats like:
235
+ // - github.com/owner/repo@skill
236
+ // - owner/repo@skill
237
+ // - github.com/owner/repo (use repo name)
238
+ const normalized = source.replace(/^github\.com\//, '');
239
+ let skillName;
240
+ if (normalized.includes('@')) {
241
+ skillName = normalized.split('@')[1];
242
+ }
243
+ else {
244
+ // Use repo name if no skill specified
245
+ skillName = normalized.split('/')[1] || 'unknown-skill';
246
+ }
247
+ // Validate the extracted skill name for security
248
+ if (!validateSkillName(skillName)) {
249
+ throw new Error(`Invalid skill name extracted from source: ${skillName}`);
250
+ }
251
+ return skillName;
252
+ }
253
+ /**
254
+ * Install a skill using npx skills command (simplified)
255
+ */
256
+ async function installSkill(options) {
257
+ const { source, skillsPath, timeout = 60000 } = options;
258
+ if (!source || typeof source !== 'string') {
259
+ throw new Error('Invalid skill source provided');
260
+ }
261
+ const normalizedSource = normalizeSkillSource(source);
262
+ const skillName = extractSkillName(normalizedSource);
263
+ if (!validateSkillName(skillName)) {
264
+ throw new Error(`Invalid skill name derived from source: ${skillName}`);
265
+ }
266
+ // Check if there's already an ongoing installation for this skill
267
+ if (ongoingInstallations.has(skillName)) {
268
+ dlog.info(`Installation for ${skillName} already in progress, waiting...`);
269
+ return ongoingInstallations.get(skillName);
270
+ }
271
+ // Check if already installed
272
+ if (skillExists(skillsPath, skillName)) {
273
+ const skillDir = node_path_1.default.join(skillsPath, AGENTS_DIR, SKILLS_DIR, skillName);
274
+ const metadata = await loadSkillMetadata(skillDir);
275
+ if (metadata) {
276
+ dlog.info(`Skill ${skillName} already installed`);
277
+ return metadata;
278
+ }
279
+ }
280
+ // Create installation promise and track it
281
+ const installationPromise = performSimpleInstallation(normalizedSource, skillName, skillsPath, timeout);
282
+ ongoingInstallations.set(skillName, installationPromise);
283
+ try {
284
+ return await installationPromise;
285
+ }
286
+ finally {
287
+ ongoingInstallations.delete(skillName);
288
+ }
289
+ }
290
+ /**
291
+ * Simple skill installation - just run npx skills add and check result
292
+ */
293
+ async function performSimpleInstallation(normalizedSource, skillName, skillsPath, timeout) {
294
+ await ensureSkillsDirectory(skillsPath);
295
+ try {
296
+ // Run the installation command safely with spawn to prevent command injection
297
+ const command = 'npx';
298
+ const args = ['skills', 'add', normalizedSource, '--yes'];
299
+ dlog.info(`Installing skill: ${command} ${args.join(' ')}`);
300
+ const { spawnSync } = await import('child_process');
301
+ const result = spawnSync(command, args, {
302
+ cwd: skillsPath,
303
+ timeout,
304
+ stdio: 'inherit', // Show output directly
305
+ encoding: 'utf8',
306
+ });
307
+ // Check if the command failed
308
+ if (result.error) {
309
+ throw result.error;
310
+ }
311
+ if (result.status !== 0) {
312
+ throw new Error(`Installation process exited with code ${result.status}`);
313
+ }
314
+ // Check if installation succeeded by looking for the skill
315
+ const skillDir = node_path_1.default.join(skillsPath, AGENTS_DIR, SKILLS_DIR, skillName);
316
+ const metadata = await loadSkillMetadata(skillDir);
317
+ if (metadata) {
318
+ dlog.info(`Successfully installed skill: ${skillName}`);
319
+ return metadata;
320
+ }
321
+ else {
322
+ throw new Error(`Skill installation completed but no SKILL.md found at ${skillDir}`);
323
+ }
324
+ }
325
+ catch (error) {
326
+ throw new Error(`Failed to install skill ${skillName}: ${error}`);
327
+ }
328
+ }
329
+ /**
330
+ * Converts skill metadata to XML format for system prompts
331
+ */
332
+ function convertSkillsToSystemPromptXml(skills) {
333
+ const skillsList = skills.map((skill) => ({
334
+ name: skill.name,
335
+ description: skill.description,
336
+ }));
337
+ return (0, skillsXMLMetaDataGenerator_1.default)(skillsList);
338
+ }
339
+ /**
340
+ * Get skills XML from a skills directory scan
341
+ */
342
+ async function getSkillsSystemPromptFromDirectory(skillsPath) {
343
+ const installedSkills = await scanInstalledSkills(skillsPath);
344
+ const skillsArray = Array.from(installedSkills.values());
345
+ return convertSkillsToSystemPromptXml(skillsArray);
346
+ }
347
+ /**
348
+ * Setup skills directory and get system prompt
349
+ * Accepts skill sources as an object mapping skill names to source configurations
350
+ */
351
+ async function setupSkillsAndGetPrompt(skillsPath, sources, log) {
352
+ var _a;
353
+ await ensureSkillsDirectory(skillsPath);
354
+ if (sources) {
355
+ const skillEntries = Object.entries(sources);
356
+ if (skillEntries.length > 0) {
357
+ (_a = log === null || log === void 0 ? void 0 : log.info) === null || _a === void 0 ? void 0 : _a.call(log, `🚀 Installing ${skillEntries.length} skills from configuration...`);
358
+ await Promise.all(skillEntries.map(async ([skillName, config]) => {
359
+ var _a, _b, _c;
360
+ try {
361
+ // Validate the source
362
+ if (!config.source || !validateSkillSource(config.source)) {
363
+ throw new Error(`Invalid source URL: ${config.source}`);
364
+ }
365
+ const normalizedSource = normalizeSkillSource(config.source);
366
+ // For object format, we use the key as the skill name
367
+ // Append it to the source if not already present
368
+ let sourceWithSkill = normalizedSource;
369
+ if (!normalizedSource.includes('@')) {
370
+ sourceWithSkill = `${normalizedSource}@${skillName}`;
371
+ }
372
+ (_a = log === null || log === void 0 ? void 0 : log.info) === null || _a === void 0 ? void 0 : _a.call(log, `⬇️ Installing skill "${skillName}" from ${config.source} (${config.sourceType || 'git'})`);
373
+ await installSkill({
374
+ source: sourceWithSkill,
375
+ skillsPath,
376
+ timeout: 60000,
377
+ });
378
+ (_b = log === null || log === void 0 ? void 0 : log.info) === null || _b === void 0 ? void 0 : _b.call(log, `✅ Successfully installed skill: ${skillName}`);
379
+ }
380
+ catch (error) {
381
+ (_c = log === null || log === void 0 ? void 0 : log.error) === null || _c === void 0 ? void 0 : _c.call(log, `❌ Failed to install skill "${skillName}":`, error);
382
+ // Continue with other skills even if one fails
383
+ }
384
+ }));
385
+ }
386
+ }
387
+ return await getSkillsSystemPromptFromDirectory(skillsPath);
388
+ }
389
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQTJCQSwwQ0E4Q0M7QUFhRCw4Q0FJQztBQUtELGtEQStCQztBQUtELHNEQUVDO0FBTUQsNENBZUM7QUFLRCxrQ0FHQztBQUtELDRDQXVCQztBQUtELGtEQXNEQztBQXFCRCxvREFrQkM7QUFLRCw0Q0FzQkM7QUFLRCxvQ0E4Q0M7QUF3REQsd0VBU0M7QUFLRCxnRkFNQztBQU1ELDBEQW9EQztBQXBmRCwyQkFBNEM7QUFDNUMsc0RBQXlCO0FBQ3pCLHNEQUF5QjtBQUN6QiwwREFBNkI7QUFFN0IsOERBQWlDO0FBR2pDLHVFQUErQztBQUUvQyw0REFBMEM7QUFDMUMsK0NBQThEO0FBQzlELDhGQUFxRTtBQVNyRTs7Ozs7R0FLRztBQUNILFNBQXdCLHVCQUF1QixDQUFDLFFBQWdCO0lBQzlELE1BQU0sTUFBTSxHQUdSO1FBQ0YsMEJBQTBCLEVBQUUsSUFBSTtRQUNoQyxxQkFBcUIsRUFBRSxJQUFJO0tBQzVCLENBQUM7SUFFRixJQUFJLENBQUM7UUFDSCwyQ0FBMkM7UUFDM0MsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUNiLE1BQU0scUJBQXFCLEdBQUcsbUJBQUksQ0FBQyxJQUFJLENBQ3JDLFFBQVEsRUFDUixtQkFBZSxDQUFDLGFBQWEsQ0FDOUIsQ0FBQztZQUVGLHVEQUF1RDtZQUN2RCxJQUFJLENBQUMsaUJBQUUsQ0FBQyxVQUFVLENBQUMscUJBQXFCLENBQUMsRUFBRSxDQUFDO2dCQUMxQyxpQkFBRSxDQUFDLFNBQVMsQ0FBQyxxQkFBcUIsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQzNELENBQUM7WUFFRCxNQUFNLENBQUMsMEJBQTBCLEdBQUcscUJBQXFCLENBQUM7UUFDNUQsQ0FBQztRQUVELHFFQUFxRTtRQUNyRSxNQUFNLFdBQVcsR0FB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