@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.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: commit-helper
3
+ description: Generates clear, conventional commit messages from git diffs. Use when writing commit messages, reviewing staged changes, or when the user asks for help with git commits.
4
+ allowed-tools: ["GitStatus", "GitDiff", "GitCommit"]
5
+ ---
6
+
7
+ # Commit Message Helper
8
+
9
+ This skill helps you write clear, informative git commit messages following conventional commit format.
10
+
11
+ ## Instructions
12
+
13
+ When the user wants to commit changes:
14
+
15
+ 1. **Check git status** to see what files are staged
16
+ 2. **Review the diff** to understand what changed
17
+ 3. **Generate a commit message** with:
18
+ - **Type**: One of `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
19
+ - **Scope**: (optional) The component or module affected
20
+ - **Summary**: Brief description under 50 characters
21
+ - **Body**: Detailed explanation of what and why (not how)
22
+ - **Footer**: (optional) Breaking changes or references
23
+
24
+ ## Commit Types
25
+
26
+ | Type | Description |
27
+ |------|-------------|
28
+ | `feat` | New feature |
29
+ | `fix` | Bug fix |
30
+ | `docs` | Documentation changes |
31
+ | `style` | Code style changes (formatting, etc.) |
32
+ | `refactor` | Code refactoring |
33
+ | `test` | Adding or updating tests |
34
+ | `chore` | Maintenance tasks |
35
+
36
+ ## Examples
37
+
38
+ ### Feature Addition
39
+ ```
40
+ feat(api): add user authentication endpoint
41
+
42
+ Add OAuth2 authentication for the REST API.
43
+ Implements login, logout, and token refresh.
44
+ ```
45
+
46
+ ### Bug Fix
47
+ ```
48
+ fix(cli): prevent crash when config file is missing
49
+
50
+ Check for config file existence before reading.
51
+ Show helpful error message if file not found.
52
+ ```
53
+
54
+ ### Documentation
55
+ ```
56
+ docs: update README with new installation instructions
57
+
58
+ Clarify NPM installation steps and add troubleshooting section.
59
+ ```
60
+
61
+ ## Best Practices
62
+
63
+ - Use present tense ("add" not "added")
64
+ - Explain what and why, not how
65
+ - Keep summary under 50 characters
66
+ - Reference issues in footer: `Closes #123`
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: pdf-processing
3
+ description: Extract text, fill forms, merge PDFs, and manipulate PDF documents. Use when working with PDF files, forms, or document extraction. Requires pdf-parse package.
4
+ ---
5
+
6
+ # PDF Processing
7
+
8
+ This skill provides PDF manipulation capabilities for text extraction, form filling, and document operations.
9
+
10
+ ## Prerequisites
11
+
12
+ Install required packages:
13
+ ```bash
14
+ npm install pdf-parse
15
+ # or
16
+ pnpm add pdf-parse
17
+ # or
18
+ yarn add pdf-parse
19
+ ```
20
+
21
+ ## Capabilities
22
+
23
+ ### 1. Text Extraction
24
+ Extract plain text from PDF files with page-by-page information.
25
+
26
+ ### 2. Form Field Detection
27
+ Identify and extract form fields from PDF documents.
28
+
29
+ ### 3. Metadata Reading
30
+ Access PDF metadata like author, creation date, title.
31
+
32
+ ### 4. Page Count & Info
33
+ Get total pages and document dimensions.
34
+
35
+ ## Common Operations
36
+
37
+ ### Extract All Text
38
+ ```typescript
39
+ import fs from 'fs';
40
+ import pdf from 'pdf-parse';
41
+
42
+ const dataBuffer = fs.readFileSync('document.pdf');
43
+ const data = await pdf(dataBuffer);
44
+
45
+ console.log(data.text); // Full text content
46
+ console.log(data.numpages); // Number of pages
47
+ console.log(data.info); // PDF metadata
48
+ ```
49
+
50
+ ### Extract Text by Page
51
+ ```typescript
52
+ const data = await pdf(dataBuffer);
53
+ // Text includes page breaks
54
+ // Split by page markers if needed
55
+ ```
56
+
57
+ ### Get PDF Info
58
+ ```typescript
59
+ const data = await pdf(dataBuffer);
60
+ console.log({
61
+ pages: data.numpages,
62
+ info: data.info,
63
+ metadata: data.metadata
64
+ });
65
+ ```
66
+
67
+ ## Troubleshooting
68
+
69
+ ### "pdf-parse not found"
70
+ Install the package: `npm install pdf-parse`
71
+
72
+ ### Scanned PDFs return no text
73
+ Scanned PDFs are images. Use OCR (tesseract.js) instead.
74
+
75
+ ### Encrypted PDFs
76
+ Password-protected PDFs cannot be read. Remove password first.
77
+
78
+ ### Memory Issues with Large Files
79
+ For very large PDFs (>100MB), process in chunks or use streaming.
80
+
81
+ ## Advanced Topics
82
+
83
+ For advanced PDF operations like filling forms, merging, or creating PDFs, see [ADVANCED.md](ADVANCED.md).
84
+
85
+ ## Examples
86
+
87
+ ### Quick Text Extraction
88
+ ```bash
89
+ # User asks: "Extract text from this PDF"
90
+ # 1. Read the PDF file
91
+ # 2. Use pdf-parse to extract text
92
+ # 3. Return the text content
93
+ ```
94
+
95
+ ### Get Page Count
96
+ ```bash
97
+ # User asks: "How many pages in this PDF?"
98
+ # 1. Parse the PDF
99
+ # 2. Return numpages value
100
+ ```
101
+
102
+ ### Form Analysis
103
+ ```bash
104
+ # User asks: "What fields are in this PDF form?"
105
+ # 1. Parse the PDF
106
+ # 2. Look for form field patterns
107
+ # 3. List detected fields
108
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indiccoder/mentis-cli",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -50,7 +50,8 @@
50
50
  "screenshot-desktop": "^1.15.3",
51
51
  "uuid": "^13.0.0",
52
52
  "vscode-ripgrep": "^1.13.2",
53
- "xlsx": "^0.18.5"
53
+ "xlsx": "^0.18.5",
54
+ "yaml": "^2.7.0"
54
55
  },
55
56
  "devDependencies": {
56
57
  "@types/figlet": "^1.7.0",
@@ -60,4 +61,4 @@
60
61
  "@types/node": "^25.0.2",
61
62
  "typescript": "^5.9.3"
62
63
  }
63
- }
64
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Custom Slash Commands System
3
+ * Users can define their own slash commands as markdown files
4
+ */
5
+
6
+ export interface CommandFrontmatter {
7
+ description?: string;
8
+ 'allowed-tools'?: string[];
9
+ 'argument-hint'?: string;
10
+ model?: string;
11
+ 'disable-model-invocation'?: boolean;
12
+ }
13
+
14
+ export interface Command {
15
+ name: string; // Command name (from filename)
16
+ type: 'personal' | 'project'; // Personal or project command
17
+ path: string; // Path to command file
18
+ directory: string; // Directory containing the command
19
+ frontmatter: CommandFrontmatter;
20
+ content: string; // Command content (markdown)
21
+ description: string; // Command description
22
+ hasParameters: boolean; // Whether command uses parameters
23
+ }
24
+
25
+ export interface CommandExecutionContext {
26
+ command: Command;
27
+ args: string[]; // Command arguments
28
+ substitutePlaceholders: (content: string, args: string[]) => string;
29
+ executeBash: (bashCommand: string) => Promise<string>;
30
+ readFile: (filePath: string) => Promise<string>;
31
+ }
32
+
33
+ /**
34
+ * Parsed command with substitutions applied
35
+ */
36
+ export interface ParsedCommand {
37
+ content: string; // Content with substitutions applied
38
+ bashCommands: string[]; // Bash commands to execute
39
+ fileReferences: string[]; // Files to read
40
+ }
@@ -0,0 +1,281 @@
1
+ /**
2
+ * CommandCreator - Interactive wizard for creating new custom slash commands
3
+ */
4
+
5
+ import inquirer from 'inquirer';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import * as os from 'os';
9
+ import { CommandManager } from './CommandManager';
10
+
11
+ export class CommandCreator {
12
+ private commandManager: CommandManager;
13
+
14
+ constructor(commandManager?: CommandManager) {
15
+ this.commandManager = commandManager || new CommandManager();
16
+ }
17
+
18
+ /**
19
+ * Run the interactive command creation wizard
20
+ */
21
+ async run(name?: string): Promise<boolean> {
22
+ console.log('\nšŸ“ Create a new Custom Slash Command\n');
23
+
24
+ let commandName: string;
25
+ let commandType: 'personal' | 'project';
26
+ let description: string;
27
+ let allowedTools: string[] | undefined;
28
+ let argumentHint: string | undefined;
29
+ let namespace: string | undefined;
30
+
31
+ // Step 1: Command Name
32
+ if (name) {
33
+ commandName = name;
34
+ } else {
35
+ const { name: inputName } = await inquirer.prompt([
36
+ {
37
+ type: 'input',
38
+ name: 'name',
39
+ message: 'Command name (lowercase, numbers, hyphens only):',
40
+ validate: (input: string) => {
41
+ if (!input) return 'Name is required';
42
+ if (!/^[a-z0-9-]+$/.test(input)) {
43
+ return 'Name must contain only lowercase letters, numbers, and hyphens';
44
+ }
45
+ if (input.length > 64) return 'Name must be 64 characters or less';
46
+ return true;
47
+ }
48
+ }
49
+ ]);
50
+ commandName = inputName;
51
+ }
52
+
53
+ // Step 2: Command Type
54
+ const { type } = await inquirer.prompt([
55
+ {
56
+ type: 'list',
57
+ name: 'type',
58
+ message: 'Command type:',
59
+ choices: [
60
+ { name: 'Personal (available in all projects)', value: 'personal' },
61
+ { name: 'Project (shared with team via git)', value: 'project' }
62
+ ],
63
+ default: 'personal'
64
+ }
65
+ ]);
66
+ commandType = type;
67
+
68
+ // Step 3: Namespace (optional, for grouping)
69
+ const { useNamespace } = await inquirer.prompt([
70
+ {
71
+ type: 'confirm',
72
+ name: 'useNamespace',
73
+ message: 'Add a namespace for grouping?',
74
+ default: false
75
+ }
76
+ ]);
77
+
78
+ if (useNamespace) {
79
+ const { ns } = await inquirer.prompt([
80
+ {
81
+ type: 'input',
82
+ name: 'ns',
83
+ message: 'Namespace (e.g., "git", "review", "test"):',
84
+ validate: (input: string) => {
85
+ if (!input) return 'Namespace is required';
86
+ if (!/^[a-z0-9-]+$/.test(input)) {
87
+ return 'Namespace must contain only lowercase letters, numbers, and hyphens';
88
+ }
89
+ return true;
90
+ }
91
+ }
92
+ ]);
93
+ namespace = ns;
94
+ }
95
+
96
+ // Step 4: Description
97
+ const { desc } = await inquirer.prompt([
98
+ {
99
+ type: 'input',
100
+ name: 'desc',
101
+ message: 'Description:',
102
+ validate: (input: string) => {
103
+ if (!input) return 'Description is required';
104
+ if (input.length > 1024) return 'Description must be 1024 characters or less';
105
+ return true;
106
+ }
107
+ }
108
+ ]);
109
+ description = desc;
110
+
111
+ // Step 5: Arguments (optional)
112
+ const { useArgs } = await inquirer.prompt([
113
+ {
114
+ type: 'confirm',
115
+ name: 'useArgs',
116
+ message: 'Does this command accept arguments?',
117
+ default: false
118
+ }
119
+ ]);
120
+
121
+ if (useArgs) {
122
+ const { hint } = await inquirer.prompt([
123
+ {
124
+ type: 'input',
125
+ name: 'hint',
126
+ message: 'Argument hint (e.g., "<file>", "[options]"):',
127
+ validate: (input: string) => {
128
+ if (!input) return 'Argument hint is required';
129
+ return true;
130
+ }
131
+ }
132
+ ]);
133
+ argumentHint = hint;
134
+ }
135
+
136
+ // Step 6: Allowed Tools (optional)
137
+ const { useAllowedTools } = await inquirer.prompt([
138
+ {
139
+ type: 'confirm',
140
+ name: 'useAllowedTools',
141
+ message: 'Restrict which tools this command can use?',
142
+ default: false
143
+ }
144
+ ]);
145
+
146
+ if (useAllowedTools) {
147
+ const { tools } = await inquirer.prompt([
148
+ {
149
+ type: 'checkbox',
150
+ name: 'tools',
151
+ message: 'Select allowed tools:',
152
+ choices: [
153
+ { name: 'Read (read_file)', value: 'Read' },
154
+ { name: 'Write (write_file)', value: 'Write' },
155
+ { name: 'Edit (edit_file)', value: 'Edit' },
156
+ { name: 'Grep (search files)', value: 'Grep' },
157
+ { name: 'Glob (find files)', value: 'Glob' },
158
+ { name: 'ListDir (list directory)', value: 'ListDir' },
159
+ { name: 'SearchFile (search in files)', value: 'SearchFile' },
160
+ { name: 'RunShell (run shell command)', value: 'RunShell' },
161
+ { name: 'WebSearch (web search)', value: 'WebSearch' },
162
+ { name: 'GitStatus', value: 'GitStatus' },
163
+ { name: 'GitDiff', value: 'GitDiff' },
164
+ { name: 'GitCommit', value: 'GitCommit' },
165
+ { name: 'GitPush', value: 'GitPush' },
166
+ { name: 'GitPull', value: 'GitPull' }
167
+ ]
168
+ }
169
+ ]);
170
+ allowedTools = tools.length > 0 ? tools : undefined;
171
+ }
172
+
173
+ // Step 7: Create the command
174
+ return this.createCommand(commandName, commandType, description, allowedTools, argumentHint, namespace);
175
+ }
176
+
177
+ /**
178
+ * Create the command file
179
+ */
180
+ async createCommand(
181
+ name: string,
182
+ type: 'personal' | 'project',
183
+ description: string,
184
+ allowedTools?: string[],
185
+ argumentHint?: string,
186
+ namespace?: string
187
+ ): Promise<boolean> {
188
+ const baseDir = type === 'personal'
189
+ ? path.join(os.homedir(), '.mentis', 'commands')
190
+ : path.join(process.cwd(), '.mentis', 'commands');
191
+
192
+ const commandDir = namespace ? path.join(baseDir, namespace) : baseDir;
193
+ const commandFile = path.join(commandDir, `${name}.md`);
194
+
195
+ // Check if command already exists
196
+ if (fs.existsSync(commandFile)) {
197
+ const { overwrite } = await inquirer.prompt([
198
+ {
199
+ type: 'confirm',
200
+ name: 'overwrite',
201
+ message: `Command "${name}" already exists. Overwrite?`,
202
+ default: false
203
+ }
204
+ ]);
205
+
206
+ if (!overwrite) {
207
+ console.log('Cancelled.');
208
+ return false;
209
+ }
210
+ }
211
+
212
+ // Create directory
213
+ if (!fs.existsSync(commandDir)) {
214
+ fs.mkdirSync(commandDir, { recursive: true });
215
+ }
216
+
217
+ // Generate command content
218
+ let content = `---\ndescription: ${description}\n`;
219
+
220
+ if (allowedTools && allowedTools.length > 0) {
221
+ content += `allowed-tools: [${allowedTools.map(t => `"${t}"`).join(', ')}]\n`;
222
+ }
223
+
224
+ if (argumentHint) {
225
+ content += `argument-hint: "${argumentHint}"\n`;
226
+ }
227
+
228
+ content += `---\n\n`;
229
+
230
+ // Add usage instructions in markdown
231
+ content += `## Usage\n\n`;
232
+ content += `Use this command by typing: /${name}${argumentHint ? ` ${argumentHint}` : ''}\n\n`;
233
+ content += `## Instructions\n\n`;
234
+ content += `Add your instructions here. You can use:\n`;
235
+ content += `- \`$1\`, \`$2\`, etc. for positional arguments\n`;
236
+ content += `- \`$ARGUMENTS\` for all arguments\n`;
237
+ content += `- \`\!\\\`command\`\` for bash commands\n`;
238
+ content += `- \`@file\` for file references\n\n`;
239
+
240
+ // Write command file
241
+ fs.writeFileSync(commandFile, content, 'utf-8');
242
+
243
+ console.log(`\nāœ“ Command created at: ${commandFile}`);
244
+ console.log(`\nNext steps:`);
245
+ console.log(` 1. Edit ${commandFile} to add instructions`);
246
+ console.log(` 2. Restart Mentis or use /commands validate to load the new command`);
247
+
248
+ return true;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Validate commands and show results
254
+ */
255
+ export async function validateCommands(commandManager: CommandManager): Promise<void> {
256
+ const commands = commandManager.getAllCommands();
257
+
258
+ console.log('\nšŸ“‹ Command Validation Results\n');
259
+
260
+ if (commands.length === 0) {
261
+ console.log('No custom commands to validate.');
262
+ console.log('Create commands with: /commands create');
263
+ return;
264
+ }
265
+
266
+ for (const cmd of commands) {
267
+ const isValid = cmd.name && cmd.name.length > 0 && cmd.content.length > 0;
268
+ const icon = isValid ? 'āœ“' : 'āœ—';
269
+ console.log(`${icon} /${cmd.name} (${cmd.type})`);
270
+
271
+ if (!isValid) {
272
+ console.log(` ERROR: Invalid command structure`);
273
+ }
274
+
275
+ if (cmd.frontmatter['allowed-tools'] && cmd.frontmatter['allowed-tools'].length > 0) {
276
+ console.log(` Allowed tools: ${cmd.frontmatter['allowed-tools'].join(', ')}`);
277
+ }
278
+ }
279
+
280
+ console.log(`\nāœ“ Validated ${commands.length} commands`);
281
+ }