Support env var and prompt input for command
This commit is contained in:
parent
e06e35f8f9
commit
b2ba9e148d
@ -1,31 +1,31 @@
|
|||||||
import CustomCommands from "./customCommand";
|
import CustomCommands from "./customCommand";
|
||||||
|
|
||||||
export interface Command {
|
export interface Command {
|
||||||
name: string;
|
name: string;
|
||||||
pattern: string;
|
pattern: string;
|
||||||
description: string;
|
description: string;
|
||||||
handler: (commandName: string, userInput: string) => Promise<string>;
|
handler: (commandName: string, userInput: string) => Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandManager {
|
class CommandManager {
|
||||||
private static instance: CommandManager;
|
private static instance: CommandManager;
|
||||||
private commands: Command[] = [];
|
private commands: Command[] = [];
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() { }
|
||||||
|
|
||||||
public static getInstance(): CommandManager {
|
public static getInstance(): CommandManager {
|
||||||
if (!CommandManager.instance) {
|
if (!CommandManager.instance) {
|
||||||
CommandManager.instance = new CommandManager();
|
CommandManager.instance = new CommandManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandManager.instance;
|
return CommandManager.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCommand(command: Command): void {
|
registerCommand(command: Command): void {
|
||||||
this.commands.push(command);
|
this.commands.push(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommandList(includeHide: boolean = false): Command[] {
|
getCommandList(includeHide: boolean = false): Command[] {
|
||||||
// load commands from CustomCommands
|
// load commands from CustomCommands
|
||||||
let newCommands: Command[] = [...this.commands];
|
let newCommands: Command[] = [...this.commands];
|
||||||
const customCommands = CustomCommands.getInstance();
|
const customCommands = CustomCommands.getInstance();
|
||||||
@ -36,49 +36,49 @@ export interface Command {
|
|||||||
pattern: command.pattern,
|
pattern: command.pattern,
|
||||||
description: command.description,
|
description: command.description,
|
||||||
handler: async (commandName: string, userInput: string) => {
|
handler: async (commandName: string, userInput: string) => {
|
||||||
return CustomCommands.getInstance().handleCommand(commandName);
|
return CustomCommands.getInstance().handleCommand(commandName, userInput);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (command.show || includeHide) {
|
if (command.show || includeHide) {
|
||||||
newCommands.push(commandObj);
|
newCommands.push(commandObj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return newCommands;
|
return newCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
async processText(text: string): Promise<string> {
|
|
||||||
// 定义一个异步函数来处理单个命令
|
|
||||||
const processCommand = async (commandObj: Command, userInput: string) => {
|
|
||||||
// 转义特殊字符
|
|
||||||
const escapedPattern = commandObj.pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
||||||
const commandPattern = new RegExp(
|
|
||||||
`\\/(${escapedPattern.replace('{{prompt}}', '(.+?)')})`,
|
|
||||||
'g'
|
|
||||||
);
|
|
||||||
|
|
||||||
const matches = Array.from(text.matchAll(commandPattern));
|
async processText(text: string): Promise<string> {
|
||||||
const replacements = await Promise.all(
|
// 定义一个异步函数来处理单个命令
|
||||||
matches.map(async (match) => {
|
const processCommand = async (commandObj: Command, userInput: string) => {
|
||||||
const matchedUserInput = match[1];
|
// 转义特殊字符
|
||||||
return await commandObj.handler(commandObj.name, matchedUserInput);
|
const escapedPattern = commandObj.pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||||
})
|
const commandPattern = new RegExp(
|
||||||
);
|
`\\/(${escapedPattern.replace('\\{\\{prompt\\}\\}', '\\{\\{(.+?)\\}\\}')})`,
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
let result = userInput;
|
const matches = Array.from(text.matchAll(commandPattern));
|
||||||
for (let i = 0; i < matches.length; i++) {
|
const replacements = await Promise.all(
|
||||||
result = result.replace(matches[i][0], replacements[i]);
|
matches.map(async (match) => {
|
||||||
}
|
const matchedUserInput = match[2];
|
||||||
return result;
|
return await commandObj.handler(commandObj.name, matchedUserInput);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = userInput;
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
result = result.replace(matches[i][0], replacements[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理所有命令
|
// 处理所有命令
|
||||||
let result = text;
|
let result = text;
|
||||||
for (const commandObj of this.getCommandList(true)) {
|
for (const commandObj of this.getCommandList(true)) {
|
||||||
result = await processCommand(commandObj, result);
|
result = await processCommand(commandObj, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CommandManager;
|
export default CommandManager;
|
||||||
|
@ -30,24 +30,33 @@ class CustomCommands {
|
|||||||
this.commands = [];
|
this.commands = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const subDirs = fs.readdirSync(workflowsDir, { withFileTypes: true })
|
const extensionDirs = fs.readdirSync(workflowsDir, { withFileTypes: true })
|
||||||
.filter(dirent => dirent.isDirectory())
|
.filter(dirent => dirent.isDirectory())
|
||||||
.map(dirent => dirent.name);
|
.map(dirent => dirent.name);
|
||||||
|
|
||||||
for (const dir of subDirs) {
|
for (const extensionDir of extensionDirs) {
|
||||||
const settingsPath = path.join(workflowsDir, dir, '_setting_.json');
|
const commandDir = path.join(workflowsDir, extensionDir, 'command');
|
||||||
if (fs.existsSync(settingsPath)) {
|
if (fs.existsSync(commandDir)) {
|
||||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
const commandSubDirs = fs.readdirSync(commandDir, { withFileTypes: true })
|
||||||
const command: Command = {
|
.filter(dirent => dirent.isDirectory())
|
||||||
name: dir,
|
.map(dirent => dirent.name);
|
||||||
pattern: settings.pattern,
|
|
||||||
description: settings.description,
|
for (const commandSubDir of commandSubDirs) {
|
||||||
message: settings.message,
|
const settingsPath = path.join(commandDir, commandSubDir, '_setting_.json');
|
||||||
default: settings.default,
|
if (fs.existsSync(settingsPath)) {
|
||||||
show: settings.show === undefined ? "true" : settings.show,
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||||
instructions: settings.instructions
|
const command: Command = {
|
||||||
};
|
name: commandSubDir,
|
||||||
this.commands.push(command);
|
pattern: settings.pattern,
|
||||||
|
description: settings.description,
|
||||||
|
message: settings.message,
|
||||||
|
default: settings.default,
|
||||||
|
show: settings.show === undefined ? "true" : settings.show,
|
||||||
|
instructions: settings.instructions.map((instruction: string) => path.join(commandDir, commandSubDir, instruction))
|
||||||
|
};
|
||||||
|
this.commands.push(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -71,7 +80,7 @@ class CustomCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public handleCommand(commandName: string): string {
|
public handleCommand(commandName: string, userInput: string): string {
|
||||||
// 获取命令对象,这里假设您已经有一个方法或属性可以获取到命令对象
|
// 获取命令对象,这里假设您已经有一个方法或属性可以获取到命令对象
|
||||||
const command = this.getCommand(commandName);
|
const command = this.getCommand(commandName);
|
||||||
if (!command) {
|
if (!command) {
|
||||||
@ -80,13 +89,39 @@ class CustomCommands {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建instructions列表字符串
|
let commandMessage = command.message;
|
||||||
|
if (userInput && userInput.length > 0) {
|
||||||
|
// userInput is "['aa', 'bb]" like string
|
||||||
|
// parse userInput to array
|
||||||
|
// handle eval exception
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userInputArray = eval(userInput);
|
||||||
|
|
||||||
|
// replace command message $1 with userInputArray[0], $2 with userInputArray[1] and so on
|
||||||
|
for (let i = 0; i < userInputArray.length; i++) {
|
||||||
|
commandMessage = commandMessage.replace(`$${i + 1}`, userInputArray[i]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.channel()?.error(`Failed to parse user input: ${userInput} error: ${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace ${Name} with enviroment var Name
|
||||||
|
const envVarRegex = /\${(\w+)}/g;
|
||||||
|
commandMessage = commandMessage.replace(envVarRegex, (match, p1) => {
|
||||||
|
return process.env[p1] || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// build instrctions
|
||||||
const instructions = command!.instructions
|
const instructions = command!.instructions
|
||||||
.map((instruction: string) => `[instruction|./.chat/workflows/${command.name}/${instruction}]`)
|
.map((instruction: string) => `[instruction|${instruction}]`)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
// 返回结果字符串
|
// 返回结果字符串
|
||||||
return `${instructions} ${command!.message}`;
|
return `${instructions} ${commandMessage}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,20 +23,24 @@ describe('CustomCommands', () => {
|
|||||||
// Mock the file system with two directories, one with _setting_.json and one without
|
// Mock the file system with two directories, one with _setting_.json and one without
|
||||||
mockFs({
|
mockFs({
|
||||||
'workflows': {
|
'workflows': {
|
||||||
'command1': {
|
"some": {
|
||||||
'_setting_.json': JSON.stringify({
|
"command": {
|
||||||
pattern: 'command1',
|
'command1': {
|
||||||
description: 'Command 1',
|
'_setting_.json': JSON.stringify({
|
||||||
message: 'Command 1 message',
|
pattern: 'command1',
|
||||||
default: false,
|
description: 'Command 1',
|
||||||
show: true,
|
message: 'Command 1 message',
|
||||||
instructions: ['instruction1', 'instruction2'],
|
default: false,
|
||||||
}),
|
show: true,
|
||||||
},
|
instructions: ['instruction1', 'instruction2'],
|
||||||
'command2': {
|
}),
|
||||||
// No _setting_.json file
|
},
|
||||||
},
|
'command2': {
|
||||||
},
|
// No _setting_.json file
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const workflowsDir = path.join(process.cwd(), 'workflows');
|
const workflowsDir = path.join(process.cwd(), 'workflows');
|
||||||
@ -54,6 +58,7 @@ describe('CustomCommands', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
expectedResult[0].instructions = [path.join(workflowsDir, 'some', 'command', 'command1', 'instruction1'), path.join(workflowsDir, 'some', 'command', 'command1', 'instruction2')];
|
||||||
expect(customCommands['commands']).to.deep.equal(expectedResult);
|
expect(customCommands['commands']).to.deep.equal(expectedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,7 +105,23 @@ describe('CustomCommands', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
customCommands.regCommand(command);
|
customCommands.regCommand(command);
|
||||||
const result = customCommands.handleCommand('test');
|
const result = customCommands.handleCommand('test', '');
|
||||||
expect(result).to.equal('[instruction|./.chat/workflows/test/instruction1] [instruction|./.chat/workflows/test/instruction2] Test message');
|
expect(result).to.equal('[instruction|instruction1] [instruction|instruction2] Test message');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle a custom command with args', () => {
|
||||||
|
const command: Command = {
|
||||||
|
name: 'test',
|
||||||
|
pattern: 'test {{prompt}}',
|
||||||
|
description: 'Test command',
|
||||||
|
message: 'Test message "$1","$2"',
|
||||||
|
default: false,
|
||||||
|
show: true,
|
||||||
|
instructions: ['instruction1', 'instruction2'],
|
||||||
|
};
|
||||||
|
|
||||||
|
customCommands.regCommand(command);
|
||||||
|
const result = customCommands.handleCommand('test', '["v1", "v2"]');
|
||||||
|
expect(result).to.equal('[instruction|instruction1] [instruction|instruction2] Test message "v1","v2"');
|
||||||
});
|
});
|
||||||
});
|
});
|
Loading…
x
Reference in New Issue
Block a user