@indiccoder/mentis-cli 1.0.9 → 1.1.1

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,181 @@
1
+ "use strict";
2
+ /**
3
+ * ProjectInitializer - Initialize project with .mentis.md
4
+ * Interactive wizard for creating project guide files
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.ProjectInitializer = void 0;
44
+ const inquirer_1 = __importDefault(require("inquirer"));
45
+ const chalk_1 = __importDefault(require("chalk"));
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ class ProjectInitializer {
49
+ /**
50
+ * Run the interactive project initialization wizard
51
+ */
52
+ async run(cwd = process.cwd()) {
53
+ console.log('\nšŸ“ Initialize Project with .mentis.md\n');
54
+ // Step 1: Project name
55
+ const { projectName } = await inquirer_1.default.prompt([
56
+ {
57
+ type: 'input',
58
+ name: 'projectName',
59
+ message: 'Project name:',
60
+ default: path.basename(cwd)
61
+ }
62
+ ]);
63
+ // Step 2: Project description
64
+ const { description } = await inquirer_1.default.prompt([
65
+ {
66
+ type: 'input',
67
+ name: 'description',
68
+ message: 'Brief description:',
69
+ default: 'A software project'
70
+ }
71
+ ]);
72
+ // Step 3: Tech stack
73
+ const { techStack } = await inquirer_1.default.prompt([
74
+ {
75
+ type: 'checkbox',
76
+ name: 'techStack',
77
+ message: 'Select technologies:',
78
+ choices: [
79
+ 'TypeScript',
80
+ 'JavaScript',
81
+ 'Python',
82
+ 'React',
83
+ 'Vue',
84
+ 'Node.js',
85
+ 'Express',
86
+ 'PostgreSQL',
87
+ 'MongoDB',
88
+ 'Redis',
89
+ 'Docker',
90
+ 'GraphQL',
91
+ 'REST API',
92
+ 'Other'
93
+ ]
94
+ }
95
+ ]);
96
+ // Step 4: Project type
97
+ const { projectType } = await inquirer_1.default.prompt([
98
+ {
99
+ type: 'list',
100
+ name: 'projectType',
101
+ message: 'Project type:',
102
+ choices: ['Web Application', 'API/Backend', 'CLI Tool', 'Library/Package', 'Mobile App', 'Desktop App', 'Other']
103
+ }
104
+ ]);
105
+ // Step 5: Conventions
106
+ const { useConventions } = await inquirer_1.default.prompt([
107
+ {
108
+ type: 'confirm',
109
+ name: 'useConventions',
110
+ message: 'Add coding conventions?',
111
+ default: true
112
+ }
113
+ ]);
114
+ let conventions = '';
115
+ if (useConventions) {
116
+ const { conventionStyle } = await inquirer_1.default.prompt([
117
+ {
118
+ type: 'list',
119
+ name: 'conventionStyle',
120
+ message: 'Style guide:',
121
+ choices: ['Standard', 'Airbnb', 'Google', 'Prettier', 'Custom']
122
+ }
123
+ ]);
124
+ conventions = this.getConventionText(conventionStyle);
125
+ }
126
+ // Create .mentis.md content
127
+ const content = this.generateMentisMd({
128
+ projectName,
129
+ description,
130
+ techStack,
131
+ projectType,
132
+ conventions
133
+ });
134
+ // Write to file
135
+ const mentisMdPath = path.join(cwd, '.mentis.md');
136
+ fs.writeFileSync(mentisMdPath, content, 'utf-8');
137
+ console.log(chalk_1.default.green(`\nāœ“ Created .mentis.md`));
138
+ console.log(chalk_1.default.dim(`\nTip: Edit .mentis.md to add project-specific instructions for Mentis.\n`));
139
+ return true;
140
+ }
141
+ /**
142
+ * Generate .mentis.md content
143
+ */
144
+ generateMentisMd(options) {
145
+ const { projectName, description, techStack, projectType, conventions } = options;
146
+ let content = `# ${projectName}\n\n`;
147
+ content += `${description}\n\n`;
148
+ content += `**Type**: ${projectType}\n\n`;
149
+ content += `## Tech Stack\n\n`;
150
+ content += techStack.map(t => `- ${t}`).join('\n');
151
+ content += `\n\n`;
152
+ if (conventions) {
153
+ content += `## Coding Conventions\n\n`;
154
+ content += `${conventions}\n\n`;
155
+ }
156
+ content += `## Project Structure\n\n`;
157
+ content += `<!-- Add project structure here -->\n\n`;
158
+ content += `## Guidelines for Mentis\n\n`;
159
+ content += `- When writing code, follow the conventions above\n`;
160
+ content += `- Prefer existing patterns in the codebase\n`;
161
+ content += `- Add comments for complex logic\n`;
162
+ content += `- Run tests before suggesting changes\n\n`;
163
+ content += `## Common Commands\n\n`;
164
+ content += `<!-- Add common development commands here -->\n`;
165
+ return content;
166
+ }
167
+ /**
168
+ * Get convention text based on style
169
+ */
170
+ getConventionText(style) {
171
+ const conventions = {
172
+ 'Standard': `- Use 2 spaces for indentation\n- Use camelCase for variables\n- Use PascalCase for classes\n- Prefer const over let\n- Use arrow functions`,
173
+ 'Airbnb': `- Follow Airbnb Style Guide\n- Use 2 spaces for indentation\n- Prefer named exports\n- Use template literals`,
174
+ 'Google': `- Follow Google JavaScript Style Guide\n- Use 2 spaces for indentation\n- JSDoc comments for functions`,
175
+ 'Prettier': `- Use Prettier for formatting\n- 2 spaces for indentation\n- Single quotes for strings\n- No trailing commas`,
176
+ 'Custom': `- Define your conventions below`
177
+ };
178
+ return conventions[style] || conventions['Standard'];
179
+ }
180
+ }
181
+ exports.ProjectInitializer = ProjectInitializer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indiccoder/mentis-cli",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -61,4 +61,4 @@
61
61
  "@types/node": "^25.0.2",
62
62
  "typescript": "^5.9.3"
63
63
  }
64
- }
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
+ }