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
This commit is contained in:
parent
a11b38c5a3
commit
7f2a869f3f
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,3 +5,8 @@ node_modules
|
|||||||
*.vsix
|
*.vsix
|
||||||
.env
|
.env
|
||||||
.chatconfig.json
|
.chatconfig.json
|
||||||
|
|
||||||
|
# DevChat
|
||||||
|
.chat/prompts.graphml
|
||||||
|
.chat/prompts.db
|
||||||
|
|
||||||
|
47
package.json
47
package.json
@ -13,8 +13,9 @@
|
|||||||
"main": "./dist/extension.js",
|
"main": "./dist/extension.js",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/*",
|
"dist/*",
|
||||||
"bin/*",
|
"bin/*",
|
||||||
"assets/*",
|
"assets/*",
|
||||||
|
"workflows/*",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md"
|
"README.md"
|
||||||
],
|
],
|
||||||
@ -30,16 +31,16 @@
|
|||||||
],
|
],
|
||||||
"description": "Select whose llm to use."
|
"description": "Select whose llm to use."
|
||||||
},
|
},
|
||||||
"DevChat.maxLogCount": {
|
"DevChat.maxLogCount": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 20,
|
"default": 20,
|
||||||
"description": "Limit the number of prompts to output"
|
"description": "Limit the number of prompts to output"
|
||||||
},
|
},
|
||||||
"DevChat.logSkip": {
|
"DevChat.logSkip": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"description": "Skip number prompts before showing the prompt history"
|
"description": "Skip number prompts before showing the prompt history"
|
||||||
},
|
},
|
||||||
"DevChat.OpenAI.model": {
|
"DevChat.OpenAI.model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "gpt-4",
|
"default": "gpt-4",
|
||||||
@ -58,18 +59,18 @@
|
|||||||
"description": "Specify llm stream",
|
"description": "Specify llm stream",
|
||||||
"when": "DevChat.llmModel == 'OpenAI'"
|
"when": "DevChat.llmModel == 'OpenAI'"
|
||||||
},
|
},
|
||||||
"DevChat.OpenAI.tokensPerPrompt": {
|
"DevChat.OpenAI.tokensPerPrompt": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 6000,
|
"default": 6000,
|
||||||
"description": "token for each prompt",
|
"description": "token for each prompt",
|
||||||
"when": "DevChat.llmModel == 'OpenAI'"
|
"when": "DevChat.llmModel == 'OpenAI'"
|
||||||
},
|
},
|
||||||
"DevChat.OpenAI.useHistoryPrompt": {
|
"DevChat.OpenAI.useHistoryPrompt": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
"description": "use history prompts as context",
|
"description": "use history prompts as context",
|
||||||
"when": "DevChat.llmModel == 'OpenAI'"
|
"when": "DevChat.llmModel == 'OpenAI'"
|
||||||
},
|
},
|
||||||
"DevChat.OpenAI.apiKey": {
|
"DevChat.OpenAI.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
|
@ -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 {
|
export interface Command {
|
||||||
name: string;
|
name: string;
|
||||||
pattern: string;
|
pattern: string;
|
||||||
description: string;
|
description: string;
|
||||||
handler: (userInput: string) => Promise<string>;
|
handler: (commandName: string, userInput: string) => Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandManager {
|
class CommandManager {
|
||||||
@ -24,7 +30,22 @@ export interface Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCommandList(): 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<string> {
|
async processText(text: string): Promise<string> {
|
||||||
@ -38,7 +59,7 @@ export interface Command {
|
|||||||
const replacements = await Promise.all(
|
const replacements = await Promise.all(
|
||||||
matches.map(async (match) => {
|
matches.map(async (match) => {
|
||||||
const matchedUserInput = match[1];
|
const matchedUserInput = match[1];
|
||||||
return await commandObj.handler(matchedUserInput);
|
return await commandObj.handler(commandObj.name, matchedUserInput);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
let result = text;
|
let result = text;
|
||||||
@ -50,7 +71,7 @@ export interface Command {
|
|||||||
|
|
||||||
// 处理所有命令
|
// 处理所有命令
|
||||||
let result = text;
|
let result = text;
|
||||||
for (const commandObj of this.commands) {
|
for (const commandObj of this.getCommandList()) {
|
||||||
result = await processCommand(commandObj, result);
|
result = await processCommand(commandObj, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<void> {
|
|
||||||
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';
|
|
||||||
},
|
|
||||||
};
|
|
81
src/command/customCommand.ts
Normal file
81
src/command/customCommand.ts
Normal file
@ -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;
|
@ -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}`;
|
|
||||||
},
|
|
||||||
};
|
|
@ -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}`;
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,11 +1,6 @@
|
|||||||
import CommandManager from './commandManager';
|
import CommandManager from './commandManager';
|
||||||
import { exampleCommand1 } from './exampleCommand1';
|
|
||||||
import { exampleCommand2 } from './exampleCommand2';
|
|
||||||
import { commitMessageCommand } from './commitMessageCommand';
|
|
||||||
|
|
||||||
const commandManager = CommandManager.getInstance();
|
const commandManager = CommandManager.getInstance();
|
||||||
|
|
||||||
// 注册命令
|
// 注册命令
|
||||||
commandManager.registerCommand(exampleCommand1);
|
|
||||||
commandManager.registerCommand(exampleCommand2);
|
|
||||||
commandManager.registerCommand(commitMessageCommand);
|
|
||||||
|
@ -16,7 +16,7 @@ function activate(context: vscode.ExtensionContext) {
|
|||||||
ExtensionContextHolder.context = context;
|
ExtensionContextHolder.context = context;
|
||||||
logger.init(context);
|
logger.init(context);
|
||||||
|
|
||||||
// 创建 .chat 目录并复制 instructions
|
// 创建 .chat 目录并复制 workflows
|
||||||
createChatDirectoryAndCopyInstructionsSync(context.extensionUri);
|
createChatDirectoryAndCopyInstructionsSync(context.extensionUri);
|
||||||
|
|
||||||
registerOpenChatPanelCommand(context);
|
registerOpenChatPanelCommand(context);
|
||||||
|
@ -5,6 +5,32 @@ import * as ncp from 'ncp';
|
|||||||
|
|
||||||
import { logger } from '../util/logger';
|
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) {
|
export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) {
|
||||||
|
|
||||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
@ -14,7 +40,7 @@ export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.
|
|||||||
|
|
||||||
const workspaceRoot = workspaceFolders[0].uri.fsPath;
|
const workspaceRoot = workspaceFolders[0].uri.fsPath;
|
||||||
const chatDirPath = path.join(workspaceRoot, '.chat');
|
const chatDirPath = path.join(workspaceRoot, '.chat');
|
||||||
const instructionsSrcPath = path.join(extensionUri.fsPath, 'instructions');
|
const instructionsSrcPath = path.join(extensionUri.fsPath, 'workflows');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 检查 .chat 目录是否存在,如果不存在,则创建它
|
// 检查 .chat 目录是否存在,如果不存在,则创建它
|
||||||
@ -24,15 +50,10 @@ export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将 instructions 目录复制到 .chat 目录中
|
// 将 workflows 目录复制到 .chat 目录中
|
||||||
ncp.ncp(instructionsSrcPath, path.join(chatDirPath, 'instructions'), (err) => {
|
copyDirSync(instructionsSrcPath, path.join(chatDirPath, 'workflows'));
|
||||||
if (err) {
|
|
||||||
logger.channel()?.error('Error copying instructions:', err);
|
|
||||||
logger.channel()?.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} 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();
|
logger.channel()?.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,14 @@
|
|||||||
// chatPanel.ts
|
// chatPanel.ts
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
import '../handler/loadHandlers';
|
import '../handler/loadHandlers';
|
||||||
import handleMessage from '../handler/messageHandler';
|
import handleMessage from '../handler/messageHandler';
|
||||||
import WebviewManager from './webviewManager';
|
import WebviewManager from './webviewManager';
|
||||||
|
|
||||||
import messageHistory from '../util/messageHistory';
|
import messageHistory from '../util/messageHistory';
|
||||||
|
import CustomCommands from '../command/customCommand';
|
||||||
|
import CommandManager from '../command/commandManager';
|
||||||
|
|
||||||
export default class ChatPanel {
|
export default class ChatPanel {
|
||||||
private static _instance: ChatPanel | undefined;
|
private static _instance: ChatPanel | undefined;
|
||||||
@ -14,6 +17,12 @@ export default class ChatPanel {
|
|||||||
private _disposables: vscode.Disposable[] = [];
|
private _disposables: vscode.Disposable[] = [];
|
||||||
|
|
||||||
public static createOrShow(extensionUri: vscode.Uri) {
|
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) {
|
if (ChatPanel._instance) {
|
||||||
ChatPanel._instance._panel.reveal();
|
ChatPanel._instance._panel.reveal();
|
||||||
} else {
|
} else {
|
||||||
|
7
workflows/code/_setting_.json
Normal file
7
workflows/code/_setting_.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pattern": "code",
|
||||||
|
"description": "work for generate code",
|
||||||
|
"message": "",
|
||||||
|
"default": true,
|
||||||
|
"instructions": ["instruct.txt", "python.txt"]
|
||||||
|
}
|
23
workflows/code/instruct.txt
Normal file
23
workflows/code/instruct.txt
Normal file
@ -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 <context> 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.
|
1
workflows/code/python.txt
Normal file
1
workflows/code/python.txt
Normal file
@ -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.
|
7
workflows/commit_message/_setting_.json
Normal file
7
workflows/commit_message/_setting_.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pattern": "commit_message",
|
||||||
|
"description": "generate commit message",
|
||||||
|
"message": "write a commit message",
|
||||||
|
"default": false,
|
||||||
|
"instructions": ["instruct.txt"]
|
||||||
|
}
|
9
workflows/commit_message/instruct.txt
Normal file
9
workflows/commit_message/instruct.txt
Normal file
@ -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 <context>, 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.
|
Loading…
x
Reference in New Issue
Block a user