@pennyfarthing/shared 7.0.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.
- package/dist/generate-skill-docs.d.ts +35 -0
- package/dist/generate-skill-docs.d.ts.map +1 -0
- package/dist/generate-skill-docs.js +442 -0
- package/dist/generate-skill-docs.js.map +1 -0
- package/dist/generate-skill-docs.test.d.ts +13 -0
- package/dist/generate-skill-docs.test.d.ts.map +1 -0
- package/dist/generate-skill-docs.test.js +519 -0
- package/dist/generate-skill-docs.test.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/portrait-resolver.d.ts +32 -0
- package/dist/portrait-resolver.d.ts.map +1 -0
- package/dist/portrait-resolver.js +147 -0
- package/dist/portrait-resolver.js.map +1 -0
- package/dist/portrait-resolver.test.d.ts +2 -0
- package/dist/portrait-resolver.test.d.ts.map +1 -0
- package/dist/portrait-resolver.test.js +156 -0
- package/dist/portrait-resolver.test.js.map +1 -0
- package/dist/skill-search.d.ts +36 -0
- package/dist/skill-search.d.ts.map +1 -0
- package/dist/skill-search.js +300 -0
- package/dist/skill-search.js.map +1 -0
- package/dist/skill-search.sh +41 -0
- package/dist/skill-search.test.d.ts +16 -0
- package/dist/skill-search.test.d.ts.map +1 -0
- package/dist/skill-search.test.js +193 -0
- package/dist/skill-search.test.js.map +1 -0
- package/dist/skill-suggest.d.ts +76 -0
- package/dist/skill-suggest.d.ts.map +1 -0
- package/dist/skill-suggest.js +256 -0
- package/dist/skill-suggest.js.map +1 -0
- package/dist/skill-suggest.test.d.ts +12 -0
- package/dist/skill-suggest.test.d.ts.map +1 -0
- package/dist/skill-suggest.test.js +257 -0
- package/dist/skill-suggest.test.js.map +1 -0
- package/dist/theme-loader.d.ts +35 -0
- package/dist/theme-loader.d.ts.map +1 -0
- package/dist/theme-loader.js +170 -0
- package/dist/theme-loader.js.map +1 -0
- package/dist/theme-loader.test.d.ts +2 -0
- package/dist/theme-loader.test.d.ts.map +1 -0
- package/dist/theme-loader.test.js +72 -0
- package/dist/theme-loader.test.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portrait Resolver - Smart path resolution for Pennyfarthing portraits
|
|
3
|
+
*
|
|
4
|
+
* Checks paths in priority order:
|
|
5
|
+
* 1. PENNYFARTHING_DIST env var (explicit override)
|
|
6
|
+
* 2. Monorepo root (pennyfarthing-dist/ at repo root for dogfooding)
|
|
7
|
+
* 3. Sibling directory (for dev scenarios)
|
|
8
|
+
* 4. Scoped npm (node_modules/@pennyfarthing/core/pennyfarthing-dist/)
|
|
9
|
+
* 5. Legacy npm (node_modules/pennyfarthing/pennyfarthing-dist/)
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
12
|
+
import { join, dirname, resolve } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
// Get the directory where this module is located
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the pennyfarthing-dist directory path
|
|
19
|
+
* Checks multiple locations in priority order
|
|
20
|
+
*/
|
|
21
|
+
export function resolvePennyfarthingDist() {
|
|
22
|
+
// 1. PENNYFARTHING_DIST env var (explicit override)
|
|
23
|
+
const envPath = process.env.PENNYFARTHING_DIST;
|
|
24
|
+
if (envPath) {
|
|
25
|
+
if (existsSync(envPath)) {
|
|
26
|
+
return envPath;
|
|
27
|
+
}
|
|
28
|
+
// Env var set but path doesn't exist - fall through to other checks
|
|
29
|
+
}
|
|
30
|
+
// 2. Monorepo root (pennyfarthing-dist/ at repo root for dogfooding)
|
|
31
|
+
// Walk up from current directory looking for pennyfarthing-dist/
|
|
32
|
+
let currentDir = __dirname;
|
|
33
|
+
for (let i = 0; i < 10; i++) {
|
|
34
|
+
const monorepoPath = join(currentDir, 'pennyfarthing-dist');
|
|
35
|
+
if (existsSync(monorepoPath)) {
|
|
36
|
+
return monorepoPath;
|
|
37
|
+
}
|
|
38
|
+
const parentDir = dirname(currentDir);
|
|
39
|
+
if (parentDir === currentDir)
|
|
40
|
+
break; // Reached root
|
|
41
|
+
currentDir = parentDir;
|
|
42
|
+
}
|
|
43
|
+
// 3. Sibling directory (for dev scenarios)
|
|
44
|
+
// Check ../pennyfarthing-dist, ../../pennyfarthing-dist from module location
|
|
45
|
+
currentDir = __dirname;
|
|
46
|
+
for (let i = 0; i < 5; i++) {
|
|
47
|
+
currentDir = dirname(currentDir);
|
|
48
|
+
const siblingPath = join(currentDir, 'pennyfarthing-dist');
|
|
49
|
+
if (existsSync(siblingPath)) {
|
|
50
|
+
return siblingPath;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// 4. Scoped npm (node_modules/@pennyfarthing/core/pennyfarthing-dist/)
|
|
54
|
+
const scopedNpmPath = resolve(__dirname, '..', '..', '..', '@pennyfarthing', 'core', 'pennyfarthing-dist');
|
|
55
|
+
if (existsSync(scopedNpmPath)) {
|
|
56
|
+
return scopedNpmPath;
|
|
57
|
+
}
|
|
58
|
+
// 5. Legacy npm (node_modules/pennyfarthing/pennyfarthing-dist/)
|
|
59
|
+
const legacyNpmPath = resolve(__dirname, '..', '..', '..', 'pennyfarthing', 'pennyfarthing-dist');
|
|
60
|
+
if (existsSync(legacyNpmPath)) {
|
|
61
|
+
return legacyNpmPath;
|
|
62
|
+
}
|
|
63
|
+
// No valid path found
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the full path to a portrait image
|
|
68
|
+
* @param theme - Theme name (e.g., 'shakespeare', 'norse-mythology')
|
|
69
|
+
* @param agent - Agent name (e.g., 'sm', 'tea', 'dev')
|
|
70
|
+
* @returns Full path to portrait file, or null if not found
|
|
71
|
+
*/
|
|
72
|
+
export function resolvePortraitPath(theme, agent) {
|
|
73
|
+
const distPath = resolvePennyfarthingDist();
|
|
74
|
+
if (!distPath) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const paths = getPortraitPaths(distPath);
|
|
78
|
+
const portraitsThemeDir = join(paths.portraitsDir, theme);
|
|
79
|
+
if (!existsSync(portraitsThemeDir)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// Look for portrait file matching the agent
|
|
83
|
+
// Portraits are in size subdirectories: large/, medium/, small/, original/
|
|
84
|
+
// Portraits follow pattern: {shortName}-{ocean}.png
|
|
85
|
+
// Agent names in tests may be short names (sm, tea, dev) or need mapping
|
|
86
|
+
try {
|
|
87
|
+
// Check size subdirectories in preference order
|
|
88
|
+
const sizeDirectories = ['large', 'medium', 'small', 'original'];
|
|
89
|
+
let files = [];
|
|
90
|
+
let searchDir = portraitsThemeDir;
|
|
91
|
+
for (const sizeDir of sizeDirectories) {
|
|
92
|
+
const sizedPath = join(portraitsThemeDir, sizeDir);
|
|
93
|
+
if (existsSync(sizedPath)) {
|
|
94
|
+
files = readdirSync(sizedPath);
|
|
95
|
+
searchDir = sizedPath;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Fallback to root directory if no size subdirectories
|
|
100
|
+
if (files.length === 0) {
|
|
101
|
+
files = readdirSync(portraitsThemeDir);
|
|
102
|
+
searchDir = portraitsThemeDir;
|
|
103
|
+
}
|
|
104
|
+
// Map agent names to portrait file prefixes based on theme conventions
|
|
105
|
+
// For most themes, portrait names use character short names
|
|
106
|
+
// We need to find a file that contains the agent name or its mapping
|
|
107
|
+
// Note: This includes characters from multiple themes (shakespeare, norse, a-team)
|
|
108
|
+
const agentMappings = {
|
|
109
|
+
'sm': ['prospero', 'baldur', 'face', 'faceman', 'sm'],
|
|
110
|
+
'tea': ['hamlet', 'tyr', 'murdock', 'tea'],
|
|
111
|
+
'dev': ['puck', 'loki', 'ba', 'dev'],
|
|
112
|
+
'reviewer': ['portia', 'heimdall', 'lynch', 'decker', 'reviewer'],
|
|
113
|
+
'architect': ['oberon', 'mimir', 'hannibal', 'architect'],
|
|
114
|
+
'pm': ['henry', 'thor', 'amy', 'pm'],
|
|
115
|
+
'tech-writer': ['horatio', 'bragi', 'tech-writer'],
|
|
116
|
+
'ux-designer': ['viola', 'idunn', 'ux-designer'],
|
|
117
|
+
'devops': ['caliban', 'norns', 'devops'],
|
|
118
|
+
'orchestrator': ['chorus', 'odin', 'orchestrator'],
|
|
119
|
+
};
|
|
120
|
+
const possiblePrefixes = agentMappings[agent] || [agent];
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
for (const prefix of possiblePrefixes) {
|
|
123
|
+
if (file.toLowerCase().startsWith(prefix.toLowerCase()) &&
|
|
124
|
+
(file.endsWith('.png') || file.endsWith('.jpg'))) {
|
|
125
|
+
return join(searchDir, file);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get all portrait-related paths for a resolved dist directory
|
|
137
|
+
*/
|
|
138
|
+
export function getPortraitPaths(distPath) {
|
|
139
|
+
// Normalize path to remove trailing slashes
|
|
140
|
+
const normalizedPath = distPath.replace(/\/+$/, '');
|
|
141
|
+
return {
|
|
142
|
+
portraitsDir: join(normalizedPath, 'personas', 'portraits'),
|
|
143
|
+
themesDir: join(normalizedPath, 'personas'),
|
|
144
|
+
agentsDir: join(normalizedPath, 'agents'),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=portrait-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portrait-resolver.js","sourceRoot":"","sources":["../src/portrait-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portrait-resolver.test.d.ts","sourceRoot":"","sources":["../src/portrait-resolver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { resolvePennyfarthingDist, resolvePortraitPath, getPortraitPaths, } from './portrait-resolver.js';
|
|
5
|
+
describe('portrait-resolver', () => {
|
|
6
|
+
describe('resolvePennyfarthingDist', () => {
|
|
7
|
+
const originalEnv = process.env.PENNYFARTHING_DIST;
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
// Restore original env
|
|
10
|
+
if (originalEnv !== undefined) {
|
|
11
|
+
process.env.PENNYFARTHING_DIST = originalEnv;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
it('should return PENNYFARTHING_DIST env var when set and path exists', () => {
|
|
18
|
+
// Scenario 1: Explicit env var override with existing path
|
|
19
|
+
// Use the actual pennyfarthing-dist path that exists
|
|
20
|
+
const actualDistPath = resolvePennyfarthingDist();
|
|
21
|
+
assert.ok(actualDistPath !== null, 'Should have a valid dist path');
|
|
22
|
+
process.env.PENNYFARTHING_DIST = actualDistPath;
|
|
23
|
+
const result = resolvePennyfarthingDist();
|
|
24
|
+
// When env var is set to valid existing path, should return it
|
|
25
|
+
assert.strictEqual(result, actualDistPath);
|
|
26
|
+
});
|
|
27
|
+
it('should fall through when PENNYFARTHING_DIST is set but path does not exist', () => {
|
|
28
|
+
// Scenario 1b: Env var set but invalid - should fall through to other checks
|
|
29
|
+
process.env.PENNYFARTHING_DIST = '/nonexistent/path/pennyfarthing-dist';
|
|
30
|
+
const result = resolvePennyfarthingDist();
|
|
31
|
+
// Should fall through and find monorepo path (since we're in monorepo)
|
|
32
|
+
// The key behavior is it doesn't return the invalid env var path
|
|
33
|
+
assert.ok(result === null || !result.includes('/nonexistent/'));
|
|
34
|
+
});
|
|
35
|
+
it('should find monorepo root pennyfarthing-dist directory', () => {
|
|
36
|
+
// Scenario 2: Monorepo root detection (dogfooding)
|
|
37
|
+
// When running from within pennyfarthing repo, find pennyfarthing-dist/ at root
|
|
38
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
39
|
+
const result = resolvePennyfarthingDist();
|
|
40
|
+
// Should find the monorepo root path
|
|
41
|
+
assert.ok(result !== null, 'Should find monorepo root');
|
|
42
|
+
assert.ok(result.endsWith('pennyfarthing-dist'), 'Path should end with pennyfarthing-dist');
|
|
43
|
+
});
|
|
44
|
+
it('should find sibling pennyfarthing-dist directory', () => {
|
|
45
|
+
// Scenario 3: Sibling directory for dev scenarios
|
|
46
|
+
// e.g., project/pennyfarthing-dist when called from project/packages/shared
|
|
47
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
48
|
+
const result = resolvePennyfarthingDist();
|
|
49
|
+
// Implementation should check ../pennyfarthing-dist, ../../pennyfarthing-dist, etc.
|
|
50
|
+
assert.ok(result === null || result.includes('pennyfarthing-dist'));
|
|
51
|
+
});
|
|
52
|
+
it('should find scoped npm package path', () => {
|
|
53
|
+
// Scenario 4: Scoped npm install
|
|
54
|
+
// node_modules/@pennyfarthing/core/pennyfarthing-dist/
|
|
55
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
56
|
+
const result = resolvePennyfarthingDist();
|
|
57
|
+
// When installed via @pennyfarthing/core, should find that path
|
|
58
|
+
assert.ok(result === null || result.includes('pennyfarthing-dist'));
|
|
59
|
+
});
|
|
60
|
+
it('should find legacy npm package path', () => {
|
|
61
|
+
// Scenario 5: Legacy npm install
|
|
62
|
+
// node_modules/pennyfarthing/pennyfarthing-dist/
|
|
63
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
64
|
+
const result = resolvePennyfarthingDist();
|
|
65
|
+
// When installed via pennyfarthing, should find that path
|
|
66
|
+
assert.ok(result === null || result.includes('pennyfarthing-dist'));
|
|
67
|
+
});
|
|
68
|
+
it('should return a valid path or null', () => {
|
|
69
|
+
// This tests that the function returns a valid result
|
|
70
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
71
|
+
const result = resolvePennyfarthingDist();
|
|
72
|
+
// Result should be either null or a valid pennyfarthing-dist path
|
|
73
|
+
if (result !== null) {
|
|
74
|
+
assert.ok(result.includes('pennyfarthing-dist'));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it('should check paths in priority order - env var takes precedence', () => {
|
|
78
|
+
// When multiple paths exist, env var should return first if it exists
|
|
79
|
+
const actualDistPath = resolvePennyfarthingDist();
|
|
80
|
+
assert.ok(actualDistPath !== null, 'Should have a valid dist path');
|
|
81
|
+
// Set env var to actual path
|
|
82
|
+
process.env.PENNYFARTHING_DIST = actualDistPath;
|
|
83
|
+
const result = resolvePennyfarthingDist();
|
|
84
|
+
// Env var should take precedence (returns same path since it exists)
|
|
85
|
+
assert.strictEqual(result, actualDistPath);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('resolvePortraitPath', () => {
|
|
89
|
+
it('should resolve portrait path for valid theme and agent', () => {
|
|
90
|
+
// Use a-team which has actual portraits (shakespeare has only .gitkeep)
|
|
91
|
+
const result = resolvePortraitPath('a-team', 'sm');
|
|
92
|
+
assert.ok(result !== null, 'Should find portrait');
|
|
93
|
+
assert.ok(result.includes('a-team'), 'Path should include theme');
|
|
94
|
+
// Portrait files use character names (faceman) not agent names (sm)
|
|
95
|
+
assert.ok(result.includes('face'), 'Path should include character name');
|
|
96
|
+
assert.ok(result.endsWith('.png') || result.endsWith('.jpg'), 'Should be image file');
|
|
97
|
+
});
|
|
98
|
+
it('should return null for invalid theme', () => {
|
|
99
|
+
const result = resolvePortraitPath('nonexistent-theme', 'sm');
|
|
100
|
+
assert.strictEqual(result, null);
|
|
101
|
+
});
|
|
102
|
+
it('should return null for invalid agent', () => {
|
|
103
|
+
const result = resolvePortraitPath('a-team', 'nonexistent-agent');
|
|
104
|
+
assert.strictEqual(result, null);
|
|
105
|
+
});
|
|
106
|
+
it('should handle theme with special characters in name', () => {
|
|
107
|
+
// Themes like 'star-trek-tos' have hyphens
|
|
108
|
+
const result = resolvePortraitPath('star-trek-tos', 'sm');
|
|
109
|
+
assert.ok(result === null || result.includes('star-trek-tos'));
|
|
110
|
+
});
|
|
111
|
+
it('should handle portrait resolution when dist is found', () => {
|
|
112
|
+
// When resolvePennyfarthingDist returns a valid path, we should be able
|
|
113
|
+
// to resolve portraits for known themes
|
|
114
|
+
delete process.env.PENNYFARTHING_DIST;
|
|
115
|
+
const result = resolvePortraitPath('a-team', 'dev');
|
|
116
|
+
// If dist is found (which it is in monorepo), should find portrait
|
|
117
|
+
// dev maps to ba in a-team theme
|
|
118
|
+
if (result !== null) {
|
|
119
|
+
assert.ok(result.includes('a-team'));
|
|
120
|
+
assert.ok(result.includes('ba'));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('getPortraitPaths', () => {
|
|
125
|
+
it('should return correct paths structure', () => {
|
|
126
|
+
const distPath = '/test/pennyfarthing-dist';
|
|
127
|
+
const result = getPortraitPaths(distPath);
|
|
128
|
+
assert.ok('portraitsDir' in result, 'Should have portraitsDir');
|
|
129
|
+
assert.ok('themesDir' in result, 'Should have themesDir');
|
|
130
|
+
assert.ok('agentsDir' in result, 'Should have agentsDir');
|
|
131
|
+
});
|
|
132
|
+
it('should build portraitsDir correctly', () => {
|
|
133
|
+
const distPath = '/test/pennyfarthing-dist';
|
|
134
|
+
const result = getPortraitPaths(distPath);
|
|
135
|
+
// Actual structure: pennyfarthing-dist/personas/portraits/
|
|
136
|
+
assert.strictEqual(result.portraitsDir, path.join(distPath, 'personas', 'portraits'), 'portraitsDir should be distPath/personas/portraits');
|
|
137
|
+
});
|
|
138
|
+
it('should build themesDir correctly', () => {
|
|
139
|
+
const distPath = '/test/pennyfarthing-dist';
|
|
140
|
+
const result = getPortraitPaths(distPath);
|
|
141
|
+
assert.strictEqual(result.themesDir, path.join(distPath, 'personas'), 'themesDir should be distPath/personas');
|
|
142
|
+
});
|
|
143
|
+
it('should build agentsDir correctly', () => {
|
|
144
|
+
const distPath = '/test/pennyfarthing-dist';
|
|
145
|
+
const result = getPortraitPaths(distPath);
|
|
146
|
+
assert.strictEqual(result.agentsDir, path.join(distPath, 'agents'), 'agentsDir should be distPath/agents');
|
|
147
|
+
});
|
|
148
|
+
it('should handle paths with trailing slashes', () => {
|
|
149
|
+
const distPath = '/test/pennyfarthing-dist/';
|
|
150
|
+
const result = getPortraitPaths(distPath);
|
|
151
|
+
// Should normalize paths correctly
|
|
152
|
+
assert.ok(!result.portraitsDir.includes('//'), 'Should not have double slashes');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
//# sourceMappingURL=portrait-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portrait-resolver.test.js","sourceRoot":"","sources":["../src/portrait-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Search Utility - Story 9-2
|
|
3
|
+
*
|
|
4
|
+
* Searches the skill registry by tag, keyword, category, or description query.
|
|
5
|
+
* Returns matching skills with metadata for discovery and suggestions.
|
|
6
|
+
*/
|
|
7
|
+
export interface SearchOptions {
|
|
8
|
+
/** Filter by tag (e.g., "tdd", "quality") */
|
|
9
|
+
tag?: string;
|
|
10
|
+
/** Filter by keyword (e.g., "jest", "vitest") */
|
|
11
|
+
keyword?: string;
|
|
12
|
+
/** Search description text */
|
|
13
|
+
query?: string;
|
|
14
|
+
/** Filter by category (e.g., "development", "tools") */
|
|
15
|
+
category?: string;
|
|
16
|
+
/** Custom path to registry file (for testing) */
|
|
17
|
+
registryPath?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SkillResult {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
category: string;
|
|
23
|
+
tags: string[];
|
|
24
|
+
keywords?: string[];
|
|
25
|
+
version?: string;
|
|
26
|
+
related_skills?: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Search skills in the registry based on provided options.
|
|
30
|
+
*
|
|
31
|
+
* @param options - Search options (tag, keyword, query, category)
|
|
32
|
+
* @returns Promise resolving to array of matching skills
|
|
33
|
+
* @throws Error if registry file not found or invalid category
|
|
34
|
+
*/
|
|
35
|
+
export declare function searchSkills(options: SearchOptions): Promise<SkillResult[]>;
|
|
36
|
+
//# sourceMappingURL=skill-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-search.d.ts","sourceRoot":"","sources":["../src/skill-search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAmLD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAoEjF"}
|