awesome-slash 2.4.3 → 2.5.0
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/.claude-plugin/marketplace.json +6 -6
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +99 -1
- package/README.md +173 -161
- package/SECURITY.md +25 -81
- package/adapters/codex/install.sh +58 -16
- package/adapters/opencode/install.sh +92 -23
- package/lib/index.js +47 -4
- package/lib/patterns/review-patterns.js +58 -11
- package/lib/patterns/slop-patterns.js +154 -147
- package/lib/platform/detect-platform.js +99 -350
- package/lib/platform/detection-configs.js +93 -0
- package/lib/platform/verify-tools.js +10 -78
- package/lib/schemas/README.md +195 -0
- package/lib/schemas/validator.js +247 -0
- package/lib/sources/custom-handler.js +199 -0
- package/lib/sources/policy-questions.js +239 -0
- package/lib/sources/source-cache.js +149 -0
- package/lib/state/workflow-state.js +363 -665
- package/lib/types/README.md +292 -0
- package/lib/types/agent-frontmatter.d.ts +134 -0
- package/lib/types/command-frontmatter.d.ts +107 -0
- package/lib/types/hook-frontmatter.d.ts +115 -0
- package/lib/types/index.d.ts +84 -0
- package/lib/types/plugin-manifest.d.ts +102 -0
- package/lib/types/skill-frontmatter.d.ts +89 -0
- package/lib/utils/cache-manager.js +154 -0
- package/lib/utils/context-optimizer.js +5 -36
- package/lib/utils/deprecation.js +37 -0
- package/lib/utils/shell-escape.js +88 -0
- package/mcp-server/index.js +513 -18
- package/package.json +6 -2
- package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop-around/lib/index.js +170 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +170 -129
- package/plugins/deslop-around/lib/platform/detect-platform.js +212 -123
- package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +10 -1
- package/plugins/deslop-around/lib/schemas/README.md +195 -0
- package/plugins/deslop-around/lib/schemas/validator.js +205 -0
- package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
- package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
- package/plugins/deslop-around/lib/sources/source-cache.js +149 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +382 -484
- package/plugins/deslop-around/lib/types/README.md +292 -0
- package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/deslop-around/lib/types/index.d.ts +84 -0
- package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
- package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
- package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/ci-monitor.md +19 -0
- package/plugins/next-task/agents/delivery-validator.md +2 -2
- package/plugins/next-task/agents/implementation-agent.md +3 -4
- package/plugins/next-task/agents/planning-agent.md +77 -19
- package/plugins/next-task/agents/review-orchestrator.md +21 -122
- package/plugins/next-task/agents/task-discoverer.md +164 -23
- package/plugins/next-task/commands/next-task.md +180 -14
- package/plugins/next-task/lib/index.js +170 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
- package/plugins/next-task/lib/patterns/slop-patterns.js +170 -129
- package/plugins/next-task/lib/platform/detect-platform.js +212 -123
- package/plugins/next-task/lib/platform/detection-configs.js +93 -0
- package/plugins/next-task/lib/platform/verify-tools.js +10 -1
- package/plugins/next-task/lib/schemas/README.md +195 -0
- package/plugins/next-task/lib/schemas/validator.js +205 -0
- package/plugins/next-task/lib/sources/custom-handler.js +199 -0
- package/plugins/next-task/lib/sources/policy-questions.js +239 -0
- package/plugins/next-task/lib/sources/source-cache.js +149 -0
- package/plugins/next-task/lib/state/workflow-state.js +382 -484
- package/plugins/next-task/lib/types/README.md +292 -0
- package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/next-task/lib/types/index.d.ts +84 -0
- package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/next-task/lib/utils/cache-manager.js +154 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
- package/plugins/next-task/lib/utils/deprecation.js +37 -0
- package/plugins/next-task/lib/utils/shell-escape.js +88 -0
- package/plugins/project-review/.claude-plugin/plugin.json +1 -1
- package/plugins/project-review/lib/index.js +170 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
- package/plugins/project-review/lib/patterns/slop-patterns.js +170 -129
- package/plugins/project-review/lib/platform/detect-platform.js +212 -123
- package/plugins/project-review/lib/platform/detection-configs.js +93 -0
- package/plugins/project-review/lib/platform/verify-tools.js +10 -1
- package/plugins/project-review/lib/schemas/README.md +195 -0
- package/plugins/project-review/lib/schemas/validator.js +205 -0
- package/plugins/project-review/lib/sources/custom-handler.js +199 -0
- package/plugins/project-review/lib/sources/policy-questions.js +239 -0
- package/plugins/project-review/lib/sources/source-cache.js +149 -0
- package/plugins/project-review/lib/state/workflow-state.js +382 -484
- package/plugins/project-review/lib/types/README.md +292 -0
- package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/project-review/lib/types/index.d.ts +84 -0
- package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/project-review/lib/utils/cache-manager.js +154 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
- package/plugins/project-review/lib/utils/deprecation.js +37 -0
- package/plugins/project-review/lib/utils/shell-escape.js +88 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
- package/plugins/reality-check/agents/code-explorer.md +1 -1
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/commands/ship-ci-review-loop.md +19 -0
- package/plugins/ship/lib/index.js +170 -0
- package/plugins/ship/lib/patterns/review-patterns.js +58 -11
- package/plugins/ship/lib/patterns/slop-patterns.js +170 -129
- package/plugins/ship/lib/platform/detect-platform.js +212 -123
- package/plugins/ship/lib/platform/detection-configs.js +93 -0
- package/plugins/ship/lib/platform/verify-tools.js +10 -1
- package/plugins/ship/lib/schemas/README.md +195 -0
- package/plugins/ship/lib/schemas/validator.js +205 -0
- package/plugins/ship/lib/sources/custom-handler.js +199 -0
- package/plugins/ship/lib/sources/policy-questions.js +239 -0
- package/plugins/ship/lib/sources/source-cache.js +149 -0
- package/plugins/ship/lib/state/workflow-state.js +382 -484
- package/plugins/ship/lib/types/README.md +292 -0
- package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/ship/lib/types/index.d.ts +84 -0
- package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/ship/lib/utils/cache-manager.js +154 -0
- package/plugins/ship/lib/utils/context-optimizer.js +115 -37
- package/plugins/ship/lib/utils/deprecation.js +37 -0
- package/plugins/ship/lib/utils/shell-escape.js +88 -0
- package/lib/state/workflow-state.schema.json +0 -282
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/agents/policy-selector.md +0 -248
- package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
- package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
- package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
- package/plugins/ship/lib/state/workflow-state.schema.json +0 -282
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Questions Builder
|
|
3
|
+
* Builds AskUserQuestion-ready structure with cache awareness
|
|
4
|
+
*
|
|
5
|
+
* @module lib/sources/policy-questions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const sourceCache = require('./source-cache');
|
|
9
|
+
const customHandler = require('./custom-handler');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Source label mapping for proper casing
|
|
13
|
+
*/
|
|
14
|
+
const SOURCE_LABELS = {
|
|
15
|
+
github: 'GitHub',
|
|
16
|
+
gitlab: 'GitLab',
|
|
17
|
+
local: 'Local',
|
|
18
|
+
custom: 'Custom',
|
|
19
|
+
other: 'Other'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get policy questions with cache-aware options
|
|
24
|
+
* Call this once - returns full question structure ready for AskUserQuestion
|
|
25
|
+
*
|
|
26
|
+
* @returns {Object} { questions: [...], cachedPreference: {...}|null }
|
|
27
|
+
*/
|
|
28
|
+
function getPolicyQuestions() {
|
|
29
|
+
const cached = sourceCache.getPreference();
|
|
30
|
+
|
|
31
|
+
// Build source options
|
|
32
|
+
const sourceOptions = [];
|
|
33
|
+
|
|
34
|
+
// If cached, add as first option
|
|
35
|
+
if (cached) {
|
|
36
|
+
const cachedLabel = cached.source === 'custom'
|
|
37
|
+
? `${cached.tool} (${cached.type})`
|
|
38
|
+
: SOURCE_LABELS[cached.source] || (cached.source.charAt(0).toUpperCase() + cached.source.slice(1));
|
|
39
|
+
|
|
40
|
+
sourceOptions.push({
|
|
41
|
+
label: `${cachedLabel} (last used)`,
|
|
42
|
+
description: 'Use your previous choice'
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Standard options
|
|
47
|
+
sourceOptions.push(
|
|
48
|
+
{ label: 'GitHub Issues', description: 'Use gh CLI to list issues' },
|
|
49
|
+
{ label: 'GitLab Issues', description: 'Use glab CLI to list issues' },
|
|
50
|
+
{ label: 'Local tasks.md', description: 'Read from PLAN.md, tasks.md, or TODO.md' },
|
|
51
|
+
{ label: 'Custom', description: 'Specify your tool: CLI, MCP, Skill, or file path' },
|
|
52
|
+
{ label: 'Other', description: 'Describe your source - agent figures it out' }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
questions: [
|
|
57
|
+
{
|
|
58
|
+
header: 'Source',
|
|
59
|
+
question: 'Where should I look for tasks?',
|
|
60
|
+
options: sourceOptions,
|
|
61
|
+
multiSelect: false
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
header: 'Priority',
|
|
65
|
+
question: 'What type of tasks to prioritize?',
|
|
66
|
+
options: [
|
|
67
|
+
{ label: 'All', description: 'Consider all tasks, pick by score' },
|
|
68
|
+
{ label: 'Bugs', description: 'Focus on bug fixes' },
|
|
69
|
+
{ label: 'Security', description: 'Security issues first' },
|
|
70
|
+
{ label: 'Features', description: 'New feature development' }
|
|
71
|
+
],
|
|
72
|
+
multiSelect: false
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
header: 'Stop Point',
|
|
76
|
+
question: 'How far should I take this task?',
|
|
77
|
+
options: [
|
|
78
|
+
{ label: 'Merged', description: 'Until PR is merged to main' },
|
|
79
|
+
{ label: 'PR Created', description: 'Stop after creating PR' },
|
|
80
|
+
{ label: 'Implemented', description: 'Stop after local implementation' },
|
|
81
|
+
{ label: 'Deployed', description: 'Deploy to staging' },
|
|
82
|
+
{ label: 'Production', description: 'Full production deployment' }
|
|
83
|
+
],
|
|
84
|
+
multiSelect: false
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
cachedPreference: cached
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get custom source follow-up questions
|
|
93
|
+
* Call after user selects "Custom"
|
|
94
|
+
*
|
|
95
|
+
* @returns {Object} Question structure for custom type selection
|
|
96
|
+
*/
|
|
97
|
+
function getCustomTypeQuestions() {
|
|
98
|
+
return {
|
|
99
|
+
questions: [customHandler.getCustomTypeQuestion()]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get custom name question based on type
|
|
105
|
+
* @param {string} type - cli, mcp, skill, or file
|
|
106
|
+
* @returns {Object} Question structure for tool/path name
|
|
107
|
+
*/
|
|
108
|
+
function getCustomNameQuestion(type) {
|
|
109
|
+
const q = customHandler.getCustomNameQuestion(type);
|
|
110
|
+
return {
|
|
111
|
+
questions: [{
|
|
112
|
+
header: q.header,
|
|
113
|
+
question: q.question,
|
|
114
|
+
options: [], // Free text input via "Other"
|
|
115
|
+
multiSelect: false
|
|
116
|
+
}]
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse policy responses and build policy object
|
|
122
|
+
* Also handles caching
|
|
123
|
+
*
|
|
124
|
+
* @param {Object} responses - User's answers
|
|
125
|
+
* @param {string} responses.source - Source selection
|
|
126
|
+
* @param {string} responses.priority - Priority selection
|
|
127
|
+
* @param {string} responses.stopPoint - Stop point selection
|
|
128
|
+
* @param {Object} [responses.custom] - Custom source details (if applicable)
|
|
129
|
+
* @returns {Object} Policy object ready for workflow state
|
|
130
|
+
*/
|
|
131
|
+
function parseAndCachePolicy(responses) {
|
|
132
|
+
const policy = {
|
|
133
|
+
taskSource: mapSource(responses.source, responses.custom),
|
|
134
|
+
priorityFilter: mapPriority(responses.priority),
|
|
135
|
+
stoppingPoint: mapStopPoint(responses.stopPoint)
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Cache source preference (unless "other" which is ad-hoc)
|
|
139
|
+
if (policy.taskSource.source !== 'other') {
|
|
140
|
+
sourceCache.savePreference(policy.taskSource);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return policy;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Map source selection to policy value
|
|
148
|
+
*/
|
|
149
|
+
function mapSource(selection, customDetails) {
|
|
150
|
+
// Check if user selected cached option
|
|
151
|
+
if (selection.includes('(last used)')) {
|
|
152
|
+
return sourceCache.getPreference();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const sourceMap = {
|
|
156
|
+
'GitHub Issues': { source: 'github' },
|
|
157
|
+
'GitLab Issues': { source: 'gitlab' },
|
|
158
|
+
'Local tasks.md': { source: 'local' },
|
|
159
|
+
'Custom': null, // Handled separately
|
|
160
|
+
'Other': null // Handled separately
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
if (selection === 'Custom' && customDetails) {
|
|
164
|
+
// Normalize type label to internal value (e.g., "CLI Tool" -> "cli")
|
|
165
|
+
const normalizedType = customHandler.mapTypeSelection(customDetails.type);
|
|
166
|
+
const config = customHandler.buildCustomConfig(normalizedType, customDetails.name);
|
|
167
|
+
return config;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (selection === 'Other') {
|
|
171
|
+
return { source: 'other', description: customDetails?.description || '' };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return sourceMap[selection] || { source: 'github' };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Map priority selection to policy value
|
|
179
|
+
*/
|
|
180
|
+
function mapPriority(selection) {
|
|
181
|
+
const map = {
|
|
182
|
+
'All': 'all',
|
|
183
|
+
'Bugs': 'bugs',
|
|
184
|
+
'Security': 'security',
|
|
185
|
+
'Features': 'features'
|
|
186
|
+
};
|
|
187
|
+
return map[selection] || 'all';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Map stop point selection to policy value
|
|
192
|
+
*/
|
|
193
|
+
function mapStopPoint(selection) {
|
|
194
|
+
const map = {
|
|
195
|
+
'Merged': 'merged',
|
|
196
|
+
'PR Created': 'pr-created',
|
|
197
|
+
'Implemented': 'implemented',
|
|
198
|
+
'Deployed': 'deployed',
|
|
199
|
+
'Production': 'production'
|
|
200
|
+
};
|
|
201
|
+
return map[selection] || 'merged';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check if user selected cached preference
|
|
206
|
+
* @param {string} selection - User's source selection
|
|
207
|
+
* @returns {boolean}
|
|
208
|
+
*/
|
|
209
|
+
function isUsingCached(selection) {
|
|
210
|
+
return selection.includes('(last used)');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if custom follow-up is needed
|
|
215
|
+
* @param {string} selection - User's source selection
|
|
216
|
+
* @returns {boolean}
|
|
217
|
+
*/
|
|
218
|
+
function needsCustomFollowUp(selection) {
|
|
219
|
+
return selection === 'Custom';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check if "other" description is needed
|
|
224
|
+
* @param {string} selection - User's source selection
|
|
225
|
+
* @returns {boolean}
|
|
226
|
+
*/
|
|
227
|
+
function needsOtherDescription(selection) {
|
|
228
|
+
return selection === 'Other';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
getPolicyQuestions,
|
|
233
|
+
getCustomTypeQuestions,
|
|
234
|
+
getCustomNameQuestion,
|
|
235
|
+
parseAndCachePolicy,
|
|
236
|
+
isUsingCached,
|
|
237
|
+
needsCustomFollowUp,
|
|
238
|
+
needsOtherDescription
|
|
239
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source Cache
|
|
3
|
+
* File-based persistence for task source preferences
|
|
4
|
+
*
|
|
5
|
+
* @module lib/sources/source-cache
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const SOURCES_DIR = '.claude/sources';
|
|
12
|
+
const PREFERENCE_FILE = 'preference.json';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validate tool name to prevent path traversal
|
|
16
|
+
* @param {string} toolName - Tool name to validate
|
|
17
|
+
* @returns {boolean} True if valid
|
|
18
|
+
*/
|
|
19
|
+
function isValidToolName(toolName) {
|
|
20
|
+
// Prevent path traversal and shell metacharacters
|
|
21
|
+
return /^[a-zA-Z0-9_-]+$/.test(toolName);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Ensure sources directory exists
|
|
26
|
+
* @returns {string} Path to sources directory
|
|
27
|
+
*/
|
|
28
|
+
function ensureDir() {
|
|
29
|
+
if (!fs.existsSync(SOURCES_DIR)) {
|
|
30
|
+
fs.mkdirSync(SOURCES_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
return SOURCES_DIR;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get cached source preference
|
|
37
|
+
* @returns {Object|null} Preference object or null if not cached
|
|
38
|
+
* @example
|
|
39
|
+
* // Returns: { source: 'github' }
|
|
40
|
+
* // Or: { source: 'custom', type: 'cli', tool: 'tea' }
|
|
41
|
+
*/
|
|
42
|
+
function getPreference() {
|
|
43
|
+
const filePath = path.join(SOURCES_DIR, PREFERENCE_FILE);
|
|
44
|
+
if (!fs.existsSync(filePath)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(`Failed to read preference file:`, err.message);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Save source preference
|
|
57
|
+
* @param {Object} preference - Preference object
|
|
58
|
+
* @param {string} preference.source - Source type (github, gitlab, local, custom, other)
|
|
59
|
+
* @param {string} [preference.type] - For custom: mcp, cli, skill, file
|
|
60
|
+
* @param {string} [preference.tool] - Tool name or path
|
|
61
|
+
* @param {string} [preference.description] - For other: user's free text
|
|
62
|
+
*/
|
|
63
|
+
function savePreference(preference) {
|
|
64
|
+
ensureDir();
|
|
65
|
+
const filePath = path.join(SOURCES_DIR, PREFERENCE_FILE);
|
|
66
|
+
fs.writeFileSync(filePath, JSON.stringify({
|
|
67
|
+
...preference,
|
|
68
|
+
savedAt: new Date().toISOString()
|
|
69
|
+
}, null, 2));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get cached tool capabilities (for custom sources)
|
|
74
|
+
* @param {string} toolName - Tool identifier (e.g., 'tea', 'glab')
|
|
75
|
+
* @returns {Object|null} Capabilities object or null
|
|
76
|
+
*/
|
|
77
|
+
function getToolCapabilities(toolName) {
|
|
78
|
+
// Prevent path traversal
|
|
79
|
+
if (!isValidToolName(toolName)) {
|
|
80
|
+
console.error(`Invalid tool name: ${toolName}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const filePath = path.join(SOURCES_DIR, `${toolName}.json`);
|
|
84
|
+
if (!fs.existsSync(filePath)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error(`Failed to read tool capabilities for ${toolName}:`, err.message);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Save tool capabilities after discovery
|
|
97
|
+
* @param {string} toolName - Tool identifier
|
|
98
|
+
* @param {Object} capabilities - Discovered capabilities
|
|
99
|
+
* @param {string[]} capabilities.features - Available features (issues, prs, ci)
|
|
100
|
+
* @param {Object} capabilities.commands - Command mappings
|
|
101
|
+
*/
|
|
102
|
+
function saveToolCapabilities(toolName, capabilities) {
|
|
103
|
+
// Prevent path traversal
|
|
104
|
+
if (!isValidToolName(toolName)) {
|
|
105
|
+
console.error(`Invalid tool name: ${toolName}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
ensureDir();
|
|
109
|
+
const filePath = path.join(SOURCES_DIR, `${toolName}.json`);
|
|
110
|
+
fs.writeFileSync(filePath, JSON.stringify({
|
|
111
|
+
...capabilities,
|
|
112
|
+
discoveredAt: new Date().toISOString()
|
|
113
|
+
}, null, 2));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Clear all cached preferences
|
|
118
|
+
*/
|
|
119
|
+
function clearCache() {
|
|
120
|
+
if (fs.existsSync(SOURCES_DIR)) {
|
|
121
|
+
const files = fs.readdirSync(SOURCES_DIR);
|
|
122
|
+
for (const file of files) {
|
|
123
|
+
const filePath = path.join(SOURCES_DIR, file);
|
|
124
|
+
const stats = fs.statSync(filePath);
|
|
125
|
+
if (stats.isFile()) {
|
|
126
|
+
fs.unlinkSync(filePath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if preference matches a specific source
|
|
134
|
+
* @param {string} source - Source to check
|
|
135
|
+
* @returns {boolean} True if preference matches
|
|
136
|
+
*/
|
|
137
|
+
function isPreferred(source) {
|
|
138
|
+
const pref = getPreference();
|
|
139
|
+
return pref?.source === source;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
getPreference,
|
|
144
|
+
savePreference,
|
|
145
|
+
getToolCapabilities,
|
|
146
|
+
saveToolCapabilities,
|
|
147
|
+
clearCache,
|
|
148
|
+
isPreferred
|
|
149
|
+
};
|