@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.
- package/.mentis/commands/ls.md +12 -0
- package/dist/commands/Command.js +6 -0
- package/dist/commands/CommandCreator.js +286 -0
- package/dist/commands/CommandManager.js +268 -0
- package/dist/commands/SlashCommandTool.js +160 -0
- package/dist/index.js +52 -2
- package/dist/repl/ReplManager.js +120 -4
- package/dist/ui/UIManager.js +2 -2
- package/dist/utils/ContextVisualizer.js +92 -0
- package/dist/utils/ConversationCompacter.js +101 -0
- package/dist/utils/ProjectInitializer.js +181 -0
- package/package.json +2 -2
- package/src/commands/Command.ts +40 -0
- package/src/commands/CommandCreator.ts +281 -0
- package/src/commands/CommandManager.ts +280 -0
- package/src/commands/SlashCommandTool.ts +152 -0
- package/src/index.ts +62 -2
- package/src/repl/ReplManager.ts +152 -4
- package/src/ui/UIManager.ts +2 -2
- package/src/utils/ContextVisualizer.ts +105 -0
- package/src/utils/ConversationCompacter.ts +128 -0
- package/src/utils/ProjectInitializer.ts +170 -0
|
@@ -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
|
@@ -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
|
+
}
|