From 7f2a869f3f39abe628e738ae0ff1ba807f06386a Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Wed, 10 May 2023 14:31:17 +0800 Subject: [PATCH] Update .gitignore and refactor code - Add DevChat related files to .gitignore - Refactor package.json and settings - Update commandManager.ts and remove example commands - Implement customCommand.ts for handling custom commands - Remove commitMessageCommand.ts and example commands - Update chatConfig.ts and chatPanel.ts for new command handling - Add new workflows for code and commit_message --- .gitignore | 5 ++ package.json | 47 +++++++------- src/command/commandManager.ts | 29 +++++++-- src/command/commitMessageCommand.ts | 41 ------------- src/command/customCommand.ts | 81 +++++++++++++++++++++++++ src/command/exampleCommand1.ts | 10 --- src/command/exampleCommand2.ts | 10 --- src/command/loadCommands.ts | 7 +-- src/extension.ts | 2 +- src/init/chatConfig.ts | 39 +++++++++--- src/panel/chatPanel.ts | 9 +++ workflows/code/_setting_.json | 7 +++ workflows/code/instruct.txt | 23 +++++++ workflows/code/python.txt | 1 + workflows/commit_message/_setting_.json | 7 +++ workflows/commit_message/instruct.txt | 9 +++ 16 files changed, 223 insertions(+), 104 deletions(-) delete mode 100644 src/command/commitMessageCommand.ts create mode 100644 src/command/customCommand.ts delete mode 100644 src/command/exampleCommand1.ts delete mode 100644 src/command/exampleCommand2.ts create mode 100644 workflows/code/_setting_.json create mode 100644 workflows/code/instruct.txt create mode 100644 workflows/code/python.txt create mode 100644 workflows/commit_message/_setting_.json create mode 100644 workflows/commit_message/instruct.txt diff --git a/.gitignore b/.gitignore index badad5f..c4dfc0d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ node_modules *.vsix .env .chatconfig.json + +# DevChat +.chat/prompts.graphml +.chat/prompts.db + diff --git a/package.json b/package.json index 4278b52..78559a1 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,9 @@ "main": "./dist/extension.js", "files": [ "dist/*", - "bin/*", + "bin/*", "assets/*", + "workflows/*", "LICENSE", "README.md" ], @@ -30,16 +31,16 @@ ], "description": "Select whose llm to use." }, - "DevChat.maxLogCount": { - "type": "number", - "default": 20, - "description": "Limit the number of prompts to output" - }, - "DevChat.logSkip": { - "type": "number", - "default": 0, - "description": "Skip number prompts before showing the prompt history" - }, + "DevChat.maxLogCount": { + "type": "number", + "default": 20, + "description": "Limit the number of prompts to output" + }, + "DevChat.logSkip": { + "type": "number", + "default": 0, + "description": "Skip number prompts before showing the prompt history" + }, "DevChat.OpenAI.model": { "type": "string", "default": "gpt-4", @@ -58,18 +59,18 @@ "description": "Specify llm stream", "when": "DevChat.llmModel == 'OpenAI'" }, - "DevChat.OpenAI.tokensPerPrompt": { - "type": "number", - "default": 6000, - "description": "token for each prompt", - "when": "DevChat.llmModel == 'OpenAI'" - }, - "DevChat.OpenAI.useHistoryPrompt": { - "type": "boolean", - "default": true, - "description": "use history prompts as context", - "when": "DevChat.llmModel == 'OpenAI'" - }, + "DevChat.OpenAI.tokensPerPrompt": { + "type": "number", + "default": 6000, + "description": "token for each prompt", + "when": "DevChat.llmModel == 'OpenAI'" + }, + "DevChat.OpenAI.useHistoryPrompt": { + "type": "boolean", + "default": true, + "description": "use history prompts as context", + "when": "DevChat.llmModel == 'OpenAI'" + }, "DevChat.OpenAI.apiKey": { "type": "string", "default": "", diff --git a/src/command/commandManager.ts b/src/command/commandManager.ts index 5fbeb6c..f1ee7b1 100644 --- a/src/command/commandManager.ts +++ b/src/command/commandManager.ts @@ -1,8 +1,14 @@ +import { vs } from "react-syntax-highlighter/dist/esm/styles/hljs"; +import CustomCommands from "./customCommand"; +import { logger } from "../util/logger"; +import * as vscode from 'vscode'; +import * as path from 'path'; + export interface Command { name: string; pattern: string; description: string; - handler: (userInput: string) => Promise; + handler: (commandName: string, userInput: string) => Promise; } class CommandManager { @@ -24,7 +30,22 @@ export interface Command { } getCommandList(): Command[] { - return this.commands; + // load commands from CustomCommands + let newCommands: Command[] = [...this.commands]; + const customCommands = CustomCommands.getInstance(); + const commands = customCommands.getCommands(); + commands.forEach(command => { + const commandObj: Command = { + name: command.name, + pattern: command.pattern, + description: command.description, + handler: async (commandName: string, userInput: string) => { + return CustomCommands.getInstance().handleCommand(commandName); + } + }; + newCommands.push(commandObj); + }); + return newCommands; } async processText(text: string): Promise { @@ -38,7 +59,7 @@ export interface Command { const replacements = await Promise.all( matches.map(async (match) => { const matchedUserInput = match[1]; - return await commandObj.handler(matchedUserInput); + return await commandObj.handler(commandObj.name, matchedUserInput); }) ); let result = text; @@ -50,7 +71,7 @@ export interface Command { // 处理所有命令 let result = text; - for (const commandObj of this.commands) { + for (const commandObj of this.getCommandList()) { result = await processCommand(commandObj, result); } diff --git a/src/command/commitMessageCommand.ts b/src/command/commitMessageCommand.ts deleted file mode 100644 index f1d3752..0000000 --- a/src/command/commitMessageCommand.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Command} from './commandManager'; - -import { exec } from 'child_process'; -import * as vscode from 'vscode'; - -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { promisify } from 'util'; -import { createTempSubdirectory } from '../util/commonUtil'; -import { logger } from '../util/logger'; - -const mkdirAsync = promisify(fs.mkdir); -const execAsync = promisify(exec); -const writeFileAsync = promisify(fs.writeFile); - - -async function createTempDirectory(tempDir: string): Promise { - try { - await mkdirAsync(tempDir, {recursive: true}); - } catch (err) { - logger.channel()?.error(`Error creating temporary directory: ${err}`); - logger.channel()?.show(); - } -} - -export const commitMessageCommand: Command = { - name: 'commitMessageCommand', - pattern: 'commit_meesage', - description: 'commit message for changes', - handler: async (userInput: string) => { - const tempDir = createTempSubdirectory('devchat/context'); - - const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath; - if (workspaceDir) { - const commitmessageInstruction = path.join(workspaceDir, '.chat', 'instructions', 'commit_message', 'instCommitMessage.txt'); - return `[instruction|${commitmessageInstruction}] Write a commit message`; - } - return 'Write a commit message'; - }, -}; diff --git a/src/command/customCommand.ts b/src/command/customCommand.ts new file mode 100644 index 0000000..819daf2 --- /dev/null +++ b/src/command/customCommand.ts @@ -0,0 +1,81 @@ +import fs from 'fs'; +import path from 'path'; +import { logger } from '../util/logger'; + +interface Command { + name: string; + pattern: string; + description: string; + message: string; + default: boolean; + instructions: string[]; +} + +class CustomCommands { + private static instance: CustomCommands | null = null; + private commands: Command[] = []; + + private constructor() { + } + + public static getInstance(): CustomCommands { + if (!CustomCommands.instance) { + CustomCommands.instance = new CustomCommands(); + } + return CustomCommands.instance; + } + + public parseCommands(workflowsDir: string): void { + this.commands = []; + + const subDirs = fs.readdirSync(workflowsDir, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name); + + for (const dir of subDirs) { + const settingsPath = path.join(workflowsDir, dir, '_setting_.json'); + if (fs.existsSync(settingsPath)) { + const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); + const command: Command = { + name: dir, + pattern: settings.pattern, + description: settings.description, + message: settings.message, + default: settings.default, + instructions: settings.instructions + }; + this.commands.push(command); + } + } + } + + public getCommands(): Command[] { + return this.commands; + } + + public getCommand(commandName: string): Command | null { + const foundCommand = this.commands.find(command => command.name === commandName); + return foundCommand ? foundCommand : null; + } + + + public handleCommand(commandName: string): string { + // 获取命令对象,这里假设您已经有一个方法或属性可以获取到命令对象 + const command = this.getCommand(commandName); + if (!command) { + logger.channel()?.error(`Command ${commandName} not found!`); + logger.channel()?.show(); + return ''; + } + + // 构建instructions列表字符串 + const instructions = command!.instructions + .map((instruction: string) => `[instruction|./workflows/${command.name}/${instruction}]`) + .join(' '); + + // 返回结果字符串 + return `${instructions} ${command!.message}`; + } +} + +export default CustomCommands; diff --git a/src/command/exampleCommand1.ts b/src/command/exampleCommand1.ts deleted file mode 100644 index df00100..0000000 --- a/src/command/exampleCommand1.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Command} from './commandManager'; - -export const exampleCommand1: Command = { - name: 'exampleCommand1', - pattern: 'example: command1 {{prompt}}', - description: '这是一个示例命令1', - handler: async (userInput: string) => { - return `示例命令1处理了以下文本:${userInput}`; - }, -}; diff --git a/src/command/exampleCommand2.ts b/src/command/exampleCommand2.ts deleted file mode 100644 index 2da3546..0000000 --- a/src/command/exampleCommand2.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Command} from './commandManager'; - -export const exampleCommand2: Command = { - name: 'exampleCommand2', - pattern: 'example: command2 {{prompt}}', - description: '这是一个示例命令2', - handler: async (userInput: string) => { - return `示例命令2处理了以下文本:${userInput}`; - }, -}; diff --git a/src/command/loadCommands.ts b/src/command/loadCommands.ts index c5724dd..e4dab74 100644 --- a/src/command/loadCommands.ts +++ b/src/command/loadCommands.ts @@ -1,11 +1,6 @@ import CommandManager from './commandManager'; -import { exampleCommand1 } from './exampleCommand1'; -import { exampleCommand2 } from './exampleCommand2'; -import { commitMessageCommand } from './commitMessageCommand'; const commandManager = CommandManager.getInstance(); // 注册命令 -commandManager.registerCommand(exampleCommand1); -commandManager.registerCommand(exampleCommand2); -commandManager.registerCommand(commitMessageCommand); \ No newline at end of file + diff --git a/src/extension.ts b/src/extension.ts index ae21273..c9c61ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,7 +16,7 @@ function activate(context: vscode.ExtensionContext) { ExtensionContextHolder.context = context; logger.init(context); - // 创建 .chat 目录并复制 instructions + // 创建 .chat 目录并复制 workflows createChatDirectoryAndCopyInstructionsSync(context.extensionUri); registerOpenChatPanelCommand(context); diff --git a/src/init/chatConfig.ts b/src/init/chatConfig.ts index 7720e19..f5eb15e 100644 --- a/src/init/chatConfig.ts +++ b/src/init/chatConfig.ts @@ -5,6 +5,32 @@ import * as ncp from 'ncp'; import { logger } from '../util/logger'; + +function copyFileSync(source: string, target: string) { + const data = fs.readFileSync(source); + fs.writeFileSync(target, data); + } + + function copyDirSync(source: string, target: string) { + // 创建目标目录 + fs.mkdirSync(target, { recursive: true }); + + // 遍历目录中的所有文件和子目录 + const files = fs.readdirSync(source); + for (const file of files) { + const sourcePath = path.join(source, file); + const targetPath = path.join(target, file); + const stats = fs.statSync(sourcePath); + if (stats.isDirectory()) { + // 递归拷贝子目录 + copyDirSync(sourcePath, targetPath); + } else { + // 拷贝文件 + copyFileSync(sourcePath, targetPath); + } + } + } + export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) { const workspaceFolders = vscode.workspace.workspaceFolders; @@ -14,7 +40,7 @@ export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode. const workspaceRoot = workspaceFolders[0].uri.fsPath; const chatDirPath = path.join(workspaceRoot, '.chat'); - const instructionsSrcPath = path.join(extensionUri.fsPath, 'instructions'); + const instructionsSrcPath = path.join(extensionUri.fsPath, 'workflows'); try { // 检查 .chat 目录是否存在,如果不存在,则创建它 @@ -24,15 +50,10 @@ export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode. return; } - // 将 instructions 目录复制到 .chat 目录中 - ncp.ncp(instructionsSrcPath, path.join(chatDirPath, 'instructions'), (err) => { - if (err) { - logger.channel()?.error('Error copying instructions:', err); - logger.channel()?.show(); - } - }); + // 将 workflows 目录复制到 .chat 目录中 + copyDirSync(instructionsSrcPath, path.join(chatDirPath, 'workflows')); } catch (error) { - logger.channel()?.error('Error creating .chat directory and copying instructions:', error); + logger.channel()?.error('Error creating .chat directory and copying workflows:', error); logger.channel()?.show(); } } \ No newline at end of file diff --git a/src/panel/chatPanel.ts b/src/panel/chatPanel.ts index 5f7ae06..9b9213e 100644 --- a/src/panel/chatPanel.ts +++ b/src/panel/chatPanel.ts @@ -1,11 +1,14 @@ // chatPanel.ts import * as vscode from 'vscode'; +import * as path from 'path'; import '../handler/loadHandlers'; import handleMessage from '../handler/messageHandler'; import WebviewManager from './webviewManager'; import messageHistory from '../util/messageHistory'; +import CustomCommands from '../command/customCommand'; +import CommandManager from '../command/commandManager'; export default class ChatPanel { private static _instance: ChatPanel | undefined; @@ -14,6 +17,12 @@ export default class ChatPanel { private _disposables: vscode.Disposable[] = []; public static createOrShow(extensionUri: vscode.Uri) { + const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath; + if (workspaceDir) { + const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows'); + CustomCommands.getInstance().parseCommands(workflowsDir); + } + if (ChatPanel._instance) { ChatPanel._instance._panel.reveal(); } else { diff --git a/workflows/code/_setting_.json b/workflows/code/_setting_.json new file mode 100644 index 0000000..aeacb58 --- /dev/null +++ b/workflows/code/_setting_.json @@ -0,0 +1,7 @@ +{ + "pattern": "code", + "description": "work for generate code", + "message": "", + "default": true, + "instructions": ["instruct.txt", "python.txt"] +} \ No newline at end of file diff --git a/workflows/code/instruct.txt b/workflows/code/instruct.txt new file mode 100644 index 0000000..5105aca --- /dev/null +++ b/workflows/code/instruct.txt @@ -0,0 +1,23 @@ +As a software developer assistant, your tasks are to: + +- Provide a clear and concise response to address the user's requirements. +- Write code and give advice based on given code or information in the if provided. +- Follow language-specific best practices and common coding standards. + +When responding: + +1. Summarize and describe the requirements or provided information in your own words. +2. The summary and description should better be written in bullet points (excluding code). +3. When modifying the provided code, include the entire modified functions, but exclude any unmodified functions. + If any global statements are changed, include the full global statements; otherwise, do not include them. +4. Enclose code or changes within blocks using triple backticks (```), and include the programming language and the file path, if available. For example: +``` +```python path=./path/to/file.py +print("Hello, World!") +``` +``` +If no file paths or folder structure are provided and you are unsure about the file path of the code, you may omit the file path. +5. Use separate code blocks for different files. +6. Utilize the previous messages, if provided in the end of this prompt, to create your response. Note that not all previous messages are necessarily relevant. +7. When providing a suggestion or instruction, begin by explaining the reason behind it. +8. If you need more information, ask for it. diff --git a/workflows/code/python.txt b/workflows/code/python.txt new file mode 100644 index 0000000..5742151 --- /dev/null +++ b/workflows/code/python.txt @@ -0,0 +1 @@ +When writing Python code, include type hints where appropriate and maintain compliance with PEP-8 guidelines, such as providing docstrings for modules, classes, and functions. diff --git a/workflows/commit_message/_setting_.json b/workflows/commit_message/_setting_.json new file mode 100644 index 0000000..edc927b --- /dev/null +++ b/workflows/commit_message/_setting_.json @@ -0,0 +1,7 @@ +{ + "pattern": "commit_message", + "description": "generate commit message", + "message": "write a commit message", + "default": false, + "instructions": ["instruct.txt"] +} \ No newline at end of file diff --git a/workflows/commit_message/instruct.txt b/workflows/commit_message/instruct.txt new file mode 100644 index 0000000..5c63e93 --- /dev/null +++ b/workflows/commit_message/instruct.txt @@ -0,0 +1,9 @@ +As a software developer assistant, your task is to provide clear and concise responses and write commit messages based on given code, requirements, or conversations. Follow these guidelines: + +1. A commit message should include a title and multiple body lines. +2. Adhere to best practices, such as keeping titles under 50 characters and limiting body lines to under 72 characters. +3. Enclose messages in code blocks using triple backticks (```), and flag code type as commitmsg. +4. Utilize the , if provided, to create the summary. +5. Utilize the previous messages, if provided in the end of this prompt, to create the summary. Note that not all previous messages are necessarily relevant. + +If you need more information, feel free to ask.