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.
@@ -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,325 @@ 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 {
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 {
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 {
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
+ await ensureSkillsDirectory(skillsPath);
353
+ if (sources) {
354
+ const skillEntries = Object.entries(sources);
355
+ if (skillEntries.length > 0) {
356
+ log?.info?.(`🚀 Installing ${skillEntries.length} skills from configuration...`);
357
+ await Promise.all(skillEntries.map(async ([skillName, config]) => {
358
+ try {
359
+ // Validate the source
360
+ if (!config.source || !validateSkillSource(config.source)) {
361
+ throw new Error(`Invalid source URL: ${config.source}`);
362
+ }
363
+ const normalizedSource = normalizeSkillSource(config.source);
364
+ // For object format, we use the key as the skill name
365
+ // Append it to the source if not already present
366
+ let sourceWithSkill = normalizedSource;
367
+ if (!normalizedSource.includes('@')) {
368
+ sourceWithSkill = `${normalizedSource}@${skillName}`;
369
+ }
370
+ log?.info?.(`⬇️ Installing skill "${skillName}" from ${config.source} (${config.sourceType || 'git'})`);
371
+ await installSkill({
372
+ source: sourceWithSkill,
373
+ skillsPath,
374
+ timeout: 60000,
375
+ });
376
+ log?.info?.(`✅ Successfully installed skill: ${skillName}`);
377
+ }
378
+ catch (error) {
379
+ log?.error?.(`❌ Failed to install skill "${skillName}":`, error);
380
+ // Continue with other skills even if one fails
381
+ }
382
+ }));
383
+ }
384
+ }
385
+ return await getSkillsSystemPromptFromDirectory(skillsPath);
386
+ }
387
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NraWxscy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQTJCQSwwQ0E4Q0M7QUFhRCw4Q0FJQztBQUtELGtEQStCQztBQUtELHNEQUVDO0FBTUQsNENBZUM7QUFLRCxrQ0FHQztBQUtELDRDQXVCQztBQUtELGtEQXNEQztBQXFCRCxvREFrQkM7QUFLRCw0Q0FzQkM7QUFLRCxvQ0E4Q0M7QUF3REQsd0VBU0M7QUFLRCxnRkFNQztBQU1ELDBEQW9EQztBQXBmRCwyQkFBNEM7QUFDNUMsc0RBQXlCO0FBQ3pCLHNEQUF5QjtBQUN6QiwwREFBNkI7QUFFN0IsOERBQWlDO0FBR2pDLHVFQUErQztBQUUvQyw0REFBMEM7QUFDMUMsK0NBQThEO0FBQzlELDhGQUFxRTtBQVNyRTs7Ozs7R0FLRztBQUNILFNBQXdCLHVCQUF1QixDQUFDLFFBQWdCO0lBQzlELE1BQU0sTUFBTSxHQUdSO1FBQ0YsMEJBQTBCLEVBQUUsSUFBSTtRQUNoQyxxQkFBcUIsRUFBRSxJQUFJO0tBQzVCLENBQUM7SUFFRixJQUFJLENBQUM7UUFDSCwyQ0FBMkM7UUFDM0MsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUNiLE1BQU0scUJBQXFCLEdBQUcsbUJBQUksQ0FBQyxJQUFJLENBQ3JDLFFBQVEsRUFDUixtQkFBZSxDQUFDLGFBQWEsQ0FDOUIsQ0FBQztZQUVGLHVEQUF1RDtZQUN2RCxJQUFJLENBQUMsaUJBQUUsQ0FBQyxVQUFVLENBQUMscUJBQXFCLENBQUMsRUFBRSxDQUFDO2dCQUMxQyxpQkFBRSxDQUFDLFNBQVMsQ0FBQyxxQkFBcUIsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQzNELENBQUM7WUFFRCxNQUFNLENBQUMsMEJBQTBCLEdBQUcscUJBQXFCLENBQUM7UUFDNUQsQ0FBQztRQUVELHFFQUFxRTtRQUNyRSxNQUFNLFdBQVcsR0FB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
@@ -0,0 +1,14 @@
1
+ import { PSAgentLogger } from '../PSAgentLogger';
2
+ import { PSAgentTool } from '../PSAgentTool';
3
+ import { PSSkillToolExecutionConfig, SkillExecutionResult } from '../skills/types';
4
+ export declare class PSSkillTool extends PSAgentTool {
5
+ static readonly toolName = "skill";
6
+ private skillsPath;
7
+ private allowedSkills?;
8
+ constructor(config: PSSkillToolExecutionConfig);
9
+ /**
10
+ * Validates if a skill is allowed to be executed
11
+ */
12
+ private isSkillAllowed;
13
+ run(input: Record<string, any>, log?: PSAgentLogger): Promise<SkillExecutionResult>;
14
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PSSkillTool = void 0;
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ const v4_1 = require("zod/v4");
9
+ const PSAgentTool_1 = require("../PSAgentTool");
10
+ const utils_1 = require("../skills/utils");
11
+ const debuglogger_1 = __importDefault(require("../utils/debuglogger"));
12
+ const dlog = (0, debuglogger_1.default)('PSAI:SkillTool:');
13
+ //@todo arguments implementations, templated variable values expected in the arguments
14
+ class PSSkillTool extends PSAgentTool_1.PSAgentTool {
15
+ static toolName = 'skill';
16
+ skillsPath;
17
+ allowedSkills;
18
+ constructor(config) {
19
+ super({
20
+ name: PSSkillTool.toolName,
21
+ description: 'Execute skills by reading their markdown content and injecting it as context. ' +
22
+ 'Skills are reusable knowledge modules that provide specialized guidance and instructions.',
23
+ type: 'API',
24
+ local_execution: true,
25
+ hitl: true,
26
+ inputschema: {
27
+ url: `http://localhost/${PSSkillTool.toolName}`,
28
+ method: 'POST',
29
+ requestBody: v4_1.z.toJSONSchema(v4_1.z
30
+ .object({
31
+ skill_name: v4_1.z.string().meta({
32
+ title: 'Skill_Name',
33
+ description: 'The name of the skill to execute. Must be a valid skill name installed in the skills directory.',
34
+ }),
35
+ arguments: v4_1.z.string().optional().meta({
36
+ title: 'Arguments',
37
+ description: 'Optional arguments or context to provide to the skill. This will be included with the skill content.',
38
+ }),
39
+ })
40
+ .meta({
41
+ title: `${PSSkillTool.toolName}_request_body`,
42
+ description: `The request body parameters for the ${PSSkillTool.toolName} tool.`,
43
+ }), { io: 'input' }),
44
+ },
45
+ });
46
+ this.skillsPath = config.skillsPath;
47
+ this.allowedSkills = config.allowedSkills;
48
+ }
49
+ /**
50
+ * Validates if a skill is allowed to be executed
51
+ */
52
+ isSkillAllowed(skillName) {
53
+ if (!this.allowedSkills) {
54
+ return true; // No restrictions
55
+ }
56
+ return this.allowedSkills.includes(skillName);
57
+ }
58
+ async run(input, log) {
59
+ const logger = log || dlog;
60
+ // Debug logging
61
+ logger.info(`🔍 PSSkillTool received input:`, input);
62
+ // Access properties from request_body (the actual skill parameters are nested)
63
+ const requestBody = input?.request_body || input;
64
+ const skill_name = requestBody?.skill_name;
65
+ // Validate input
66
+ if (!skill_name || typeof skill_name !== 'string') {
67
+ throw new Error('Invalid skill name provided.');
68
+ }
69
+ // Validate skill name for security
70
+ if (!(0, utils_1.validateSkillName)(skill_name)) {
71
+ throw new Error(`Invalid skill name format: ${skill_name}. Skill names must contain only alphanumeric characters, hyphens, and underscores.`);
72
+ }
73
+ // Check if skill is allowed
74
+ if (!this.isSkillAllowed(skill_name)) {
75
+ throw new Error(`Skill not allowed: ${skill_name}. This agent is restricted to specific skills only.`);
76
+ }
77
+ // Check if skill exists on filesystem
78
+ if (!(0, utils_1.skillExists)(this.skillsPath, skill_name)) {
79
+ throw new Error(`Skill not found: ${skill_name}. Please ensure the skill is installed in ${this.skillsPath}.`);
80
+ }
81
+ try {
82
+ // Read skill content
83
+ const skillContent = await (0, utils_1.readSkillContent)(this.skillsPath, skill_name);
84
+ logger.info(`✅ Skill executed successfully: ${skill_name}`);
85
+ return {
86
+ status: 'success',
87
+ skill_name,
88
+ content: skillContent.content,
89
+ message: `Skill '${skill_name}' has been loaded and its content is now available in your context.`,
90
+ };
91
+ }
92
+ catch (error) {
93
+ logger.error(`❌ Failed to execute skill ${skill_name}:`, error);
94
+ if (error instanceof Error) {
95
+ throw new Error(`Failed to execute skill '${skill_name}': ${error.message}`);
96
+ }
97
+ throw new Error(`Unknown error occurred while executing skill '${skill_name}'`);
98
+ }
99
+ }
100
+ }
101
+ exports.PSSkillTool = PSSkillTool;
102
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUFNTa2lsbFRvb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xzL1BTU2tpbGxUb29sLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHVEQUF1RDtBQUN2RCwrQkFBMkI7QUFHM0IsZ0RBQTZDO0FBSzdDLDJDQUl5QjtBQUN6Qix1RUFBK0M7QUFFL0MsTUFBTSxJQUFJLEdBQUcsSUFBQSxxQkFBVyxFQUFDLGlCQUFpQixDQUFDLENBQUM7QUFFNUMsc0ZBQXNGO0FBQ3RGLE1BQWEsV0FBWSxTQUFRLHlCQUFXO0lBQ25DLE1BQU0sQ0FBVSxRQUFRLEdBQUcsT0FBTyxDQUFDO0lBQ2xDLFVBQVUsQ0FBUztJQUNuQixhQUFhLENBQVk7SUFFakMsWUFBWSxNQUFrQztRQUM1QyxLQUFLLENBQUM7WUFDSixJQUFJLEVBQUUsV0FBVyxDQUFDLFFBQVE7WUFDMUIsV0FBVyxFQUNULGdGQUFnRjtnQkFDaEYsMkZBQTJGO1lBQzdGLElBQUksRUFBRSxLQUFLO1lBQ1gsZUFBZSxFQUFFLElBQUk7WUFDckIsSUFBSSxFQUFFLElBQUk7WUFDVixXQUFXLEVBQUU7Z0JBQ1gsR0FBRyxFQUFFLG9CQUFvQixXQUFXLENBQUMsUUFBUSxFQUFFO2dCQUMvQyxNQUFNLEVBQUUsTUFBTTtnQkFDZCxXQUFXLEVBQUUsTUFBQyxDQUFDLFlBQVksQ0FDekIsTUFBQztxQkFDRSxNQUFNLENBQUM7b0JBQ04sVUFBVSxFQUFFLE1BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUM7d0JBQzFCLEtBQUssRUFBRSxZQUFZO3dCQUNuQixXQUFXLEVBQ1QsaUdBQWlHO3FCQUNwRyxDQUFDO29CQUNGLFNBQVMsRUFBRSxNQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDO3dCQUNwQyxLQUFLLEVBQUUsV0FBVzt3QkFDbEIsV0FBVyxFQUNULHNHQUFzRztxQkFDekcsQ0FBQztpQkFD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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "psadk",
3
3
  "email": "rakesh.ravuri@publicissapient.com",
4
- "version": "1.3.3",
4
+ "version": "1.3.5",
5
5
  "description": "ps adk adapter",
6
6
  "main": "build/main/index.js",
7
7
  "typings": "build/main/index.d.ts",
@@ -53,6 +53,7 @@
53
53
  "debug": "^4.4.3",
54
54
  "express": "^5.2.1",
55
55
  "fetch-retry": "^6.0.0",
56
+ "gray-matter": "^4.0.3",
56
57
  "jsonwebtoken": "^9.0.3",
57
58
  "uuid": "^13.0.0",
58
59
  "yaml": "^2.8.3",