@indiccoder/mentis-cli 1.0.8 → 1.1.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.
@@ -17,6 +17,13 @@ import { Tool } from '../tools/Tool';
17
17
  import { McpClient } from '../mcp/McpClient';
18
18
 
19
19
  import { CheckpointManager } from '../checkpoint/CheckpointManager';
20
+ import { SkillsManager } from '../skills/SkillsManager';
21
+ import { LoadSkillTool, ListSkillsTool, ReadSkillFileTool } from '../skills/LoadSkillTool';
22
+ import { ContextVisualizer } from '../utils/ContextVisualizer';
23
+ import { ProjectInitializer } from '../utils/ProjectInitializer';
24
+ import { ConversationCompacter } from '../utils/ConversationCompacter';
25
+ import { CommandManager } from '../commands/CommandManager';
26
+ import { SlashCommandTool, ListCommandsTool } from '../commands/SlashCommandTool';
20
27
  import * as readline from 'readline';
21
28
  import * as fs from 'fs';
22
29
  import * as path from 'path';
@@ -31,18 +38,29 @@ export class ReplManager {
31
38
  private modelClient!: ModelClient;
32
39
  private contextManager: ContextManager;
33
40
  private checkpointManager: CheckpointManager;
41
+ private skillsManager: SkillsManager;
42
+ private contextVisualizer: ContextVisualizer;
43
+ private conversationCompacter: ConversationCompacter;
44
+ private commandManager: CommandManager;
34
45
  private history: ChatMessage[] = [];
35
46
  private mode: 'PLAN' | 'BUILD' = 'BUILD';
36
47
  private tools: Tool[] = [];
37
48
  private mcpClients: McpClient[] = [];
38
49
  private shell: PersistentShell;
39
50
  private currentModelName: string = 'Unknown';
51
+ private activeSkill: string | null = null; // Track currently active skill for allowed-tools
40
52
 
41
53
  constructor() {
42
54
  this.configManager = new ConfigManager();
43
55
  this.contextManager = new ContextManager();
44
56
  this.checkpointManager = new CheckpointManager();
57
+ this.skillsManager = new SkillsManager();
58
+ this.contextVisualizer = new ContextVisualizer();
59
+ this.conversationCompacter = new ConversationCompacter();
60
+ this.commandManager = new CommandManager();
45
61
  this.shell = new PersistentShell();
62
+
63
+ // Create tools array without skill tools first
46
64
  this.tools = [
47
65
  new WriteFileTool(),
48
66
  new ReadFileTool(),
@@ -64,6 +82,76 @@ export class ReplManager {
64
82
  });
65
83
  // Default to Ollama if not specified, assuming compatible endpoint
66
84
  this.initializeClient();
85
+
86
+ // Initialize skills system after client is ready
87
+ this.initializeSkills();
88
+ }
89
+
90
+ /**
91
+ * Initialize the skills and custom commands system
92
+ */
93
+ private async initializeSkills() {
94
+ // Initialize skills
95
+ this.skillsManager.ensureDirectoriesExist();
96
+ await this.skillsManager.discoverSkills();
97
+
98
+ // Initialize custom commands
99
+ this.commandManager.ensureDirectoriesExist();
100
+ await this.commandManager.discoverCommands();
101
+
102
+ // Add skill tools to the tools list
103
+ // Pass callback to LoadSkillTool to track active skill
104
+ this.tools.push(
105
+ new LoadSkillTool(this.skillsManager, (skill) => {
106
+ this.activeSkill = skill ? skill.name : null;
107
+ }),
108
+ new ListSkillsTool(this.skillsManager),
109
+ new ReadSkillFileTool(this.skillsManager),
110
+ new SlashCommandTool(this.commandManager),
111
+ new ListCommandsTool(this.commandManager)
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Check if a tool is allowed by the currently active skill
117
+ * Returns true if tool is allowed, false if it requires confirmation
118
+ */
119
+ private isToolAllowedBySkill(toolName: string): boolean {
120
+ if (!this.activeSkill) {
121
+ // No active skill, all tools require confirmation as per normal flow
122
+ return false;
123
+ }
124
+
125
+ const skill = this.skillsManager.getSkill(this.activeSkill);
126
+ if (!skill || !skill.allowedTools || skill.allowedTools.length === 0) {
127
+ // No skill or no allowed-tools restriction
128
+ return false;
129
+ }
130
+
131
+ // Map tool names to allowed tool names
132
+ const toolMapping: Record<string, string> = {
133
+ 'write_file': 'Write',
134
+ 'read_file': 'Read',
135
+ 'edit_file': 'Edit',
136
+ 'search_files': 'Grep',
137
+ 'list_dir': 'ListDir',
138
+ 'search_file': 'SearchFile',
139
+ 'run_shell': 'RunShell',
140
+ 'web_search': 'WebSearch',
141
+ 'git_status': 'GitStatus',
142
+ 'git_diff': 'GitDiff',
143
+ 'git_commit': 'GitCommit',
144
+ 'git_push': 'GitPush',
145
+ 'git_pull': 'GitPull',
146
+ 'load_skill': 'Read',
147
+ 'list_skills': 'Read',
148
+ 'read_skill_file': 'Read',
149
+ 'slash_command': 'Read',
150
+ 'list_commands': 'Read'
151
+ };
152
+
153
+ const mappedToolName = toolMapping[toolName] || toolName;
154
+ return skill.allowedTools.includes(mappedToolName);
67
155
  }
68
156
 
69
157
  private initializeClient() {
@@ -184,16 +272,17 @@ export class ReplManager {
184
272
  console.log(' /drop <file> - Remove file from context');
185
273
  console.log(' /plan - Switch to PLAN mode');
186
274
  console.log(' /build - Switch to BUILD mode');
187
- console.log(' /plan - Switch to PLAN mode');
188
- console.log(' /build - Switch to BUILD mode');
189
275
  console.log(' /model - Interactively select Provider & Model');
190
276
  console.log(' /use <provider> [model] - Quick switch (legacy)');
191
277
  console.log(' /mcp <cmd> - Manage MCP servers');
278
+ console.log(' /skills <list|show|create|validate> - Manage Agent Skills');
279
+ console.log(' /commands <list|create|validate> - Manage Custom Commands');
192
280
  console.log(' /resume - Resume last session');
193
281
  console.log(' /checkpoint <save|load|list> [name] - Manage checkpoints');
194
282
  console.log(' /search <query> - Search codebase');
195
283
  console.log(' /run <cmd> - Run shell command');
196
284
  console.log(' /commit [msg] - Git commit all changes');
285
+ console.log(' /init - Initialize project with .mentis.md');
197
286
  break;
198
287
  case '/plan':
199
288
  this.mode = 'PLAN';
@@ -263,6 +352,15 @@ export class ReplManager {
263
352
  const updater = new UpdateManager();
264
353
  await updater.checkAndPerformUpdate(true);
265
354
  break;
355
+ case '/init':
356
+ await this.handleInitCommand();
357
+ break;
358
+ case '/skills':
359
+ await this.handleSkillsCommand(args);
360
+ break;
361
+ case '/commands':
362
+ await this.handleCommandsCommand(args);
363
+ break;
266
364
  default:
267
365
  console.log(chalk.red(`Unknown command: ${command}`));
268
366
  }
@@ -270,6 +368,8 @@ export class ReplManager {
270
368
 
271
369
  private async handleChat(input: string) {
272
370
  const context = this.contextManager.getContextString();
371
+ const skillsContext = this.skillsManager.getSkillsContext();
372
+ const commandsContext = this.commandManager.getCommandsContext();
273
373
  let fullInput = input;
274
374
 
275
375
  let modeInstruction = '';
@@ -281,6 +381,16 @@ export class ReplManager {
281
381
 
282
382
  fullInput = `${input}${modeInstruction}`;
283
383
 
384
+ // Add skills context if available
385
+ if (skillsContext) {
386
+ fullInput = `${skillsContext}\n\n${fullInput}`;
387
+ }
388
+
389
+ // Add commands context if available
390
+ if (commandsContext) {
391
+ fullInput = `${commandsContext}\n\n${fullInput}`;
392
+ }
393
+
284
394
  if (context) {
285
395
  fullInput = `${context}\n\nUser Question: ${fullInput}`;
286
396
  }
@@ -343,7 +453,8 @@ export class ReplManager {
343
453
  console.log(chalk.dim(` [Action] ${toolName}(${displayArgs})`));
344
454
 
345
455
  // Safety check for write_file
346
- if (toolName === 'write_file') {
456
+ // Skip confirmation if tool is allowed by active skill
457
+ if (toolName === 'write_file' && !this.isToolAllowedBySkill('Write')) {
347
458
  // Pause cancellation listener during user interaction
348
459
  if (process.stdin.isTTY) {
349
460
  process.stdin.removeListener('keypress', keyListener);
@@ -441,8 +552,22 @@ export class ReplManager {
441
552
  console.log(chalk.dim(`\n(Tokens: ${input_tokens} in / ${output_tokens} out | Est. Cost: $${totalCost.toFixed(5)})`));
442
553
  }
443
554
 
555
+ // Display context bar
556
+ const contextBar = this.contextVisualizer.getContextBar(this.history);
557
+ console.log(chalk.dim(`\n${contextBar}`));
558
+
444
559
  console.log('');
445
560
  this.history.push({ role: 'assistant', content: response.content });
561
+
562
+ // Auto-compact prompt when context is at 80%
563
+ const usage = this.contextVisualizer.calculateUsage(this.history);
564
+ if (usage.percentage >= 80) {
565
+ this.history = await this.conversationCompacter.promptIfCompactNeeded(
566
+ usage.percentage,
567
+ this.history,
568
+ this.modelClient
569
+ );
570
+ }
446
571
  }
447
572
  } catch (error: any) {
448
573
  spinner.stop();
@@ -863,6 +988,180 @@ export class ReplManager {
863
988
  }
864
989
  }
865
990
 
991
+ private async handleSkillsCommand(args: string[]) {
992
+ const { SkillCreator, validateSkills } = await import('../skills/SkillCreator');
993
+
994
+ if (args.length < 1) {
995
+ // Show skills list by default
996
+ await this.handleSkillsCommand(['list']);
997
+ return;
998
+ }
999
+
1000
+ const action = args[0];
1001
+
1002
+ switch (action) {
1003
+ case 'list':
1004
+ await this.handleSkillsList();
1005
+ break;
1006
+ case 'show':
1007
+ if (args.length < 2) {
1008
+ console.log(chalk.red('Usage: /skills show <name>'));
1009
+ return;
1010
+ }
1011
+ await this.handleSkillsShow(args[1]);
1012
+ break;
1013
+ case 'create':
1014
+ const creator = new SkillCreator(this.skillsManager);
1015
+ await creator.run(args[1]);
1016
+ // Re-discover skills after creation
1017
+ await this.skillsManager.discoverSkills();
1018
+ break;
1019
+ case 'validate':
1020
+ await validateSkills(this.skillsManager);
1021
+ break;
1022
+ default:
1023
+ console.log(chalk.red(`Unknown skills action: ${action}`));
1024
+ console.log(chalk.yellow('Available actions: list, show, create, validate'));
1025
+ }
1026
+ }
1027
+
1028
+ private async handleSkillsList(): Promise<void> {
1029
+ const skills = this.skillsManager.getAllSkills();
1030
+
1031
+ if (skills.length === 0) {
1032
+ console.log(chalk.yellow('No skills available.'));
1033
+ console.log(chalk.dim('Create skills with: /skills create'));
1034
+ console.log(chalk.dim('Add skills to: ~/.mentis/skills/ or .mentis/skills/'));
1035
+ return;
1036
+ }
1037
+
1038
+ console.log(chalk.cyan(`\nAvailable Skills (${skills.length}):\n`));
1039
+
1040
+ for (const skill of skills) {
1041
+ const statusIcon = skill.isValid ? '✓' : '✗';
1042
+ const typeLabel = skill.type === 'personal' ? 'Personal' : 'Project';
1043
+
1044
+ console.log(`${statusIcon} ${chalk.bold(skill.name)} (${typeLabel})`);
1045
+ console.log(` ${skill.description}`);
1046
+
1047
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
1048
+ console.log(chalk.dim(` Allowed tools: ${skill.allowedTools.join(', ')}`));
1049
+ }
1050
+
1051
+ if (!skill.isValid && skill.errors) {
1052
+ console.log(chalk.red(` Errors: ${skill.errors.join(', ')}`));
1053
+ }
1054
+
1055
+ console.log('');
1056
+ }
1057
+ }
1058
+
1059
+ private async handleSkillsShow(name: string): Promise<void> {
1060
+ const skill = await this.skillsManager.loadFullSkill(name);
1061
+
1062
+ if (!skill) {
1063
+ console.log(chalk.red(`Skill "${name}" not found.`));
1064
+ return;
1065
+ }
1066
+
1067
+ console.log(chalk.cyan(`\n# ${skill.name}\n`));
1068
+ console.log(chalk.dim(`Type: ${skill.type}`));
1069
+ console.log(chalk.dim(`Path: ${skill.path}`));
1070
+
1071
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
1072
+ console.log(chalk.dim(`Allowed tools: ${skill.allowedTools.join(', ')}`));
1073
+ }
1074
+
1075
+ console.log('');
1076
+ console.log(skill.content || 'No content available');
1077
+
1078
+ // List supporting files
1079
+ const files = this.skillsManager.listSkillFiles(name);
1080
+ if (files.length > 0) {
1081
+ console.log(chalk.dim(`\nSupporting files: ${files.join(', ')}`));
1082
+ }
1083
+ }
1084
+
1085
+ private async handleInitCommand(): Promise<void> {
1086
+ const initializer = new ProjectInitializer();
1087
+ await initializer.run();
1088
+ }
1089
+
1090
+ private async handleCommandsCommand(args: string[]) {
1091
+ if (args.length < 1) {
1092
+ // Show commands list by default
1093
+ await this.handleCommandsCommand(['list']);
1094
+ return;
1095
+ }
1096
+
1097
+ const action = args[0];
1098
+
1099
+ switch (action) {
1100
+ case 'list':
1101
+ await this.handleCommandsList();
1102
+ break;
1103
+ case 'create':
1104
+ await this.handleCommandsCreate(args[1]);
1105
+ break;
1106
+ case 'validate':
1107
+ await this.handleCommandsValidate();
1108
+ break;
1109
+ default:
1110
+ console.log(chalk.red(`Unknown commands action: ${action}`));
1111
+ console.log(chalk.yellow('Available actions: list, create, validate'));
1112
+ }
1113
+ }
1114
+
1115
+ private async handleCommandsList(): Promise<void> {
1116
+ const commands = this.commandManager.getAllCommands();
1117
+
1118
+ if (commands.length === 0) {
1119
+ console.log(chalk.yellow('No custom commands available.'));
1120
+ console.log(chalk.dim('Create commands with: /commands create'));
1121
+ console.log(chalk.dim('Add commands to: ~/.mentis/commands/ or .mentis/commands/'));
1122
+ return;
1123
+ }
1124
+
1125
+ console.log(chalk.cyan(`\nCustom Commands (${commands.length}):\n`));
1126
+
1127
+ // Group by namespace
1128
+ const grouped = new Map<string, any[]>();
1129
+ for (const cmd of commands) {
1130
+ const ns = cmd.description.match(/\(([^)]+)\)/)?.[1] || cmd.type;
1131
+ if (!grouped.has(ns)) {
1132
+ grouped.set(ns, []);
1133
+ }
1134
+ grouped.get(ns)!.push(cmd);
1135
+ }
1136
+
1137
+ for (const [namespace, cmds] of grouped) {
1138
+ console.log(chalk.bold(`\n${namespace}`));
1139
+ for (const cmd of cmds) {
1140
+ const params = cmd.frontmatter['argument-hint'] ? ` ${cmd.frontmatter['argument-hint']}` : '';
1141
+ console.log(` /${cmd.name}${params}`);
1142
+ console.log(` ${cmd.description.replace(/\s*\([^)]+\)/, '')}`);
1143
+
1144
+ if (cmd.frontmatter['allowed-tools'] && cmd.frontmatter['allowed-tools'].length > 0) {
1145
+ console.log(chalk.dim(` Allowed tools: ${cmd.frontmatter['allowed-tools'].join(', ')}`));
1146
+ }
1147
+ }
1148
+ }
1149
+ console.log('');
1150
+ }
1151
+
1152
+ private async handleCommandsCreate(name?: string): Promise<void> {
1153
+ const { CommandCreator } = await import('../commands/CommandCreator');
1154
+ const creator = new CommandCreator(this.commandManager);
1155
+ await creator.run(name);
1156
+ // Re-discover commands after creation
1157
+ await this.commandManager.discoverCommands();
1158
+ }
1159
+
1160
+ private async handleCommandsValidate(): Promise<void> {
1161
+ const { validateCommands } = await import('../commands/CommandCreator');
1162
+ await validateCommands(this.commandManager);
1163
+ }
1164
+
866
1165
  private estimateCost(input: number, output: number): number {
867
1166
  const config = this.configManager.getConfig();
868
1167
  const provider = config.defaultProvider;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * LoadSkillTool - Tool for model-invoked skill loading
3
+ * The model can call this tool when it determines a skill is relevant to the current task
4
+ */
5
+
6
+ import { Tool } from '../tools/Tool';
7
+ import { SkillsManager, Skill } from './SkillsManager';
8
+
9
+ interface LoadSkillArgs {
10
+ name: string;
11
+ }
12
+
13
+ type SkillLoadedCallback = (skill: Skill | null) => void;
14
+
15
+ export class LoadSkillTool implements Tool {
16
+ name = 'load_skill';
17
+ description = 'Load the full content of a skill by name. Use this when you need detailed instructions from a skill. Available skills can be seen in the system prompt. Example: load_skill({ name: "commit-helper" })';
18
+ parameters = {
19
+ type: 'object',
20
+ properties: {
21
+ name: {
22
+ type: 'string',
23
+ description: 'The name of the skill to load (e.g., "commit-helper", "pdf-processing")'
24
+ }
25
+ },
26
+ required: ['name']
27
+ };
28
+
29
+ private skillsManager: SkillsManager;
30
+ private onSkillLoaded?: SkillLoadedCallback;
31
+
32
+ constructor(skillsManager: SkillsManager, onSkillLoaded?: SkillLoadedCallback) {
33
+ this.skillsManager = skillsManager;
34
+ this.onSkillLoaded = onSkillLoaded;
35
+ }
36
+
37
+ async execute(args: LoadSkillArgs): Promise<string> {
38
+ const { name } = args;
39
+
40
+ if (!name) {
41
+ return 'Error: Skill name is required';
42
+ }
43
+
44
+ const skill = this.skillsManager.getSkill(name);
45
+ if (!skill) {
46
+ const availableSkills = this.skillsManager.getAllSkills().map(s => s.name).join(', ');
47
+ return `Error: Skill "${name}" not found. Available skills: ${availableSkills || 'none'}`;
48
+ }
49
+
50
+ // Load full skill content
51
+ const fullSkill = await this.skillsManager.loadFullSkill(name);
52
+ if (!fullSkill || !fullSkill.content) {
53
+ return `Error: Failed to load content for skill "${name}"`;
54
+ }
55
+
56
+ // Notify callback that skill was loaded
57
+ if (this.onSkillLoaded) {
58
+ this.onSkillLoaded(fullSkill);
59
+ }
60
+
61
+ // Format response with skill content
62
+ let response = `# Loaded Skill: ${skill.name}\n\n`;
63
+ response += `**Type**: ${skill.type}\n`;
64
+ response += `**Description**: ${skill.description}\n`;
65
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
66
+ response += `**Allowed Tools**: ${skill.allowedTools.join(', ')}\n`;
67
+ }
68
+ response += `\n---\n\n`;
69
+ response += fullSkill.content;
70
+
71
+ return response;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * ListSkillsTool - Tool for listing available skills
77
+ */
78
+ export class ListSkillsTool implements Tool {
79
+ name = 'list_skills';
80
+ description = 'List all available skills with their descriptions. Use this to see what skills are available.';
81
+ parameters = {
82
+ type: 'object',
83
+ properties: {},
84
+ required: []
85
+ };
86
+
87
+ private skillsManager: SkillsManager;
88
+
89
+ constructor(skillsManager: SkillsManager) {
90
+ this.skillsManager = skillsManager;
91
+ }
92
+
93
+ async execute(): Promise<string> {
94
+ const skills = this.skillsManager.getAllSkills();
95
+
96
+ if (skills.length === 0) {
97
+ return 'No skills available. Add skills to ~/.mentis/skills/ or .mentis/skills/';
98
+ }
99
+
100
+ let response = `# Available Skills (${skills.length})\n\n`;
101
+
102
+ for (const skill of skills) {
103
+ const statusIcon = skill.isValid ? '✓' : '✗';
104
+ response += `**${statusIcon} ${skill.name}** (${skill.type})\n`;
105
+ response += ` ${skill.description}\n`;
106
+
107
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
108
+ response += ` Allowed tools: ${skill.allowedTools.join(', ')}\n`;
109
+ }
110
+
111
+ if (!skill.isValid && skill.errors) {
112
+ response += ` Errors: ${skill.errors.join(', ')}\n`;
113
+ }
114
+
115
+ response += '\n';
116
+ }
117
+
118
+ return response;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * ReadSkillFileTool - Tool for reading supporting files within a skill
124
+ * Used for progressive disclosure of skill resources
125
+ */
126
+ export class ReadSkillFileTool implements Tool {
127
+ name = 'read_skill_file';
128
+ description = 'Read a supporting file from within a skill directory. Use this when a skill references additional files like [reference.md](reference.md). Example: read_skill_file({ skill: "pdf-processing", file: "reference.md" })';
129
+ parameters = {
130
+ type: 'object',
131
+ properties: {
132
+ skill: {
133
+ type: 'string',
134
+ description: 'The name of the skill'
135
+ },
136
+ file: {
137
+ type: 'string',
138
+ description: 'The filename within the skill directory (e.g., "reference.md", "examples.md")'
139
+ }
140
+ },
141
+ required: ['skill', 'file']
142
+ };
143
+
144
+ private skillsManager: SkillsManager;
145
+
146
+ constructor(skillsManager: SkillsManager) {
147
+ this.skillsManager = skillsManager;
148
+ }
149
+
150
+ async execute(args: { skill: string; file: string }): Promise<string> {
151
+ const { skill, file } = args;
152
+
153
+ if (!skill || !file) {
154
+ return 'Error: Both skill and file parameters are required';
155
+ }
156
+
157
+ const content = this.skillsManager.readSkillFile(skill, file);
158
+ if (content === null) {
159
+ const availableFiles = this.skillsManager.listSkillFiles(skill);
160
+ if (availableFiles.length === 0) {
161
+ return `Error: Skill "${skill}" has no supporting files`;
162
+ }
163
+ return `Error: File "${file}" not found in skill "${skill}". Available files: ${availableFiles.join(', ')}`;
164
+ }
165
+
166
+ return content;
167
+ }
168
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Skill data structure for Agent Skills system
3
+ * Based on Claude Code's Agent Skills format
4
+ */
5
+
6
+ export interface SkillMetadata {
7
+ name: string; // Lowercase, numbers, hyphens only (max 64 chars)
8
+ description: string; // What it does + when to use it (max 1024 chars)
9
+ allowedTools?: string[]; // Optional tool restrictions
10
+ }
11
+
12
+ export interface Skill extends SkillMetadata {
13
+ path: string; // Path to SKILL.md
14
+ type: 'personal' | 'project' | 'plugin';
15
+ content?: string; // Loaded on demand (progressive disclosure)
16
+ directory: string; // Path to skill directory (for resolving supporting files)
17
+ isValid: boolean; // Whether skill passes validation
18
+ errors?: string[]; // Validation errors if any
19
+ }
20
+
21
+ export interface SkillFrontmatter {
22
+ name: string;
23
+ description: string;
24
+ 'allowed-tools'?: string[];
25
+ }
26
+
27
+ /**
28
+ * Validation result for a skill
29
+ */
30
+ export interface SkillValidationResult {
31
+ isValid: boolean;
32
+ errors: string[];
33
+ warnings: string[];
34
+ }
35
+
36
+ /**
37
+ * Skill context format for model injection
38
+ */
39
+ export interface SkillContext {
40
+ name: string;
41
+ description: string;
42
+ }
43
+
44
+ /**
45
+ * Options for skill discovery
46
+ */
47
+ export interface SkillDiscoveryOptions {
48
+ includePersonal?: boolean;
49
+ includeProject?: boolean;
50
+ includePlugin?: boolean;
51
+ }